From 8d13bdc6cac0e20c43c6f909fc0208774b9c5c84 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 21:47:39 +0200 Subject: Adding upstream version 2020.10.7. Signed-off-by: Daniel Baumann --- src/js/options.js | 976 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 976 insertions(+) create mode 100644 src/js/options.js (limited to 'src/js/options.js') diff --git a/src/js/options.js b/src/js/options.js new file mode 100644 index 0000000..7ca7008 --- /dev/null +++ b/src/js/options.js @@ -0,0 +1,976 @@ +/* + * This file is part of Adblock Plus , + * Copyright (C) 2006-2013 Eyeo GmbH + * + * Adblock Plus 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. + * + * Adblock Plus 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 Adblock Plus. If not, see . + */ + +window.OPTIONS_INITIALIZED = false; +window.SLIDERS_DONE = false; + +const TOOLTIP_CONF = { + maxWidth: 400 +}; +const USER_DATA_EXPORT_KEYS = ["action_map", "snitch_map", "settings_map"]; + +let i18n = chrome.i18n; + +let constants = require("constants"); +let { getOriginsArray } = require("optionslib"); +let htmlUtils = require("htmlutils").htmlUtils; +let utils = require("utils"); + +let OPTIONS_DATA = {}; + +/* + * Loads options from pb storage and sets UI elements accordingly. + */ +function loadOptions() { + // Set page title to i18n version of "Privacy Badger Options" + document.title = i18n.getMessage("options_title"); + + // Add event listeners + $("#allowlist-form").on("submit", addDisabledSite); + $("#remove-disabled-site").on("click", removeDisabledSite); + $("#cloud-upload").on("click", uploadCloud); + $("#cloud-download").on("click", downloadCloud); + $('#importTrackerButton').on("click", loadFileChooser); + $('#importTrackers').on("change", importTrackerList); + $('#exportTrackers').on("click", exportUserData); + $('#resetData').on("click", resetData); + $('#removeAllData').on("click", removeAllData); + + if (OPTIONS_DATA.settings.showTrackingDomains) { + $('#tracking-domains-overlay').hide(); + } else { + $('#blockedResourcesContainer').hide(); + + $('#show-tracking-domains-checkbox').on("click", () => { + $('#tracking-domains-overlay').hide(); + $('#blockedResourcesContainer').show(); + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { + showTrackingDomains: true + } + }); + }); + } + + // Set up input for searching through tracking domains. + $("#trackingDomainSearch").on("input", filterTrackingDomains); + $("#tracking-domains-type-filter").on("change", filterTrackingDomains); + $("#tracking-domains-status-filter").on("change", filterTrackingDomains); + $("#tracking-domains-show-not-yet-blocked").on("change", filterTrackingDomains); + + // Add event listeners for origins container. + $('#blockedResourcesContainer').on('change', 'input:radio', function () { + let $radio = $(this), + $clicker = $radio.parents('.clicker').first(), + origin = $clicker.data('origin'), + action = $radio.val(); + + // update domain slider row tooltip/status indicators + updateOrigin(origin, action, true); + + // persist the change + saveToggle(origin, action); + }); + $('#blockedResourcesContainer').on('click', '.userset .honeybadgerPowered', revertDomainControl); + $('#blockedResourcesContainer').on('click', '.removeOrigin', removeOrigin); + + // Display jQuery UI elements + $("#tabs").tabs({ + activate: function (event, ui) { + // update options page URL fragment identifier + // to preserve selected tab on page reload + history.replaceState(null, null, "#" + ui.newPanel.attr('id')); + } + }); + $("button").button(); + $("#add-disabled-site").button("option", "icons", {primary: "ui-icon-plus"}); + $("#remove-disabled-site").button("option", "icons", {primary: "ui-icon-minus"}); + $("#cloud-upload").button("option", "icons", {primary: "ui-icon-arrowreturnthick-1-n"}); + $("#cloud-download").button("option", "icons", {primary: "ui-icon-arrowreturnthick-1-s"}); + $(".importButton").button("option", "icons", {primary: "ui-icon-plus"}); + $("#exportTrackers").button("option", "icons", {primary: "ui-icon-extlink"}); + $("#resetData").button("option", "icons", {primary: "ui-icon-arrowrefresh-1-w"}); + $("#removeAllData").button("option", "icons", {primary: "ui-icon-closethick"}); + $("#show_counter_checkbox").on("click", updateShowCounter); + $("#show_counter_checkbox").prop("checked", OPTIONS_DATA.settings.showCounter); + $("#replace-widgets-checkbox") + .on("click", updateWidgetReplacement) + .prop("checked", OPTIONS_DATA.isWidgetReplacementEnabled); + $("#enable_dnt_checkbox").on("click", updateDNTCheckboxClicked); + $("#enable_dnt_checkbox").prop("checked", OPTIONS_DATA.settings.sendDNTSignal); + $("#check_dnt_policy_checkbox").on("click", updateCheckingDNTPolicy); + $("#check_dnt_policy_checkbox").prop("checked", OPTIONS_DATA.settings.checkForDNTPolicy).prop("disabled", !OPTIONS_DATA.settings.sendDNTSignal); + + // only show the alternateErrorPagesEnabled override if browser supports it + if (chrome.privacy && chrome.privacy.services && chrome.privacy.services.alternateErrorPagesEnabled) { + $("#privacy-settings-header").show(); + $("#disable-google-nav-error-service").show(); + $('#disable-google-nav-error-service-checkbox') + .prop("checked", OPTIONS_DATA.settings.disableGoogleNavErrorService) + .on("click", overrideAlternateErrorPagesSetting); + } + + // only show the hyperlinkAuditingEnabled override if browser supports it + if (chrome.privacy && chrome.privacy.websites && chrome.privacy.websites.hyperlinkAuditingEnabled) { + $("#privacy-settings-header").show(); + $("#disable-hyperlink-auditing").show(); + $("#disable-hyperlink-auditing-checkbox") + .prop("checked", OPTIONS_DATA.settings.disableHyperlinkAuditing) + .on("click", overrideHyperlinkAuditingSetting); + } + + if (OPTIONS_DATA.webRTCAvailable) { + $("#webRTCToggle").show(); + $("#toggle_webrtc_mode").on("click", toggleWebRTCIPProtection); + + chrome.privacy.network.webRTCIPHandlingPolicy.get({}, result => { + // auto check the option box if ip leak is already protected at diff levels, via pb or another extension + if (result.value == "default_public_interface_only" || result.value == "disable_non_proxied_udp") { + $("#toggle_webrtc_mode").prop("checked", true); + } + }); + } + + $('#local-learning-checkbox') + .prop("checked", OPTIONS_DATA.settings.learnLocally) + .on("click", (event) => { + const enabled = $(event.currentTarget).prop("checked"); + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { + learnLocally: enabled + } + }, function () { + $("#learn-in-incognito-checkbox") + .prop("disabled", (enabled ? false : "disabled")) + .prop("checked", (enabled ? OPTIONS_DATA.settings.learnInIncognito : false)); + $("#show-nontracking-domains-checkbox") + .prop("disabled", (enabled ? false : "disabled")) + .prop("checked", (enabled ? OPTIONS_DATA.settings.showNonTrackingDomains : false)); + + $("#learning-setting-divs").slideToggle(enabled); + $("#not-yet-blocked-filter").toggle(enabled); + }); + }); + if (OPTIONS_DATA.settings.learnLocally) { + $("#learning-setting-divs").show(); + $("#not-yet-blocked-filter").show(); + } + + $("#learn-in-incognito-checkbox") + .prop("disabled", OPTIONS_DATA.settings.learnLocally ? false : "disabled") + .prop("checked", ( + OPTIONS_DATA.settings.learnLocally ? + OPTIONS_DATA.settings.learnInIncognito : false + )) + .on("click", (event) => { + const enabled = $(event.currentTarget).prop("checked"); + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { + learnInIncognito: enabled + } + }, function () { + OPTIONS_DATA.settings.learnInIncognito = enabled; + }); + }); + + $('#show-nontracking-domains-checkbox') + .prop("disabled", OPTIONS_DATA.settings.learnLocally ? false : "disabled") + .prop("checked", ( + OPTIONS_DATA.settings.learnLocally ? + OPTIONS_DATA.settings.showNonTrackingDomains : false + )) + .on("click", (event) => { + const enabled = $(event.currentTarget).prop("checked"); + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { + showNonTrackingDomains: enabled + } + }, function () { + OPTIONS_DATA.settings.showNonTrackingDomains = enabled; + }); + }); + + const widgetSelector = $("#hide-widgets-select"); + widgetSelector.prop("disabled", + OPTIONS_DATA.isWidgetReplacementEnabled ? false : "disabled"); + + $("#replace-widgets-checkbox").change(function () { + if ($(this).is(":checked")) { + widgetSelector.prop("disabled", false); + } else { + widgetSelector.prop("disabled", "disabled"); + } + }); + + // Initialize Select2 and populate options + widgetSelector.select2(); + OPTIONS_DATA.widgets.forEach(function (key) { + const isSelected = OPTIONS_DATA.settings.widgetReplacementExceptions.includes(key); + const option = new Option(key, key, false, isSelected); + widgetSelector.append(option).trigger("change"); + }); + + widgetSelector.on('select2:select', updateWidgetReplacementExceptions); + widgetSelector.on('select2:unselect', updateWidgetReplacementExceptions); + widgetSelector.on('select2:clear', updateWidgetReplacementExceptions); + + reloadDisabledSites(); + reloadTrackingDomainsTab(); + + $('html').css({ + overflow: 'visible', + visibility: 'visible' + }); + + window.OPTIONS_INITIALIZED = true; +} + +/** + * Opens the file chooser to allow a user to select + * a file to import. + */ +function loadFileChooser() { + var fileChooser = document.getElementById('importTrackers'); + fileChooser.click(); +} + +/** + * Import a list of trackers supplied by the user + * NOTE: list must be in JSON format to be parsable + */ +function importTrackerList() { + var file = this.files[0]; + + if (file) { + var reader = new FileReader(); + reader.readAsText(file); + reader.onload = function(e) { + parseUserDataFile(e.target.result); + }; + } else { + var selectFile = i18n.getMessage("import_select_file"); + confirm(selectFile); + } + + document.getElementById("importTrackers").value = ''; +} + +/** + * Parses Privacy Badger data uploaded by the user. + * + * @param {String} storageMapsList data from JSON file that user provided + */ +function parseUserDataFile(storageMapsList) { + let lists; + + try { + lists = JSON.parse(storageMapsList); + } catch (e) { + return confirm(i18n.getMessage("invalid_json")); + } + + // validate by checking we have the same keys in the import as in the export + if (!_.isEqual( + Object.keys(lists).sort(), + USER_DATA_EXPORT_KEYS.sort() + )) { + return confirm(i18n.getMessage("invalid_json")); + } + + // check for webrtc setting in the imported settings map + if (lists.settings_map.preventWebRTCIPLeak) { + // verify that the user hasn't already enabled this option + if (!$("#toggle_webrtc_mode").prop("checked")) { + toggleWebRTCIPProtection(); + } + // this browser-controlled setting doesn't belong in Badger's settings object + delete lists.settings_map.preventWebRTCIPLeak; + } + + chrome.runtime.sendMessage({ + type: "mergeUserData", + data: lists + }, (response) => { + OPTIONS_DATA.settings.disabledSites = response.disabledSites; + OPTIONS_DATA.origins = response.origins; + + reloadDisabledSites(); + reloadTrackingDomainsTab(); + // TODO general settings are not updated + + confirm(i18n.getMessage("import_successful")); + }); +} + +function resetData() { + var resetWarn = i18n.getMessage("reset_data_confirm"); + if (confirm(resetWarn)) { + chrome.runtime.sendMessage({type: "resetData"}, () => { + // reload page to refresh tracker list + location.reload(); + }); + } +} + +function removeAllData() { + var removeWarn = i18n.getMessage("remove_all_data_confirm"); + if (confirm(removeWarn)) { + chrome.runtime.sendMessage({type: "removeAllData"}, () => { + location.reload(); + }); + } +} + +function downloadCloud() { + chrome.runtime.sendMessage({type: "downloadCloud"}, + function (response) { + if (response.success) { + alert(i18n.getMessage("download_cloud_success")); + OPTIONS_DATA.settings.disabledSites = response.disabledSites; + reloadDisabledSites(); + } else { + console.error("Cloud sync error:", response.message); + if (response.message === i18n.getMessage("download_cloud_no_data")) { + alert(response.message); + } else { + alert(i18n.getMessage("download_cloud_failure")); + } + } + } + ); +} + +function uploadCloud() { + chrome.runtime.sendMessage({type: "uploadCloud"}, + function (status) { + if (status.success) { + alert(i18n.getMessage("upload_cloud_success")); + } else { + console.error("Cloud sync error:", status.message); + alert(i18n.getMessage("upload_cloud_failure")); + } + } + ); +} + +/** + * Export the user's data, including their list of trackers from + * action_map and snitch_map, along with their settings. + * List will be in JSON format that can be edited and reimported + * in another instance of Privacy Badger. + */ +function exportUserData() { + chrome.storage.local.get(USER_DATA_EXPORT_KEYS, function (maps) { + + // exports the user's prevent webrtc leak setting if it's checked + if ($("#toggle_webrtc_mode").prop("checked")) { + maps.settings_map.preventWebRTCIPLeak = true; + } + + let mapJSON = JSON.stringify(maps); + + // Append the formatted date to the exported file name + let currDate = new Date().toLocaleString(); + let escapedDate = currDate + // illegal filename charset regex from + // https://github.com/parshap/node-sanitize-filename/blob/ef1e8ad58e95eb90f8a01f209edf55cd4176e9c8/index.js + .replace(/[\/\?<>\\:\*\|"]/g, '_') /* eslint no-useless-escape:off */ + // also collapse-replace commas and spaces + .replace(/[, ]+/g, '_'); + let filename = 'PrivacyBadger_user_data-' + escapedDate + '.json'; + + // Download workaround taken from uBlock Origin + // https://github.com/gorhill/uBlock/blob/40a85f8c04840ae5f5875c1e8b5fa17578c5bd1a/platform/chromium/vapi-common.js + let a = document.createElement('a'); + a.setAttribute('download', filename || ''); + + let blob = new Blob([mapJSON], { type: 'application/json' }); // pass a useful mime type here + a.href = URL.createObjectURL(blob); + + function clickBlobLink() { + a.dispatchEvent(new MouseEvent('click')); + URL.revokeObjectURL(blob); + } + + /** + * Firefox workaround to insert the blob link in an iFrame + * https://bugzilla.mozilla.org/show_bug.cgi?id=1420419#c18 + */ + function addBlobWorkAroundForFirefox() { + // Create or use existing iframe for the blob 'a' element + let iframe = document.getElementById('exportUserDataIframe'); + if (!iframe) { + iframe = document.createElement('iframe'); + iframe.id = "exportUserDataIframe"; + iframe.setAttribute("style", "visibility: hidden; height: 0; width: 0"); + document.getElementById('export').appendChild(iframe); + + iframe.contentWindow.document.open(); + iframe.contentWindow.document.write(''); + iframe.contentWindow.document.close(); + } else { + // Remove the old 'a' element from the iframe + let oldElement = iframe.contentWindow.document.body.lastChild; + iframe.contentWindow.document.body.removeChild(oldElement); + } + iframe.contentWindow.document.body.appendChild(a); + } + + // TODO remove browser check and simplify code once Firefox 58 goes away + // https://bugzilla.mozilla.org/show_bug.cgi?id=1420419 + if (chrome.runtime.getBrowserInfo) { + chrome.runtime.getBrowserInfo((info) => { + if (info.name == "Firefox" || info.name == "Waterfox") { + addBlobWorkAroundForFirefox(); + } + clickBlobLink(); + }); + } else { + clickBlobLink(); + } + }); +} + +/** + * Update setting for whether or not to show counter on Privacy Badger badge. + */ +function updateShowCounter() { + const showCounter = $("#show_counter_checkbox").prop("checked"); + + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { showCounter } + }, () => { + // Refresh display for each tab's PB badge. + chrome.tabs.query({}, function(tabs) { + tabs.forEach(function(tab) { + chrome.runtime.sendMessage({ + type: "updateBadge", + tab_id: tab.id + }); + }); + }); + }); +} + +/** + * Update setting for whether or not to replace + * social buttons/video players/commenting widgets. + */ +function updateWidgetReplacement() { + const socialWidgetReplacementEnabled = $("#replace-widgets-checkbox").prop("checked"); + + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { socialWidgetReplacementEnabled } + }); +} + +/** + * Update DNT checkbox clicked + */ +function updateDNTCheckboxClicked() { + const enabled = $("#enable_dnt_checkbox").prop("checked"); + + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { + sendDNTSignal: enabled + } + }); + + $("#check_dnt_policy_checkbox").prop("checked", enabled).prop("disabled", !enabled); + updateCheckingDNTPolicy(); +} + +function updateCheckingDNTPolicy() { + const enabled = $("#check_dnt_policy_checkbox").prop("checked"); + + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { + checkForDNTPolicy: enabled + } + }); +} + +function reloadDisabledSites() { + let sites = OPTIONS_DATA.settings.disabledSites, + $select = $('#allowlist-select'); + + // sort disabled sites the same way blocked sites are sorted + sites = htmlUtils.sortDomains(sites); + + $select.empty(); + for (let i = 0; i < sites.length; i++) { + $('