diff options
Diffstat (limited to 'data/extensions/https-everywhere@eff.org/pages/popup')
3 files changed, 493 insertions, 0 deletions
diff --git a/data/extensions/https-everywhere@eff.org/pages/popup/index.html b/data/extensions/https-everywhere@eff.org/pages/popup/index.html new file mode 100644 index 0000000..21dac99 --- /dev/null +++ b/data/extensions/https-everywhere@eff.org/pages/popup/index.html @@ -0,0 +1,84 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8"> + <title data-i18n="about_ext_name"></title> + + <link href="style.css" rel="stylesheet"> + <script src="ux.js"></script> + <script src="../translation.js"></script> + <script src="../send-message.js"></script> + </head> + <body> + <header> + <h1 data-i18n="about_ext_name"></h1> + </header> + + <section id="disableButton" class="options" style="visibility: hidden;"> + <div class="onoffswitch"> + <input id="onoffswitch" type="checkbox" checked><label data-i18n="menu_globalEnable" for="onoffswitch"></label> + </div> + </section> + + <section id="HttpNowhere" class="options" style="visibility: hidden;"> + <input id="http-nowhere-checkbox" type="checkbox"><label data-i18n="menu_blockUnencryptedRequests" for="http-nowhere-checkbox"></label> + </section> + + <div id="RuleManagement"> + <section> + <a href="javascript:void 0" id="add-rule-link" data-i18n="chrome_add_rule"></a> + <div id="add-new-rule-div" style="display: none"> + <h3 data-i18n="about_add_new_rule"></h3> + <p data-i18n="chrome_always_https_for_host"></p> + <label for="new-rule-host" data-i18n="chrome_host"></label><br> + <input size="50" id="new-rule-host" type="text" disabled><br> + <div id="new-rule-regular-text"> + <a href="javascript:void 0" id="new-rule-show-advanced-link" data-i18n="chrome_show_advanced"></a><br> + </div> + <div id="new-rule-advanced" style="display: none"> + <a href="javascript:void 0" id="new-rule-hide-advanced-link" data-i18n="chrome_hide_advanced"></a><br> + <br> + <label for="new-rule-name" data-i18n="chrome_rule_name"></label><br> + <input size="50" id="new-rule-name" type="text"><br> + <label for="new-rule-regex" data-i18n="chrome_regex"></label><br> + <input size="50" id="new-rule-regex" type="text"><br> + <label for="new-rule-redirect" data-i18n="chrome_redirect_to"></label><br> + <input size="50" id="new-rule-redirect" type="text"><br> + </div> + <button id="add-new-rule-button" data-i18n="chrome_add_new_rule"></button><br> + <button id="cancel-new-rule" data-i18n="chrome_status_cancel_button"></button> + </div> + </section> + + <section id="StableRules" class="rules"> + <h3 data-i18n="chrome_stable_rules"></h3> + <p class="description" data-i18n="chrome_stable_rules_description"></p> + </section> + + <section id="UnstableRules" class="rules"> + <h3 data-i18n="chrome_experimental_rules"></h3> + <p class="description" data-i18n="chrome_experimental_rules_description"></p> + </section> + + <section id="resetButton" class="options"> + <a href="javascript:void 0" id="reset-to-defaults" data-i18n="prefs_reset_defaults"></a> + </section> + </div> + + <footer> + <a id="viewAllRules" href="https://www.eff.org/https-everywhere/atlas" target="_blank" data-i18n="menu_viewAllRules"></a><br> + <br> + <a id="aboutTitle" href="https://www.eff.org/https-everywhere" target="_blank" data-i18n="about_title"></a><br> + <br> + <a id="donateEFF" href="https://supporters.eff.org/donate/support-https-everywhere" target="_blank" data-i18n="menu_donate_eff_imperative"></a> + <p> + <small> + <span data-i18n="about_version">Version</span>: <span id="current-version"></span> + <span id="rulesets-versions"> + </span> + </small> + </p> + </footer> + + </body> +</html> diff --git a/data/extensions/https-everywhere@eff.org/pages/popup/style.css b/data/extensions/https-everywhere@eff.org/pages/popup/style.css new file mode 100644 index 0000000..a1cc358 --- /dev/null +++ b/data/extensions/https-everywhere@eff.org/pages/popup/style.css @@ -0,0 +1,128 @@ +html { + background-color: #fff +} + +body { + cursor: default; + margin-left: 1em; + margin-right: 1em; + margin-top: 0; + margin-bottom: 0; + min-width: 20em; + font-family: 'Lucida Grande', 'Segoe UI', Tahoma, 'DejaVu Sans', Arial, sans-serif; + font-size: 75%; + color: #303942; +} + +p { + line-height: 1.8em; +} + +h1, h2, h3 { + user-select: none; + font-weight: normal; + line-height: 1; +} + +h1 { + font-size: 1.5em; +} + +h2 { + font-size: 1.3em; + margin-bottom: 0.4em; +} + +h3 { + color: black; + font-size: 1.2em; + margin-bottom: 0.8em; +} + +a { + color: #15c; + text-decoration: underline; +} + +a:active { + color: #052577; +} + +/* Don't wrap text for important stuff. */ +h1, h2, h3, .rule > label { + white-space: nowrap; +} + +/* Hide rules & options if the extension is off. */ +.disabled #RuleManagement,.disabled #HttpNowhere { + display: none; +} + +/* Initially hide section (until rules get added). */ +section.rules { + display: none; +} + +.rule { + height: 25px; +} + +.rule label { + padding: 0; + max-width: 93%; + overflow: hidden; +} + +/* Underline rules that have notes. */ +.rule[title] span { + border-bottom: 1px dotted; + cursor: help; +} + +input[type='checkbox'] { + vertical-align: middle; +} + +.rule img.remove { + float: right; +} + +header { + border-bottom: 1px solid #eee; +} + +header > h1 { + margin: 0; + padding: 21px 0 13px; +} + +section { + padding-left: 18px; + padding-top: 9px; + padding-bottom: 9px; +} + +section > h3 { + margin-left: -18px; +} + +footer { + border-top: 1px solid #eee; + margin-top: 8px; + padding-top: 8px; + padding-bottom: 8px; +} + +/* By default the "Add a rule" link is hidden. It's shown on HTTPS sites only. */ +#add-rule-link { + display: none; +} + +#rulesets-versions{ + display: block; + clear: both; +} + +.rulesets-version{ + display: block; +} diff --git a/data/extensions/https-everywhere@eff.org/pages/popup/ux.js b/data/extensions/https-everywhere@eff.org/pages/popup/ux.js new file mode 100644 index 0000000..a35c00c --- /dev/null +++ b/data/extensions/https-everywhere@eff.org/pages/popup/ux.js @@ -0,0 +1,281 @@ +/* global sendMessage */ + +"use strict"; + +function e(id) { + return document.getElementById(id); +} + +/** + * Handles rule (de)activation in the popup + */ +function toggleRuleLine(event) { + if (event.target.matches("input[type=checkbox]")) { + getTab(activeTab => { + const set_ruleset = { + active: event.target.checked, + name: event.target.nextSibling.innerText, + tab_id: activeTab.id, + }; + + sendMessage("set_ruleset_active_status", set_ruleset, () => { + // purge the name from the cache so that this unchecking is persistent. + sendMessage("delete_from_ruleset_cache", set_ruleset.name, () => { + // Now reload the selected tab of the current window. + chrome.tabs.reload(set_ruleset.tab_id); + }); + }); + }); + } +} + + +/** + * Creates rule lines (including checkbox and icon) for the popup + * @param rulesets + * @param list_div + * @returns {*} + */ +function appendRulesToListDiv(rulesets, list_div) { + if (rulesets && rulesets.length) { + // template parent block for each ruleset + let templateLine = document.createElement("div"); + templateLine.className = "rule checkbox"; + + // label "container" + let templateLabel = document.createElement("label"); + + // checkbox + let templateCheckbox = document.createElement("input"); + templateCheckbox.type = "checkbox"; + + // label text + let templateLabelText = document.createElement("span"); + + // img "remove" button + let templateRemove = document.createElement("img"); + templateRemove.src = chrome.extension.getURL("images/remove.png"); + templateRemove.className = "remove"; + + templateLabel.appendChild(templateCheckbox); + templateLabel.appendChild(templateLabelText); + templateLine.appendChild(templateLabel); + + for (const ruleset of rulesets) { + let line = templateLine.cloneNode(true); + let checkbox = line.querySelector("input[type=checkbox]"); + let text = line.querySelector("span"); + + checkbox.checked = ruleset.active; + text.innerText = ruleset.name; + + if (ruleset.note && ruleset.note.length) { + line.title = ruleset.note; + + if (ruleset.note === "user rule") { + let remove = templateRemove.cloneNode(true); + line.appendChild(remove); + + remove.addEventListener("click", () => { + sendMessage("remove_rule", ruleset, () => { + list_div.removeChild(line); + }); + }); + } + } + list_div.appendChild(line); + } + show(list_div); + } +} + +function showHttpNowhereUI() { + // Set up checkbox for HTTP nowhere mode + getOption_('httpNowhere', false, function(item) { + if (item.httpNowhere) { + e('http-nowhere-checkbox').checked = true; + } + e('HttpNowhere').style.visibility = "visible"; + }); +}; + +// Change the UI to reflect extension enabled/disabled +function updateEnabledDisabledUI() { + getOption_('globalEnabled', true, function(item) { + e('onoffswitch').checked = item.globalEnabled; + e('disableButton').style.visibility = "visible"; + // Hide or show the rules sections + if (item.globalEnabled) { + document.body.className = "" + showHttpNowhereUI() + } else { + document.body.className = "disabled" + } + }); +} + +// Toggle extension enabled/disabled status +function toggleEnabledDisabled() { + var extension_toggle_effect = function() { + updateEnabledDisabledUI(); + // The extension state changed, so reload this tab. + chrome.tabs.reload(); + window.close(); + } + + getOption_('globalEnabled', true, function(item) { + setOption_('globalEnabled', !item.globalEnabled, extension_toggle_effect); + }); + +} + +/** + * Create the list of rules for a specific tab + * @param activeTab + */ +function gotTab(activeTab) { + sendMessage("get_active_rulesets", activeTab.id, function(rulesets) { + if (rulesets) { + const stableRules = rulesets.filter(ruleset => ruleset.default_state); + const unstableRules = rulesets.filter(ruleset => !ruleset.default_state); + + appendRulesToListDiv(stableRules, e("StableRules")); + appendRulesToListDiv(unstableRules, e("UnstableRules")); + + // Add listener to capture the toggle event + e("RuleManagement").addEventListener("click", toggleRuleLine); + } + + // Only show the "Add a rule" link if we're on an HTTPS page + if (/^https:/.test(activeTab.url)) { + show(e("add-rule-link")); + } + }); +} + +/** + * Fill in content into the popup on load + */ +document.addEventListener("DOMContentLoaded", function () { + getTab(gotTab); + + // Set up the enabled/disabled switch & hide/show rules + updateEnabledDisabledUI(); + e('onoffswitch').addEventListener('click', toggleEnabledDisabled); + e('http-nowhere-checkbox').addEventListener('click', toggleHttpNowhere, false); + e('reset-to-defaults').addEventListener('click', () => { + if (confirm(chrome.i18n.getMessage("prefs_reset_defaults_message"))) { + sendMessage("reset_to_defaults", null, () => { + window.close(); + }); + } + }); + + // Print the extension's current version. + var the_manifest = chrome.runtime.getManifest(); + var version_info = e('current-version'); + version_info.innerText = the_manifest.version; + + let rulesets_versions = e('rulesets-versions'); + sendMessage("get_ruleset_timestamps", null, timestamps => { + for(let [update_channel, timestamp] of timestamps){ + if(timestamp > 0){ + let ruleset_date = new Date(timestamp * 1000); + let ruleset_version_string = ruleset_date.getUTCFullYear() + "." + (ruleset_date.getUTCMonth() + 1) + "." + ruleset_date.getUTCDate(); + + let timestamp_span = document.createElement("span"); + timestamp_span.className = "rulesets-version"; + timestamp_span.innerText = chrome.i18n.getMessage("about_rulesets_version") + " " + update_channel + ": " + ruleset_version_string; + rulesets_versions.appendChild(timestamp_span); + } + } + }); + e("aboutTitle").title = chrome.i18n.getMessage("about_title"); + e("add-rule-link").addEventListener("click", addManualRule); +}); + + +var escapeForRegex = function( value ) { + return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); +}; + +function hide(elem) { + elem.style.display = "none"; +} + +function show(elem) { + elem.style.display = "block"; +} + +/** + * Handles the manual addition of rules + */ +function addManualRule() { + getTab(function(tab) { + hide(e("add-rule-link")); + show(e("add-new-rule-div")); + + const url = new URL(tab.url); + + e("new-rule-host").value = url.host; + + const escapedHost = escapeForRegex(url.host); + + e("new-rule-regex").value = `^http://${escapedHost}/`; + e("new-rule-redirect").value = `https://${url.host}/`; + e("new-rule-name").value = "Manual rule for " + url.host; + + e("add-new-rule-button").addEventListener("click", function() { + const params = { + host : e("new-rule-host").value, + redirectTo : e("new-rule-redirect").value, + urlMatcher : e("new-rule-regex").value + }; + sendMessage("add_new_rule", params, function() { + location.reload(); + }); + }); + + e("cancel-new-rule").addEventListener("click", function() { + show(e("add-rule-link")); + hide(e("add-new-rule-div")); + }); + + e("new-rule-show-advanced-link").addEventListener("click", function() { + show(e("new-rule-advanced")); + hide(e("new-rule-regular-text")); + }); + + e("new-rule-hide-advanced-link").addEventListener("click", function() { + hide(e("new-rule-advanced")); + show(e("new-rule-regular-text")); + }); + }); +} + +function toggleHttpNowhere() { + getOption_('httpNowhere', false, function(item) { + setOption_('httpNowhere', !item.httpNowhere); + }); +} + +function getOption_(opt, defaultOpt, callback) { + var details = {}; + details[opt] = defaultOpt; + sendMessage("get_option", details, callback); +} + +function setOption_(opt, value, callback) { + var details = {}; + details[opt] = value; + sendMessage("set_option", details, callback); +} + +function getTab(callback) { + let url = new URL(window.location.href); + if (url.searchParams.has('tabId')) { + let parentId = Number(url.searchParams.get('tabId')); + return chrome.tabs.get(parentId, callback); + } + chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => callback(tabs[0])); +} |