commit e60f22833af9050cafef5ab00931c0c69867130a Author: Arlo Breault arlolra@gmail.com Date: Sun Jul 14 09:41:51 2019 +0200
Reimagine the badge
Trac 27385 --- .gitignore | 4 + proxy/init-badge.js | 125 +++++++++++++++++---- proxy/init-testing.js | 83 ++++++++++++++ proxy/init-webext.js | 106 ++++++++++++++++- proxy/make.js | 22 ++-- proxy/static/.htaccess | 1 - proxy/{webext/popup.css => static/embed.css} | 7 +- proxy/static/embed.html | 72 ++++-------- .../icons/arrowhead-right-12.svg | 0 proxy/{webext => static}/icons/status-off.png | Bin proxy/{webext => static}/icons/status-off.svg | 0 proxy/{webext => static}/icons/status-off@2x.png | Bin proxy/{webext => static}/icons/status-off@3x.png | Bin proxy/{webext => static}/icons/status-on.png | Bin proxy/{webext => static}/icons/status-on.svg | 0 proxy/{webext => static}/icons/status-on@2x.png | Bin proxy/{webext => static}/icons/status-on@3x.png | Bin proxy/{webext => static}/icons/status-running.png | Bin proxy/{webext => static}/icons/status-running.svg | 0 proxy/static/index.css | 5 + proxy/static/index.html | 11 ++ proxy/static/koch.jpg | Bin 131664 -> 0 bytes proxy/static/popup.js | 28 +++++ proxy/static/snowflake.html | 60 ---------- proxy/ui.js | 118 ------------------- proxy/util.js | 24 ++-- proxy/webext/{popup.js => embed.js} | 29 +---- proxy/webext/manifest.json | 32 +++--- proxy/webext/popup.html | 25 ----- 29 files changed, 401 insertions(+), 351 deletions(-)
diff --git a/.gitignore b/.gitignore index a187011..315500c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,5 +15,9 @@ proxy/build proxy/node_modules proxy/spec/support proxy/webext/snowflake.js +proxy/webext/popup.js +proxy/webext/embed.html +proxy/webext/embed.css +proxy/webext/icons/ ignore/ npm-debug.log diff --git a/proxy/init-badge.js b/proxy/init-badge.js index 8646bc4..f85be29 100644 --- a/proxy/init-badge.js +++ b/proxy/init-badge.js @@ -1,18 +1,71 @@ /* global TESTING, Util, Params, Config, DebugUI, BadgeUI, UI, Broker, Snowflake */
/* -Entry point. +UI */
-var snowflake, query, debug, silenceNotifications, log, dbg, init; +class BadgeUI extends UI {
-(function() { + constructor() { + super(); + this.popup = new Popup(); + } + + setStatus() {} + + missingFeature(missing) { + this.popup.setImgSrc('off'); + this.popup.setStatusText("Snowflake is off"); + this.popup.setStatusDesc(missing, 'firebrick'); + this.popup.hideButton(); + }
- if (((typeof TESTING === "undefined" || TESTING === null) || !TESTING) && !Util.featureDetect()) { - console.log('webrtc feature not detected. shutting down'); - return; + turnOn() { + const clients = this.active ? 1 : 0; + this.popup.setChecked(true); + this.popup.setToggleText('Turn Off'); + this.popup.setStatusText(`${clients} client${(clients !== 1) ? 's' : ''} connected.`); + // FIXME: Share stats from webext + const total = 0; + this.popup.setStatusDesc(`Your snowflake has helped ${total} user${(total !== 1) ? 's' : ''} circumvent censorship in the last 24 hours.`); + this.popup.setImgSrc(this.active ? "running" : "on"); }
+ turnOff() { + this.popup.setChecked(false); + this.popup.setToggleText('Turn On'); + this.popup.setStatusText("Snowflake is off"); + this.popup.setStatusDesc(""); + this.popup.setImgSrc("off"); + } + + setActive(connected) { + super.setActive(connected); + turnOn(); + } + +} + +BadgeUI.prototype.popup = null; + + +/* +Entry point. +*/ + +// Defaults to opt-in. +var COOKIE_NAME = "snowflake-allow"; +var COOKIE_LIFETIME = "Thu, 01 Jan 2038 00:00:00 GMT"; +var COOKIE_EXPIRE = "Thu, 01 Jan 1970 00:00:01 GMT"; + +function setSnowflakeCookie(val, expires) { + document.cookie = `${COOKIE_NAME}=${val}; path=/; expires=${expires};`; +} + +var debug, snowflake, config, broker, ui, log, dbg, init, update, silenceNotifications, query; + +(function() { + snowflake = null;
query = new URLSearchParams(location.search); @@ -35,32 +88,56 @@ var snowflake, query, debug, silenceNotifications, log, dbg, init; } };
+ update = function() { + const cookies = Parse.cookie(document.cookie); + if (cookies[COOKIE_NAME] === '1') { + ui.turnOn(); + dbg('Contacting Broker at ' + broker.url); + log('Starting snowflake'); + snowflake.setRelayAddr(config.relayAddr); + snowflake.beginWebRTC(); + } else { + ui.turnOff(); + snowflake.disable(); + log('Currently not active.'); + } + }; + init = function() { - var broker, config, ui; + ui = new BadgeUI(); + + if (!Util.hasWebRTC()) { + ui.missingFeature("WebRTC feature is not detected."); + return; + } + + if (!Util.hasCookies()) { + ui.missingFeature("Cookies are not enabled."); + return; + } + + if (Util.mightBeTBB()) { + ui.missingFeature("Will not run within Tor Browser."); + return; + } + config = new Config; if ('off' !== query.get('ratelimit')) { config.rateLimitBytes = Params.getByteCount(query, 'ratelimit', config.rateLimitBytes); } - ui = null; - if (document.getElementById('badge') !== null) { - ui = new BadgeUI(); - } else if (document.getElementById('status') !== null) { - ui = new DebugUI(); - } else { - ui = new UI(); - } broker = new Broker(config.brokerUrl); snowflake = new Snowflake(config, ui, broker); log('== snowflake proxy =='); - if (Util.snowflakeIsDisabled(config.cookieName)) { - // Do not activate the proxy if any number of conditions are true. - log('Currently not active.'); - return; - } - // Otherwise, begin setting up WebRTC and acting as a proxy. - dbg('Contacting Broker at ' + broker.url); - snowflake.setRelayAddr(config.relayAddr); - return snowflake.beginWebRTC(); + update(); + + document.getElementById('enabled').addEventListener('change', (event) => { + if (event.target.checked) { + setSnowflakeCookie('1', COOKIE_LIFETIME); + } else { + setSnowflakeCookie('', COOKIE_EXPIRE); + } + update(); + }) };
// Notification of closing tab with active proxy. diff --git a/proxy/init-testing.js b/proxy/init-testing.js new file mode 100644 index 0000000..003f2b6 --- /dev/null +++ b/proxy/init-testing.js @@ -0,0 +1,83 @@ +/* global TESTING, Util, Params, Config, DebugUI, UI, Broker, Snowflake */ + +/* +Entry point. +*/ + +var snowflake, query, debug, silenceNotifications, log, dbg, init; + +(function() { + + if (((typeof TESTING === "undefined" || TESTING === null) || !TESTING) && !Util.featureDetect()) { + console.log('webrtc feature not detected. shutting down'); + return; + } + + snowflake = null; + + query = new URLSearchParams(location.search); + + debug = Params.getBool(query, 'debug', false); + + silenceNotifications = Params.getBool(query, 'silent', false); + + // Log to both console and UI if applicable. + // Requires that the snowflake and UI objects are hooked up in order to + // log to console. + log = function(msg) { + console.log('Snowflake: ' + msg); + return snowflake != null ? snowflake.ui.log(msg) : void 0; + }; + + dbg = function(msg) { + if (debug || ((snowflake != null ? snowflake.ui : void 0) instanceof DebugUI)) { + return log(msg); + } + }; + + init = function() { + var broker, config, ui; + config = new Config; + if ('off' !== query['ratelimit']) { + config.rateLimitBytes = Params.getByteCount(query, 'ratelimit', config.rateLimitBytes); + } + ui = null; + if (document.getElementById('status') !== null) { + ui = new DebugUI(); + } else { + ui = new UI(); + } + broker = new Broker(config.brokerUrl); + snowflake = new Snowflake(config, ui, broker); + log('== snowflake proxy =='); + if (Util.snowflakeIsDisabled(config.cookieName)) { + // Do not activate the proxy if any number of conditions are true. + log('Currently not active.'); + return; + } + // Otherwise, begin setting up WebRTC and acting as a proxy. + dbg('Contacting Broker at ' + broker.url); + snowflake.setRelayAddr(config.relayAddr); + return snowflake.beginWebRTC(); + }; + + // Notification of closing tab with active proxy. + window.onbeforeunload = function() { + if ( + !silenceNotifications && + snowflake !== null && + Snowflake.MODE.WEBRTC_READY === snowflake.state + ) { + return Snowflake.MESSAGE.CONFIRMATION; + } + return null; + }; + + window.onunload = function() { + if (snowflake !== null) { snowflake.disable(); } + return null; + }; + + window.onload = init; + +}()); diff --git a/proxy/init-webext.js b/proxy/init-webext.js index c641621..df618e6 100644 --- a/proxy/init-webext.js +++ b/proxy/init-webext.js @@ -2,6 +2,110 @@ /* eslint no-unused-vars: 0 */
/* +UI +*/ + +class WebExtUI extends UI { + + constructor() { + super(); + this.onConnect = this.onConnect.bind(this); + this.onMessage = this.onMessage.bind(this); + this.onDisconnect = this.onDisconnect.bind(this); + this.initStats(); + chrome.runtime.onConnect.addListener(this.onConnect); + } + + initStats() { + this.stats = [0]; + return setInterval((() => { + this.stats.unshift(0); + this.stats.splice(24); + return this.postActive(); + }), 60 * 60 * 1000); + } + + initToggle() { + chrome.storage.local.get("snowflake-enabled", (result) => { + if (result['snowflake-enabled'] !== void 0) { + this.enabled = result['snowflake-enabled']; + } else { + log("Toggle state not yet saved"); + } + this.setEnabled(this.enabled); + }); + } + + postActive() { + var ref; + return (ref = this.port) != null ? ref.postMessage({ + active: this.active, + total: this.stats.reduce((function(t, c) { + return t + c; + }), 0), + enabled: this.enabled + }) : void 0; + } + + onConnect(port) { + this.port = port; + port.onDisconnect.addListener(this.onDisconnect); + port.onMessage.addListener(this.onMessage); + return this.postActive(); + } + + onMessage(m) { + this.enabled = m.enabled; + this.setEnabled(this.enabled); + this.postActive(); + chrome.storage.local.set({ + "snowflake-enabled": this.enabled + }, function() { + log("Stored toggle state"); + }); + } + + onDisconnect() { + this.port = null; + } + + setActive(connected) { + super.setActive(connected); + if (connected) { + this.stats[0] += 1; + } + this.postActive(); + if (this.active) { + return chrome.browserAction.setIcon({ + path: { + 32: "icons/status-running.png" + } + }); + } else { + return chrome.browserAction.setIcon({ + path: { + 32: "icons/status-on.png" + } + }); + } + } + + setEnabled(enabled) { + update(); + return chrome.browserAction.setIcon({ + path: { + 32: "icons/status-" + (enabled ? "on" : "off") + ".png" + } + }); + } + +} + +WebExtUI.prototype.port = null; + +WebExtUI.prototype.stats = null; + +/* Entry point. */
@@ -30,7 +134,7 @@ var debug, snowflake, config, broker, ui, log, dbg, init, update, silenceNotific } };
- if (!Util.featureDetect()) { + if (!Util.hasWebRTC()) { chrome.runtime.onConnect.addListener(function(port) { return port.postMessage({ missingFeature: true diff --git a/proxy/make.js b/proxy/make.js index 5d6e013..52ee098 100755 --- a/proxy/make.js +++ b/proxy/make.js @@ -26,14 +26,19 @@ var FILES_SPEC = [ 'spec/websocket.spec.js' ];
-var OUTFILE = 'snowflake.js'; - var STATIC = 'static';
-var concatJS = function(outDir, init) { +var SHARED_FILES = [ + 'embed.html', + 'embed.css', + 'popup.js', + 'icons' +]; + +var concatJS = function(outDir, init, outFile) { var files; files = FILES.concat(`init-${init}.js`); - return exec(`cat ${files.join(' ')} > ${outDir}/${OUTFILE}`, function(err) { + return exec(`cat ${files.join(' ')} > ${outDir}/${outFile}`, function(err) { if (err) { throw err; } @@ -53,7 +58,7 @@ task('test', 'snowflake unit tests', function() { exec('mkdir -p test'); exec('jasmine init >&-'); // Simply concat all the files because we're not using node exports. - jasmineFiles = FILES.concat('init-badge.js', FILES_SPEC); + jasmineFiles = FILES.concat('init-testing.js', FILES_SPEC); outFile = 'test/bundle.spec.js'; exec('echo "TESTING = true" > ' + outFile); exec('cat ' + jasmineFiles.join(' ') + ' | cat >> ' + outFile); @@ -68,19 +73,20 @@ task('test', 'snowflake unit tests', function() { task('build', 'build the snowflake proxy', function() { exec('rm -r build'); exec('cp -r ' + STATIC + '/ build/'); - concatJS('build', 'badge'); + concatJS('build', 'badge', 'embed.js'); console.log('Snowflake prepared.'); });
task('webext', 'build the webextension', function() { exec('mkdir -p webext'); - concatJS('webext', 'webext'); + exec(`cp -r ${STATIC}/{${SHARED_FILES.join(',')}} webext/`); + concatJS('webext', 'webext', 'snowflake.js'); console.log('Webextension prepared.'); });
task('node', 'build the node binary', function() { exec('mkdir -p build'); - concatJS('build', 'node'); + concatJS('build', 'node', 'snowflake.js'); console.log('Node prepared.'); });
diff --git a/proxy/static/.htaccess b/proxy/static/.htaccess index 3dd217d..f733194 100644 --- a/proxy/static/.htaccess +++ b/proxy/static/.htaccess @@ -1,4 +1,3 @@ <Files "embed.html"> Header always unset X-Frame-Options </Files> -Redirect permanent /options.html /index.html diff --git a/proxy/webext/popup.css b/proxy/static/embed.css similarity index 95% rename from proxy/webext/popup.css rename to proxy/static/embed.css index 856e855..e6d8149 100644 --- a/proxy/webext/popup.css +++ b/proxy/static/embed.css @@ -2,7 +2,7 @@ body { margin: 10px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; width: 300px; - font-size:12px; + font-size: 12px; }
#active { @@ -27,14 +27,13 @@ body { display: block; position: absolute; top: 12px; - background-image: url(../icons/arrowhead-right-12.svg); + background-image: url('icons/arrowhead-right-12.svg'); width: 12px; height: 12px; opacity : 0.6; z-index: 9999; right: 0px; margin-right: 10px; - }
/* Snowflake Status */ @@ -43,14 +42,12 @@ body { -webkit-animation:spin 8s linear infinite; -moz-animation:spin 8s linear infinite; animation:spin 8s linear infinite; - fill: BlueViolet; } @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } } @-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
- /* Toggle */
.switch { diff --git a/proxy/static/embed.html b/proxy/static/embed.html index 32c26ca..a48daf3 100644 --- a/proxy/static/embed.html +++ b/proxy/static/embed.html @@ -1,52 +1,28 @@ <!doctype html> <html> -<head> - <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> - <meta http-equiv="refresh" content="86400"> - <script type="text/javascript" src="snowflake.js"></script> - <style> - * { - box-sizing: border-box; - } - body { - position: absolute; width: 100%; height: 100%; - top: 0; margin: 0 auto; padding: 0; - background-color: #424; - text-align: center; cursor: default; - } - #badge { - margin: auto; padding: 0; - width: 88px; height: 16px; - background-image: url('koch.jpg'); - white-space: nowrap; - color: #000; - font-size: 12px; font-weight: 900; - font-variant: small-caps; - text-shadow: 0 0 5px #fef, - 0 1px 2px #fef, - 0 -1px 2px #fef, - 1px 0px 3px #fef, - -1px 0px 3px #fef; - } - .active { - -webkit-animation: bgScroll 8s linear infinite; - animation: bgScroll 8s linear infinite; - } - @-webkit-keyframes bgScroll { - from {background-position: 0 -4%;} - to {background-position: 0 104%;} - } - @keyframes bgScroll { - from {background-position: 0 -4%;} - to {background-position: 0 104%;} - } - </style> -</head> -<body> - <a target="_blank" href="index.html"> - <div id="badge"> - Internet Freedom + <head> + <meta charset="utf-8" /> + <!-- This should be essentially be a no-opt in the popup --> + <meta http-equiv="refresh" content="86400" /> + <link rel="stylesheet" href="embed.css" /> + <script src="popup.js"></script> + <script src="embed.js"></script> + </head> + <body> + <div id="active"> + <img src="icons/status-off.png" /> + <p>Snowflake is off</p> + <p></p> </div> - </a> -</body> + <div class="b button"> + <label id="toggle" for="enabled">Turn On</label> + <label class="switch"> + <input id="enabled" type="checkbox" /> + <span class="slider round"></span> + </label> + </div> + <div class="b learn"> + <a target="_blank" href="https://snowflake.torproject.org/">Learn more</a> + </div> + </body> </html> diff --git a/proxy/webext/icons/arrowhead-right-12.svg b/proxy/static/icons/arrowhead-right-12.svg similarity index 100% rename from proxy/webext/icons/arrowhead-right-12.svg rename to proxy/static/icons/arrowhead-right-12.svg diff --git a/proxy/webext/icons/status-off.png b/proxy/static/icons/status-off.png similarity index 100% rename from proxy/webext/icons/status-off.png rename to proxy/static/icons/status-off.png diff --git a/proxy/webext/icons/status-off.svg b/proxy/static/icons/status-off.svg similarity index 100% rename from proxy/webext/icons/status-off.svg rename to proxy/static/icons/status-off.svg diff --git a/proxy/webext/icons/status-off@2x.png b/proxy/static/icons/status-off@2x.png similarity index 100% rename from proxy/webext/icons/status-off@2x.png rename to proxy/static/icons/status-off@2x.png diff --git a/proxy/webext/icons/status-off@3x.png b/proxy/static/icons/status-off@3x.png similarity index 100% rename from proxy/webext/icons/status-off@3x.png rename to proxy/static/icons/status-off@3x.png diff --git a/proxy/webext/icons/status-on.png b/proxy/static/icons/status-on.png similarity index 100% rename from proxy/webext/icons/status-on.png rename to proxy/static/icons/status-on.png diff --git a/proxy/webext/icons/status-on.svg b/proxy/static/icons/status-on.svg similarity index 100% rename from proxy/webext/icons/status-on.svg rename to proxy/static/icons/status-on.svg diff --git a/proxy/webext/icons/status-on@2x.png b/proxy/static/icons/status-on@2x.png similarity index 100% rename from proxy/webext/icons/status-on@2x.png rename to proxy/static/icons/status-on@2x.png diff --git a/proxy/webext/icons/status-on@3x.png b/proxy/static/icons/status-on@3x.png similarity index 100% rename from proxy/webext/icons/status-on@3x.png rename to proxy/static/icons/status-on@3x.png diff --git a/proxy/webext/icons/status-running.png b/proxy/static/icons/status-running.png similarity index 100% rename from proxy/webext/icons/status-running.png rename to proxy/static/icons/status-running.png diff --git a/proxy/webext/icons/status-running.svg b/proxy/static/icons/status-running.svg similarity index 100% rename from proxy/webext/icons/status-running.svg rename to proxy/static/icons/status-running.svg diff --git a/proxy/static/index.css b/proxy/static/index.css index 70b5a24..9502a37 100644 --- a/proxy/static/index.css +++ b/proxy/static/index.css @@ -76,3 +76,8 @@ h3 { .diagram img, .screenshot img { max-width: 100%; } + +textarea { + max-width: 100%; + width: 600px; +} diff --git a/proxy/static/index.html b/proxy/static/index.html index e46c373..20fe5c8 100644 --- a/proxy/static/index.html +++ b/proxy/static/index.html @@ -77,6 +77,17 @@ Consider adding keywords <em>snowflake-webextension</em> or <em>snowflake-client</em> to let us know how which part of the Snowflake system is experiencing problems.</p> + + <h3>EMBED</h3> + + <p>It is now possible to embed the Snowflake badge on any website:</p> + + <textarea readonly><iframe src="https://snowflake.torproject.org/embed.html" width="320px" height="200px" frameborder="0" scrolling="no"></iframe></textarea> + + <p>Which looks like this:</p> + + <iframe src="embed.html" width="320px" height="200px" frameborder="0" scrolling="no"></iframe> + </div> </body> </html> diff --git a/proxy/static/koch.jpg b/proxy/static/koch.jpg deleted file mode 100644 index 1a3c1e3..0000000 Binary files a/proxy/static/koch.jpg and /dev/null differ diff --git a/proxy/static/popup.js b/proxy/static/popup.js new file mode 100644 index 0000000..0995775 --- /dev/null +++ b/proxy/static/popup.js @@ -0,0 +1,28 @@ +/* exported Popup */ + +class Popup { + constructor() { + this.div = document.getElementById('active'); + this.ps = this.div.querySelectorAll('p'); + this.img = this.div.querySelector('img'); + } + setImgSrc(src) { + this.img.src = `icons/status-${src}.png`; + } + setStatusText(txt) { + this.ps[0].innerText = txt; + } + setStatusDesc(desc, color) { + this.ps[1].innerText = desc; + this.ps[1].style.color = color || 'black'; + } + hideButton() { + document.querySelector('.button').style.display = 'none'; + } + setChecked(checked) { + document.getElementById('enabled').checked = checked; + } + setToggleText(txt) { + document.getElementById('toggle').innerText = txt; + } +} diff --git a/proxy/static/snowflake.html b/proxy/static/snowflake.html deleted file mode 100644 index 84790ed..0000000 --- a/proxy/static/snowflake.html +++ /dev/null @@ -1,60 +0,0 @@ -<!doctype html> -<html> -<head> - <title>Snowflake</title> - <meta content="text/html;charset=utf-8" http-equiv="Content-Type"> - <script type="text/javascript" src="snowflake.js"></script> - <style> - * { - box-sizing: border-box; - -webkit-transition: all 0.3s; - -moz-transition: all 0.3s; - transition: all 0.3s; - } - body { - position: absolute; - width: 100%; height: 100%; top: 0; margin: 0 auto; - background-color: #424; - color: #000; - text-align: center; - font-size: 24px; - font-family: monospace; - background-image: url('koch.jpg'); - } - textarea { - background-color: rgba(0,0,0,0.8); - color: #fff; - resize: none; - } - .chatarea { - position: relative; border: none; - width: 50%; min-width: 40em; - padding: 0.5em; margin: auto; - } - .active { background-color: rgba(0,50,0,0.8); } - #msglog { - display: block; - width: 100%; - min-height: 40em; - margin-bottom: 1em; - padding: 8px; - } - #status { - background-color: rgba(0,0,0,0.9); color: #999; - margin: 8px 0; padding: 8px 1em; cursor: default; - font-size: 12px; - text-align: left; - } - </style> -</head> -<body> - <div class="chatarea"> - <div id="status"> - Timeout... - </div> - <textarea id="msglog" readonly> - </textarea> - </div> - -</body> -</html> diff --git a/proxy/ui.js b/proxy/ui.js index 54e0897..f99affa 100644 --- a/proxy/ui.js +++ b/proxy/ui.js @@ -21,23 +21,6 @@ UI.prototype.active = false; UI.prototype.enabled = true;
-class BadgeUI extends UI { - - constructor() { - super(); - this.$badge = document.getElementById('badge'); - } - - setActive(connected) { - super.setActive(connected); - return this.$badge.className = connected ? 'active' : ''; - } - -} - -BadgeUI.prototype.$badge = null; - - class DebugUI extends UI {
constructor() { @@ -75,104 +58,3 @@ class DebugUI extends UI { DebugUI.prototype.$msglog = null;
DebugUI.prototype.$status = null; - - -class WebExtUI extends UI { - - constructor() { - super(); - this.onConnect = this.onConnect.bind(this); - this.onMessage = this.onMessage.bind(this); - this.onDisconnect = this.onDisconnect.bind(this); - this.initStats(); - chrome.runtime.onConnect.addListener(this.onConnect); - } - - initStats() { - this.stats = [0]; - return setInterval((() => { - this.stats.unshift(0); - this.stats.splice(24); - return this.postActive(); - }), 60 * 60 * 1000); - } - - initToggle() { - chrome.storage.local.get("snowflake-enabled", (result) => { - if (result['snowflake-enabled'] !== void 0) { - this.enabled = result['snowflake-enabled']; - } else { - log("Toggle state not yet saved"); - } - this.setEnabled(this.enabled); - }); - } - - postActive() { - var ref; - return (ref = this.port) != null ? ref.postMessage({ - active: this.active, - total: this.stats.reduce((function(t, c) { - return t + c; - }), 0), - enabled: this.enabled - }) : void 0; - } - - onConnect(port) { - this.port = port; - port.onDisconnect.addListener(this.onDisconnect); - port.onMessage.addListener(this.onMessage); - return this.postActive(); - } - - onMessage(m) { - this.enabled = m.enabled; - this.setEnabled(this.enabled); - this.postActive(); - chrome.storage.local.set({ - "snowflake-enabled": this.enabled - }, function() { - log("Stored toggle state"); - }); - } - - onDisconnect() { - this.port = null; - } - - setActive(connected) { - super.setActive(connected); - if (connected) { - this.stats[0] += 1; - } - this.postActive(); - if (this.active) { - return chrome.browserAction.setIcon({ - path: { - 32: "icons/status-running.png" - } - }); - } else { - return chrome.browserAction.setIcon({ - path: { - 32: "icons/status-on.png" - } - }); - } - } - - setEnabled(enabled) { - update(); - return chrome.browserAction.setIcon({ - path: { - 32: "icons/status-" + (enabled ? "on" : "off") + ".png" - } - }); - } - -} - -WebExtUI.prototype.port = null; - -WebExtUI.prototype.stats = null; diff --git a/proxy/util.js b/proxy/util.js index 387f0a1..88a9bf6 100644 --- a/proxy/util.js +++ b/proxy/util.js @@ -10,31 +10,21 @@ Contains helpers for parsing query strings and other utilities. class Util {
static mightBeTBB() { - return Util.TBB_UAS.indexOf(window.navigator.userAgent) > -1 && (window.navigator.mimeTypes && window.navigator.mimeTypes.length === 0); + return Util.TBB_UAS.indexOf(window.navigator.userAgent) > -1 && ( + window.navigator.mimeTypes && window.navigator.mimeTypes.length === 0 + ); }
static genSnowflakeID() { return Math.random().toString(36).substring(2); }
- static snowflakeIsDisabled(cookieName) { - var cookies; - cookies = Parse.cookie(document.cookie); - // Do nothing if snowflake has not been opted in by user. - if (cookies[cookieName] !== '1') { - log('Not opted-in. Please click the badge to change options.'); - return true; - } - // Also do nothing if running in Tor Browser. - if (Util.mightBeTBB()) { - log('Will not run within Tor Browser.'); - return true; - } - return false; + static hasWebRTC() { + return typeof PeerConnection === 'function'; }
- static featureDetect() { - return typeof PeerConnection === 'function'; + static hasCookies() { + return navigator.cookieEnabled; }
} diff --git a/proxy/webext/popup.js b/proxy/webext/embed.js similarity index 60% rename from proxy/webext/popup.js rename to proxy/webext/embed.js index d8d6464..e1364bf 100644 --- a/proxy/webext/popup.js +++ b/proxy/webext/embed.js @@ -1,36 +1,9 @@ -/* global chrome */ +/* global chrome, Popup */
const port = chrome.runtime.connect({ name: "popup" });
-class Popup { - constructor() { - this.div = document.getElementById('active'); - this.ps = this.div.querySelectorAll('p'); - this.img = this.div.querySelector('img'); - } - setImgSrc(src) { - this.img.src = `icons/status-${src}.png`; - } - setStatusText(txt) { - this.ps[0].innerText = txt; - } - setStatusDesc(desc, color) { - this.ps[1].innerText = desc; - this.ps[1].style.color = color || 'black'; - } - hideButton() { - document.querySelector('.button').style.display = 'none'; - } - setChecked(checked) { - document.getElementById('enabled').checked = checked; - } - setToggleText(txt) { - document.getElementById('toggle').innerText = txt; - } -} - port.onMessage.addListener((m) => { const { active, enabled, total, missingFeature } = m; const popup = new Popup(); diff --git a/proxy/webext/manifest.json b/proxy/webext/manifest.json index 75475ae..3b96055 100644 --- a/proxy/webext/manifest.json +++ b/proxy/webext/manifest.json @@ -1,18 +1,18 @@ { - "manifest_version": 2, - "name": "Snowflake", - "version": "0.0.8", - "description": "Snowflake is a WebRTC pluggable transport for Tor.", - "background": { - "scripts": ["snowflake.js"], - "persistent": true - }, - "browser_action": { - "default_icon": { - "32": "icons/status-on.png" - }, - "default_title": "Snowflake", - "default_popup": "popup.html" - }, - "permissions": ["storage"] + "manifest_version": 2, + "name": "Snowflake", + "version": "0.0.8", + "description": "Snowflake is a WebRTC pluggable transport for Tor.", + "background": { + "scripts": ["snowflake.js"], + "persistent": true + }, + "browser_action": { + "default_icon": { + "32": "icons/status-on.png" + }, + "default_title": "Snowflake", + "default_popup": "embed.html" + }, + "permissions": ["storage"] } diff --git a/proxy/webext/popup.html b/proxy/webext/popup.html deleted file mode 100644 index e3ba2f5..0000000 --- a/proxy/webext/popup.html +++ /dev/null @@ -1,25 +0,0 @@ -<!doctype html> -<html> - <head> - <meta charset="utf-8" /> - <link rel="stylesheet" href="popup.css" /> - <script src="popup.js"></script> - </head> - <body> - <div id="active"> - <img src="icons/status-on.png" /> - <p></p> - <p></p> - </div> - <div class="b button"> - <label id="toggle" for="enabled">Turn On</label> - <label class="switch"> - <input id="enabled" type="checkbox" checked/> - <span class="slider round"></span> - </label> - </div> - <div class="b learn"> - <a target="_blank" href="https://snowflake.torproject.org/">Learn more</a> - </div> - </body> -</html>
tor-commits@lists.torproject.org