diff --git a/CHANGELOG.md b/CHANGELOG.md index 84ac2f6..b085f0c 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.9.0] - 2019-10-22 + +### Compatibility note +- Require Firefox >= 55 +- Require Chrome >= 22 + +### Fixed +- Fixed bug in "history tracking injection protection". This option was not disabled, when the global filter switch are on off +- Fixed [#241](https://gitlab.com/KevinRoebert/ClearUrls/issues/241) +- Possible fix for [#203](https://gitlab.com/KevinRoebert/ClearUrls/issues/203) + +## Changed +- Refactoring +- Changed background script loading sequence to prevent that required functions are not yet loaded. + +### Added +- Added an option to im-/export the log (requires the `downloads` permission) +- Added an option to im-/export the settings (requires the `downloads` permission) +- Added information page for blocked sites, when they are called in the `main_frame` +- Added option to allow referral marketing ([#284](https://gitlab.com/KevinRoebert/ClearUrls/issues/284)) +- Added "multiple times URL encodes" recognition +- Added an option to limit the log entries ([#56](https://gitlab.com/KevinRoebert/ClearUrls/issues/56)) + ## [1.8.5] - 2019-09-29 ### Compatibility note diff --git a/README.md b/README.md index db799b9..d52e543 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,20 @@ **ClearURLs** is an add-on based on the new WebExtensions technology and is optimized for *Firefox* and *Chrome* based browsers. -This add-on will remove the tracking fields from all URLs which are visited by the browser and use a rule file, namely `data.min.json`, -to protects your privacy. +This add-on will remove the tracking fields from all URLs, which are visited by the browser, with the help of an external rule file, +which is regularly updated by us and can be found [here](https://gitlab.com/KevinRoebert/ClearUrls/raw/master/data/data.min.json), +to protect your privacy. ## Application -Large (and small) webpages use elements in the URL, e.g.: https://example.com?source=thisIstheSiteIvisitedBefore to track your online activities. In this example, the source field tells the provider which page you visited before. The add-on will remove these tracking fields from the URL. +Large (and small) websites use elements in the URL, e.g.: `https://example.com?utm_source=newsletter1&utm_medium=email&utm_campaign=sale` +to track your online activities. In this example, the `utm` fields used to track a custom campaign, managed by Google. It tells the provider, and also Google, +the source, the medium (e.g. email or web) and the specific campaign of the link. All these tracking fields are not necessary for a website to be displayed +or work correctly and can therefore be removed - that is exactly what ClearURLs does, so you can think of it as an *URL clean maid*. + +Another common example are Amazon URLs. If you search for a product on Amazon you will get a very long URL for this product, e.g. +`https://www.amazon.com/dp/exampleProduct/ref=sxin_0_pb?__mk_de_DE=ÅMÅŽÕÑ&keywords=tea&pd_rd_i=exampleProduct&pd_rd_r=8d39e4cd-1e4f-43db-b6e7-72e969a84aa5&pd_rd_w=1pcKM&pd_rd_wg=hYrNl&pf_rd_p=50bbfd25-5ef7-41a2-68d6-74d854b30e30&pf_rd_r=0GMWD0YYKA7XFGX55ADP&qid=1517757263&rnid=2914120011`, +the most of this URL is available for tracking and is not needed. After ClearURLs has cleaned this URL, it looks like this: +`https://www.amazon.com/dp/exampleProduct` ## Screenshot ![Interface (version 1.8.5)](https://gitlab.com/KevinRoebert/ClearUrls/raw/master/promotion/screens/Popup_1.8.5.png) @@ -31,7 +40,8 @@ You want to help translating ClearURLs into many languages? – Nice You can choose between two options to contribute. You can create a merge request, or you can use the POEditor to translate ClearURLs. -*Hint*: The description field in the translation files are only an information for what the translation is used. It is not necessary to translate the description field; in the most cases it is empty. +*Hint: The description field in the translation files are only an information for what the translation is used. +It is not necessary to translate the description field; in the most cases it is empty.* #### Merge request If you want to create a merge request, you must open the path [`_locales/en/messages.json`](https://github.com/KevinRoebert/ClearUrls/blob/master/_locales/en/messages.json) in the ClearURLs repo diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 3fae90d..9d498b0 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -59,6 +59,22 @@ "message": "Setzt das globale Protokoll zurück", "description": "Diese Zeichenfolge wird als Titel für die Schaltfläche zum Zurücksetzen auf der Protokollseite verwendet." }, + "log_html_export_button": { + "message": "Exportieren", + "description": "Diese Zeichenfolge wird für die Schaltfläche zum Exportieren auf der Protokollseite verwendet." + }, + "log_html_export_button_title": { + "message": "Exportieren des globalen Protokolls", + "description": "Diese Zeichenfolge wird als Titel für die Schaltfläche zum Exportieren auf der Protokollseite verwendet." + }, + "log_html_import_button": { + "message": "Importieren", + "description": "Diese Zeichenfolge wird für die Schaltfläche zum Importieren auf der Protokollseite verwendet." + }, + "log_html_import_button_title": { + "message": "Importieren des globalen Protokolls", + "description": "Diese Zeichenfolge wird als Titel für die Schaltfläche zum Importieren auf der Protokollseite verwendet." + }, "popup_html_configs_head": { "message": "Einstellungen", "description": "Diese Zeichenfolge wird als Titel für die Konfigurationen auf der Popup-Seite verwendet." @@ -163,10 +179,36 @@ "message": "Setzt alle Einstellungen zurück", "description": "Diese Zeichenfolge wird als Titel für die Schaltfläche zum Zurücksetzen auf der Einstellungsseite verwendet." }, + "setting_html_export_button": { + "message": "Exportieren", + "description": "Diese Zeichenfolge wird als Name für die Schaltfläche zum Exportieren auf der Einstellungsseite verwendet." + }, + "setting_html_export_button_title": { + "message": "Exportiert alle Einstellungen", + "description": "Diese Zeichenfolge wird als Titel für die Schaltfläche zum Exportieren auf der Einstellungsseite verwendet." + }, + "setting_html_import_button": { + "message": "Importieren", + "description": "Diese Zeichenfolge wird als Name für die Schaltfläche zum Importieren auf der Einstellungsseite verwendet." + }, + "setting_html_import_button_title": { + "message": "Importiert alle Einstellungen inkl. Regeln und Protokoll", + "description": "Diese Zeichenfolge wird als Titel für die Schaltfläche zum Importieren auf der Einstellungsseite verwendet." + }, "setting_rule_url_label": { "message": "Die Url zu der data.json Datei (Regeln)", "description": "Diese Zeichenfolge wird als Name für die Regel-URL-Bezeichnung verwendet." }, + "setting_log_limit_label": { + "message": "Limitiert das Protokoll auf $LIMIT$ Einträge. (-1 := ∞)", + "description": "Diese Zeichenfolge wird als Name für das Protokolllimit verwendet.", + "placeholders": { + "limit": { + "content": "$1", + "example": "100" + } + } + }, "settings_html_save_button": { "message": "Speichern & Addon neustarten", "description": "Diese Zeichenfolge wird als Name für die Schaltfläche zum Speichern und erneuten Laden auf der Einstellungsseite verwendet." @@ -238,5 +280,29 @@ "local_hosts_skipping_title": { "message": "Auslassen von lokalen URLs", "description": "Diese Zeichenkette wird als Titel für das Auslassen von lokalen URLs verwendet." + }, + "blocked_html_title": { + "message": "Diese Seite wurde von ClearURLs blockiert.", + "description": "Diese Zeichenfolge wird als Titel auf der Hinweisseite für blockierte Seiten verwendet." + }, + "blocked_html_body": { + "message": "Diese Seite wurde vom ClearURLs Add-On blockiert, weil wir sie als Werbe- bzw. Trackingservice identifiziert haben. Um diese Seite zu besuchen, müssen Sie das Add-On temporär deaktivieren. Dann können Sie auf den untenstehenden Button klicken, um die Seite aufzurufen.", + "description": "Diese Zeichenfolge wird als Mengentext auf der Hinweisseite für blockierte Seiten verwendet." + }, + "blocked_html_button": { + "message": "Seite besuchen", + "description": "Diese Zeichenfolge wird als Buttontext auf der Hinweisseite für blockierte Seiten verwendet." + }, + "referral_marketing_enabled": { + "message": "Erlaube Referral-Marketing", + "description": "Diese Zeichenkette wird als Beschreibung für das Referral-Marketing verwendet." + }, + "referral_marketing_enabled_title": { + "message": "Erlaubt Referral-Marketing", + "description": "Diese Zeichenkette wird als Titel für das Referral-Marketing verwendet." + }, + "watchdog": { + "message": "[ClearURLs]: Der Watchdog hat ein Problem festgestellt. ClearURLs wird neugestartet.", + "description": "Diese Zeichenkette wird als Text für den Watchdog verwendet." } } diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 640235d..775e433 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -59,6 +59,22 @@ "message": "Reset the global log", "description": "This string is used as title for the reset button on the log page." }, + "log_html_export_button": { + "message": "Export", + "description": "This string is used for the export button on the log page." + }, + "log_html_export_button_title": { + "message": "Export the global log", + "description": "This string is used as title for the export button on the log page." + }, + "log_html_import_button": { + "message": "Import", + "description": "This string is used for the import button on the log page." + }, + "log_html_import_button_title": { + "message": "Import the global log", + "description": "This string is used as title for the import button on the log page." + }, "popup_html_configs_head": { "message": "Configs", "description": "This string is used as title for the configs on the popup page." @@ -163,10 +179,36 @@ "message": "Reset everything", "description": "This string is used as title for the reset button on the settings page." }, + "setting_html_export_button": { + "message": "Export", + "description": "This string is used as name for the export button on the settings page." + }, + "setting_html_export_button_title": { + "message": "Export everything", + "description": "This string is used as title for the export button on the settings page." + }, + "setting_html_import_button": { + "message": "Import", + "description": "This string is used as name for the reset button on the settings page." + }, + "setting_html_import_button_title": { + "message": "Import everything", + "description": "This string is used as title for the import button on the settings page." + }, "setting_rule_url_label": { "message": "The url to the data.json file (rules)", "description": "This string is used as name for the rule url label." }, + "setting_log_limit_label": { + "message": "Limits the log to $LIMIT$ entries. (-1 := ∞)", + "description": "This string is used as name for the log limit label.", + "placeholders": { + "limit": { + "content": "$1", + "example": "100" + } + } + }, "settings_html_save_button": { "message": "Save & reload addon", "description": "This string is used as name for the save&reload button on the settings page." @@ -238,5 +280,29 @@ "local_hosts_skipping_title": { "message": "Skipping URLs on local hosts", "description": "This string is used as title for the local host skipping switch" + }, + "blocked_html_title": { + "message": "This site was blocked by ClearURLs", + "description": "This string is used as title on the blocked site page." + }, + "blocked_html_body": { + "message": "This site was blocked by the ClearURLs Add-on, because we identify this site as an advertising and/or tracking service. To visit this site, you must temporarily deactivate the Add-on. After deactivating ClearURLs you can click on the button below to load the page.", + "description": "This string is used as body on the blocked site page." + }, + "blocked_html_button": { + "message": "Visit page", + "description": "This string is used as button on the blocked site page." + }, + "referral_marketing_enabled": { + "message": "Allow referral marketing", + "description": "This string is used as label for the referral marketing switch" + }, + "referral_marketing_enabled_title": { + "message": "Allows referral marketing", + "description": "This string is used as title for the referral marketing switch" + }, + "watchdog": { + "message": "[ClearURLs]: The watchdog has detected a problem. ClearURLs is restarted.", + "description": "This string is used as text for the watchdog" } } diff --git a/clearurls.js b/clearurls.js index 8d28ff5..c0b3e6d 100644 --- a/clearurls.js +++ b/clearurls.js @@ -31,22 +31,24 @@ var os; var currentURL; /** -* Helper function which remove the tracking fields -* for each provider given as parameter. -* -* @param {Provider} provider Provider-Object -* @return {Array} Array with changes and url fields -*/ -function removeFieldsFormURL(provider, pureUrl) + * Helper function which remove the tracking fields + * for each provider given as parameter. + * + * @param {Provider} provider Provider-Object + * @param pureUrl URL as String + * @param {boolean} quiet if the action should be displayed in log and statistics + * @return {Array} Array with changes and url fields + */ +function removeFieldsFormURL(provider, pureUrl, quiet = false) { - var url = pureUrl; - var domain = ""; - var fragments = ""; - var fields = ""; - var rules = provider.getRules(); - var changes = false; - var cancel = false; - var rawRules = provider.getRawRules(); + let url = pureUrl; + let domain = ""; + let fragments = ""; + let fields = ""; + let rules = provider.getRules(); + let changes = false; + let cancel = false; + let rawRules = provider.getRawRules(); if(storage.localHostsSkipping && checkLocalURL(pureUrl)) { return { @@ -60,19 +62,19 @@ function removeFieldsFormURL(provider, pureUrl) * Apply raw rules to the URL. */ rawRules.forEach(function(rawRule) { - var beforReplace = url; + let beforeReplace = url; url = url.replace(new RegExp(rawRule, "gi"), ""); - if(beforReplace !== url) { + if(beforeReplace !== url) { //Log the action - if(storage.loggingStatus) + if(storage.loggingStatus && !quiet) { - pushToLog(beforReplace, url, rawRule); + pushToLog(beforeReplace, url, rawRule); } if(badges[tabid] == null) badges[tabid] = 0; - increaseURLCounter(); + if(!quiet) increaseURLCounter(); checkOSAndroid().then((res) => { if(!res) { @@ -102,12 +104,13 @@ function removeFieldsFormURL(provider, pureUrl) * Expand the url by provider redirections. So no tracking on * url redirections form sites to sites. */ - var re = provider.getRedirection(url); + let re = provider.getRedirection(url); if(re !== null) { - url = decodeURIComponent(re); + url = decodeURL(re); + //Log the action - pushToLog(pureUrl, url, translate('log_redirect')); + if(!quiet) pushToLog(pureUrl, url, translate('log_redirect')); return { "redirect": true, @@ -129,30 +132,30 @@ function removeFieldsFormURL(provider, pureUrl) if(fields !== "" || fragments !== "") { rules.forEach(function(rule) { - var beforReplace = fields; - var beforReplaceFragments = fragments; + let beforeReplace = fields; + let beforeReplaceFragments = fragments; fields = fields.replace(new RegExp(rule, "gi"), ""); fragments = fragments.replace(new RegExp(rule, "gi"), ""); - if(beforReplace !== fields || beforReplaceFragments !== fragments) + if(beforeReplace !== fields || beforeReplaceFragments !== fragments) { //Log the action if(storage.loggingStatus) { - var tempURL = domain; - var tempBeforeURL = domain; + let tempURL = domain; + let tempBeforeURL = domain; if(fields !== "") tempURL += "?"+fields.replace("?&", "?").replace("?", ""); if(fragments !== "") tempURL += "#"+fragments.replace("#&","#").replace("#", ""); - if(beforReplace !== "") tempBeforeURL += "?"+beforReplace.replace("?&", "?").replace("?", ""); - if(beforReplaceFragments !== "") tempBeforeURL += "#"+beforReplaceFragments.replace("#&","#").replace("#", ""); + if(beforeReplace !== "") tempBeforeURL += "?"+beforeReplace.replace("?&", "?").replace("?", ""); + if(beforeReplaceFragments !== "") tempBeforeURL += "#"+beforeReplaceFragments.replace("#&","#").replace("#", ""); - pushToLog(tempBeforeURL, tempURL, rule); + if(!quiet) pushToLog(tempBeforeURL, tempURL, rule); } if(badges[tabid] == null) badges[tabid] = 0; - increaseURLCounter(); + if(!quiet) increaseURLCounter(); checkOSAndroid().then((res) => { if(!res) { @@ -171,7 +174,7 @@ function removeFieldsFormURL(provider, pureUrl) } }); - var finalURL = domain; + let finalURL = domain; if(fields !== "") finalURL += "?"+fields.replace("?", ""); if(fragments !== "") finalURL += "#"+fragments.replace("#", ""); @@ -180,13 +183,13 @@ function removeFieldsFormURL(provider, pureUrl) } if(provider.isCaneling()){ - pushToLog(pureUrl, pureUrl, translate('log_domain_blocked')); + if(!quiet) pushToLog(pureUrl, pureUrl, translate('log_domain_blocked')); if(badges[tabid] == null) { badges[tabid] = 0; } - increaseURLCounter(); + if(!quiet) increaseURLCounter(); checkOSAndroid().then((res) => { if(!res) { @@ -218,7 +221,7 @@ function start() * @param {JSON Object} obj */ function getKeys(obj){ - for(var key in obj){ + for(const key in obj){ prvKeys.push(key); } } @@ -229,9 +232,9 @@ function start() */ function createProviders() { - data = storage.ClearURLsData; + let data = storage.ClearURLsData; - for(var p = 0; p < prvKeys.length; p++) + for(let p = 0; p < prvKeys.length; p++) { //Create new provider providers.push(new Provider(prvKeys[p], data.providers[prvKeys[p]].completeProvider, @@ -241,25 +244,31 @@ function start() providers[p].setURLPattern(data.providers[prvKeys[p]].urlPattern); //Add rules to provider - for(var r = 0; r < data.providers[prvKeys[p]].rules.length; r++) + for(let r = 0; r < data.providers[prvKeys[p]].rules.length; r++) { providers[p].addRule(data.providers[prvKeys[p]].rules[r]); } //Add raw rules to provider - for(var raw = 0; raw < data.providers[prvKeys[p]].rawRules.length; raw++) + for(let raw = 0; raw < data.providers[prvKeys[p]].rawRules.length; raw++) { providers[p].addRawRule(data.providers[prvKeys[p]].rawRules[raw]); } + //Add referral marketing rules to provider + for(let referralMarketing = 0; referralMarketing < data.providers[prvKeys[p]].referralMarketing.length; referralMarketing++) + { + providers[p].addReferralMarketing(data.providers[prvKeys[p]].referralMarketing[referralMarketing]); + } + //Add exceptions to provider - for(var e = 0; e < data.providers[prvKeys[p]].exceptions.length; e++) + for(let e = 0; e < data.providers[prvKeys[p]].exceptions.length; e++) { providers[p].addException(data.providers[prvKeys[p]].exceptions[e]); } //Add redirections to provider - for(var re = 0; re < data.providers[prvKeys[p]].redirections.length; re++) + for(let re = 0; re < data.providers[prvKeys[p]].redirections.length; re++) { providers[p].addRedirection(data.providers[prvKeys[p]].redirections[re]); } @@ -288,22 +297,18 @@ function start() //Get the target hash from github fetch(storage.hashURL) .then(function(response){ - var responseTextHash = response.clone().text().then(function(responseTextHash){ - if(response.ok && $.trim(responseTextHash)) - { + const responseTextHash = response.clone().text().then(function (responseTextHash) { + if (response.ok && $.trim(responseTextHash)) { dataHash = responseTextHash; - if($.trim(dataHash) !== $.trim(localDataHash)) - { + if ($.trim(dataHash) !== $.trim(localDataHash)) { fetchFromURL(); - } - else { + } else { toObject(storage.ClearURLsData); storeHashStatus(1); saveOnDisk(['hashStatus']); } - } - else { + } else { dataHash = false; } }); @@ -322,18 +327,15 @@ function start() function checkResponse(response) { - var responseText = response.clone().text().then(function(responseText){ - if(response.ok && $.trim(responseText)) - { - var downloadedFileHash = $.sha256(responseText); + const responseText = response.clone().text().then(function (responseText) { + if (response.ok && $.trim(responseText)) { + const downloadedFileHash = $.sha256(responseText); - if($.trim(downloadedFileHash) === $.trim(dataHash)) - { + if ($.trim(downloadedFileHash) === $.trim(dataHash)) { storage.ClearURLsData = responseText; storage.dataHash = downloadedFileHash; storeHashStatus(2); - } - else { + } else { storeHashStatus(3); } storage.ClearURLsData = JSON.parse(storage.ClearURLsData); @@ -360,18 +362,20 @@ function start() * @param {boolean} _isActive Is the provider active? */ function Provider(_name, _completeProvider = false, _forceRedirection = false, _isActive = true){ - var name = _name; - var urlPattern; - var enabled_rules = {}; - var disabled_rules = {}; - var enabled_exceptions = {}; - var disabled_exceptions = {}; - var canceling = _completeProvider; - var enabled_redirections = {}; - var disabled_redirections = {}; - var active = _isActive; - var enabled_rawRules = {}; - var disabled_rawRules = {}; + let name = _name; + let urlPattern; + let enabled_rules = {}; + let disabled_rules = {}; + let enabled_exceptions = {}; + let disabled_exceptions = {}; + let canceling = _completeProvider; + let enabled_redirections = {}; + let disabled_redirections = {}; + let active = _isActive; + let enabled_rawRules = {}; + let disabled_rawRules = {}; + let enabled_referralMarketing = {}; + let disabled_referralMarketing = {}; if(_completeProvider){ enabled_rules[".*"] = true; @@ -419,6 +423,33 @@ function start() return urlPattern.test(url) && !(this.matchException(url)); }; + /** + * Apply a rule to a given tuple of rule array. + * @param enabledRuleArray array for enabled rules + * @param disabledRulesArray array for disabled rules + * @param {String} rule RegExp as string + * @param {boolean} isActive Is this rule active? + */ + this.applyRule = (enabledRuleArray, disabledRulesArray, rule, isActive = true) => { + if(isActive) + { + enabledRuleArray[rule] = true; + + if(disabledRulesArray[rule] !== undefined) + { + delete disabledRulesArray[rule]; + } + } + else { + disabledRulesArray[rule] = true; + + if(enabledRuleArray[rule] !== undefined) + { + delete enabledRuleArray[rule]; + } + } + }; + /** * Add a rule to the rule array * and replace old rule with new rule. @@ -429,23 +460,7 @@ function start() this.addRule = function(rule, isActive = true) { rule = "([\\/|\\?|#]|(&|&))+("+rule+"=[^\\/|\\?|&]*)"; - if(isActive) - { - enabled_rules[rule] = true; - - if(disabled_rules[rule] !== undefined) - { - delete disabled_rules[rule]; - } - } - else { - disabled_rules[rule] = true; - - if(enabled_rules[rule] !== undefined) - { - delete enabled_rules[rule]; - } - } + this.applyRule(enabled_rules, disabled_rules, rule, isActive); }; /** @@ -465,23 +480,7 @@ function start() * @param {boolean} isActive Is this rule active? */ this.addRawRule = function(rule, isActive = true) { - if(isActive) - { - enabled_rawRules[rule] = true; - - if(disabled_rawRules[rule] !== undefined) - { - delete disabled_rawRules[rule]; - } - } - else { - disabled_rawRules[rule] = true; - - if(enabled_rawRules[rule] !== undefined) - { - delete enabled_rawRules[rule]; - } - } + this.applyRule(enabled_rawRules, disabled_rawRules, rule, isActive); }; /** @@ -490,9 +489,24 @@ function start() * @return Array RegExp strings */ this.getRawRules = function() { + if(!storage.referralMarketing) { + return Object.keys(Object.assign(enabled_rawRules, enabled_referralMarketing)); + } + return Object.keys(enabled_rawRules); }; + /** + * Add a referral marketing rule to the referral marketing array + * and replace old referral marketing rule with new referral marketing rule. + * + * @param {String} rule RegExp as string + * @param {boolean} isActive Is this rule active? + */ + this.addReferralMarketing = function(rule, isActive = true) { + this.applyRule(enabled_referralMarketing, disabled_referralMarketing, rule, isActive); + }; + /** * Add a exception to the exceptions array * and replace old with new exception. @@ -528,15 +542,15 @@ function start() * @return {boolean} if matching? true: false */ this.matchException = function(url) { - var result = false; + let result = false; //Add the site blocked alert to every exception - if(url == siteBlockedAlert) return true; + if(url === siteBlockedAlert) return true; - for(var exception in enabled_exceptions) { + for(const exception in enabled_exceptions) { if(result) break; - exception_regex = new RegExp(exception, "i"); + let exception_regex = new RegExp(exception, "i"); result = exception_regex.test(url); } @@ -576,10 +590,10 @@ function start() * @return url */ this.getRedirection = function(url) { - var re = null; + let re = null; - for(var redirection in enabled_redirections) { - result = (url.match(new RegExp(redirection, "i"))); + for(const redirection in enabled_redirections) { + let result = (url.match(new RegExp(redirection, "i"))); if (result && result.length > 0 && redirection) { @@ -603,13 +617,13 @@ function start() */ function clearUrl(request) { - var URLbeforeReplaceCount = countFields(request.url); + const URLbeforeReplaceCount = countFields(request.url); //Add Fields form Request to global url counter increaseGlobalURLCounter(URLbeforeReplaceCount); if(storage.globalStatus){ - var result = { + let result = { "changes": false, "url": "", "redirect": false, @@ -619,7 +633,7 @@ function start() /* * Call for every provider the removeFieldsFormURL method. */ - for (var i = 0; i < providers.length; i++) { + for (let i = 0; i < providers.length; i++) { if(providers[i].matchURL(request.url)) { @@ -648,9 +662,16 @@ function start() * to inform the user about the full url blocking. */ if(result.cancel){ - return { - redirectUrl: siteBlockedAlert - }; + if(request.type === 'main_frame') { + const blockingPage = browser.extension.getURL("html/siteBlockedAlert.html?source="+encodeURIComponent(request.url)); + browser.tabs.update(request.tabId, {url: blockingPage}); + + return {cancel: true}; + } else { + return { + redirectUrl: siteBlockedAlert + }; + } } /* @@ -719,8 +740,7 @@ function start() return {}; } else { - var ret = clearUrl(requestDetails); - return ret; + return clearUrl(requestDetails); } } @@ -732,9 +752,9 @@ function start() * @return {boolean} */ function isDataURL(requestDetails) { - var s = requestDetails.url; + const s = requestDetails.url; - return s.substring(0,4) == "data"; + return s.substring(0,4) === "data"; } /** @@ -761,8 +781,15 @@ function start() */ function pushToLog(beforeProcessing, afterProcessing, rule) { - if(storage.loggingStatus) + const limit = storage.logLimit; + if(storage.loggingStatus && limit !== 0) { + if(limit > 0 && !isNaN(limit)) { + while(storage.log.log.length >= limit) { + storage.log.log.shift(); + } + } + storage.log.log.push( { "before": beforeProcessing, diff --git a/core_js/cleaning_tool.js b/core_js/cleaning_tool.js index 259f733..b4a7f37 100644 --- a/core_js/cleaning_tool.js +++ b/core_js/cleaning_tool.js @@ -33,9 +33,9 @@ $(document).ready(function(){ * 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'); + const cleanTArea = $('#cleanURLs'); + const dirtyTArea = $('#dirtyURLs'); + const urls = dirtyTArea.val().split('\n'); cleanedURLs = []; length = urls.length; diff --git a/core_js/google_link_fix.js b/core_js/google_link_fix.js index 0b73dbf..095436c 100644 --- a/core_js/google_link_fix.js +++ b/core_js/google_link_fix.js @@ -25,8 +25,8 @@ "use strict"; function injectFunction() { - var ele = document.createElement('script'); - var s = document.getElementsByTagName('script')[0]; + let ele = document.createElement('script'); + let s = document.getElementsByTagName('script')[0]; ele.type = 'text/javascript'; ele.textContent = "Object.defineProperty(window, 'rwt', {" + @@ -46,13 +46,13 @@ injectFunction(); document.addEventListener('mouseover', function (event) { - var a = event.target, depth = 1; + let a = event.target, depth = 1; - while (a && a.tagName != 'A' && depth-- > 0) { + while (a && a.tagName !== 'A' && depth-- > 0) { a = a.parentNode; } - if (a && a.tagName == 'A') { + if (a && a.tagName === 'A') { try { a.removeAttribute('data-cthref'); delete a.dataset.cthref; diff --git a/core_js/historyListener.js b/core_js/historyListener.js index 2d6a6ac..8ea5783 100644 --- a/core_js/historyListener.js +++ b/core_js/historyListener.js @@ -37,14 +37,16 @@ function historyListenerStart() { * which is associated with the new history entry created by replaceState() */ function historyCleaner(details) { - var urlBefore = details.url; - var urlAfter = pureCleaning(details.url); + if(storage.globalStatus) { + const urlBefore = details.url; + const urlAfter = pureCleaning(details.url); - if(urlBefore != urlAfter) { - browser.tabs.executeScript(details.tabId, { - frameId: details.frameId, - code: 'history.replaceState({state: "cleaned_history"},"",'+JSON.stringify(urlAfter)+');' - }).then(() => {}, onError); + if(urlBefore !== urlAfter) { + browser.tabs.executeScript(details.tabId, { + frameId: details.frameId, + code: 'history.replaceState({state: "cleaned_history"},"",'+JSON.stringify(urlAfter)+');' + }).then(() => {}, onError); + } } } diff --git a/core_js/log.js b/core_js/log.js index 10cfdbd..d8c3f26 100644 --- a/core_js/log.js +++ b/core_js/log.js @@ -26,7 +26,7 @@ var log = {}; * Reset the global log */ function resetGlobalLog(){ - obj = {"log": []}; + let obj = {"log": []}; browser.runtime.sendMessage({ function: "setData", @@ -53,11 +53,11 @@ function getLog() return b.timestamp - a.timestamp; }); - var length = Object.keys(log.log).length; - var row; - if(length != 0) + const length = Object.keys(log.log).length; + let row; + if(length !== 0) { - for(var i=0; i"+log.log[i].before+"" + @@ -81,11 +81,9 @@ function getLog() */ function getDataTableTranslation() { - var lang = browser.i18n.getUILanguage(); + let lang = browser.i18n.getUILanguage(); lang = lang.substring(0,2); - var file = browser.extension.getURL('./external_js/dataTables/i18n/'+lang+'.lang'); - - return file; + return browser.extension.getURL('./external_js/dataTables/i18n/' + lang + '.lang'); } /** @@ -96,6 +94,42 @@ function toDate(time) return new Date(time).toLocaleString(); } +/** + * This function export the global log as json file. + */ +function exportGlobalLog() { + browser.runtime.sendMessage({ + function: "getData", + params: ['log'] + }).then((data) => { + let blob = new Blob([JSON.stringify(data.response)], {type: 'application/json'}); + + browser.downloads.download({ + 'url': URL.createObjectURL(blob), + 'filename': 'ClearURLsLogExport.json', + 'saveAs': true + }); + }); +} + +/** + * This function imports an exported global log and overwrites the old one. + */ +function importGlobalLog(evt) { + let file = evt.target.files[0]; + let fileReader = new FileReader(); + + fileReader.onload = function(e) { + browser.runtime.sendMessage({ + function: "setData", + params: ["log", e.target.result] + }).then(() => { + location.reload(); + }, handleError); + }; + fileReader.readAsText(file); +} + /** * Load only when document is ready */ @@ -103,6 +137,8 @@ $(document).ready(function(){ setText(); getLog(); $('#reset_log_btn').on("click", resetGlobalLog); + $('#export_log_btn').on("click", exportGlobalLog); + $('#importLog').on("change", importGlobalLog); }); /** @@ -122,12 +158,16 @@ function setText() { document.title = translate('log_html_page_title'); $('#page_title').text(translate('log_html_page_title')); - $('#reset_log_btn').text(translate('log_html_reset_button')); - $('#log_html_reset_button').prop('title', translate('log_html_reset_button_title')); + $('#reset_log_btn').text(translate('log_html_reset_button')) + .prop('title', translate('log_html_reset_button_title')); $('#head_1').text(translate('log_html_table_head_1')); $('#head_2').text(translate('log_html_table_head_2')); $('#head_3').text(translate('log_html_table_head_3')); $('#head_4').text(translate('log_html_table_head_4')); + $('#export_log_btn_text').text(translate('log_html_export_button')); + $('#export_log_btn').prop('title', translate('log_html_export_button_title')); + $('#import_log_btn_text').text(translate('log_html_import_button')); + $('#import_log_btn').prop('title', translate('log_html_import_button_title')); } function handleError(error) { diff --git a/core_js/message_handler.js b/core_js/message_handler.js index fe4fa03..8010201 100644 --- a/core_js/message_handler.js +++ b/core_js/message_handler.js @@ -29,11 +29,11 @@ */ function handleMessage(request, sender, sendResponse) { - var fn = window[request.function]; + let fn = window[request.function]; if(typeof fn === "function") { - var response = fn.apply(null, request.params); + let response = fn.apply(null, request.params); return Promise.resolve({response}); } diff --git a/core_js/popup.js b/core_js/popup.js index 04c3038..8035ed7 100644 --- a/core_js/popup.js +++ b/core_js/popup.js @@ -107,7 +107,6 @@ function init() /** * Get the globalCounter and globalurlcounter value from the storage -* @param {(data){} Return value form storage */ function changeStatistics() { @@ -127,7 +126,7 @@ function changeStatistics() */ function setHashStatus() { - var element = $('#hashStatus'); + let element = $('#hashStatus'); if(hashStatus) { @@ -146,7 +145,7 @@ function setHashStatus() */ function changeSwitchButton(id, storageID) { - var element = $('#'+id); + let element = $('#'+id); changeVisibility(id, storageID); @@ -155,7 +154,7 @@ function changeSwitchButton(id, storageID) function: "setData", params: [storageID, element.is(':checked')] }).then((data) => { - if(storageID == "globalStatus"){ + if(storageID === "globalStatus"){ browser.runtime.sendMessage({ function: "changeIcon", params: [] @@ -176,7 +175,7 @@ function changeSwitchButton(id, storageID) */ function changeVisibility(id, storageID) { - var element; + let element; switch(storageID) { @@ -190,7 +189,7 @@ function changeVisibility(id, storageID) element = "undefine"; } - if(element != "undefine") + if(element !== "undefine") { if($('#'+id).is(':checked')) { @@ -211,7 +210,7 @@ function changeVisibility(id, storageID) */ function setSwitchButton(id, varname) { - var element = $('#'+id); + let element = $('#'+id); element.prop('checked', this[varname]); } @@ -281,11 +280,11 @@ function setText() * * @param {string} id ID of the HTML element * @param {string} attribute Name of the attribute used for localization -* @param {boolean} tooltip +* @param {string} tooltip */ -function injectText(id, attribute, tooltip) +function injectText(id, attribute, tooltip = "") { - object = $('#'+id); + let object = $('#'+id); object.text(translate(attribute)); /* @@ -294,7 +293,7 @@ function injectText(id, attribute, tooltip) */ tooltip = translate(attribute+"_title"); - if(tooltip != "") + if(tooltip !== "") { object.prop('title', tooltip); } diff --git a/core_js/pureCleaning.js b/core_js/pureCleaning.js index aeca4fe..2345bbc 100644 --- a/core_js/pureCleaning.js +++ b/core_js/pureCleaning.js @@ -19,20 +19,23 @@ /*jshint esversion: 6 */ /** -* Cleans given links. Also do automatic redirection. -* -* @param {[type]} url url as string -* @return {Array} redirectUrl or none -*/ -function pureCleaning(url) { - var cleanURL = url; - var URLbeforeReplaceCount = countFields(url); + * Cleans given links. Also do automatic redirection. + * + * @param {String} url url as string + * @param {boolean} quiet if the action should be displayed in log and statistics + * @return {String} redirectUrl or none + */ +function pureCleaning(url, quiet = false) { + let cleanURL = url; + const URLbeforeReplaceCount = countFields(url); - //Add Fields form Request to global url counter - increaseGlobalURLCounter(URLbeforeReplaceCount); + if(!quiet) { + //Add Fields form Request to global url counter + increaseGlobalURLCounter(URLbeforeReplaceCount); + } - for (var i = 0; i < providers.length; i++) { - var result = { + for (let i = 0; i < providers.length; i++) { + let result = { "changes": false, "url": "", "redirect": false, @@ -41,7 +44,7 @@ function pureCleaning(url) { if(providers[i].matchURL(cleanURL)) { - result = removeFieldsFormURL(providers[i], cleanURL); + result = removeFieldsFormURL(providers[i], cleanURL, quiet); cleanURL = result.url; } diff --git a/core_js/settings.js b/core_js/settings.js index b701ec9..7699b44 100644 --- a/core_js/settings.js +++ b/core_js/settings.js @@ -27,6 +27,8 @@ $(document).ready(function(){ setText(); $(".pick-a-color").pickAColor(); $('#reset_settings_btn').on("click", reset); + $('#export_settings_btn').on("click", exportSettings); + $('#importSettings').on("change", importSettings); $('#save_settings_btn').on("click", save); $("#badged_color input").on("change", function () { @@ -101,6 +103,11 @@ function save() params: ["types", $('input[name=types]').val()] }).then(handleResponse, handleError); + browser.runtime.sendMessage({ + function: "setData", + params: ["logLimit", $('input[name=logLimit]').val()] + }).then(handleResponse, handleError); + browser.runtime.sendMessage({ function: "saveOnExit", params: [] @@ -113,13 +120,14 @@ function save() } /** -* Translate a string with the i18n API. -* -* @param {string} string Name of the attribute used for localization + * Translate a string with the i18n API. + * + * @param {string} string Name of the attribute used for localization + * @param {string[]} placeholders Array of placeholders */ -function translate(string) +function translate(string, ...placeholders) { - return browser.i18n.getMessage(string); + return browser.i18n.getMessage(string, placeholders); } /** @@ -147,6 +155,18 @@ function getData() params: ["types"] }).then((data) => handleResponseData(data, "types", "types"), handleError); + browser.runtime.sendMessage({ + function: "getData", + params: ["logLimit"] + }).then((data) => { + handleResponseData(data, "logLimit", "logLimit"); + if(data.response === undefined || data.response === -1) { + $('#logLimit_label').text(translate('setting_log_limit_label', "∞")); + } else { + $('#logLimit_label').text(translate('setting_log_limit_label', data.response)); + } + }, handleError); + browser.runtime.sendMessage({ function: "getData", params: ["contextMenuEnabled"] @@ -162,9 +182,16 @@ function getData() params: ["localHostsSkipping"] }).then((data) => { handleResponseData(data, "localHostsSkipping", "localHostsSkipping"); - changeSwitchButton("contextMenuEnabled", "contextMenuEnabled"); - changeSwitchButton("historyListenerEnabled", "historyListenerEnabled"); - changeSwitchButton("localHostsSkipping", "localHostsSkipping"); + browser.runtime.sendMessage({ + function: "getData", + params: ["referralMarketing"] + }).then((data) => { + handleResponseData(data, "referralMarketing", "referralMarketing"); + changeSwitchButton("contextMenuEnabled", "contextMenuEnabled"); + changeSwitchButton("historyListenerEnabled", "historyListenerEnabled"); + changeSwitchButton("localHostsSkipping", "localHostsSkipping"); + changeSwitchButton("referralMarketing", "referralMarketing"); + }, handleError); }, handleError); }, handleError); }, handleError); @@ -178,22 +205,74 @@ function setText() document.title = translate('settings_html_page_title'); $('#page_title').text(translate('settings_html_page_title')); $('#badged_color_label').text(translate('badged_color_label')); - $('#reset_settings_btn').text(translate('setting_html_reset_button')); - $('#reset_settings_btn').prop('title', translate('setting_html_reset_button_title')); + $('#reset_settings_btn').text(translate('setting_html_reset_button')) + .prop('title', translate('setting_html_reset_button_title')); $('#rule_url_label').text(translate('setting_rule_url_label')); $('#hash_url_label').text(translate('setting_hash_url_label')); $('#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')); + $('#save_settings_btn').text(translate('settings_html_save_button')) + .prop('title', translate('settings_html_save_button_title')); injectText("context_menu_enabled", "context_menu_enabled"); $('#history_listener_enabled').html(translate('history_listener_enabled')); injectText("local_hosts_skipping", "local_hosts_skipping"); + $('#export_settings_btn_text').text(translate('setting_html_export_button')); + $('#export_settings_btn').prop('title', translate('setting_html_export_button_title')); + $('#import_settings_btn_text').text(translate('setting_html_import_button')); + $('#import_settings_btn').prop('title', translate('setting_html_import_button_title')); + injectText("referral_marketing_enabled", "referral_marketing_enabled"); } /** -* Handle the response from the storage and saves the data. -* @param {JSON-Object} data Data JSON-Object -*/ + * This function exports all ClearURLs settings with statistics and rules. + */ +function exportSettings() { + browser.runtime.sendMessage({ + function: "storageAsJSON", + params: [] + }).then((data) => { + let blob = new Blob([JSON.stringify(data.response)], {type: 'application/json'}); + + browser.downloads.download({ + 'url': URL.createObjectURL(blob), + 'filename': 'ClearURLs.conf', + 'saveAs': true + }); + }); +} + +/** + * This function imports an exported ClearURLs setting and overwrites the old one. + */ +function importSettings(evt) { + let file = evt.target.files[0]; + let fileReader = new FileReader(); + + fileReader.onload = function(e) { + let data = JSON.parse(e.target.result); + const length = Object.keys(data).length; + let i=0; + + Object.entries(data).forEach(([key, value]) => { + browser.runtime.sendMessage({ + function: "setData", + params: [key, value] + }).then(() => { + i++; + if(i === length) { + location.reload(); + } + }, handleError); + }); + }; + fileReader.readAsText(file); +} + +/** + * Handle the response from the storage and saves the data. + * @param {JSON-Object} data Data JSON-Object + * @param varName + * @param inputID + */ function handleResponseData(data, varName, inputID) { settings[varName] = data.response; @@ -215,14 +294,14 @@ function handleError(error) { */ function changeSwitchButton(id, storageID) { - var element = $('#'+id); + let element = $('#'+id); element.on('change', function(){ browser.runtime.sendMessage({ function: "setData", params: [storageID, element.is(':checked')] }).then((data) => { - if(storageID == "globalStatus"){ + if(storageID === "globalStatus"){ browser.runtime.sendMessage({ function: "changeIcon", params: [] @@ -245,9 +324,9 @@ function changeSwitchButton(id, storageID) * @param {string} attribute Name of the attribute used for localization * @param {boolean} tooltip */ -function injectText(id, attribute, tooltip) +function injectText(id, attribute, tooltip = "") { - object = $('#'+id); + let object = $('#'+id); object.text(translate(attribute)); /* @@ -256,7 +335,7 @@ function injectText(id, attribute, tooltip) */ tooltip = translate(attribute+"_title"); - if(tooltip != "") + if(tooltip !== "") { object.prop('title', tooltip); } @@ -269,6 +348,6 @@ function injectText(id, attribute, tooltip) */ function setSwitchButton(id, varname) { - var element = $('#'+id); + let element = $('#'+id); element.prop('checked', settings[varname]); } diff --git a/core_js/siteBlockedAlert.js b/core_js/siteBlockedAlert.js new file mode 100644 index 0000000..3f2a6d0 --- /dev/null +++ b/core_js/siteBlockedAlert.js @@ -0,0 +1,51 @@ +/* +* 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 the blocked alert page. +*/ + +/** + * Set the text for the UI. + */ +function setText() +{ + document.title = translate('blocked_html_title'); + $('#title').html(translate('blocked_html_title')); + $('#body').html(translate('blocked_html_body')); + $('#page').text(translate('blocked_html_button')); + +} + +$(document).ready(function(){ + setText(); + + let source = new URLSearchParams(window.location.search).get("source"); + $('#page').attr('href', decodeURIComponent(source)); +}); + +/** + * 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); +} \ No newline at end of file diff --git a/core_js/storage.js b/core_js/storage.js index 6ab6b69..68c3f59 100644 --- a/core_js/storage.js +++ b/core_js/storage.js @@ -29,23 +29,39 @@ var pendingSaves = new Set(); */ function saveOnExit() { - var json = {}; + saveOnDisk(Object.keys(storage)); +} + +/** + * Returns the storage as JSON. + */ +function storageAsJSON() { + let json = {}; Object.entries(storage).forEach(([key, value]) => { - switch (key) { - case "ClearURLsData": - case "log": - json[key] = JSON.stringify(value); - break; - case "types": - json[key] = value.toString(); - break; - default: - json[key] = value; - } + json[key] = storageDataAsString(key); }); - console.log(translate('core_save_on_disk')); - browser.storage.local.set(json); + + return json; +} + +/** + * Converts a given storage data to its string representation. + * @param key key of the storage data + * @returns {string} string representation + */ +function storageDataAsString(key) { + let value = storage[key]; + + switch (key) { + case "ClearURLsData": + case "log": + return JSON.stringify(value); + case "types": + return value.toString(); + default: + return value; + } } /** @@ -54,22 +70,12 @@ function saveOnExit() */ function saveOnDisk(keys) { - var json = {}; + let json = {}; keys.forEach(function(key) { - var value = storage[key]; - switch (key) { - case "ClearURLsData": - case "log": - json[key] = JSON.stringify(value); - break; - case "types": - json[key] = value.toString(); - break; - default: - json[key] = value; - } + json[key] = storageDataAsString(key); }); + console.log(translate('core_save_on_disk')); browser.storage.local.set(json); } @@ -145,17 +151,20 @@ function setData(key, value) switch (key) { case "ClearURLsData": case "log": - storage[key] = JSON.parse(value); - break; + storage[key] = JSON.parse(value); + break; case "hashURL": case "ruleURL": - storage[key] = replaceOldURLs(value); - break; + storage[key] = replaceOldURLs(value); + break; case "types": - storage[key] = value.split(','); - break; + storage[key] = value.split(','); + break; + case "logLimit": + storage[key] = Number(value); + break; default: - storage[key] = value; + storage[key] = value; } } @@ -204,6 +213,8 @@ function initSettings() storage.contextMenuEnabled = true; storage.historyListenerEnabled = true; storage.localHostsSkipping = true; + storage.referralMarketing = false; + storage.logLimit = -1; 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"]; diff --git a/core_js/tools.js b/core_js/tools.js index d02aa61..3be2d6e 100644 --- a/core_js/tools.js +++ b/core_js/tools.js @@ -75,19 +75,13 @@ async function checkOSAndroid() }); } - if(os == "android") - { - return true; - } - else{ - return false; - } + return os === "android"; } /** * Extract the host without port from an url. * @param {String} url URL as String -* @return {Array} host as string +* @return {String} host as string */ function extractHost(url) { let parsed_url = new URL(url); @@ -126,8 +120,8 @@ function countFields(url) */ function existsFields(url) { - var matches = (url.match(/\?.+/i) || []); - var count = matches.length; + let matches = (url.match(/\?.+/i) || []); + let count = matches.length; return (count > 0); } @@ -140,7 +134,7 @@ function existsFields(url) function extractFileds(url) { if(existsFields(url)) { - var fields = url.replace(new RegExp(".*?\\?", "i"), ""); + let fields = url.replace(new RegExp(".*?\\?", "i"), ""); if(existsFragments(url)) { fields = fields.replace(new RegExp("#.*", "i"), ""); } @@ -169,7 +163,7 @@ function countFragments(url) function extractFragments(url) { if(existsFragments(url)) { - var fragments = url.replace(new RegExp(".*?#", "i"), ""); + let fragments = url.replace(new RegExp(".*?#", "i"), ""); return (fragments.match(/[^&]+=?[^&]*/gi) || []); } @@ -183,8 +177,8 @@ function extractFragments(url) */ function existsFragments(url) { - var matches = (url.match(/\#.+/i) || []); - var count = matches.length; + let matches = (url.match(/\#.+/i) || []); + let count = matches.length; return (count > 0); } @@ -298,3 +292,16 @@ function getBrowser() { return "Chrome"; } } + +/** + * Decodes an URL, also one that is encoded multiple times. + * @param url the url, that should be decoded + */ +function decodeURL(url) { + const rtn = decodeURIComponent(url); + if(rtn.indexOf("http://") === -1 && rtn.indexOf("https://") === -1) { + return decodeURL(rtn); + } + + return rtn; +} \ No newline at end of file diff --git a/core_js/watchdog.js b/core_js/watchdog.js new file mode 100644 index 0000000..b551ed2 --- /dev/null +++ b/core_js/watchdog.js @@ -0,0 +1,38 @@ +/* +* 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 to check in fixed intervals, that ClearURLs works properly. +* In issue #203, some users reported, that ClearURLs filter function doesn't work after +* some time, but without any recognizable reason. +* +* This watchdog restarts the whole Add-on, when the check fails. +*/ +const CHECK_INTERVAL = 15000; + +setInterval(function() { + const dirtyURL = "https://clearurls.roebert.eu?utm_source=addon"; + const cleanURL = "https://clearurls.roebert.eu"; + + if(pureCleaning(dirtyURL, true) !== cleanURL) { + console.log(translate('watchdog')); + saveOnExit(); + reload(); + } +}, CHECK_INTERVAL); \ No newline at end of file diff --git a/core_js/write_version.js b/core_js/write_version.js index 10a02a4..23f5370 100644 --- a/core_js/write_version.js +++ b/core_js/write_version.js @@ -20,5 +20,5 @@ * This file writes only the version into every page. * @return version */ -var version = browser.runtime.getManifest().version; +const version = browser.runtime.getManifest().version; $('#version').text(version); diff --git a/data/data.min.json b/data/data.min.json index 0dbd0a1..c52aba5 100644 --- a/data/data.min.json +++ b/data/data.min.json @@ -28,12 +28,15 @@ "keywords", "cv_ct_[a-zA-Z]+", "linkCode", - "tag", "creativeASIN", "ascsubtag", "aaxitk", "hsa_cr_id", - "sb-ci-[a-zA-Z]+" + "sb-ci-[a-zA-Z]+", + "rnid" + ], + "referralMarketing": [ + "([\\/|\\?|#]|(&|&))+(tag=[^\\/|\\?|&]*)" ], "exceptions": [ ".*(amazon\\.).*(\\/gp).*\\/redirector.html\\/.*", @@ -50,6 +53,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(fls-na\\.amazon)(\\.[a-zA-Z]{2,}).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -92,6 +96,7 @@ "sourceid", "sxsrf" ], + "referralMarketing": [], "rawRules": [], "exceptions": [ ".*(mail\\.google\\.).*(\\/mail\\/u\\/0).*", @@ -119,15 +124,17 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(googlesyndication)(\\.[a-zA-Z]{2,}).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], "forceRedirection": false }, "doubleclick": { - "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(doubleclick)(\\.[a-zA-Z]{2,}).*", + "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-\\.]*)?(doubleclick)(\\.[a-zA-Z]{2,}).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -139,6 +146,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(googleadservices)(\\.[a-zA-Z]{2,}).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -178,6 +186,7 @@ "(%3F)?cmpid", "(%3F)?os_ehash" ], + "referralMarketing": [], "rawRules": [], "exceptions": [ ".*([\\.]?matrix\\.org)(\\/_matrix)\\/.*", @@ -206,6 +215,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(adtech)(\\.[a-zA-Z]{2,}).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -215,6 +225,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(contentpass\\.(net|de)).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -224,6 +235,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(bf-ad)(\\.[a-zA-Z]{2,}).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -233,6 +245,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(amazon-adsystem)(\\.[a-zA-Z]{2,}).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -244,6 +257,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(adsensecustomsearchads)(\\.[a-zA-Z]{2,}).*", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -257,6 +271,7 @@ "gclid", "kw" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -268,6 +283,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(youtube)(\\.[a-zA-Z]{2,})\\/pagead", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -277,6 +293,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(youtube)(\\.[a-zA-Z]{2,})\\/api\\/stats\\/ads", "completeProvider": true, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -303,6 +320,7 @@ "action_history", "fbid" ], + "referralMarketing": [], "rawRules": [], "exceptions": [ ".*(facebook\\.)\\w{2,}.*(\\/plugins\\/).*", @@ -325,6 +343,7 @@ "cn", "ref_url" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -334,6 +353,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(reddit)(\\.[a-zA-Z]{2,}).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -350,6 +370,7 @@ "tctx", "jb[a-zA-Z]*" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -363,6 +384,7 @@ "sr", "sr_share" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -380,6 +402,7 @@ "qs", "qp" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -392,6 +415,7 @@ "nb", "u" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -404,6 +428,7 @@ "tt_medium", "tt_content" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -416,6 +441,7 @@ "pk_campaign", "pk_kwd" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -429,6 +455,7 @@ "alid", "[a-zA-Z]*tk" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -447,6 +474,7 @@ "exp", "plim" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -461,6 +489,7 @@ "_from", "hash" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -472,6 +501,7 @@ "rules": [ "ftag" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -484,6 +514,7 @@ "ref_", "pf_rd_[a-zA-Z]*" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -493,6 +524,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(govdelivery\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -508,6 +540,7 @@ "u1", "ath[a-zA-Z]*" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -519,6 +552,7 @@ "rules": [ "pl" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -530,6 +564,7 @@ "rules": [ "xid" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -542,6 +577,7 @@ "spm", "tpa" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -553,6 +589,7 @@ "rules": [ "smid" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -564,6 +601,7 @@ "rules": [ "wbdcd" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -575,6 +613,7 @@ "rules": [ "snr" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -584,6 +623,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(steamcommunity\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -595,6 +635,7 @@ "urlPattern": "(https:\\/\\/|http:\\/\\/)([a-zA-Z0-9-]*\\.)?(disq\\.us).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -606,6 +647,7 @@ "urlPattern": "https?:\\/\\/outgoing\\.prod\\.mozaws\\.net/.*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -619,6 +661,7 @@ "rules": [ "src" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -631,6 +674,7 @@ "src", "platform" ], + "referralMarketing": [], "rawRules": [], "exceptions": [ ".*mozilla.org\\/api.*" @@ -644,6 +688,7 @@ "rules": [ "ref" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -655,6 +700,7 @@ "rules": [ "email" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -667,6 +713,7 @@ "email_token", "email_source" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -676,6 +723,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(deviantart\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -687,6 +735,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(site2\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -698,6 +747,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(site\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -709,6 +759,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(site3\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -729,6 +780,7 @@ "scm[_a-zA-Z\\-]*", "pvid" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -740,6 +792,7 @@ "rules": [ "sid" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -751,6 +804,7 @@ "rules": [ "ref" ], + "referralMarketing": [], "rawRules": [], "exceptions": [ ".*(comment-cdn\\.9gag\\.com).*(\\/comment-list.json\\?).*" @@ -762,6 +816,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(linksynergy\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -775,6 +830,7 @@ "rules": [ "ref" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -784,6 +840,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(gate\\.sc).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -795,6 +852,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(vk\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -808,6 +866,7 @@ "rules": [ "ref_?" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -824,6 +883,7 @@ "sd", "bi" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -833,6 +893,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(curseforge\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -844,6 +905,7 @@ "urlPattern": "https?:\\/\\/([a-zA-Z0-9-]*\\.)?(messenger\\.com).*", "completeProvider": false, "rules": [], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [ @@ -857,6 +919,7 @@ "rules": [ "__twitter_impression" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -868,6 +931,7 @@ "rules": [ "partner" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -883,6 +947,7 @@ "email_referrer", "email_subject" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], @@ -896,6 +961,7 @@ "trk", "li[a-zA-Z]{2}" ], + "referralMarketing": [], "rawRules": [], "exceptions": [], "redirections": [], diff --git a/html/log.html b/html/log.html index c259fac..f7554ce 100644 --- a/html/log.html +++ b/html/log.html @@ -32,10 +32,27 @@ along with this program. If not, see . @@ -58,7 +75,23 @@ along with this program. If not, see .

+ class="btn btn-danger" title="Reset the global log"> + + + + + + + + + + +

. word-wrap: break-word; max-width: 200px; } + + .fileinput-button { + position: relative; + overflow: hidden; + display: inline-block; + } + + .fileinput-button input { + position: absolute; + top: 0; + right: 0; + margin: 0; + opacity: 0; + -ms-filter: 'alpha(opacity=0)'; + direction: ltr; + cursor: pointer; + } @@ -60,6 +77,19 @@ along with this program. If not, see .

+ + + + + + + +


@@ -80,9 +110,14 @@ along with this program. If not, see .


- -
- +

+
+

+
+ +

+
+



+

+
+ +

+

diff --git a/html/siteBlockedAlert.html b/html/siteBlockedAlert.html index 4ad2937..f373c60 100644 --- a/html/siteBlockedAlert.html +++ b/html/siteBlockedAlert.html @@ -20,7 +20,7 @@ along with this program. If not, see . - This site or element was blocked by ClearURLs Add-on + This site was blocked by ClearURLs Add-on @@ -53,12 +53,10 @@ along with this program. If not, see .

-

This site or element was blocked by ClearURLs Add-on

+

+

- This site or element was blocked by ClearURLs Add-on, because we identify this site as an - advertising and/or tracking service. -
- To visit this site, you must temporarily deactivate the Add-on. +

@@ -66,13 +64,16 @@ along with this program. If not, see .
+ + + diff --git a/manifest.json b/manifest.json index 4e1b492..85b7df6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "ClearURLs", - "version": "1.8.5", + "version": "1.9.0", "author": "Kevin Röbert", "description": "Remove tracking elements from URLs.", "homepage_url": "https://gitlab.com/KevinRoebert/ClearUrls", @@ -52,7 +52,8 @@ "contextMenus", "clipboardWrite", "webNavigation", - "tabs" + "tabs", + "downloads" ], "background": { "scripts": [ @@ -60,13 +61,14 @@ "external_js/jquery-3.2.1.min.js", "external_js/sha256.jquery.js", "core_js/message_handler.js", + "external_js/ip-range-check.js", "core_js/tools.js", - "core_js/storage.js", - "clearurls.js", "core_js/pureCleaning.js", "core_js/context_menu.js", "core_js/historyListener.js", - "external_js/ip-range-check.js" + "clearurls.js", + "core_js/storage.js", + "core_js/watchdog.js" ] }, "content_scripts": [