diff --git a/CHANGELOG.md b/CHANGELOG.md index 3127b5d..8d2ba99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.6] - 2019-04-11 + +### Compatibility note +- Require Firefox >= 55 +- Require Chrome >= 28 + +### Added +- Added listener to the browser history to prevent tracking with the [history.pushState method](https://developer.mozilla.org/en-US/docs/Web/API/History_API) +- Added webNavigation and tabs permissions, for the new feature +- Added switches in settings to enable and disable the context menu entry and the history listener +- Added tool to clean URLs, that was pasted into a textbox +- Added icon for new tool to clean URLs + +### Fixed +- [#40](https://gitlab.com/KevinRoebert/ClearUrls/issues/40), see also https://curl.kevinroebert.de +- [#103](https://gitlab.com/KevinRoebert/ClearUrls/issues/103), see also https://curl.kevinroebert.de + +### Changed +- Changed clipboard-helper.js path to be absolute to prevent problems +- Changed rewrite of old GitHub links to the new data.min.json and rules.min.hash +- Config icon is now bigger and above the config label +- Update Traditional Chinese Translation by [@yipinghuang](https://gitlab.com/yipinghuang) + ## [[1.5.8] - 2019-04-10](https://gitlab.com/KevinRoebert/ClearUrls/commit/1b6cc37bdd23011d006bf7ef6824463e7c96067a) ### Compatibility note diff --git a/_locales/de/messages.json b/_locales/de/messages.json index a1fd632..50d38f0 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -249,7 +249,35 @@ "description": "" }, "clipboard_copy_link": { - "message": "Link-Adresse kopieren", + "message": "Gesäuberte Link-Adresse kopieren", + "description": "" + }, + "context_menu_enabled": { + "message": "Kontextmenü-Eintrag anzeigen", + "description": "" + }, + "history_listener_enabled": { + "message": "Verhindere Tracking über die History-API (Siehe auch: The pushState() method)", + "description": "" + }, + "cleaning_tool_page_title": { + "message": "Säuberungswerkzeug von ClearURLs", + "description": "" + }, + "cleaning_tool_description": { + "message": "Mit diesem Werkzeug können Sie URLs in die Textbox einfügen. Nach einem klick auf den grünen Button reinigt ClearURLs die Links. Sie können mehrer URLs auf einmal einfügen, jede URL muss aber in einer eigenen Zeile stehen.", + "description": "" + }, + "cleaning_tool_btn": { + "message": "URLs säubern", + "description": "" + }, + "cleaning_tool_dirty_urls_label": { + "message": "Hier können Sie die ungesäuberten URLs einfügen:", + "description": "" + }, + "cleaning_tool_clean_urls_label": { + "message": "Hier finden Sie die gesäuberten URLs:", "description": "" } } diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 8338fa7..aaecc8f 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -249,7 +249,35 @@ "description": "" }, "clipboard_copy_link": { - "message": "Copy Link Location", + "message": "Copy Clean Link Location", + "description": "" + }, + "context_menu_enabled": { + "message": "Display context menu entry", + "description": "" + }, + "history_listener_enabled": { + "message": "Prevent tracking injection over history API (See also: The pushState() method)", + "description": "" + }, + "cleaning_tool_page_title": { + "message": "Cleaning tool from ClearURLs", + "description": "" + }, + "cleaning_tool_description": { + "message": "With this tool you can paste in URLs and ClearURLs will cleaned the URLs after a click on the green button. You can paste in multiple URLs at once, but every URL must be in a separate line.", + "description": "" + }, + "cleaning_tool_btn": { + "message": "Clean URLs", + "description": "" + }, + "cleaning_tool_dirty_urls_label": { + "message": "Here you can paste in the dirty URLs:", + "description": "" + }, + "cleaning_tool_clean_urls_label": { + "message": "Here you can find the cleaned URLs:", "description": "" } } diff --git a/core_js/cleaning_tool.js b/core_js/cleaning_tool.js new file mode 100644 index 0000000..203649c --- /dev/null +++ b/core_js/cleaning_tool.js @@ -0,0 +1,81 @@ +/* +* ClearURLs +* Copyright (c) 2017-2019 Kevin Röbert +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*/ + +/*jshint esversion: 6 */ +var cleanedURLs = []; +var i = 0; +var length = 0; + +/** +* Load only when document is ready +*/ +$(document).ready(function(){ + setText(); + $('#cleaning_tool_btn').on("click", cleanURLs); +}); + +/** +* This function cleans all URLs line by line in the textarea. +*/ +function cleanURLs() { + var cleanTArea = $('#cleanURLs'); + var dirtyTArea = $('#dirtyURLs'); + var urls = dirtyTArea.val().split('\n'); + cleanedURLs = []; + length = urls.length; + + for(i=0; i < length; i++) { + browser.runtime.sendMessage({ + function: "pureCleaning", + params: [urls[i]] + }).then((data) => { + cleanedURLs.push(data.response); + if(i >= length-1) { + console.log("End of loop."); + cleanTArea.val(cleanedURLs.join('\n')); + } + }, handleError); + } +} + +/** +* Translate a string with the i18n API. +* +* @param {string} string Name of the attribute used for localization +*/ +function translate(string) +{ + return browser.i18n.getMessage(string); +} + +/** +* Set the text for the UI. +*/ +function setText() +{ + document.title = translate('cleaning_tool_page_title'); + $('#page_title').text(translate('cleaning_tool_page_title')); + $('#cleaning_tool_description').text(translate('cleaning_tool_description')); + $('#cleaning_tool_btn').text(translate('cleaning_tool_btn')); + $('#cleaning_tool_dirty_urls_label').text(translate('cleaning_tool_dirty_urls_label')); + $('#cleaning_tool_clean_urls_label').text(translate('cleaning_tool_clean_urls_label')); +} + +function handleError(error) { + console.log(`Error: ${error}`); +} diff --git a/core_js/context_menu.js b/core_js/context_menu.js index 24fe231..97c8c24 100644 --- a/core_js/context_menu.js +++ b/core_js/context_menu.js @@ -22,69 +22,36 @@ * and based on: https://github.com/mdn/webextensions-examples/tree/master/context-menu-copy-link-with-types */ -browser.contextMenus.create({ - id: "copy-link-to-clipboard", - title: translate("clipboard_copy_link"), - contexts: ["link"] -}); +function contextMenuStart() { + if(storage.contextMenuEnabled) { + browser.contextMenus.create({ + id: "copy-link-to-clipboard", + title: translate("clipboard_copy_link"), + contexts: ["link"] + }); -browser.contextMenus.onClicked.addListener((info, tab) => { - if (info.menuItemId === "copy-link-to-clipboard") { - const url = contextCleaning(info.linkUrl); - const code = "copyToClipboard(" + - JSON.stringify(url)+");"; + browser.contextMenus.onClicked.addListener((info, tab) => { + if (info.menuItemId === "copy-link-to-clipboard") { + const url = pureCleaning(info.linkUrl); + const code = "copyToClipboard(" + + JSON.stringify(url)+");"; - browser.tabs.executeScript({ - code: "typeof copyToClipboard === 'function';", - }).then((results) => { - if (!results || results[0] !== true) { - return browser.tabs.executeScript(tab.id, { - file: "external_js/clipboard-helper.js", + browser.tabs.executeScript({ + code: "typeof copyToClipboard === 'function';", + }).then((results) => { + if (!results || results[0] !== true) { + return browser.tabs.executeScript(tab.id, { + file: "/external_js/clipboard-helper.js", + }); + } + }).then(() => { + return browser.tabs.executeScript(tab.id, { + code, + }); + }).catch((error) => { + console.error("Failed to copy text: " + error); }); } - }).then(() => { - return browser.tabs.executeScript(tab.id, { - code, - }); - }).catch((error) => { - console.error("Failed to copy text: " + error); }); } -}); - -/** -* Cleans links for the context menue. Also do automatic redirection. -* -* @param {[type]} url url as string -* @return {Array} redirectUrl or none -*/ -function contextCleaning(url) { - // The URL is already cleaned - if(lastVisited === url) { - return url; - } - - var cleanURL = url; - - for (var i = 0; i < providers.length; i++) { - var result = { - "changes": false, - "url": "", - "redirect": false, - "cancel": false - }; - - if(providers[i].matchURL(cleanURL)) - { - result = removeFieldsFormURL(providers[i], cleanURL); - cleanURL = result.url; - } - - if(result.redirect) - { - return result.url; - } - } - - return cleanURL; } diff --git a/core_js/historyListener.js b/core_js/historyListener.js new file mode 100644 index 0000000..8631b19 --- /dev/null +++ b/core_js/historyListener.js @@ -0,0 +1,53 @@ +/* +* ClearURLs +* Copyright (c) 2017-2019 Kevin Röbert +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*/ + +/*jshint esversion: 6 */ +/* +* This script is responsible for listen on history changes. +* This technique is often used to inject tracking code into the location bar, +* because all feature events will use the updated URL. +*/ + +function historyListenerStart() { + if(storage.historyListenerEnabled) { + browser.webNavigation.onHistoryStateUpdated.addListener(historyCleaner); + } +} + +/** +* Function that is triggered on history changes. Injects script into page +* to clean links that were pushed to the history stack with the +* history.pushState method. +* @param {state object} details The state object is a JavaScript object +* which is associated with the new history entry created by pushState() +*/ +function historyCleaner(details) { + var urlBefore = details.url; + var urlAfter = pureCleaning(details.url); + + if(urlBefore != urlAfter) { + browser.tabs.executeScript(details.tabId, { + frameId: details.frameId, + code: 'history.pushState({state: "cleaned_history"},"",'+JSON.stringify(urlAfter)+');' + }).then(() => {}, onError); + } +} + +function onError(error) { + console.log(`[ClearURLs] Error: ${error}`); +} diff --git a/core_js/popup.js b/core_js/popup.js index 2be411a..04c3038 100644 --- a/core_js/popup.js +++ b/core_js/popup.js @@ -250,6 +250,7 @@ $(document).ready(function(){ changeSwitchButton("statistics", "statisticsStatus"); $('#loggingPage').attr('href', browser.extension.getURL('./html/log.html')); $('#settings').attr('href', browser.extension.getURL('./html/settings.html')); + $('#cleaning_tools').attr('href', browser.extension.getURL('./html/cleaningTool.html')); setText(); }); @@ -272,7 +273,6 @@ function setText() injectText('configs_switch_filter','popup_html_configs_switch_filter'); injectText('configs_head','popup_html_configs_head'); injectText('configs_switch_statistics','configs_switch_statistics'); - injectText('reportButton', 'popup_html_report_button', true); $('#donate').prop('title', translate('donate_button')); } diff --git a/core_js/pureCleaning.js b/core_js/pureCleaning.js new file mode 100644 index 0000000..6c4c45f --- /dev/null +++ b/core_js/pureCleaning.js @@ -0,0 +1,56 @@ +/* +* ClearURLs +* Copyright (c) 2017-2019 Kevin Röbert +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*/ + +/*jshint esversion: 6 */ + +/** +* Cleans given links. Also do automatic redirection. +* +* @param {[type]} url url as string +* @return {Array} redirectUrl or none +*/ +function pureCleaning(url) { + // The URL is already cleaned + if(lastVisited === url) { + return url; + } + + var cleanURL = url; + + for (var i = 0; i < providers.length; i++) { + var result = { + "changes": false, + "url": "", + "redirect": false, + "cancel": false + }; + + if(providers[i].matchURL(cleanURL)) + { + result = removeFieldsFormURL(providers[i], cleanURL); + cleanURL = result.url; + } + + if(result.redirect) + { + return result.url; + } + } + + return cleanURL; +} diff --git a/core_js/settings.js b/core_js/settings.js index 5c65650..761f1e7 100644 --- a/core_js/settings.js +++ b/core_js/settings.js @@ -1,20 +1,20 @@ /* - * ClearURLs - * Copyright (c) 2017-2019 Kevin Röbert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ +* ClearURLs +* Copyright (c) 2017-2019 Kevin Röbert +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Lesser General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License +* along with this program. If not, see . +*/ var settings = []; @@ -50,9 +50,9 @@ $(document).ready(function(){ }); /** - * Reset everything. - * Set everthing to the default values. - */ +* Reset everything. +* Set everthing to the default values. +*/ function reset() { browser.runtime.sendMessage({ @@ -72,8 +72,8 @@ function reset() } /** - * Saves the settings. - */ +* Saves the settings. +*/ function save() { browser.runtime.sendMessage({ @@ -125,8 +125,8 @@ function translate(string) } /** - * Get the data. - */ +* Get the data. +*/ function getData() { browser.runtime.sendMessage({ @@ -148,11 +148,28 @@ function getData() function: "getData", params: ["types"] }).then((data) => handleResponseData(data, "types", "types"), handleError); + + browser.runtime.sendMessage({ + function: "getData", + params: ["contextMenuEnabled"] + }).then((data) => { + handleResponseData(data, "contextMenuEnabled", "contextMenuEnabled"); + browser.runtime.sendMessage({ + function: "getData", + params: ["historyListenerEnabled"] + }).then((data) => { + handleResponseData(data, "historyListenerEnabled", "historyListenerEnabled"); + changeSwitchButton("contextMenuEnabled", "contextMenuEnabled"); + changeSwitchButton("historyListenerEnabled", "historyListenerEnabled"); + }, handleError); + }, handleError); + + } /** - * Set the text for the UI. - */ +* Set the text for the UI. +*/ function setText() { document.title = translate('settings_html_page_title'); @@ -165,12 +182,14 @@ function setText() $('#types_label').html(translate('setting_types_label')); $('#save_settings_btn').text(translate('settings_html_save_button')); $('#save_settings_btn').prop('title', translate('settings_html_save_button_title')); + injectText("context_menu_enabled", "context_menu_enabled"); + $('#history_listener_enabled').html(translate('history_listener_enabled')); } /** - * Handle the response from the storage and saves the data. - * @param {JSON-Object} data Data JSON-Object - */ +* Handle the response from the storage and saves the data. +* @param {JSON-Object} data Data JSON-Object +*/ function handleResponseData(data, varName, inputID) { settings[varName] = data.response; @@ -178,9 +197,74 @@ function handleResponseData(data, varName, inputID) } function handleResponse(message) { - console.log(`Message from the background script: ${message.response}`); + console.log(`Message from the background script: ${message.response}`); } function handleError(error) { - console.log(`Error: ${error}`); + console.log(`Error: ${error}`); +} + +/** +* Change the value of a switch button. +* @param {string} id HTML id +* @param {string} storageID storage internal id +*/ +function changeSwitchButton(id, storageID) +{ + var element = $('#'+id); + + element.on('change', function(){ + browser.runtime.sendMessage({ + function: "setData", + params: [storageID, element.is(':checked')] + }).then((data) => { + if(storageID == "globalStatus"){ + browser.runtime.sendMessage({ + function: "changeIcon", + params: [] + }); + } + + browser.runtime.sendMessage({ + function: "saveOnExit", + params: [] + }); + }); + }); + setSwitchButton(id, storageID); +} + +/** +* Helper function to inject the translated text and tooltip. +* +* @param {string} id ID of the HTML element +* @param {string} attribute Name of the attribute used for localization +* @param {boolean} tooltip +*/ +function injectText(id, attribute, tooltip) +{ + object = $('#'+id); + object.text(translate(attribute)); + + /* + This function will throw an error if no translation + is found for the tooltip. This is a planned error. + */ + tooltip = translate(attribute+"_title"); + + if(tooltip != "") + { + object.prop('title', tooltip); + } +} + +/** +* Set the value of a switch button. +* @param {string} id HTML id +* @param {string} varname js internal variable name +*/ +function setSwitchButton(id, varname) +{ + var element = $('#'+id); + element.prop('checked', settings[varname]); } diff --git a/core_js/storage.js b/core_js/storage.js index 9a4efa3..de9a908 100644 --- a/core_js/storage.js +++ b/core_js/storage.js @@ -132,6 +132,12 @@ function initStorage(items) // Start the clearurls.js start(); + + // Start the context_menu + contextMenuStart(); + + // Start history listener + historyListenerStart(); } /** @@ -153,6 +159,8 @@ function initSettings() storage.hashURL = "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; storage.ruleURL = "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json"; storage.reportServer = "https://clearurls.xn--rb-fka.it"; + storage.contextMenuEnabled = true; + storage.historyListenerEnabled = true; if(getBrowser() === "Firefox") { storage.types = ["font", "image", "imageset", "main_frame", "media", "object", "object_subrequest", "other", "script", "stylesheet", "sub_frame", "websocket", "xbl", "xml_dtd", "xmlhttprequest", "xslt"]; @@ -169,9 +177,9 @@ function replaceOldURLs(url) { switch (url) { case "https://raw.githubusercontent.com/KevinRoebert/ClearUrls/master/data/rules.hash?flush_cache=true": - return "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/rules.hash"; + return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; case "https://raw.githubusercontent.com/KevinRoebert/ClearUrls/master/data/data.json?flush_cache=true": - return "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.json"; + return "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json"; case "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/rules.hash": return "https://gitlab.com/KevinRoebert/ClearUrls/-/jobs/artifacts/master/raw/rules.min.hash?job=hash%20rules"; case "https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.json": diff --git a/data/data.min.json b/data/data.min.json index 53d4822..729ed1d 100644 --- a/data/data.min.json +++ b/data/data.min.json @@ -191,11 +191,14 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(facebook)(\\.[a-zA-Z]{2,})(.*\\?.*)", "completeProvider": false, "rules": [ - "hc_[a-zA-Z_\\[\\]0-9]*", + "hc_[a-zA-Z_%\\[\\]0-9]*", "[a-zA-Z]*ref[a-zA-Z]*", "__tn__", "eid", - "__xts__%5B[0-9]%5D" + "__xts__%5B[0-9]%5D", + "__xts__\\[[0-9]\\]", + "comment_tracking", + "dti" ], "exceptions": [ ".*(facebook\\.)\\w{2,}.*(\\/plugins\\/).*" @@ -439,6 +442,16 @@ ], "exceptions": [], "redirections": [] + }, + "mozilla.org": { + "urlPattern": "https?://([a-zA-Z0-9-]*\\.)?(mozilla\\.org)(.*\\?.*)", + "completeProvider": false, + "rules": [ + "src", + "platform" + ], + "exceptions": [], + "redirections": [] } } } diff --git a/html/cleaningTool.html b/html/cleaningTool.html new file mode 100644 index 0000000..ce35c29 --- /dev/null +++ b/html/cleaningTool.html @@ -0,0 +1,87 @@ + + + + + + Cleaning tool from ClearURLs + + + + + + + + + + + + + + + +
+
+

+ +
+ +
+ + +
+
+

+ +

+
+
+ + +
+
+
+ + + + + + + + + + diff --git a/html/popup.html b/html/popup.html index 4b49153..5129f09 100644 --- a/html/popup.html +++ b/html/popup.html @@ -57,8 +57,11 @@ along with this program. If not, see .
+
+ + +
-
+
+ + +
+
+

+ +

+
+

+
+ +

+
+

+
+ +

+
+

+
+ +

+
+

+
+ + +
+ +
+ +

+
+

+
+ +

+
+

+ +

- -
-
-

- -

-
-

-
- -

-
-

-
- -

-
-

-
- -

-
-

-
- -

-
-

- -

-
-
- - - - - - - - - - - + + + + + + + + + + diff --git a/manifest.json b/manifest.json index 270f78a..15addce 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "ClearURLs", - "version": "1.5.8", + "version": "1.6", "author": "Kevin R.", "description": "Remove tracking elements form URLs.", "homepage_url": "https://gitlab.com/KevinRoebert/ClearUrls", @@ -50,7 +50,9 @@ "storage", "unlimitedStorage", "contextMenus", - "clipboardWrite" + "clipboardWrite", + "webNavigation", + "tabs" ], "background": { "scripts": [ @@ -61,7 +63,9 @@ "core_js/tools.js", "core_js/storage.js", "clearurls.js", - "core_js/context_menu.js" + "core_js/pureCleaning.js", + "core_js/context_menu.js", + "core_js/historyListener.js" ] }, "content_scripts": [