diff options
Diffstat (limited to 'src/js/popup.js')
-rw-r--r-- | src/js/popup.js | 723 |
1 files changed, 723 insertions, 0 deletions
diff --git a/src/js/popup.js b/src/js/popup.js new file mode 100644 index 0000000..7e02e23 --- /dev/null +++ b/src/js/popup.js @@ -0,0 +1,723 @@ +/* + * This file is part of Privacy Badger <https://www.eff.org/privacybadger> + * Copyright (C) 2014 Electronic Frontier Foundation + * Derived from Adblock Plus + * Copyright (C) 2006-2013 Eyeo GmbH + * + * Privacy Badger is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * Privacy Badger is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Badger. If not, see <http://www.gnu.org/licenses/>. + */ + +window.POPUP_INITIALIZED = false; +window.SLIDERS_DONE = false; + +var constants = require("constants"); +var FirefoxAndroid = require("firefoxandroid"); +var htmlUtils = require("htmlutils").htmlUtils; + +let POPUP_DATA = {}; + +/* if they aint seen the comic*/ +function showNagMaybe() { + var $nag = $("#instruction"); + var $outer = $("#instruction-outer"); + let intro_page_url = chrome.runtime.getURL("/skin/firstRun.html"); + + function _setSeenComic(cb) { + chrome.runtime.sendMessage({ + type: "seenComic" + }, cb); + } + + function _setSeenLearningPrompt(cb) { + chrome.runtime.sendMessage({ + type: "seenLearningPrompt" + }, cb); + } + + function _hideNag() { + $nag.fadeOut(); + $outer.fadeOut(); + } + + function _showNag() { + $nag.show(); + $outer.show(); + // Attach event listeners + $('#fittslaw').on("click", function (e) { + e.preventDefault(); + _setSeenComic(() => { + _hideNag(); + }); + }); + $("#intro-reminder-btn").on("click", function () { + // If there is a firstRun.html tab, switch to the tab. + // Otherwise, create a new tab + chrome.tabs.query({url: intro_page_url}, function (tabs) { + if (tabs.length == 0) { + chrome.tabs.create({ + url: intro_page_url + }); + } else { + chrome.tabs.update(tabs[0].id, {active: true}, function (tab) { + chrome.windows.update(tab.windowId, {focused: true}); + }); + } + _setSeenComic(() => { + window.close(); + }); + }); + }); + } + + function _showError(error_text) { + $('#instruction-text').hide(); + $('#error-text').show().find('a') + .attr('id', 'critical-error-link') + .css({ + padding: '5px', + display: 'inline-block', + width: 'auto', + }); + $('#error-message').text(error_text); + + $('#fittslaw').on("click", function (e) { + e.preventDefault(); + _hideNag(); + }); + + $nag.show(); + $outer.show(); + } + + function _showLearningPrompt() { + $('#instruction-text').hide(); + + $("#learning-prompt-btn").on("click", function () { + chrome.tabs.create({ + url: "https://www.eff.org/badger-evolution" + }); + _setSeenLearningPrompt(function () { + window.close(); + }); + }); + + $('#fittslaw').on("click", function (e) { + e.preventDefault(); + _setSeenLearningPrompt(function () { + _hideNag(); + }); + }); + + $('#learning-prompt-div').show(); + $nag.show(); + $outer.show(); + } + + if (POPUP_DATA.showLearningPrompt) { + _showLearningPrompt(); + + } else if (!POPUP_DATA.seenComic) { + chrome.tabs.query({active: true, currentWindow: true}, function (focusedTab) { + // Show the popup instruction if the active tab is not firstRun.html page + if (!focusedTab[0].url.startsWith(intro_page_url)) { + _showNag(); + } + }); + + } else if (POPUP_DATA.criticalError) { + _showError(POPUP_DATA.criticalError); + } +} + +/** + * Init function. Showing/hiding popup.html elements and setting up event handler + */ +function init() { + showNagMaybe(); + + $("#activate_site_btn").on("click", activateOnSite); + $("#deactivate_site_btn").on("click", deactivateOnSite); + $("#donate").on("click", function() { + chrome.tabs.create({ + url: "https://supporters.eff.org/donate/support-privacy-badger" + }); + }); + + $('#error_input').on('input propertychange', function() { + // No easy way of sending message on popup close, send message for every change + chrome.runtime.sendMessage({ + type: 'saveErrorText', + tabId: POPUP_DATA.tabId, + errorText: $("#error_input").val() + }); + }); + + let overlay = $('#overlay'); + + // show error layout if the user was writing an error report + if (POPUP_DATA.hasOwnProperty('errorText') && POPUP_DATA.errorText) { + overlay.toggleClass('active'); + } + + $("#error").on("click", function() { + overlay.toggleClass('active'); + }); + $("#report-cancel").on("click", function() { + clearSavedErrorText(); + closeOverlay(); + }); + $("#report-button").on("click", function() { + $(this).prop("disabled", true); + $("#report-cancel").prop("disabled", true); + send_error($("#error_input").val()); + }); + $("#report_close").on("click", function (e) { + e.preventDefault(); + clearSavedErrorText(); + closeOverlay(); + }); + $('#blockedResourcesContainer').on('change', 'input:radio', updateOrigin); + $('#blockedResourcesContainer').on('click', '.userset .honeybadgerPowered', revertDomainControl); + + $("#version").text( + chrome.i18n.getMessage("version", chrome.runtime.getManifest().version) + ); + + // improve on Firefox's built-in options opening logic + if (typeof browser == "object" && typeof browser.runtime.getBrowserInfo == "function") { + browser.runtime.getBrowserInfo().then(function (info) { + if (info.name == "Firefox") { + $("#options").on("click", function (e) { + e.preventDefault(); + openPage(chrome.runtime.getURL("/skin/options.html")); + }); + $("#help").on("click", function (e) { + e.preventDefault(); + openPage(this.getAttribute('href')); + }); + } + }); + } + + $("#share").on("click", function (e) { + e.preventDefault(); + share(); + }); + $("#share_close").on("click", function (e) { + e.preventDefault(); + $("#share_overlay").toggleClass('active', false); + }); + $("#copy-button").on("click", function() { + $("#share_output").select(); + document.execCommand('copy'); + $(this).text(chrome.i18n.getMessage("copy_button_copied")); + }); + + window.POPUP_INITIALIZED = true; +} + +function openPage(url) { + // first get the active tab + chrome.tabs.query({ active: true, lastFocusedWindow: true }, (tabs) => { + let activeTab = tabs[0], + tabProps = { + url, + windowId: activeTab.windowId, + active: true, + index: activeTab.index + 1, + openerTabId: activeTab.id + }; + + // create the new tab + try { + chrome.tabs.create(tabProps); + } catch (e) { + // TODO workaround for pre-57 Firefox + delete tabProps.openerTabId; + chrome.tabs.create(tabProps); + } + + window.close(); + }); +} + +function clearSavedErrorText() { + chrome.runtime.sendMessage({ + type: 'removeErrorText', + tabId: POPUP_DATA.tabId + }); +} + +/** + * Close the error reporting overlay + */ +function closeOverlay() { + $('#overlay').toggleClass('active', false); + $("#report-success").hide(); + $("#report-fail").hide(); + $("#error_input").val(""); +} + +/** + * Send errors to PB error reporting server + * + * @param {String} message The message to send + */ +function send_error(message) { + // get the latest domain list from the background page + chrome.runtime.sendMessage({ + type: "getPopupData", + tabId: POPUP_DATA.tabId, + tabUrl: POPUP_DATA.tabUrl + }, (response) => { + const origins = response.origins; + + if (!origins) { + return; + } + + let out = { + browser: window.navigator.userAgent, + fqdn: response.tabHost, + message: message, + url: response.tabUrl, + version: chrome.runtime.getManifest().version + }; + + for (let origin in origins) { + let action = origins[origin]; + + // adjust action names for error reporting + if (action == constants.USER_ALLOW) { + action = "usernoaction"; + } else if (action == constants.USER_BLOCK) { + action = "userblock"; + } else if (action == constants.USER_COOKIEBLOCK) { + action = "usercookieblock"; + } else if (action == constants.ALLOW) { + action = "noaction"; + } else if (action == constants.BLOCK || action == constants.COOKIEBLOCK) { + // no need to adjust action + } else if (action == constants.DNT || action == constants.NO_TRACKING) { + action = "notracking"; + } + + if (out[action]) { + out[action] += ","+origin; + } else { + out[action] = origin; + } + } + + var sendReport = $.ajax({ + type: "POST", + url: "https://privacybadger.org/reporting", + data: JSON.stringify(out), + contentType: "application/json" + }); + + sendReport.done(function() { + $("#error_input").val(""); + $("#report-success").slideDown(); + + clearSavedErrorText(); + + setTimeout(function() { + $("#report-button").prop("disabled", false); + $("#report-cancel").prop("disabled", false); + closeOverlay(); + }, 3000); + }); + + sendReport.fail(function() { + $("#report-fail").slideDown(); + + setTimeout(function() { + $("#report-button").prop("disabled", false); + $("#report-cancel").prop("disabled", false); + $("#report-fail").slideUp(); + }, 3000); + }); + }); +} + +/** + * activate PB for site event handler + */ +function activateOnSite() { + $("#activate_site_btn").toggle(); + $("#deactivate_site_btn").toggle(); + $("#blockedResourcesContainer").show(); + + chrome.runtime.sendMessage({ + type: "activateOnSite", + tabHost: POPUP_DATA.tabHost, + tabId: POPUP_DATA.tabId, + tabUrl: POPUP_DATA.tabUrl + }, () => { + chrome.tabs.reload(POPUP_DATA.tabId); + window.close(); + }); +} + +/** + * de-activate PB for site event handler + */ +function deactivateOnSite() { + $("#activate_site_btn").toggle(); + $("#deactivate_site_btn").toggle(); + $("#blockedResourcesContainer").hide(); + + chrome.runtime.sendMessage({ + type: "deactivateOnSite", + tabHost: POPUP_DATA.tabHost, + tabId: POPUP_DATA.tabId, + tabUrl: POPUP_DATA.tabUrl + }, () => { + chrome.tabs.reload(POPUP_DATA.tabId); + window.close(); + }); +} + +/** + * Open the share overlay + */ +function share() { + $("#share_overlay").toggleClass('active'); + let share_msg = chrome.i18n.getMessage("share_base_message"); + + // only add language about found trackers if we actually found trackers + // (but regardless of whether we are actually blocking them) + if (POPUP_DATA.noTabData) { + $("#share_output").val(share_msg); + return; + } + + let origins = POPUP_DATA.origins; + let originsArr = []; + if (origins) { + originsArr = Object.keys(origins); + } + + if (!originsArr.length) { + $("#share_output").val(share_msg); + return; + } + + originsArr = htmlUtils.sortDomains(originsArr); + let tracking = []; + + for (let origin of originsArr) { + let action = origins[origin]; + + if (action == constants.BLOCK || action == constants.COOKIEBLOCK) { + tracking.push(origin); + } + } + + if (tracking.length) { + share_msg += "\n\n"; + share_msg += chrome.i18n.getMessage( + "share_tracker_header", [tracking.length, POPUP_DATA.tabHost]); + share_msg += "\n\n"; + share_msg += tracking.join("\n"); + } + $("#share_output").val(share_msg); +} + +/** + * Handler to undo user selection for a tracker + */ +function revertDomainControl(event) { + event.preventDefault(); + + let origin = $(event.target).parent().data('origin'); + + chrome.runtime.sendMessage({ + type: "revertDomainControl", + origin: origin + }, () => { + chrome.tabs.reload(POPUP_DATA.tabId); + window.close(); + }); +} + +/** + * Refresh the content of the popup window + * + * @param {Integer} tabId The id of the tab + */ +function refreshPopup() { + window.SLIDERS_DONE = false; + + // must be a special browser page, + if (POPUP_DATA.noTabData) { + // show the "nothing to do here" message + $('#blockedResourcesContainer').hide(); + $('#special-browser-page').show(); + + // hide inapplicable buttons + $('#deactivate_site_btn').hide(); + $('#error').hide(); + + // activate tooltips + $('.tooltip').tooltipster(); + + window.SLIDERS_DONE = true; + + return; + } + + // revert any hiding/showing above for cases when refreshPopup gets called + // more than once for the same popup, such as during functional testing + $('#blockedResourcesContainer').show(); + $('#special-browser-page').hide(); + $('#deactivate_site_btn').show(); + $('#error').show(); + + // toggle activation buttons if privacy badger is not enabled for current url + if (!POPUP_DATA.enabled) { + $("#blockedResourcesContainer").hide(); + $("#activate_site_btn").show(); + $("#deactivate_site_btn").hide(); + $("#disabled-site-message").show(); + $("#title").addClass("faded-bw-color-scheme"); + } + + // if there is any saved error text, fill the error input with it + if (POPUP_DATA.hasOwnProperty('errorText')) { + $("#error_input").val(POPUP_DATA.errorText); + } + + let origins = POPUP_DATA.origins; + let originsArr = []; + if (origins) { + originsArr = Object.keys(origins); + } + + if (!originsArr.length) { + // hide the number of trackers and slider instructions message + // if no sliders will be displayed + $("#instructions-many-trackers").hide(); + + // show "no trackers" message + $("#instructions-no-trackers").show(); + + if (POPUP_DATA.learnLocally && POPUP_DATA.showNonTrackingDomains) { + // show the "no third party resources on this site" message + $("#no-third-parties").show(); + } + + // activate tooltips + $('.tooltip').tooltipster(); + + window.SLIDERS_DONE = true; + + return; + } + + let printable = []; + let unblockedTrackers = []; + let nonTracking = []; + originsArr = htmlUtils.sortDomains(originsArr); + + for (let origin of originsArr) { + let action = origins[origin]; + + if (action == constants.NO_TRACKING) { + nonTracking.push(origin); + } else if (action == constants.ALLOW) { + unblockedTrackers.push(origin); + } else { + let show_breakage_warning = ( + action == constants.USER_BLOCK && + POPUP_DATA.cookieblocked.hasOwnProperty(origin) + ); + printable.push( + htmlUtils.getOriginHtml(origin, action, show_breakage_warning) + ); + } + } + + if (POPUP_DATA.learnLocally && unblockedTrackers.length) { + printable.push( + '<div class="clicker tooltip" id="not-yet-blocked-header" title="' + + chrome.i18n.getMessage("intro_not_an_adblocker_paragraph") + + '" data-tooltipster=\'{"side":"top"}\'>' + + chrome.i18n.getMessage("not_yet_blocked_header") + + '</div>' + ); + unblockedTrackers.forEach(domain => { + printable.push( + htmlUtils.getOriginHtml(domain, constants.ALLOW) + ); + }); + + // reduce margin if we have hasn't-decided-yet-to-block domains to show + $("#instructions-no-trackers").css("margin", "10px 0"); + } + + if (POPUP_DATA.learnLocally && POPUP_DATA.showNonTrackingDomains && nonTracking.length) { + printable.push( + '<div class="clicker tooltip" id="non-trackers-header" title="' + + chrome.i18n.getMessage("non_tracker_tip") + + '" data-tooltipster=\'{"side":"top"}\'>' + + chrome.i18n.getMessage("non_tracker") + + '</div>' + ); + for (let i = 0; i < nonTracking.length; i++) { + printable.push( + htmlUtils.getOriginHtml(nonTracking[i], constants.NO_TRACKING) + ); + } + + // reduce margin if we have non-tracking domains to show + $("#instructions-no-trackers").css("margin", "10px 0"); + } + + if (printable.length) { + // get containing HTML for domain list along with toggle legend icons + $("#blockedResources")[0].innerHTML = htmlUtils.getTrackerContainerHtml(); + } + + // activate tooltips + $('.tooltip').tooltipster(); + + if (POPUP_DATA.trackerCount === 0) { + // hide multiple trackers message + $("#instructions-many-trackers").hide(); + + // show "no trackers" message + $("#instructions-no-trackers").show(); + + } else { + $('#instructions-many-trackers').html(chrome.i18n.getMessage( + "popup_instructions", [ + POPUP_DATA.trackerCount, + "<a target='_blank' title='" + _.escape(chrome.i18n.getMessage("what_is_a_tracker")) + "' class='tooltip' href='https://privacybadger.org/#What-is-a-third-party-tracker'>" + ] + )).find(".tooltip").tooltipster(); + } + + function renderDomains() { + const CHUNK = 1; + + let $printable = $(printable.splice(0, CHUNK).join("")); + + // Hide elements for removing origins (controlled from the options page). + // Popup shows what's loaded for the current page so it doesn't make sense + // to have removal ability here. + $printable.find('.removeOrigin').hide(); + + $printable.appendTo('#blockedResourcesInner'); + + // activate tooltips + $('#blockedResourcesInner .tooltip:not(.tooltipstered)').tooltipster( + htmlUtils.DOMAIN_TOOLTIP_CONF); + + if (printable.length) { + requestAnimationFrame(renderDomains); + } else { + window.SLIDERS_DONE = true; + } + } + + if (printable.length) { + requestAnimationFrame(renderDomains); + } else { + window.SLIDERS_DONE = true; + } +} + +/** + * Update the user preferences displayed in the domain list for this origin. + * These UI changes will later be used to update user preferences data. + * + * @param {Event} event Click event triggered by user. + */ +function updateOrigin() { + // get the origin and new action for it + let $radio = $(this), + action = $radio.val(), + $switchContainer = $radio.parents('.switch-container').first(); + + // update slider color via CSS + $switchContainer.removeClass([ + constants.BLOCK, + constants.COOKIEBLOCK, + constants.ALLOW, + constants.NO_TRACKING].join(" ")).addClass(action); + + let $clicker = $radio.parents('.clicker').first(), + origin = $clicker.data('origin'), + show_breakage_warning = ( + action == constants.BLOCK && + POPUP_DATA.cookieblocked.hasOwnProperty(origin) + ); + + htmlUtils.toggleBlockedStatus($clicker, true, show_breakage_warning); + + // reinitialize the domain tooltip + $clicker.find('.origin-inner').tooltipster('destroy'); + $clicker.find('.origin-inner').attr( + 'title', htmlUtils.getActionDescription(action, origin)); + $clicker.find('.origin-inner').tooltipster(htmlUtils.DOMAIN_TOOLTIP_CONF); + + // persist the change + saveToggle(origin, action); +} + +/** + * Save the user setting for a domain by messaging the background page. + */ +function saveToggle(origin, action) { + chrome.runtime.sendMessage({ + type: "savePopupToggle", + origin, + action, + tabId: POPUP_DATA.tabId + }); +} + +function getTab(callback) { + // Temporary fix for Firefox Android + if (!FirefoxAndroid.hasPopupSupport) { + FirefoxAndroid.getParentOfPopup(callback); + return; + } + + chrome.tabs.query({active: true, lastFocusedWindow: true}, function(t) { callback(t[0]); }); +} + +/** + * Workaround for geckodriver being unable to modify page globals. + */ +function setPopupData(data) { + POPUP_DATA = data; +} + +$(function () { + $.tooltipster.setDefaults(htmlUtils.TOOLTIPSTER_DEFAULTS); + + getTab(function (tab) { + chrome.runtime.sendMessage({ + type: "getPopupData", + tabId: tab.id, + tabUrl: tab.url + }, (response) => { + setPopupData(response); + refreshPopup(); + init(); + }); + }); +}); |