From 36956cbc7a971ae397615f38bbbaff92d5a5a7ca Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Dec 2017 16:42:54 -0500 Subject: [PATCH 001/106] remove obsolete resource caching code --- src/js/3p-filters.js | 9 +-- src/js/assets.js | 167 ++----------------------------------------- src/js/messaging.js | 24 +------ src/js/storage.js | 77 ++------------------ 4 files changed, 15 insertions(+), 262 deletions(-) diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 7da055c1925bc..baf128e873ea0 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -547,16 +547,9 @@ var fromCloudData = function(data, append) { checked = data.ignoreGenericCosmeticFilters === true || append && elem.checked; elem.checked = listDetails.ignoreGenericCosmeticFilters = checked; - var listKey; - for ( i = 0, n = data.selectedLists.length; i < n; i++ ) { - listKey = data.selectedLists[i]; - if ( listDetails.aliases[listKey] ) { - data.selectedLists[i] = listDetails.aliases[listKey]; - } - } var selectedSet = new Set(data.selectedLists), listEntries = uDom('#lists .listEntry'), - listEntry, input; + listEntry, listKey, input; for ( i = 0, n = listEntries.length; i < n; i++ ) { listEntry = listEntries.at(i); listKey = listEntry.attr('data-listkey'); diff --git a/src/js/assets.js b/src/js/assets.js index da04779dc34e0..5ee4a92c5109a 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -167,157 +167,6 @@ api.fetchText = function(url, onLoad, onError) { } }; -/******************************************************************************* - - TODO(seamless migration): - This block of code will be removed when I am confident all users have - moved to a version of uBO which does not require the old way of caching - assets. - - api.listKeyAliases: a map of old asset keys to new asset keys. - - migrate(): to seamlessly migrate the old cache manager to the new one: - - attempt to preserve and move content of cached assets to new locations; - - removes all traces of now obsolete cache manager entries in cacheStorage. - - This code will typically execute only once, when the newer version of uBO - is first installed and executed. - -**/ - -api.listKeyAliases = { - "assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat": "public_suffix_list.dat", - "assets/user/filters.txt": "user-filters", - "assets/ublock/resources.txt": "ublock-resources", - "assets/ublock/filters.txt": "ublock-filters", - "assets/ublock/privacy.txt": "ublock-privacy", - "assets/ublock/unbreak.txt": "ublock-unbreak", - "assets/ublock/badware.txt": "ublock-badware", - "assets/ublock/experimental.txt": "ublock-experimental", - "https://easylist-downloads.adblockplus.org/easylistchina.txt": "CHN-0", - "https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt": "CHN-1", - "https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjx-annoyance.txt": "CHN-2", - "https://easylist-downloads.adblockplus.org/easylistgermany.txt": "DEU-0", - "https://adblock.dk/block.csv": "DNK-0", - "assets/thirdparties/easylist-downloads.adblockplus.org/easylist.txt": "easylist", - "https://easylist-downloads.adblockplus.org/easylist_noelemhide.txt": "easylist-nocosmetic", - "assets/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt": "easyprivacy", - "https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt": "fanboy-annoyance", - "https://easylist-downloads.adblockplus.org/fanboy-social.txt": "fanboy-social", - "https://easylist-downloads.adblockplus.org/liste_fr.txt": "FRA-0", - "http://adblock.gardar.net/is.abp.txt": "ISL-0", - "https://easylist-downloads.adblockplus.org/easylistitaly.txt": "ITA-0", - "https://dl.dropboxusercontent.com/u/1289327/abpxfiles/filtri.txt": "ITA-1", - "https://easylist-downloads.adblockplus.org/advblock.txt": "RUS-0", - "https://easylist-downloads.adblockplus.org/bitblock.txt": "RUS-1", - "https://filters.adtidy.org/extension/chromium/filters/1.txt": "RUS-2", - "https://adguard.com/en/filter-rules.html?id=1": "RUS-2", - "https://easylist-downloads.adblockplus.org/easylistdutch.txt": "NLD-0", - "https://notabug.org/latvian-list/adblock-latvian/raw/master/lists/latvian-list.txt": "LVA-0", - "http://hosts-file.net/.%5Cad_servers.txt": "hphosts", - "http://adblock.ee/list.php": "EST-0", - "https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt": "disconnect-malvertising", - "https://s3.amazonaws.com/lists.disconnect.me/simple_malware.txt": "disconnect-malware", - "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt": "disconnect-tracking", - "https://www.certyficate.it/adblock/adblock.txt": "POL-0", - "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt": "POL-0", - "https://easylist-downloads.adblockplus.org/antiadblockfilters.txt": "awrl-0", - "http://adb.juvander.net/Finland_adb.txt": "FIN-0", - "https://raw.githubusercontent.com/gfmaster/adblock-korea-contrib/master/filter.txt": "KOR-0", - "https://raw.githubusercontent.com/yous/YousList/master/youslist.txt": "KOR-1", - "https://www.fanboy.co.nz/fanboy-korean.txt": "KOR-2", - "https://raw.githubusercontent.com/heradhis/indonesianadblockrules/master/subscriptions/abpindo.txt": "IDN-0", - "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt": "IDN-0", - "https://raw.githubusercontent.com/k2jp/abp-japanese-filters/master/abpjf.txt": "JPN-0", - "https://raw.githubusercontent.com/liamja/Prebake/master/obtrusive.txt": "EU-prebake", - "https://easylist-downloads.adblockplus.org/Liste_AR.txt": "ara-0", - "http://margevicius.lt/easylistlithuania.txt": "LTU-0", - "assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt": "malware-0", - "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains": "malware-1", - "http://malwaredomains.lehigh.edu/files/immortal_domains.txt": "malware-2", - "assets/thirdparties/pgl.yoyo.org/as/serverlist": "plowe-0", - "https://raw.githubusercontent.com/easylist/EasyListHebrew/master/EasyListHebrew.txt": "ISR-0", - "https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt": "reek-0", - "https://raw.githubusercontent.com/szpeter80/hufilter/master/hufilter.txt": "HUN-0", - "https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt": "CZE-0", - "http://someonewhocares.org/hosts/hosts": "dpollock-0", - "https://raw.githubusercontent.com/Dawsey21/Lists/master/adblock-list.txt": "spam404-0", - "http://stanev.org/abp/adblock_bg.txt": "BGR-0", - "http://winhelp2002.mvps.org/hosts.txt": "mvps-0", - "https://www.fanboy.co.nz/enhancedstats.txt": "fanboy-enhanced", - "https://www.fanboy.co.nz/fanboy-antifacebook.txt": "fanboy-thirdparty_social", - "https://easylist-downloads.adblockplus.org/easylistspanish.txt": "spa-0", - "https://www.fanboy.co.nz/fanboy-swedish.txt": "SWE-0", - "https://www.fanboy.co.nz/r/fanboy-ultimate.txt": "fanboy-ultimate", - "https://filters.adtidy.org/extension/chromium/filters/13.txt": "TUR-0", - "https://adguard.com/filter-rules.html?id=13": "TUR-0", - "https://www.fanboy.co.nz/fanboy-vietnam.txt": "VIE-0", - "https://www.void.gr/kargig/void-gr-filters.txt": "GRC-0", - "https://raw.githubusercontent.com/betterwebleon/slovenian-list/master/filters.txt": "SVN-0" -}; - -var migrate = function(callback) { - var entries, - moveCount = 0, - toRemove = []; - - var countdown = function(change) { - moveCount -= (change || 0); - if ( moveCount !== 0 ) { return; } - vAPI.cacheStorage.remove(toRemove); - saveAssetCacheRegistry(); - callback(); - }; - - var onContentRead = function(oldKey, newKey, bin) { - var content = bin && bin['cached_asset_content://' + oldKey] || undefined; - if ( content ) { - assetCacheRegistry[newKey] = { - readTime: Date.now(), - writeTime: entries[oldKey] - }; - if ( reIsExternalPath.test(oldKey) ) { - assetCacheRegistry[newKey].remoteURL = oldKey; - } - bin = {}; - bin['cache/' + newKey] = content; - vAPI.cacheStorage.set(bin); - } - countdown(1); - }; - - var onEntries = function(bin) { - entries = bin && bin['cached_asset_entries']; - if ( !entries ) { return callback(); } - if ( bin && bin['assetCacheRegistry'] ) { - assetCacheRegistry = bin['assetCacheRegistry']; - } - var aliases = api.listKeyAliases; - for ( var oldKey in entries ) { - if ( oldKey.endsWith('assets/user/filters.txt') ) { continue; } - var newKey = aliases[oldKey]; - if ( !newKey && /^https?:\/\//.test(oldKey) ) { - newKey = oldKey; - } - if ( newKey ) { - vAPI.cacheStorage.get( - 'cached_asset_content://' + oldKey, - onContentRead.bind(null, oldKey, newKey) - ); - moveCount += 1; - } - toRemove.push('cached_asset_content://' + oldKey); - } - toRemove.push('cached_asset_entries', 'extensionLastVersion'); - countdown(); - }; - - vAPI.cacheStorage.get( - [ 'cached_asset_entries', 'assetCacheRegistry' ], - onEntries - ); -}; - /******************************************************************************* The purpose of the asset source registry is to keep key detail information @@ -523,16 +372,12 @@ var getAssetCacheRegistry = function(callback) { } }; - var migrationDone = function() { - vAPI.cacheStorage.get('assetCacheRegistry', function(bin) { - if ( bin && bin.assetCacheRegistry ) { - assetCacheRegistry = bin.assetCacheRegistry; - } - registryReady(); - }); - }; - - migrate(migrationDone); + vAPI.cacheStorage.get('assetCacheRegistry', function(bin) { + if ( bin && bin.assetCacheRegistry ) { + assetCacheRegistry = bin.assetCacheRegistry; + } + registryReady(); + }); }; var saveAssetCacheRegistry = (function() { diff --git a/src/js/messaging.js b/src/js/messaging.js index 29c783bd46275..508d1166aedee 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -711,15 +711,7 @@ var backupUserData = function(callback) { dynamicFilteringString: µb.permanentFirewall.toString(), urlFilteringString: µb.permanentURLFiltering.toString(), hostnameSwitchesString: µb.hnSwitches.toString(), - userFilters: '', - // TODO(seamless migration): - // The following is strictly for convenience, to be minimally - // forward-compatible. This will definitely be removed in the - // short term, as I do not expect the need to install an older - // version of uBO to ever be needed beyond the short term. - // >>>>>>>> - filterLists: µb.oldDataFromNewListKeys(µb.selectedFilterLists) - // <<<<<<<< + userFilters: '' }; var onUserFiltersReady = function(details) { @@ -760,17 +752,8 @@ var restoreUserData = function(request) { lastBackupTime: 0 }); µb.assets.put(µb.userFiltersPath, userData.userFilters); - - // 'filterLists' is available up to uBO v1.10.4, not beyond. - // 'selectedFilterLists' is available from uBO v1.11 and beyond. - var listKeys; if ( Array.isArray(userData.selectedFilterLists) ) { - listKeys = userData.selectedFilterLists; - } else if ( userData.filterLists instanceof Object ) { - listKeys = µb.newListKeysFromOldData(userData.filterLists); - } - if ( listKeys !== undefined ) { - µb.saveSelectedFilterLists(listKeys, restart); + µb.saveSelectedFilterLists(userData.selectedFilterLists, restart); } else { restart(); } @@ -828,8 +811,7 @@ var getLists = function(callback) { ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters, netFilterCount: µb.staticNetFilteringEngine.getFilterCount(), parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters, - userFiltersPath: µb.userFiltersPath, - aliases: µb.assets.listKeyAliases + userFiltersPath: µb.userFiltersPath }; var onMetadataReady = function(entries) { r.cache = entries; diff --git a/src/js/storage.js b/src/js/storage.js index a9a5d93746e24..256e80d15298b 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -168,29 +168,16 @@ µBlock.loadSelectedFilterLists = function(callback) { var µb = this; - vAPI.storage.get([ 'selectedFilterLists', 'remoteBlacklists' ], function(bin) { - if ( !bin || !bin.selectedFilterLists && !bin.remoteBlacklists ) { - // Select default filter lists if first-time launch. + vAPI.storage.get('selectedFilterLists', function(bin) { + // Select default filter lists if first-time launch. + if ( !bin || Array.isArray(bin.selectedFilterLists) === false ) { µb.assets.metadata(function(availableLists) { µb.saveSelectedFilterLists(µb.autoSelectRegionalFilterLists(availableLists)); callback(); }); return; } - var listKeys = []; - if ( bin.selectedFilterLists ) { - listKeys = bin.selectedFilterLists; - } else if ( bin.remoteBlacklists ) { - var oldListKeys = µb.newListKeysFromOldData(bin.remoteBlacklists); - if ( oldListKeys.sort().join() !== listKeys.sort().join() ) { - listKeys = oldListKeys; - µb.saveSelectedFilterLists(listKeys); - } - // TODO(seamless migration): - // Uncomment when all have moved to v1.11 and beyond. - //vAPI.storage.remove('remoteBlacklists'); - } - µb.selectedFilterLists = listKeys; + µb.selectedFilterLists = bin.selectedFilterLists; callback(); }); }; @@ -213,63 +200,12 @@ } newKeys = this.arrayFrom(newSet); var bin = { - selectedFilterLists: newKeys, - remoteBlacklists: this.oldDataFromNewListKeys(newKeys) + selectedFilterLists: newKeys }; this.selectedFilterLists = newKeys; vAPI.storage.set(bin, callback); }; -// TODO(seamless migration): -// Remove when all have moved to v1.11 and beyond. -// >>>>>>>> -µBlock.newListKeysFromOldData = function(oldLists) { - var aliases = this.assets.listKeyAliases, - listKeys = [], newKey; - for ( var oldKey in oldLists ) { - if ( oldLists[oldKey].off !== true ) { - newKey = aliases[oldKey]; - listKeys.push(newKey ? newKey : oldKey); - } - } - return listKeys; -}; - -µBlock.oldDataFromNewListKeys = function(selectedFilterLists) { - var µb = this, - remoteBlacklists = {}; - var reverseAliases = Object.keys(this.assets.listKeyAliases).reduce( - function(a, b) { - a[µb.assets.listKeyAliases[b]] = b; return a; - }, - {} - ); - remoteBlacklists = selectedFilterLists.reduce( - function(a, b) { - a[reverseAliases[b] || b] = { off: false }; - return a; - }, - {} - ); - remoteBlacklists = Object.keys(µb.assets.listKeyAliases).reduce( - function(a, b) { - var aliases = µb.assets.listKeyAliases; - if ( - b.startsWith('assets/') && - aliases[b] !== 'public_suffix_list.dat' && - aliases[b] !== 'ublock-resources' && - !a[b] - ) { - a[b] = { off: true }; - } - return a; - }, - remoteBlacklists - ); - return remoteBlacklists; -}; -// <<<<<<<< - /******************************************************************************/ µBlock.applyFilterListSelection = function(details, callback) { @@ -1028,9 +964,6 @@ if ( Array.isArray(data.selectedFilterLists) ) { bin.selectedFilterLists = data.selectedFilterLists; binNotEmpty = true; - } else if ( typeof data.filterLists === 'object' ) { - bin.selectedFilterLists = µb.newListKeysFromOldData(data.filterLists); - binNotEmpty = true; } if ( typeof data.netWhitelist === 'string' ) { From 46d446ec92e052e08fcfea1c6e526f0f2d41a729 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 14 Dec 2017 18:28:14 -0500 Subject: [PATCH 002/106] fix https://github.com/uBlockOrigin/uAssets/issues/999 --- assets/assets.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/assets.json b/assets/assets.json index ede40f04c5a1d..9b12c2db929ad 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -585,14 +585,14 @@ "contentURL": "https://raw.githubusercontent.com/betterwebleon/slovenian-list/master/filters.txt", "supportURL": "https://github.com/betterwebleon/slovenian-list" }, - "SWE-0": { + "SWE-1": { "content": "filters", "group": "regions", "off": true, - "title": "SWE: Fanboy's Swedish", + "title": "SWE: Frellwit's Swedish Filter", "lang": "sv", - "contentURL": "https://www.fanboy.co.nz/fanboy-swedish.txt", - "supportURL": "https://github.com/ryanbr/fanboy-adblock/issues" + "contentURL": "https://raw.githubusercontent.com/lassekongo83/Frellwits-filter-lists/master/Frellwits-Swedish-Filter.txt", + "supportURL": "https://github.com/lassekongo83/Frellwits-filter-lists" }, "TUR-0": { "content": "filters", From 6a8c27b6dfb81faa37a878a58b5241feaeea5747 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 15 Dec 2017 07:39:21 -0500 Subject: [PATCH 003/106] fix #3331: ability to fetch sublists using `!# include` directives --- src/js/assets.js | 78 ++++++++++++++++++++++++++++++++++++++++++++-- src/js/uritools.js | 2 +- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 5ee4a92c5109a..c2fb47ff75979 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -167,6 +167,72 @@ api.fetchText = function(url, onLoad, onError) { } }; +/******************************************************************************/ + +// https://github.com/gorhill/uBlock/issues/3331 +// Support the seamless loading of sublists. + +api.fetchFilterList = function(mainlistURL, onLoad, onError) { + var µburi = µBlock.URI, + content = [], + errored = false, + pendingSublistURLs = new Set([ mainlistURL ]), + loadedSublistURLs = new Set(), + mainOriginURL = µburi.originFromURI(mainlistURL); + + var onLocalLoadSuccess = function(details) { + if ( errored ) { return; } + + var isSublist = details.url !== mainlistURL, + sublistURL; + + pendingSublistURLs.delete(details.url); + loadedSublistURLs.add(details.url); + if ( isSublist ) { content.push('\n! ' + '>>>>>>>> ' + details.url); } + content.push(details.content.trim()); + if ( isSublist ) { content.push('! <<<<<<<< ' + details.url); } + + if ( mainOriginURL !== '' ) { + var subOriginURL, + reInclude = /^!# include (\S+)/gm, + match = reInclude.exec(details.content); + while ( match !== null ) { + sublistURL = match[1]; + subOriginURL = µburi.originFromURI(sublistURL); + if ( subOriginURL !== '' && subOriginURL !== mainOriginURL ) { + continue; + } + if ( subOriginURL === '' ) { + sublistURL = mainOriginURL + '/' + sublistURL; + } + if ( loadedSublistURLs.has(sublistURL) ) { continue; } + pendingSublistURLs.add(sublistURL); + match = reInclude.exec(details.content); + } + } + + if ( pendingSublistURLs.size !== 0 ) { + for ( sublistURL of pendingSublistURLs ) { + api.fetchText(sublistURL, onLocalLoadSuccess, onLocalLoadError); + } + return; + } + + details.url = mainlistURL; + details.content = content.join('\n').trim(); + onLoad(details); + }; + + var onLocalLoadError = function(details) { + errored = true; + details.url = mainlistURL; + details.content = ''; + onError(details); + }; + + this.fetchText(mainlistURL, onLocalLoadSuccess, onLocalLoadError); +}; + /******************************************************************************* The purpose of the asset source registry is to keep key detail information @@ -651,7 +717,11 @@ api.get = function(assetKey, options, callback) { if ( !contentURL ) { return reportBack('', 'E_NOTFOUND'); } - api.fetchText(contentURL, onContentLoaded, onContentNotLoaded); + if ( assetDetails.content === 'filters' ) { + api.fetchFilterList(contentURL, onContentLoaded, onContentNotLoaded); + } else { + api.fetchText(contentURL, onContentLoaded, onContentNotLoaded); + } }; var onContentLoaded = function(details) { @@ -735,7 +805,11 @@ var getRemote = function(assetKey, callback) { if ( !contentURL ) { return reportBack('', 'E_NOTFOUND'); } - api.fetchText(contentURL, onRemoteContentLoaded, onRemoteContentError); + if ( assetDetails.content === 'filters' ) { + api.fetchFilterList(contentURL, onRemoteContentLoaded, onRemoteContentError); + } else { + api.fetchText(contentURL, onRemoteContentLoaded, onRemoteContentError); + } }; getAssetSourceRegistry(function(registry) { diff --git a/src/js/uritools.js b/src/js/uritools.js index f624d891edf13..eaca7731d51d7 100644 --- a/src/js/uritools.js +++ b/src/js/uritools.js @@ -51,7 +51,7 @@ var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/; // Derived var reSchemeFromURI = /^[^:\/?#]+:/; var reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/; -var reOriginFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]+)/; +var reOriginFromURI = /^(?:[^:\/?#]+:)\/\/(?:[^\/?#]+)?/; var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//; var rePathFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?([^?#]*)/; var reMustNormalizeHostname = /[^0-9a-z._-]/; From 912582ce4b44a37a8a238a7443e2dc2d92c2dfca Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 15 Dec 2017 07:55:15 -0500 Subject: [PATCH 004/106] code review: remove space as per https://github.com/AdguardTeam/AdguardBrowserExtension/issues/917 --- src/js/assets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/assets.js b/src/js/assets.js index c2fb47ff75979..c3165e538a613 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -194,7 +194,7 @@ api.fetchFilterList = function(mainlistURL, onLoad, onError) { if ( mainOriginURL !== '' ) { var subOriginURL, - reInclude = /^!# include (\S+)/gm, + reInclude = /^!#include (\S+)/gm, match = reInclude.exec(details.content); while ( match !== null ) { sublistURL = match[1]; From 8e7ccef14cdc4129f5cce2b6edca0bbef3b36455 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 15 Dec 2017 09:24:06 -0500 Subject: [PATCH 005/106] code review for #3331: support relative paths as per https://github.com/AdguardTeam/AdguardBrowserExtension/issues/917 --- src/js/assets.js | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index c3165e538a613..659f83b0968b4 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -173,12 +173,12 @@ api.fetchText = function(url, onLoad, onError) { // Support the seamless loading of sublists. api.fetchFilterList = function(mainlistURL, onLoad, onError) { - var µburi = µBlock.URI, - content = [], + var content = [], errored = false, pendingSublistURLs = new Set([ mainlistURL ]), loadedSublistURLs = new Set(), - mainOriginURL = µburi.originFromURI(mainlistURL); + toParsedURL = api.fetchFilterList.toParsedURL, + parsedMainURL = toParsedURL(mainlistURL); var onLocalLoadSuccess = function(details) { if ( errored ) { return; } @@ -192,21 +192,20 @@ api.fetchFilterList = function(mainlistURL, onLoad, onError) { content.push(details.content.trim()); if ( isSublist ) { content.push('! <<<<<<<< ' + details.url); } - if ( mainOriginURL !== '' ) { - var subOriginURL, - reInclude = /^!#include (\S+)/gm, + if ( parsedMainURL !== undefined ) { + var reInclude = /^!#include +(\S+)/gm, match = reInclude.exec(details.content); while ( match !== null ) { - sublistURL = match[1]; - subOriginURL = µburi.originFromURI(sublistURL); - if ( subOriginURL !== '' && subOriginURL !== mainOriginURL ) { - continue; + var parsedSubURL = toParsedURL(match[1]); + if ( parsedSubURL === undefined ) { + parsedSubURL = toParsedURL( + parsedMainURL.href.replace(/[^/?]+(?:\?.*)?$/, match[1]) + ); + if ( parsedSubURL === undefined ) { continue; } } - if ( subOriginURL === '' ) { - sublistURL = mainOriginURL + '/' + sublistURL; - } - if ( loadedSublistURLs.has(sublistURL) ) { continue; } - pendingSublistURLs.add(sublistURL); + if ( parsedSubURL.origin !== parsedMainURL.origin ) { continue; } + if ( loadedSublistURLs.has(parsedSubURL.href) ) { continue; } + pendingSublistURLs.add(parsedSubURL.href); match = reInclude.exec(details.content); } } @@ -233,6 +232,13 @@ api.fetchFilterList = function(mainlistURL, onLoad, onError) { this.fetchText(mainlistURL, onLocalLoadSuccess, onLocalLoadError); }; +api.fetchFilterList.toParsedURL = function(url) { + try { + return new URL(url); + } catch (ex) { + } +}; + /******************************************************************************* The purpose of the asset source registry is to keep key detail information From f753952adc29ddde890505d310308946b63e38ba Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 15 Dec 2017 10:10:09 -0500 Subject: [PATCH 006/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index b5f610a8aa44b..50f76adbff1d8 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.22", + "version": "1.14.23.0", "commands": { "launch-element-zapper": { From dec0b80a7214ae2296bbe74654617c14fb48738c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 17 Dec 2017 08:09:47 -0500 Subject: [PATCH 007/106] fix #2877 --- src/js/background.js | 2 +- src/js/cosmetic-filtering.js | 110 ++++++++++++++++++++--------------- src/js/messaging.js | 11 +++- 3 files changed, 72 insertions(+), 51 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index a60e2ea05b9de..c1a85de06250c 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -121,7 +121,7 @@ var µBlock = (function() { // jshint ignore:line // read-only systemSettings: { compiledMagic: 'vrgorlgelgws', - selfieMagic: 'vrgorlgelgws' + selfieMagic: 'pxpclstriajk' }, restoreBackupSettings: { diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 9baf3a5ee052d..502df46d94b04 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -673,6 +673,7 @@ var FilterContainer = function() { this.setRegister0 = new Set(); this.setRegister1 = new Set(); this.setRegister2 = new Set(); + this.mapRegister0 = new Map(); this.reset(); }; @@ -722,7 +723,6 @@ FilterContainer.prototype.reset = function() { this.scriptTagFilters = {}; this.scriptTagFilterCount = 0; this.userScripts.clear(); - this.userScriptCount = 0; }; /******************************************************************************/ @@ -1603,8 +1603,12 @@ FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) { // userScripts{hash} => FilterHostname | FilterBucket -FilterContainer.prototype.createUserScriptRule = function(hash, hostname, selector) { - var filter = new FilterHostname(selector, hostname); +FilterContainer.prototype.createUserScriptRule = function( + hash, + hostname, + selector +) { + var filter = new FilterHostname(selector.slice(14, -1).trim(), hostname); var bucket = this.userScripts.get(hash); if ( bucket === undefined ) { this.userScripts.set(hash, filter); @@ -1613,7 +1617,6 @@ FilterContainer.prototype.createUserScriptRule = function(hash, hostname, select } else { this.userScripts.set(hash, new FilterBucket(bucket, filter)); } - this.userScriptCount += 1; }; /******************************************************************************/ @@ -1625,29 +1628,34 @@ FilterContainer.prototype.createUserScriptRule = function(hash, hostname, select // ^ ^ // 14 -1 -FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { - if ( this.userScriptCount === 0 ) { return; } +FilterContainer.prototype.retrieveUserScripts = function( + domain, + hostname, + details +) { + if ( this.userScripts.size === 0 ) { return; } if ( µb.hiddenSettings.ignoreScriptInjectFilters === true ) { return; } var reng = µb.redirectEngine; if ( !reng ) { return; } - var out = [], - scripts = new Map(), + this.mapRegister0.clear(); + + var toInject = this.mapRegister0, pos = domain.indexOf('.'), entity = pos !== -1 ? domain.slice(0, pos) + '.*' : ''; // Implicit var hn = hostname; for (;;) { - this._lookupUserScript(scripts, hn + '.js', reng, out); + this._lookupUserScript(hn + '.js', reng, toInject); if ( hn === domain ) { break; } pos = hn.indexOf('.'); if ( pos === -1 ) { break; } hn = hn.slice(pos + 1); } if ( entity !== '' ) { - this._lookupUserScript(scripts, entity + '.js', reng, out); + this._lookupUserScript(entity + '.js', reng, toInject); } // Explicit (hash is domain). @@ -1660,12 +1668,10 @@ FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { bucket.retrieve(entity, selectors); } for ( var selector of selectors ) { - this._lookupUserScript(scripts, selector.slice(14, -1).trim(), reng, out); + this._lookupUserScript(selector, reng, toInject); } - if ( out.length === 0 ) { - return; - } + if ( toInject.size === 0 ) { return; } // https://github.com/gorhill/uBlock/issues/2835 // Do not inject scriptlets if the site is under an `allow` rule. @@ -1678,26 +1684,43 @@ FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) { // Exceptions should be rare, so we check for exception only if there are // scriptlets returned. - var exceptions = new Set(), - j, token; + var exceptions = new Set(); if ( (bucket = this.userScripts.get('!' + domain)) ) { bucket.retrieve(hostname, exceptions); } if ( entity !== '' && (bucket = this.userScripts.get('!' + entity)) ) { bucket.retrieve(hostname, exceptions); } - for ( var exception of exceptions ) { - token = exception.slice(14, -1); - if ( (j = scripts.get(token)) !== undefined ) { - out[j] = '// User script "' + token + '" excepted.\n'; - } + + // Return an array of scriptlets, and log results if needed. + var out = [], + logger = µb.logger.isEnabled() ? µb.logger : null, + isException; + + for ( var entry of toInject ) { + if ( (isException = exceptions.has(entry[0])) === false ) { + out.push(entry[1]); + } + if ( logger === null ) { continue; } + logger.writeOne( + details.tabId, + 'cosmetic', + { + source: 'cosmetic', + raw: (isException ? '#@#' : '##') + 'script:inject(' + entry[0] + ')' + }, + 'dom', + details.locationURL, + null, + hostname + ); } return out.join('\n'); }; -FilterContainer.prototype._lookupUserScript = function(dict, raw, reng, out) { - if ( dict.has(raw) ) { return; } +FilterContainer.prototype._lookupUserScript = function(raw, reng, toInject) { + if ( toInject.has(raw) ) { return; } var token, args, pos = raw.indexOf(','); if ( pos === -1 ) { @@ -1712,8 +1735,7 @@ FilterContainer.prototype._lookupUserScript = function(dict, raw, reng, out) { content = this._fillupUserScript(content, args); if ( !content ) { return; } } - dict.set(raw, out.length); - out.push(content); + toInject.set(raw, content); }; // Fill template placeholders. Return falsy if: @@ -1762,8 +1784,7 @@ FilterContainer.prototype.toSelfie = function() { genericDonthideArray: µb.arrayFrom(this.genericDonthideSet), scriptTagFilters: this.scriptTagFilters, scriptTagFilterCount: this.scriptTagFilterCount, - userScripts: selfieFromMap(this.userScripts), - userScriptCount: this.userScriptCount + userScripts: selfieFromMap(this.userScripts) }; }; @@ -1798,7 +1819,6 @@ FilterContainer.prototype.fromSelfie = function(selfie) { this.scriptTagFilters = selfie.scriptTagFilters; this.scriptTagFilterCount = selfie.scriptTagFilterCount; this.userScripts = mapFromSelfie(selfie.userScripts); - this.userScriptCount = selfie.userScriptCount; this.frozen = true; }; @@ -1911,10 +1931,7 @@ FilterContainer.prototype.randomAlphaToken = function() { /******************************************************************************/ -FilterContainer.prototype.retrieveGenericSelectors = function( - request, - sender -) { +FilterContainer.prototype.retrieveGenericSelectors = function(request) { if ( this.acceptedCount === 0 ) { return; } if ( !request.ids && !request.classes ) { return; } @@ -1995,9 +2012,8 @@ FilterContainer.prototype.retrieveGenericSelectors = function( // cosmetic filters now. if ( this.supportsUserStylesheets && - sender instanceof Object && - sender.tab instanceof Object && - typeof sender.frameId === 'number' + request.tabId !== undefined && + request.frameId !== undefined ) { var injected = []; if ( out.simple.length !== 0 ) { @@ -2009,10 +2025,10 @@ FilterContainer.prototype.retrieveGenericSelectors = function( out.complex = []; } out.injected = injected.join(',\n'); - vAPI.insertCSS(sender.tab.id, { + vAPI.insertCSS(request.tabId, { code: out.injected + '\n{display:none!important;}', cssOrigin: 'user', - frameId: sender.frameId, + frameId: request.frameId, runAt: 'document_start' }); } @@ -2030,7 +2046,6 @@ FilterContainer.prototype.retrieveGenericSelectors = function( FilterContainer.prototype.retrieveDomainSelectors = function( request, - sender, options ) { if ( !request.locationURL ) { return; } @@ -2041,7 +2056,8 @@ FilterContainer.prototype.retrieveDomainSelectors = function( domain = this.µburi.domainFromHostname(hostname) || hostname, pos = domain.indexOf('.'), entity = pos === -1 ? '' : domain.slice(0, pos - domain.length) + '.*', - cacheEntry = this.selectorCache.get(hostname); + cacheEntry = this.selectorCache.get(hostname), + entry; // https://github.com/chrisaljoudi/uBlock/issues/587 // out.ready will tell the content script the cosmetic filtering engine is @@ -2177,7 +2193,7 @@ FilterContainer.prototype.retrieveDomainSelectors = function( if ( options.noGenericCosmeticFiltering !== true ) { var exceptionHash = out.exceptionFilters.join(); for ( var type in this.highlyGeneric ) { - var entry = this.highlyGeneric[type]; + entry = this.highlyGeneric[type]; var str = entry.mru.lookup(exceptionHash); if ( str === undefined ) { str = { s: entry.str }; @@ -2206,8 +2222,9 @@ FilterContainer.prototype.retrieveDomainSelectors = function( } // Scriptlet injection. - out.scripts = this.retrieveUserScripts(domain, hostname); + out.scripts = this.retrieveUserScripts(domain, hostname, request); + // CSS selectors for collapsible blocked elements if ( cacheEntry ) { var networkFilters = []; cacheEntry.retrieve('net', networkFilters); @@ -2219,9 +2236,8 @@ FilterContainer.prototype.retrieveDomainSelectors = function( // cosmetic filters now. if ( this.supportsUserStylesheets && - sender instanceof Object && - sender.tab instanceof Object && - typeof sender.frameId === 'number' + request.tabId !== undefined && + request.frameId !== undefined ) { var injectedHideFilters = []; if ( out.declarativeFilters.length !== 0 ) { @@ -2244,16 +2260,16 @@ FilterContainer.prototype.retrieveDomainSelectors = function( var details = { code: '', cssOrigin: 'user', - frameId: sender.frameId, + frameId: request.frameId, runAt: 'document_start' }; if ( out.injectedHideFilters.length !== 0 ) { details.code = out.injectedHideFilters + '\n{display:none!important;}'; - vAPI.insertCSS(sender.tab.id, details); + vAPI.insertCSS(request.tabId, details); } if ( out.networkFilters.length !== 0 ) { details.code = out.networkFilters + '\n{display:none!important;}'; - vAPI.insertCSS(sender.tab.id, details); + vAPI.insertCSS(request.tabId, details); out.networkFilters = ''; } } diff --git a/src/js/messaging.js b/src/js/messaging.js index 508d1166aedee..40ccaee61f1d2 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -464,11 +464,12 @@ var onMessage = function(request, sender, callback) { // Sync var µb = µBlock, response, - tabId, + tabId, frameId, pageStore; if ( sender && sender.tab ) { tabId = sender.tab.id; + frameId = sender.frameId; pageStore = µb.pageStoreFromTabId(tabId); } @@ -497,9 +498,11 @@ var onMessage = function(request, sender, callback) { noGenericCosmeticFiltering: pageStore.noGenericCosmeticFiltering === true }; + request.tabId = tabId; + request.frameId = frameId; response.specificCosmeticFilters = µb.cosmeticFilteringEngine - .retrieveDomainSelectors(request, sender, response); + .retrieveDomainSelectors(request, response); if ( request.isRootFrame && µb.logger.isEnabled() ) { µb.logCosmeticFilters(tabId); } @@ -508,9 +511,11 @@ var onMessage = function(request, sender, callback) { case 'retrieveGenericCosmeticSelectors': if ( pageStore && pageStore.getGenericCosmeticFilteringSwitch() ) { + request.tabId = tabId; + request.frameId = frameId; response = { result: µb.cosmeticFilteringEngine - .retrieveGenericSelectors(request, sender) + .retrieveGenericSelectors(request) }; } break; From 904e550fc074407978f0d64a20c9893d05dc932c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 17 Dec 2017 08:15:24 -0500 Subject: [PATCH 008/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 50f76adbff1d8..a060d87d70bf2 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.0", + "version": "1.14.23.1", "commands": { "launch-element-zapper": { From 367001a3de006f1f9efc36aecc720c0e547bf948 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 17 Dec 2017 09:37:10 -0500 Subject: [PATCH 009/106] address https://github.com/uBlockOrigin/uAssets/issues/1026 --- assets/assets.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/assets/assets.json b/assets/assets.json index 9b12c2db929ad..edbba5e5a1803 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -74,6 +74,13 @@ "assets/ublock/resource-abuse.txt" ] }, + "ublock-annoyances": { + "content": "filters", + "group": "default", + "title": "uBlock filters – Annoyances", + "off": true, + "contentURL": "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/annoyances.txt" + }, "ublock-unbreak": { "content": "filters", "group": "default", From 7afc78e86675115a5021baefee2d8ea705ae1e03 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 17 Dec 2017 09:42:24 -0500 Subject: [PATCH 010/106] do not include optional annoyances.txt in extension package --- tools/make-assets.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/make-assets.sh b/tools/make-assets.sh index ec3c5b38daca2..0ce141c1088dc 100755 --- a/tools/make-assets.sh +++ b/tools/make-assets.sh @@ -25,5 +25,7 @@ cp -R ../uAssets/thirdparties/www.malwaredomainlist.com $DES/thirdparti mkdir $DES/ublock cp -R ../uAssets/filters/* $DES/ublock/ +# Optional filter lists: do not include in package +rm $DES/ublock/annoyances.txt echo "done." From 4a09c9f866d3b7efc78ff6c052dbe48ef2f0ae7d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 17 Dec 2017 10:28:12 -0500 Subject: [PATCH 011/106] improve slightly pre-parsing of `##script:...` filters --- src/js/background.js | 2 +- src/js/cosmetic-filtering.js | 61 ++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index c1a85de06250c..57f94f645639b 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -120,7 +120,7 @@ var µBlock = (function() { // jshint ignore:line // read-only systemSettings: { - compiledMagic: 'vrgorlgelgws', + compiledMagic: 'pxpclstriajk', selfieMagic: 'pxpclstriajk' }, diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 502df46d94b04..9f738fa050037 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -778,7 +778,6 @@ FilterContainer.prototype.compileSelector = (function() { var reAfterBeforeSelector = /^(.+?)(::?after|::?before)$/, reStyleSelector = /^(.+?):style\((.+?)\)$/, reStyleBad = /url\([^)]+\)/, - reScriptSelector = /^script:(contains|inject)\((.+)\)$/, reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/, reExtendedSyntaxParser = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/, div = document.createElement('div'); @@ -865,7 +864,7 @@ FilterContainer.prototype.compileSelector = (function() { } // `script:` filter? - if ( (matches = reScriptSelector.exec(raw)) !== null ) { + if ( (matches = this.reScriptSelector.exec(raw)) !== null ) { // :inject if ( matches[1] === 'inject' ) { return raw; @@ -1258,17 +1257,25 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) /******************************************************************************/ FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, writer) { - var selector = parsed.suffix; + var selector = parsed.suffix, + compiled; // script:contains(...) // script:inject(...) if ( this.reScriptSelector.test(selector) ) { - writer.push([ 6 /* js */, '!', '', selector ]); + compiled = [ 6 /* js */, 0, '!', '', '' ]; + if ( selector.startsWith('script:inject') ) { + compiled[4] = selector.slice(14, -1).trim(); + } else { + compiled[1] = 1; + compiled[4] = selector.slice(16, -1).trim(); + } + writer.push(compiled); return; } // Procedural cosmetic filters are acceptable as generic exception filters. - var compiled = this.compileSelector(selector); + compiled = this.compileSelector(selector); if ( compiled === undefined ) { return; } // https://github.com/chrisaljoudi/uBlock/issues/497 @@ -1294,7 +1301,8 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, w var selector = parsed.suffix, domain = this.µburi.domainFromHostname(hostname), - hash; + hash, + compiled; // script:contains(...) // script:inject(...) @@ -1303,11 +1311,18 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, w if ( unhide ) { hash = '!' + hash; } - writer.push([ 6 /* js */, hash, hostname, selector ]); + compiled = [ 6 /* js */, 0, hash, hostname, '' ]; + if ( selector.startsWith('script:inject') ) { + compiled[4] = selector.slice(14, -1).trim(); + } else { + compiled[1] = 1; + compiled[4] = selector.slice(16, -1).trim(); + } + writer.push(compiled); return; } - var compiled = this.compileSelector(selector); + compiled = this.compileSelector(selector); if ( compiled === undefined ) { return; } // https://github.com/chrisaljoudi/uBlock/issues/188 @@ -1411,7 +1426,7 @@ FilterContainer.prototype.fromCompiledContent = function( // js, hash, example.com, script:contains(...) // js, hash, example.com, script:inject(...) case 6: - this.createScriptFilter(args[1], args[2], args[3]); + this.createScriptFilter(args); break; // https://github.com/chrisaljoudi/uBlock/issues/497 @@ -1465,7 +1480,7 @@ FilterContainer.prototype.skipGenericCompiledContent = function(reader) { // js, hash, example.com, script:inject(...) case 6: this.duplicateBuster.add(fingerprint); - this.createScriptFilter(args[1], args[2], args[3]); + this.createScriptFilter(args); break; // https://github.com/chrisaljoudi/uBlock/issues/497 @@ -1515,7 +1530,7 @@ FilterContainer.prototype.skipCompiledContent = function(reader) { fingerprint = reader.fingerprint(); if ( this.duplicateBuster.has(fingerprint) === false ) { this.duplicateBuster.add(fingerprint); - this.createScriptFilter(args[1], args[2], args[3]); + this.createScriptFilter(args); } continue; } @@ -1526,12 +1541,12 @@ FilterContainer.prototype.skipCompiledContent = function(reader) { /******************************************************************************/ -FilterContainer.prototype.createScriptFilter = function(hash, hostname, selector) { - if ( selector.startsWith('script:contains') ) { - return this.createScriptTagFilter(hash, hostname, selector); +FilterContainer.prototype.createScriptFilter = function(args) { + if ( args[1] === 0 ) { + return this.createUserScriptRule(args); } - if ( selector.startsWith('script:inject') ) { - return this.createUserScriptRule(hash, hostname, selector); + if ( args[1] === 1 ) { + return this.createScriptTagFilter(args); } }; @@ -1542,8 +1557,9 @@ FilterContainer.prototype.createScriptFilter = function(hash, hostname, selector // ^ ^ // 16 -1 -FilterContainer.prototype.createScriptTagFilter = function(hash, hostname, selector) { - var token = selector.slice(16, -1); +FilterContainer.prototype.createScriptTagFilter = function(args) { + var hostname = args[3], + token = args[4]; token = token.startsWith('/') && token.endsWith('/') ? token.slice(1, -1) : token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); @@ -1603,12 +1619,9 @@ FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) { // userScripts{hash} => FilterHostname | FilterBucket -FilterContainer.prototype.createUserScriptRule = function( - hash, - hostname, - selector -) { - var filter = new FilterHostname(selector.slice(14, -1).trim(), hostname); +FilterContainer.prototype.createUserScriptRule = function(args) { + var hash = args[2], + filter = new FilterHostname(args[4], args[3]); var bucket = this.userScripts.get(hash); if ( bucket === undefined ) { this.userScripts.set(hash, filter); From 607968de7fed9686f1ea1a2cfa8f13fb31278d41 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 21 Dec 2017 17:05:25 -0500 Subject: [PATCH 012/106] code review: cache most-recently-used pre-filled scriptlets --- src/js/cosmetic-filtering.js | 32 +++++++++++++++++++++----------- src/js/redirect-engine.js | 5 ++++- src/js/utils.js | 2 ++ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 9f738fa050037..939f6314f6caf 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -665,6 +665,7 @@ var FilterContainer = function() { }; this.userScripts = new Map(); + this.userScriptCache = new µb.MRUCache(32); // Short-lived: content is valid only during one function call. These // is to prevent repeated allocation/deallocation overheads -- the @@ -722,7 +723,9 @@ FilterContainer.prototype.reset = function() { this.scriptTagFilters = {}; this.scriptTagFilterCount = 0; + this.userScripts.clear(); + this.userScriptCache.reset(); }; /******************************************************************************/ @@ -1734,19 +1737,26 @@ FilterContainer.prototype.retrieveUserScripts = function( FilterContainer.prototype._lookupUserScript = function(raw, reng, toInject) { if ( toInject.has(raw) ) { return; } - var token, args, - pos = raw.indexOf(','); - if ( pos === -1 ) { - token = raw; - } else { - token = raw.slice(0, pos).trim(); - args = raw.slice(pos + 1).trim(); + if ( this.userScriptCache.resetTime < reng.modifyTime ) { + this.userScriptCache.reset(); } - var content = reng.resourceContentFromName(token, 'application/javascript'); - if ( !content ) { return; } - if ( args ) { - content = this._fillupUserScript(content, args); + var content = this.userScriptCache.lookup(raw); + if ( content === undefined ) { + var token, args, + pos = raw.indexOf(','); + if ( pos === -1 ) { + token = raw; + } else { + token = raw.slice(0, pos).trim(); + args = raw.slice(pos + 1).trim(); + } + content = reng.resourceContentFromName(token, 'application/javascript'); if ( !content ) { return; } + if ( args ) { + content = this._fillupUserScript(content, args); + if ( !content ) { return; } + } + this.userScriptCache.add(raw, content); } toInject.set(raw, content); }; diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 70912195a5801..47efae2a1f402 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2015-2016 Raymond Hill + Copyright (C) 2015-2017 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -95,6 +95,7 @@ RedirectEngine.prototype.reset = function() { this.ruleTypes = new Set(); this.ruleSources = new Set(); this.ruleDestinations = new Set(); + this.modifyTime = Date.now(); }; /******************************************************************************/ @@ -189,6 +190,7 @@ RedirectEngine.prototype.addRule = function(src, des, type, pattern, redirect) { entries = this.rules.get(key); if ( entries === undefined ) { this.rules.set(key, [ { tok: redirect, pat: pattern } ]); + this.modifyTime = Date.now(); return; } var entry; @@ -363,6 +365,7 @@ RedirectEngine.prototype.fromSelfie = function(selfie) { this.ruleTypes = new Set(selfie.ruleTypes); this.ruleSources = new Set(selfie.ruleSources); this.ruleDestinations = new Set(selfie.ruleDestinations); + this.modifyTime = Date.now(); return true; }; diff --git a/src/js/utils.js b/src/js/utils.js index fdde0d3ecdeba..970b72fe95e87 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -351,6 +351,7 @@ this.size = size; this.array = []; this.map = new Map(); + this.resetTime = Date.now(); }; µBlock.MRUCache.prototype = { @@ -380,6 +381,7 @@ reset: function() { this.array = []; this.map.clear(); + this.resetTime = Date.now(); } }; From 4ab63e70fee9c087cd8eff7fcb6e9720ebbaa55e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 22 Dec 2017 09:37:26 -0500 Subject: [PATCH 013/106] code review: avoid Array.splice/unshift The array size stays the same, items are just moved around. --- src/js/utils.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/js/utils.js b/src/js/utils.js index 970b72fe95e87..709937524fe6c 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -373,8 +373,11 @@ lookup: function(key) { var value = this.map.get(key); if ( value !== undefined && this.array[0] !== key ) { - this.array.splice(this.array.indexOf(key), 1); - this.array.unshift(key); + var i = this.array.indexOf(key); + do { + this.array[i] = this.array[i-1]; + } while ( --i ); + this.array[0] = key; } return value; }, From 11ccf8e2b9d473706df4d6705b6d8c67200575a4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 22 Dec 2017 09:43:28 -0500 Subject: [PATCH 014/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index a060d87d70bf2..28765ebc107eb 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.1", + "version": "1.14.23.2", "commands": { "launch-element-zapper": { From 8688461b1a2c1cb4b441fb871c8e842920c0af9d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 22 Dec 2017 09:44:55 -0500 Subject: [PATCH 015/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/ko/messages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 8e71b6b0a22c5..4a05331e7bab0 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -48,7 +48,7 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "클릭: 이 사이트에서 uBlock₀을 끕니다.\n\nCtrl+클릭: 이 페이지에서 uBlock₀을 끕니다.", + "message": "클릭: 이 사이트에서 uBlock₀을 끕니다.\n\nCtrl+클릭: 이 페이지에서만 uBlock₀을 끕니다.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { @@ -364,7 +364,7 @@ "description": "English: Malware domains" }, "3pGroupAnnoyances": { - "message": "성가신 것", + "message": "방해되는 것", "description": "The header identifying the filter lists in the category 'annoyances'" }, "3pGroupMultipurpose": { From dfcfa5ab9e113fd5429f95102003c3a1d718f047 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 22 Dec 2017 09:57:28 -0500 Subject: [PATCH 016/106] fix shell script for Opera --- tools/make-opera.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/make-opera.sh b/tools/make-opera.sh index 596b7ce82edb6..5dd9c91e3b4d2 100755 --- a/tools/make-opera.sh +++ b/tools/make-opera.sh @@ -23,6 +23,13 @@ cp platform/chromium/*.html $DES/ cp platform/chromium/*.json $DES/ cp LICENSE.txt $DES/ +echo "*** uBlock0.opera: concatenating content scripts" +cat $DES/js/vapi-usercss.js > /tmp/contentscript.js +echo >> /tmp/contentscript.js +grep -v "^'use strict';$" $DES/js/contentscript.js >> /tmp/contentscript.js +mv /tmp/contentscript.js $DES/js/contentscript.js +rm $DES/js/vapi-usercss.js + # Opera-specific cp platform/opera/manifest.json $DES/ rm -r $DES/_locales/cv From b446f9f8bd59a784508854e87e8c55dcf389abd7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 22 Dec 2017 11:45:07 -0500 Subject: [PATCH 017/106] fix regression reported in https://github.com/gorhill/uBlock/commit/dec0b80a7214ae2296bbe74654617c14fb48738c#commitcomment-26435928 by partially reverting changes from 4a09c9f866d3 --- src/js/background.js | 2 +- src/js/cosmetic-filtering.js | 28 ++++++++-------------------- 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index 57f94f645639b..c1a85de06250c 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -120,7 +120,7 @@ var µBlock = (function() { // jshint ignore:line // read-only systemSettings: { - compiledMagic: 'pxpclstriajk', + compiledMagic: 'vrgorlgelgws', selfieMagic: 'pxpclstriajk' }, diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 939f6314f6caf..671adf0b2eb97 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -1266,13 +1266,7 @@ FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, writer // script:contains(...) // script:inject(...) if ( this.reScriptSelector.test(selector) ) { - compiled = [ 6 /* js */, 0, '!', '', '' ]; - if ( selector.startsWith('script:inject') ) { - compiled[4] = selector.slice(14, -1).trim(); - } else { - compiled[1] = 1; - compiled[4] = selector.slice(16, -1).trim(); - } + compiled = [ 6 /* js */, '!', '', selector ]; writer.push(compiled); return; } @@ -1314,13 +1308,7 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, w if ( unhide ) { hash = '!' + hash; } - compiled = [ 6 /* js */, 0, hash, hostname, '' ]; - if ( selector.startsWith('script:inject') ) { - compiled[4] = selector.slice(14, -1).trim(); - } else { - compiled[1] = 1; - compiled[4] = selector.slice(16, -1).trim(); - } + compiled = [ 6 /* js */, hash, hostname, selector ]; writer.push(compiled); return; } @@ -1545,10 +1533,10 @@ FilterContainer.prototype.skipCompiledContent = function(reader) { /******************************************************************************/ FilterContainer.prototype.createScriptFilter = function(args) { - if ( args[1] === 0 ) { + if ( args[3].startsWith('script:inject') ) { return this.createUserScriptRule(args); } - if ( args[1] === 1 ) { + if ( args[3].startsWith('script:contains') ) { return this.createScriptTagFilter(args); } }; @@ -1561,8 +1549,8 @@ FilterContainer.prototype.createScriptFilter = function(args) { // 16 -1 FilterContainer.prototype.createScriptTagFilter = function(args) { - var hostname = args[3], - token = args[4]; + var hostname = args[2], + token = args[3].slice(16, -1); token = token.startsWith('/') && token.endsWith('/') ? token.slice(1, -1) : token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); @@ -1623,8 +1611,8 @@ FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) { // userScripts{hash} => FilterHostname | FilterBucket FilterContainer.prototype.createUserScriptRule = function(args) { - var hash = args[2], - filter = new FilterHostname(args[4], args[3]); + var hash = args[1], + filter = new FilterHostname(args[3].slice(14, -1), args[2]); var bucket = this.userScripts.get(hash); if ( bucket === undefined ) { this.userScripts.set(hash, filter); From 4f5f3652edf69bd6459350f854a5f65dc77d6af4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 22 Dec 2017 11:56:27 -0500 Subject: [PATCH 018/106] code review re last commit b446f9f8bd59 --- src/js/cosmetic-filtering.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 671adf0b2eb97..0e21e8c46270b 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -1260,19 +1260,17 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) /******************************************************************************/ FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, writer) { - var selector = parsed.suffix, - compiled; + var selector = parsed.suffix; // script:contains(...) // script:inject(...) if ( this.reScriptSelector.test(selector) ) { - compiled = [ 6 /* js */, '!', '', selector ]; - writer.push(compiled); + writer.push([ 6 /* js */, '!', '', selector ]); return; } // Procedural cosmetic filters are acceptable as generic exception filters. - compiled = this.compileSelector(selector); + var compiled = this.compileSelector(selector); if ( compiled === undefined ) { return; } // https://github.com/chrisaljoudi/uBlock/issues/497 @@ -1298,8 +1296,7 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, w var selector = parsed.suffix, domain = this.µburi.domainFromHostname(hostname), - hash, - compiled; + hash; // script:contains(...) // script:inject(...) @@ -1308,12 +1305,11 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, w if ( unhide ) { hash = '!' + hash; } - compiled = [ 6 /* js */, hash, hostname, selector ]; - writer.push(compiled); + writer.push([ 6 /* js */, hash, hostname, selector ]); return; } - compiled = this.compileSelector(selector); + var compiled = this.compileSelector(selector); if ( compiled === undefined ) { return; } // https://github.com/chrisaljoudi/uBlock/issues/188 From 3f335ad432d62c7570226b4a9025bdd5e1a4d2d3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 23 Dec 2017 08:14:08 -0500 Subject: [PATCH 019/106] address incomplete fix for #2877 as per feedback: https://github.com/gorhill/uBlock/commit/dec0b80a7214ae2296bbe74654617c14fb48738c#commitcomment-26447318 --- src/js/reverselookup-worker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index bb3a01a3b78e9..7b2662c8782e5 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -198,7 +198,7 @@ var fromCosmeticFilter = function(details) { case 8: case 9: if ( - fargs[0] === 8 && fargs[3] !== selector || + fargs[0] !== 9 && fargs[3] !== selector || fargs[0] === 9 && JSON.parse(fargs[3]).raw !== selector ) { break; From d2df01dc0804416ff53d81516cc316d66bd27b86 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 27 Dec 2017 09:46:12 -0500 Subject: [PATCH 020/106] remove unused library --- src/lib/yamd5.js | 402 ----------------------------------------------- 1 file changed, 402 deletions(-) delete mode 100644 src/lib/yamd5.js diff --git a/src/lib/yamd5.js b/src/lib/yamd5.js deleted file mode 100644 index 582b5764a4378..0000000000000 --- a/src/lib/yamd5.js +++ /dev/null @@ -1,402 +0,0 @@ -/******************************************************************************* - -YaMD5 - Yet another MD5 hasher. -home: https://github.com/gorhill/yamd5.js - -I needed an MD5 hasher, and as usual I want small code base, and fast. - -Originally found md5-o-matic [1]. It was fast but did not work with Unicode -strings. Also, eventually realized it was really based on code from -Joseph Myers [2] with no proper credits given (not nice). - -Then I found SparkMD5 [3], which works with Unicode strings, but at a steep -cost to performance. Also, glancing at the code I saw avoidable redundancies -causing the code base to be much larger than needed. - -So from this point I set out to write my own version, YaMD5 (sorry, I am -not good with naming projects), of course heavily relying on the original -code from Joseph Myers [2], and bits from SparkMD5 -- I started to work from -SparkMD5 implementation, so there might be bits of code original to SparkMD5 -code left in a few places (like say, MD5.end()). - -Advantages of YaMD5: - -- Can handle Unicode strings -- Natively incremental -- Small code base -- Fastest MD5 hasher out there so far for large input [4] -- Even faster than versions supporting only simpler ascii strings - - - [1] https://github.com/trentmillar/md5-o-matic - [2] http://www.myersdaily.org/joseph/javascript/md5-text.html - [3] https://github.com/satazor/SparkMD5 - [4] http://jsperf.com/md5-shootout/75 - -So with that said, I don't know what license covers Joseph Myers' code (need -to find out). In any case, concerning whatever original code I contributed in -there: - -The MIT License (MIT) - -Copyright (C) 2014 Raymond Hill - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -**/ - -/* jshint bitwise: false */ - -(function(root) { - - 'use strict'; - - /* - * Fastest md5 implementation around (JKM md5) - * Credits: Joseph Myers - * - * @see http://www.myersdaily.org/joseph/javascript/md5-text.html - * @see http://jsperf.com/md5-shootout/7 - */ - - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence - - var md5cycle = function(x, k) { - var a = x[0], - b = x[1], - c = x[2], - d = x[3]; - // ff() - a += (b & c | ~b & d) + k[0] - 680876936 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[1] - 389564586 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[2] + 606105819 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[3] - 1044525330 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[4] - 176418897 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[5] + 1200080426 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[6] - 1473231341 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[7] - 45705983 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[8] + 1770035416 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[9] - 1958414417 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[10] - 42063 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[11] - 1990404162 | 0; - b = (b << 22 | b >>> 10) + c | 0; - a += (b & c | ~b & d) + k[12] + 1804603682 | 0; - a = (a << 7 | a >>> 25) + b | 0; - d += (a & b | ~a & c) + k[13] - 40341101 | 0; - d = (d << 12 | d >>> 20) + a | 0; - c += (d & a | ~d & b) + k[14] - 1502002290 | 0; - c = (c << 17 | c >>> 15) + d | 0; - b += (c & d | ~c & a) + k[15] + 1236535329 | 0; - b = (b << 22 | b >>> 10) + c | 0; - // gg() - a += (b & d | c & ~d) + k[1] - 165796510 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[6] - 1069501632 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[11] + 643717713 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[0] - 373897302 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[5] - 701558691 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[10] + 38016083 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[15] - 660478335 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[4] - 405537848 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[9] + 568446438 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[14] - 1019803690 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[3] - 187363961 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[8] + 1163531501 | 0; - b = (b << 20 | b >>> 12) + c | 0; - a += (b & d | c & ~d) + k[13] - 1444681467 | 0; - a = (a << 5 | a >>> 27) + b | 0; - d += (a & c | b & ~c) + k[2] - 51403784 | 0; - d = (d << 9 | d >>> 23) + a | 0; - c += (d & b | a & ~b) + k[7] + 1735328473 | 0; - c = (c << 14 | c >>> 18) + d | 0; - b += (c & a | d & ~a) + k[12] - 1926607734 | 0; - b = (b << 20 | b >>> 12) + c | 0; - // hh() - a += (b ^ c ^ d) + k[5] - 378558 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[8] - 2022574463 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[11] + 1839030562 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[14] - 35309556 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[1] - 1530992060 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[4] + 1272893353 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[7] - 155497632 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[10] - 1094730640 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[13] + 681279174 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[0] - 358537222 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[3] - 722521979 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[6] + 76029189 | 0; - b = (b << 23 | b >>> 9) + c | 0; - a += (b ^ c ^ d) + k[9] - 640364487 | 0; - a = (a << 4 | a >>> 28) + b | 0; - d += (a ^ b ^ c) + k[12] - 421815835 | 0; - d = (d << 11 | d >>> 21) + a | 0; - c += (d ^ a ^ b) + k[15] + 530742520 | 0; - c = (c << 16 | c >>> 16) + d | 0; - b += (c ^ d ^ a) + k[2] - 995338651 | 0; - b = (b << 23 | b >>> 9) + c | 0; - // ii() - a += (c ^ (b | ~d)) + k[0] - 198630844 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[5] - 57434055 | 0; - b = (b << 21 |b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[10] - 1051523 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0; - b = (b << 21 |b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[15] - 30611744 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0; - b = (b << 21 |b >>> 11) + c | 0; - a += (c ^ (b | ~d)) + k[4] - 145523070 | 0; - a = (a << 6 | a >>> 26) + b | 0; - d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0; - d = (d << 10 | d >>> 22) + a | 0; - c += (a ^ (d | ~b)) + k[2] + 718787259 | 0; - c = (c << 15 | c >>> 17) + d | 0; - b += (d ^ (c | ~a)) + k[9] - 343485551 | 0; - b = (b << 21 | b >>> 11) + c | 0; - - x[0] = a + x[0] | 0; - x[1] = b + x[1] | 0; - x[2] = c + x[2] | 0; - x[3] = d + x[3] | 0; - }; - - var hexChars = '0123456789abcdef'; - var hexOut = []; - - var hex = function(x) { - var hc = hexChars; - var ho = hexOut; - var n, offset, j; - for (var i = 0; i < 4; i++) { - offset = i * 8; - n = x[i]; - for ( j = 0; j < 8; j += 2 ) { - ho[offset+1+j] = hc.charAt(n & 0x0F); - n >>>= 4; - ho[offset+0+j] = hc.charAt(n & 0x0F); - n >>>= 4; - } - } - return ho.join(''); - }; - - var MD5 = function() { - this._dataLength = 0; - this._state = new Int32Array(4); - this._buffer = new ArrayBuffer(68); - this._bufferLength = 0; - this._buffer8 = new Uint8Array(this._buffer, 0, 68); - this._buffer32 = new Uint32Array(this._buffer, 0, 17); - this.start(); - }; - - var stateIdentity = new Int32Array([1732584193, -271733879, -1732584194, 271733878]); - var buffer32Identity = new Int32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); - - // Char to code point to to array conversion: - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt#Example.3A_Fixing_charCodeAt_to_handle_non-Basic-Multilingual-Plane_characters_if_their_presence_earlier_in_the_string_is_unknown - MD5.prototype.appendStr = function(str) { - var buf8 = this._buffer8; - var buf32 = this._buffer32; - var bufLen = this._bufferLength; - var code; - for ( var i = 0; i < str.length; i++ ) { - code = str.charCodeAt(i); - if ( code < 128 ) { - buf8[bufLen++] = code; - } else if ( code < 0x800 ) { - buf8[bufLen++] = (code >>> 6) + 0xC0; - buf8[bufLen++] = code & 0x3F | 0x80; - } else if ( code < 0xD800 || code > 0xDBFF ) { - buf8[bufLen++] = (code >>> 12) + 0xE0; - buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80; - buf8[bufLen++] = (code & 0x3F) | 0x80; - } else { - code = ((code - 0xD800) * 0x400) + (str.charCodeAt(++i) - 0xDC00) + 0x10000; - if ( code > 0x10FFFF ) { - throw 'Unicode standard supports code points up to U+10FFFF'; - } - buf8[bufLen++] = (code >>> 18) + 0xF0; - buf8[bufLen++] = (code >>> 12 & 0x3F) | 0x80; - buf8[bufLen++] = (code >>> 6 & 0x3F) | 0x80; - buf8[bufLen++] = (code & 0x3F) | 0x80; - } - if ( bufLen >= 64 ) { - this._dataLength += 64; - md5cycle(this._state, buf32); - bufLen -= 64; - buf32[0] = buf32[16]; - } - } - this._bufferLength = bufLen; - return this; - }; - - MD5.prototype.appendAsciiStr = function(str) { - var buf8 = this._buffer8; - var buf32 = this._buffer32; - var bufLen = this._bufferLength; - var i, j = 0; - for (;;) { - i = Math.min(str.length-j, 64-bufLen); - while ( i-- ) { - buf8[bufLen++] = str.charCodeAt(j++); - } - if ( bufLen < 64 ) { - break; - } - this._dataLength += 64; - md5cycle(this._state, buf32); - bufLen = 0; - } - this._bufferLength = bufLen; - return this; - }; - - MD5.prototype.appendByteArray = function(input) { - var buf8 = this._buffer8; - var buf32 = this._buffer32; - var bufLen = this._bufferLength; - var i, j = 0; - for (;;) { - i = Math.min(input.length-j, 64-bufLen); - while ( i-- ) { - buf8[bufLen++] = input[j++]; - } - if ( bufLen < 64 ) { - break; - } - this._dataLength += 64; - md5cycle(this._state, buf32); - bufLen = 0; - } - this._bufferLength = bufLen; - return this; - }; - - MD5.prototype.start = function() { - this._dataLength = 0; - this._bufferLength = 0; - this._state.set(stateIdentity); - return this; - }; - - MD5.prototype.end = function(raw) { - var bufLen = this._bufferLength; - this._dataLength += bufLen; - var buf8 = this._buffer8; - buf8[bufLen] = 0x80; - buf8[bufLen+1] = buf8[bufLen+2] = buf8[bufLen+3] = 0; - var buf32 = this._buffer32; - var i = (bufLen >> 2) + 1; - buf32.set(buffer32Identity.subarray(i), i); - if (bufLen > 55) { - md5cycle(this._state, buf32); - buf32.set(buffer32Identity); - } - // Do the final computation based on the tail and length - // Beware that the final length may not fit in 32 bits so we take care of that - var dataBitsLen = this._dataLength * 8; - if ( dataBitsLen <= 0xFFFFFFFF ) { - buf32[14] = dataBitsLen; - } else { - var matches = dataBitsLen.toString(16).match(/(.*?)(.{0,8})$/); - var lo = parseInt(matches[2], 16); - var hi = parseInt(matches[1], 16) || 0; - buf32[14] = lo; - buf32[15] = hi; - } - md5cycle(this._state, buf32); - - return !!raw ? this._state : hex(this._state); - }; - - // This permanent instance is to use for one-call hashing - var onePassHasher = new MD5(); - - MD5.hashStr = function(str, raw) { - return onePassHasher - .start() - .appendStr(str) - .end(raw); - }; - - MD5.hashAsciiStr = function(str, raw) { - return onePassHasher - .start() - .appendAsciiStr(str) - .end(raw); - }; - - // Self-test - // In some cases the fast add32 function cannot be used.. - if ( MD5.hashStr('hello') !== '5d41402abc4b2a76b9719d911017c592' ) { - console.error('YaMD5> this javascript engine does not support YaMD5. Sorry.'); - } - - if ( typeof root === 'object' ) { - root.YaMD5 = MD5; - } - return MD5; -})(this); From a9f68fe02f40472c3e16287f8a67b0abe9421e10 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 28 Dec 2017 13:49:02 -0500 Subject: [PATCH 021/106] Fix #3069, and consequently #3374, #3378. A new filtering class has been created: "static extended filtering". This new class is an umbrella class for more specialized filtering engines: - Cosmetic filtering - Scriptlet filtering - HTML filtering HTML filtering is available only on platforms which support modifying the response body on the fly, so only Firefox 57+ at the moment. With the ability to modify the response body, HTML filtering has been introduced: removing elements from the DOM before the source data has been parsed by the browser. A consequence of HTML filtering ability is to bring back script tag filtering feature. --- platform/webext/vapi-webrequest.js | 6 +- src/background.html | 3 + src/js/assets.js | 9 +- src/js/background.js | 5 +- src/js/contentscript.js | 21 +- src/js/cosmetic-filtering.js | 1014 +++------------------------- src/js/html-filtering.js | 357 ++++++++++ src/js/messaging.js | 46 +- src/js/reverselookup-worker.js | 47 +- src/js/rpcreceiver.js | 13 +- src/js/scriptlet-filtering.js | 270 ++++++++ src/js/start.js | 4 +- src/js/static-ext-filtering.js | 680 +++++++++++++++++++ src/js/static-net-filtering.js | 6 + src/js/storage.js | 48 +- src/js/traffic.js | 278 +++++++- src/js/uritools.js | 9 +- src/js/utils.js | 73 +- 18 files changed, 1841 insertions(+), 1048 deletions(-) create mode 100644 src/js/html-filtering.js create mode 100644 src/js/scriptlet-filtering.js create mode 100644 src/js/static-ext-filtering.js diff --git a/platform/webext/vapi-webrequest.js b/platform/webext/vapi-webrequest.js index 2caeb33e7bb51..c2551ffbc366a 100644 --- a/platform/webext/vapi-webrequest.js +++ b/platform/webext/vapi-webrequest.js @@ -29,7 +29,11 @@ vAPI.net = { onBeforeRequest: {}, onBeforeMaybeSpuriousCSPReport: {}, onHeadersReceived: {}, - nativeCSPReportFiltering: true + nativeCSPReportFiltering: true, + webRequest: browser.webRequest, + canFilterResponseBody: + typeof browser.webRequest === 'object' && + typeof browser.webRequest.filterResponseData === 'function' }; /******************************************************************************/ diff --git a/src/background.html b/src/background.html index d3ccd04992c7f..168e9ae2044d4 100644 --- a/src/background.html +++ b/src/background.html @@ -22,7 +22,10 @@ + + + diff --git a/src/js/assets.js b/src/js/assets.js index 659f83b0968b4..845ef68b47416 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -53,11 +53,10 @@ api.removeObserver = function(observer) { }; var fireNotification = function(topic, details) { - var result; + var result, r; for ( var i = 0; i < observers.length; i++ ) { - if ( observers[i](topic, details) === false ) { - result = false; - } + r = observers[i](topic, details); + if ( r !== undefined ) { result = r; } } return result; }; @@ -955,7 +954,7 @@ var updateNext = function() { fireNotification( 'before-asset-updated', { assetKey: assetKey, type: assetEntry.content } - ) !== false + ) === true ) { return assetKey; } diff --git a/src/js/background.js b/src/js/background.js index c1a85de06250c..3d9a33018c3d1 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -95,6 +95,7 @@ var µBlock = (function() { // jshint ignore:line // Features detection. privacySettingsSupported: vAPI.browserSettings instanceof Object, cloudStorageSupported: vAPI.cloud instanceof Object, + canFilterResponseBody: vAPI.net.canFilterResponseBody === true, // https://github.com/chrisaljoudi/uBlock/issues/180 // Whitelist directives need to be loaded once the PSL is available @@ -120,8 +121,8 @@ var µBlock = (function() { // jshint ignore:line // read-only systemSettings: { - compiledMagic: 'vrgorlgelgws', - selfieMagic: 'pxpclstriajk' + compiledMagic: 'puuijtkfpspv', + selfieMagic: 'puuijtkfpspv' }, restoreBackupSettings: { diff --git a/src/js/contentscript.js b/src/js/contentscript.js index a231524af8233..1ba69ad5c1494 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -1379,20 +1379,9 @@ vAPI.domSurveyor = (function() { // Library of resources is located at: // https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt - if ( cfeDetails.scripts ) { - // Have the injected script tag remove itself when execution completes: - // to keep DOM as clean as possible. - var text = cfeDetails.scripts + - "\n" + - "(function() {\n" + - " var c = document.currentScript,\n" + - " p = c && c.parentNode;\n" + - " if ( p ) {\n" + - " p.removeChild(c);\n" + - " }\n" + - "})();"; - vAPI.injectScriptlet(document, text); - vAPI.injectedScripts = text; + if ( response.scriptlets ) { + vAPI.injectScriptlet(document, response.scriptlets); + vAPI.injectedScripts = response.scriptlets; } if ( vAPI.domSurveyor instanceof Object ) { @@ -1414,13 +1403,11 @@ vAPI.domSurveyor = (function() { }; // This starts bootstrap process. - var url = window.location.href; vAPI.messaging.send( 'contentscript', { what: 'retrieveContentScriptParameters', - pageURL: url, - locationURL: url, + url: window.location.href, isRootFrame: window === window.top }, bootstrapPhase1 diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 0e21e8c46270b..2561b8c37f50b 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -19,9 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint bitwise: false */ -/* global punycode */ - 'use strict'; /******************************************************************************/ @@ -31,56 +28,6 @@ /******************************************************************************/ var µb = µBlock; - -/******************************************************************************/ - -var isValidCSSSelector = (function() { - var div = document.createElement('div'), - matchesFn; - // Keep in mind: - // https://github.com/gorhill/uBlock/issues/693 - // https://github.com/gorhill/uBlock/issues/1955 - if ( div.matches instanceof Function ) { - matchesFn = div.matches.bind(div); - } else if ( div.mozMatchesSelector instanceof Function ) { - matchesFn = div.mozMatchesSelector.bind(div); - } else if ( div.webkitMatchesSelector instanceof Function ) { - matchesFn = div.webkitMatchesSelector.bind(div); - } else if ( div.msMatchesSelector instanceof Function ) { - matchesFn = div.msMatchesSelector.bind(div); - } else { - matchesFn = div.querySelector.bind(div); - } - // https://github.com/gorhill/uBlock/issues/3111 - // Workaround until https://bugzilla.mozilla.org/show_bug.cgi?id=1406817 - // is fixed. - try { - matchesFn(':scope'); - } catch (ex) { - matchesFn = div.querySelector.bind(div); - } - return function(s) { - try { - matchesFn(s + ', ' + s + ':not(#foo)'); - } catch (ex) { - return false; - } - return true; - }; -})(); - -var reIsRegexLiteral = /^\/.+\/$/; - -var isBadRegex = function(s) { - try { - void new RegExp(s); - } catch (ex) { - isBadRegex.message = ex.toString(); - return true; - } - return false; -}; - var cosmeticSurveyingMissCountMax = parseInt(vAPI.localStorage.getItem('cosmeticSurveyingMissCountMax'), 10) || 15; /******************************************************************************/ @@ -222,174 +169,6 @@ registerFilterClass(FilterBucket); /******************************************************************************/ /******************************************************************************/ -var FilterParser = function() { - this.prefix = this.suffix = ''; - this.unhide = 0; - this.hostnames = []; - this.invalid = false; - this.cosmetic = true; - this.reNeedHostname = /^(?:script:contains|script:inject|.+?:-abp-contains|.+?:-abp-has|.+?:contains|.+?:has|.+?:has-text|.+?:if|.+?:if-not|.+?:matches-css(?:-before|-after)?|.*?:xpath)\(.+\)$/; -}; - -/******************************************************************************/ - -FilterParser.prototype.reset = function() { - this.raw = ''; - this.prefix = this.suffix = ''; - this.unhide = 0; - this.hostnames.length = 0; - this.invalid = false; - this.cosmetic = true; - return this; -}; - -/******************************************************************************/ - -FilterParser.prototype.parse = function(raw) { - // important! - this.reset(); - - this.raw = raw; - - // Find the bounds of the anchor. - var lpos = raw.indexOf('#'); - if ( lpos === -1 ) { - this.cosmetic = false; - return this; - } - var rpos = raw.indexOf('#', lpos + 1); - if ( rpos === -1 ) { - this.cosmetic = false; - return this; - } - - // Coarse-check that the anchor is valid. - // `##`: l = 1 - // `#@#`, `#$#`, `#%#`, `#?#`: l = 2 - // `#@$#`, `#@%#`, `#@?#`: l = 3 - if ( (rpos - lpos) > 3 ) { - this.cosmetic = false; - return this; - } - - // Find out type of cosmetic filter. - // Exception filter? - if ( raw.charCodeAt(lpos + 1) === 0x40 /* '@' */ ) { - this.unhide = 1; - } - - // https://github.com/gorhill/uBlock/issues/952 - // Find out whether we are dealing with an Adguard-specific cosmetic - // filter, and if so, translate it if supported, or discard it if not - // supported. - var cCode = raw.charCodeAt(rpos - 1); - if ( cCode !== 0x23 /* '#' */ && cCode !== 0x40 /* '@' */ ) { - // We have an Adguard/ABP cosmetic filter if and only if the character - // is `$`, `%` or `?`, otherwise it's not a cosmetic filter. - if ( - cCode !== 0x24 /* '$' */ && - cCode !== 0x25 /* '%' */ && - cCode !== 0x3F /* '?' */ - ) { - this.cosmetic = false; - return this; - } - // Adguard's scriptlet injection: not supported. - if ( cCode === 0x25 /* '%' */ ) { - this.invalid = true; - return this; - } - // Adguard's style injection: supported, but translate to uBO's format. - if ( cCode === 0x24 /* '$' */ ) { - raw = this.translateAdguardCSSInjectionFilter(raw); - if ( raw === '' ) { - this.invalid = true; - return this; - } - } - rpos = raw.indexOf('#', lpos + 1); - } - - // Extract the hostname(s). - if ( lpos !== 0 ) { - this.prefix = raw.slice(0, lpos); - } - - // Extract the selector. - this.suffix = raw.slice(rpos + 1).trim(); - if ( this.suffix.length === 0 ) { - this.cosmetic = false; - return this; - } - - // 2014-05-23: - // https://github.com/gorhill/httpswitchboard/issues/260 - // Any sequence of `#` longer than one means the line is not a valid - // cosmetic filter. - if ( this.suffix.indexOf('##') !== -1 ) { - this.cosmetic = false; - return this; - } - - // Normalize high-medium selectors: `href` is assumed to imply `a` tag. We - // need to do this here in order to correctly avoid duplicates. The test - // is designed to minimize overhead -- this is a low occurrence filter. - if ( this.suffix.startsWith('[href^="', 1) ) { - this.suffix = this.suffix.slice(1); - } - - if ( this.prefix !== '' ) { - this.hostnames = this.prefix.split(/\s*,\s*/); - } - - // For some selectors, it is mandatory to have a hostname or entity: - // ##script:contains(...) - // ##script:inject(...) - // ##.foo:-abp-contains(...) - // ##.foo:-abp-has(...) - // ##.foo:contains(...) - // ##.foo:has(...) - // ##.foo:has-text(...) - // ##.foo:if(...) - // ##.foo:if-not(...) - // ##.foo:matches-css(...) - // ##.foo:matches-css-after(...) - // ##.foo:matches-css-before(...) - // ##:xpath(...) - if ( - this.hostnames.length === 0 && - this.unhide === 0 && - this.reNeedHostname.test(this.suffix) - ) { - this.invalid = true; - return this; - } - - return this; -}; - -/******************************************************************************/ - -// Reference: https://adguard.com/en/filterrules.html#cssInjection - -FilterParser.prototype.translateAdguardCSSInjectionFilter = function(raw) { - var matches = /^([^#]*)#(@?)\$#([^{]+)\{([^}]+)\}$/.exec(raw); - if ( matches === null ) { - return ''; - } - // For now we do not allow generic CSS injections (prolly never). - if ( matches[1] === '' && matches[2] !== '@' ) { - return ''; - } - return matches[1] + - '#' + matches[2] + '#' + - matches[3].trim() + - ':style(' + matches[4].trim() + ')'; -}; - -/******************************************************************************/ -/******************************************************************************/ - var SelectorCacheEntry = function() { this.reset(); }; @@ -538,17 +317,11 @@ SelectorCacheEntry.prototype.retrieve = function(type, out) { /******************************************************************************/ /******************************************************************************/ -// Two Unicode characters: -// T0HHHHHHH HHHHHHHHH -// | | | -// | | | -// | | | -// | | +-- bit 8-0 of FNV -// | | -// | +-- bit 15-9 of FNV -// | -// +-- filter type (0=hide 1=unhide) -// +// 0000HHHHHHHHHHHH +// | +// | +// | +// +-- bit 11-0 of FNV var makeHash = function(token) { // Ref: Given a URL, returns a unique 4-character long hash string @@ -607,7 +380,6 @@ var makeHash = function(token) { var FilterContainer = function() { this.noDomainHash = '-'; - this.parser = new FilterParser(); this.reHasUnicode = /[^\x00-\x7F]/; this.rePlainSelector = /^[#.][\w\\-]+/; this.rePlainSelectorEscaped = /^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+/; @@ -615,8 +387,25 @@ var FilterContainer = function() { this.reEscapeSequence = /\\([0-9A-Fa-f]+ |.)/g; this.reSimpleHighGeneric1 = /^[a-z]*\[[^[]+]$/; this.reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/; - this.reScriptSelector = /^script:(contains|inject)\((.+)\)$/; - this.punycode = punycode; + this.reNeedHostname = new RegExp([ + '^', + '(?:', + [ + 'script:contains', + '.+?:has', + '.+?:has-text', + '.+?:if', + '.+?:if-not', + '.+?:matches-css(?:-before|-after)?', + '.*?:xpath', + '.+?:-abp-contains', // ABP-specific for `:has-text` + '.+?:-abp-has', // ABP-specific for `:if` + '.+?:contains' // Adguard-specific for `:has-text` + ].join('|'), + ')', + '\\(.+\\)', + '$' + ].join('')); this.selectorCache = new Map(); this.selectorCachePruneDelay = 10 * 60 * 1000; // 10 minutes @@ -630,6 +419,9 @@ var FilterContainer = function() { // generic exception filters this.genericDonthideSet = new Set(); + // TODO: Think about reusing µb.staticExtFilteringEngine.HostnameBasedDB + // for both specific and procedural filters. This would require some + // refactoring. // hostname, entity-based filters this.specificFilters = new Map(); this.proceduralFilters = new Map(); @@ -664,9 +456,6 @@ var FilterContainer = function() { mru: new µb.MRUCache(16) }; - this.userScripts = new Map(); - this.userScriptCache = new µb.MRUCache(32); - // Short-lived: content is valid only during one function call. These // is to prevent repeated allocation/deallocation overheads -- the // constructors/destructors of javascript Set/Map is assumed to be costlier @@ -684,7 +473,6 @@ var FilterContainer = function() { // Reset all, thus reducing to a minimum memory footprint of the context. FilterContainer.prototype.reset = function() { - this.parser.reset(); this.µburi = µb.URI; this.frozen = false; this.acceptedCount = 0; @@ -720,12 +508,6 @@ FilterContainer.prototype.reset = function() { this.highlyGeneric.complex.dict.clear(); this.highlyGeneric.complex.str = ''; this.highlyGeneric.complex.mru.reset(); - - this.scriptTagFilters = {}; - this.scriptTagFilterCount = 0; - - this.userScripts.clear(); - this.userScriptCache.reset(); }; /******************************************************************************/ @@ -759,345 +541,9 @@ FilterContainer.prototype.freeze = function() { this.highlyGeneric.simple.str = µb.arrayFrom(this.highlyGeneric.simple.dict).join(',\n'); this.highlyGeneric.complex.str = µb.arrayFrom(this.highlyGeneric.complex.dict).join(',\n'); - this.parser.reset(); - this.compileSelector.reset(); - this.compileProceduralSelector.reset(); this.frozen = true; }; -/******************************************************************************/ - -// https://github.com/chrisaljoudi/uBlock/issues/1004 -// Detect and report invalid CSS selectors. - -// Discard new ABP's `-abp-properties` directive until it is -// implemented (if ever). Unlikely, see: -// https://github.com/gorhill/uBlock/issues/1752 - -// https://github.com/gorhill/uBlock/issues/2624 -// Convert Adguard's `-ext-has='...'` into uBO's `:has(...)`. - -FilterContainer.prototype.compileSelector = (function() { - var reAfterBeforeSelector = /^(.+?)(::?after|::?before)$/, - reStyleSelector = /^(.+?):style\((.+?)\)$/, - reStyleBad = /url\([^)]+\)/, - reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/, - reExtendedSyntaxParser = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/, - div = document.createElement('div'); - - var normalizedExtendedSyntaxOperators = new Map([ - [ 'contains', ':has-text' ], - [ 'has', ':if' ], - [ 'matches-css', ':matches-css' ], - [ 'matches-css-after', ':matches-css-after' ], - [ 'matches-css-before', ':matches-css-before' ], - ]); - - var isValidStyleProperty = function(cssText) { - if ( reStyleBad.test(cssText) ) { return false; } - div.style.cssText = cssText; - if ( div.style.cssText === '' ) { return false; } - div.style.cssText = ''; - return true; - }; - - var entryPoint = function(raw) { - var extendedSyntax = reExtendedSyntax.test(raw); - if ( isValidCSSSelector(raw) && extendedSyntax === false ) { - return raw; - } - - // We rarely reach this point -- majority of selectors are plain - // CSS selectors. - - var matches, operator; - - // Supported Adguard/ABP advanced selector syntax: will translate into - // uBO's syntax before further processing. - // Mind unsupported advanced selector syntax, such as ABP's - // `-abp-properties`. - // Note: extended selector syntax has been deprecated in ABP, in favor - // of the procedural one (i.e. `:operator(...)`). See - // https://issues.adblockplus.org/ticket/5287 - if ( extendedSyntax ) { - while ( (matches = reExtendedSyntaxParser.exec(raw)) !== null ) { - operator = normalizedExtendedSyntaxOperators.get(matches[1]); - if ( operator === undefined ) { return; } - raw = raw.slice(0, matches.index) + - operator + '(' + matches[3] + ')' + - raw.slice(matches.index + matches[0].length); - } - return this.compileSelector(raw); - } - - var selector = raw, - pseudoclass, style; - - // `:style` selector? - if ( (matches = reStyleSelector.exec(selector)) !== null ) { - selector = matches[1]; - style = matches[2]; - } - - // https://github.com/gorhill/uBlock/issues/2448 - // :after- or :before-based selector? - if ( (matches = reAfterBeforeSelector.exec(selector)) ) { - selector = matches[1]; - pseudoclass = matches[2]; - } - - if ( style !== undefined || pseudoclass !== undefined ) { - if ( isValidCSSSelector(selector) === false ) { - return; - } - if ( pseudoclass !== undefined ) { - selector += pseudoclass; - } - if ( style !== undefined ) { - if ( isValidStyleProperty(style) === false ) { return; } - return JSON.stringify({ - raw: raw, - style: [ selector, style ] - }); - } - return JSON.stringify({ - raw: raw, - pseudoclass: true - }); - } - - // `script:` filter? - if ( (matches = this.reScriptSelector.exec(raw)) !== null ) { - // :inject - if ( matches[1] === 'inject' ) { - return raw; - } - // :contains - if ( - reIsRegexLiteral.test(matches[2]) === false || - isBadRegex(matches[2].slice(1, -1)) === false - ) { - return raw; - } - } - - // Procedural selector? - var compiled; - if ( (compiled = this.compileProceduralSelector(raw)) ) { - return compiled; - } - - µb.logger.writeOne('', 'error', 'Cosmetic filtering – invalid filter: ' + raw); - }; - - entryPoint.reset = function() { - }; - - return entryPoint; -})(); - -/******************************************************************************/ - -FilterContainer.prototype.compileProceduralSelector = (function() { - var reOperatorParser = /(:(?:-abp-contains|-abp-has|contains|has|has-text|if|if-not|matches-css|matches-css-after|matches-css-before|xpath))\(.+\)$/, - reFirstParentheses = /^\(*/, - reLastParentheses = /\)*$/, - reEscapeRegex = /[.*+?^${}()|[\]\\]/g, - reNeedScope = /^\s*[+>~]/; - - var lastProceduralSelector = '', - lastProceduralSelectorCompiled, - regexToRawValue = new Map(); - - var compileCSSSelector = function(s) { - // https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277 - // Prepend `:scope ` if needed. - if ( reNeedScope.test(s) ) { - s = ':scope ' + s; - } - if ( isValidCSSSelector(s) ) { - return s; - } - }; - - var compileText = function(s) { - var reText; - if ( reIsRegexLiteral.test(s) ) { - reText = s.slice(1, -1); - if ( isBadRegex(reText) ) { return; } - } else { - reText = s.replace(reEscapeRegex, '\\$&'); - regexToRawValue.set(reText, s); - } - return reText; - }; - - var compileCSSDeclaration = function(s) { - var name, value, reText, - pos = s.indexOf(':'); - if ( pos === -1 ) { return; } - name = s.slice(0, pos).trim(); - value = s.slice(pos + 1).trim(); - if ( reIsRegexLiteral.test(value) ) { - reText = value.slice(1, -1); - if ( isBadRegex(reText) ) { return; } - } else { - reText = '^' + value.replace(reEscapeRegex, '\\$&') + '$'; - regexToRawValue.set(reText, value); - } - return { name: name, value: reText }; - }; - - var compileConditionalSelector = function(s) { - // https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277 - // Prepend `:scope ` if needed. - if ( reNeedScope.test(s) ) { - s = ':scope ' + s; - } - return compile(s); - }; - - var compileXpathExpression = function(s) { - var dummy; - try { - dummy = document.createExpression(s, null) instanceof XPathExpression; - } catch (e) { - return; - } - return s; - }; - - // https://github.com/gorhill/uBlock/issues/2793 - var normalizedOperators = new Map([ - [ ':-abp-contains', ':has-text' ], - [ ':-abp-has', ':if' ], - [ ':contains', ':has-text' ] - ]); - - var compileArgument = new Map([ - [ ':has', compileCSSSelector ], - [ ':has-text', compileText ], - [ ':if', compileConditionalSelector ], - [ ':if-not', compileConditionalSelector ], - [ ':matches-css', compileCSSDeclaration ], - [ ':matches-css-after', compileCSSDeclaration ], - [ ':matches-css-before', compileCSSDeclaration ], - [ ':xpath', compileXpathExpression ] - ]); - - // https://github.com/gorhill/uBlock/issues/2793#issuecomment-333269387 - // - Normalize (somewhat) the stringified version of procedural cosmetic - // filters -- this increase the likelihood of detecting duplicates given - // that uBO is able to understand syntax specific to other blockers. - // The normalized string version is what is reported in the logger, by - // design. - var decompile = function(compiled) { - var raw = [ compiled.selector ], - tasks = compiled.tasks, - value; - if ( Array.isArray(tasks) ) { - for ( var i = 0, n = tasks.length, task; i < n; i++ ) { - task = tasks[i]; - switch ( task[0] ) { - case ':has': - case ':xpath': - raw.push(task[0], '(', task[1], ')'); - break; - case ':has-text': - value = regexToRawValue.get(task[1]); - if ( value === undefined ) { - value = '/' + task[1] + '/'; - } - raw.push(task[0], '(', value, ')'); - break; - case ':matches-css': - case ':matches-css-after': - case ':matches-css-before': - value = regexToRawValue.get(task[1].value); - if ( value === undefined ) { - value = '/' + task[1].value + '/'; - } - raw.push(task[0], '(', task[1].name, ': ', value, ')'); - break; - case ':if': - case ':if-not': - raw.push(task[0], '(', decompile(task[1]), ')'); - break; - } - } - } - return raw.join(''); - }; - - var compile = function(raw) { - var matches = reOperatorParser.exec(raw); - if ( matches === null ) { - if ( isValidCSSSelector(raw) ) { return { selector: raw }; } - return; - } - var tasks = [], - firstOperand = raw.slice(0, matches.index), - currentOperator = matches[1], - selector = raw.slice(matches.index + currentOperator.length), - currentArgument = '', nextOperand, nextOperator, - depth = 0, opening, closing; - if ( firstOperand !== '' && isValidCSSSelector(firstOperand) === false ) { return; } - for (;;) { - matches = reOperatorParser.exec(selector); - if ( matches !== null ) { - nextOperand = selector.slice(0, matches.index); - nextOperator = matches[1]; - } else { - nextOperand = selector; - nextOperator = ''; - } - opening = reFirstParentheses.exec(nextOperand)[0].length; - closing = reLastParentheses.exec(nextOperand)[0].length; - if ( opening > closing ) { - if ( depth === 0 ) { currentArgument = ''; } - depth += 1; - } else if ( closing > opening && depth > 0 ) { - depth -= 1; - if ( depth === 0 ) { nextOperand = currentArgument + nextOperand; } - } - if ( depth !== 0 ) { - currentArgument += nextOperand + nextOperator; - } else { - currentOperator = normalizedOperators.get(currentOperator) || currentOperator; - currentArgument = compileArgument.get(currentOperator)(nextOperand.slice(1, -1)); - if ( currentArgument === undefined ) { return; } - tasks.push([ currentOperator, currentArgument ]); - currentOperator = nextOperator; - } - if ( nextOperator === '' ) { break; } - selector = selector.slice(matches.index + nextOperator.length); - } - if ( tasks.length === 0 || depth !== 0 ) { return; } - return { selector: firstOperand, tasks: tasks }; - }; - - var entryPoint = function(raw) { - if ( raw === lastProceduralSelector ) { - return lastProceduralSelectorCompiled; - } - lastProceduralSelector = raw; - var compiled = compile(raw); - if ( compiled !== undefined ) { - compiled.raw = decompile(compiled); - compiled = JSON.stringify(compiled); - } - lastProceduralSelectorCompiled = compiled; - return compiled; - }; - - entryPoint.reset = function() { - regexToRawValue = new Map(); - lastProceduralSelector = ''; - lastProceduralSelectorCompiled = undefined; - }; - - return entryPoint; -})(); /******************************************************************************/ @@ -1135,17 +581,12 @@ FilterContainer.prototype.keyFromSelector = function(selector) { /******************************************************************************/ -FilterContainer.prototype.compile = function(s, writer) { - var parsed = this.parser.parse(s); - if ( parsed.cosmetic === false ) { - return false; - } - if ( parsed.invalid ) { - return true; - } +FilterContainer.prototype.compile = function(parsed, writer) { + // 1000 = cosmetic filtering + writer.select(1000); - var hostnames = parsed.hostnames; - var i = hostnames.length; + var hostnames = parsed.hostnames, + i = hostnames.length; if ( i === 0 ) { this.compileGenericSelector(parsed, writer); return true; @@ -1155,9 +596,8 @@ FilterContainer.prototype.compile = function(s, writer) { // Negated hostname means the filter applies to all non-negated hostnames // of same filter OR globally if there is no non-negated hostnames. var applyGlobally = true; - var hostname; while ( i-- ) { - hostname = hostnames[i]; + var hostname = hostnames[i]; if ( hostname.startsWith('~') === false ) { applyGlobally = false; } @@ -1173,7 +613,7 @@ FilterContainer.prototype.compile = function(s, writer) { /******************************************************************************/ FilterContainer.prototype.compileGenericSelector = function(parsed, writer) { - if ( parsed.unhide === 0 ) { + if ( parsed.exception === false ) { this.compileGenericHideSelector(parsed, writer); } else { this.compileGenericUnhideSelector(parsed, writer); @@ -1183,6 +623,20 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, writer) { /******************************************************************************/ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) { + // For some selectors, it is mandatory to have a hostname or entity: + // ##.foo:-abp-contains(...) + // ##.foo:-abp-has(...) + // ##.foo:contains(...) + // ##.foo:has(...) + // ##.foo:has-text(...) + // ##.foo:if(...) + // ##.foo:if-not(...) + // ##.foo:matches-css(...) + // ##.foo:matches-css-after(...) + // ##.foo:matches-css-before(...) + // ##:xpath(...) + if ( this.reNeedHostname.test(selector) ) { return; } + var selector = parsed.suffix, type = selector.charCodeAt(0), key; @@ -1198,7 +652,7 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) return; } // Complex selector-based CSS rule. - if ( this.compileSelector(selector) !== undefined ) { + if ( µb.staticExtFilteringEngine.compileSelector(selector) !== undefined ) { writer.push([ 1 /* lg+ */, key.slice(1), selector ]); } return; @@ -1215,13 +669,13 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) return; } // Complex selector-based CSS rule. - if ( this.compileSelector(selector) !== undefined ) { + if ( µb.staticExtFilteringEngine.compileSelector(selector) !== undefined ) { writer.push([ 3 /* lg+ */, key.slice(1), selector ]); } return; } - var compiled = this.compileSelector(selector); + var compiled = µb.staticExtFilteringEngine.compileSelector(selector); if ( compiled === undefined ) { return; } // TODO: Detect and error on procedural cosmetic filters. @@ -1259,18 +713,12 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) /******************************************************************************/ -FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, writer) { - var selector = parsed.suffix; - - // script:contains(...) - // script:inject(...) - if ( this.reScriptSelector.test(selector) ) { - writer.push([ 6 /* js */, '!', '', selector ]); - return; - } - +FilterContainer.prototype.compileGenericUnhideSelector = function( + parsed, + writer +) { // Procedural cosmetic filters are acceptable as generic exception filters. - var compiled = this.compileSelector(selector); + var compiled = µb.staticExtFilteringEngine.compileSelector(parsed.suffix); if ( compiled === undefined ) { return; } // https://github.com/chrisaljoudi/uBlock/issues/497 @@ -1281,37 +729,24 @@ FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, writer /******************************************************************************/ -FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, writer) { +FilterContainer.prototype.compileHostnameSelector = function( + hostname, + parsed, + writer +) { // https://github.com/chrisaljoudi/uBlock/issues/145 - var unhide = parsed.unhide; + var unhide = parsed.exception ? 1 : 0; if ( hostname.startsWith('~') ) { hostname = hostname.slice(1); unhide ^= 1; } - // punycode if needed - if ( this.reHasUnicode.test(hostname) ) { - hostname = this.punycode.toASCII(hostname); - } + var compiled = µb.staticExtFilteringEngine.compileSelector(parsed.suffix); + if ( compiled === undefined ) { return; } - var selector = parsed.suffix, - domain = this.µburi.domainFromHostname(hostname), + var domain = this.µburi.domainFromHostname(hostname), hash; - // script:contains(...) - // script:inject(...) - if ( this.reScriptSelector.test(selector) ) { - hash = domain !== '' ? domain : this.noDomainHash; - if ( unhide ) { - hash = '!' + hash; - } - writer.push([ 6 /* js */, hash, hostname, selector ]); - return; - } - - var compiled = this.compileSelector(selector); - if ( compiled === undefined ) { return; } - // https://github.com/chrisaljoudi/uBlock/issues/188 // If not a real domain as per PSL, assign a synthetic one if ( hostname.endsWith('.*') === false ) { @@ -1319,7 +754,7 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, w } else { hash = makeHash(hostname); } - if ( unhide ) { + if ( unhide === 1 ) { hash = '!' + hash; } @@ -1336,23 +771,22 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, w /******************************************************************************/ -FilterContainer.prototype.fromCompiledContent = function( - reader, - skipGenericCosmetic, - skipCosmetic -) { - if ( skipCosmetic ) { +FilterContainer.prototype.fromCompiledContent = function(reader, options) { + if ( options.skipCosmetic ) { this.skipCompiledContent(reader); return; } - if ( skipGenericCosmetic ) { + if ( options.skipGenericCosmetic ) { this.skipGenericCompiledContent(reader); return; } var fingerprint, args, db, filter, bucket; - while ( reader.next() === true ) { + // 1000 = cosmetic filtering + reader.select(1000); + + while ( reader.next() ) { this.acceptedCount += 1; fingerprint = reader.fingerprint(); if ( this.duplicateBuster.has(fingerprint) ) { @@ -1410,12 +844,6 @@ FilterContainer.prototype.fromCompiledContent = function( this.highlyGeneric.complex.dict.add(args[1]); break; - // js, hash, example.com, script:contains(...) - // js, hash, example.com, script:inject(...) - case 6: - this.createScriptFilter(args); - break; - // https://github.com/chrisaljoudi/uBlock/issues/497 // Generic exception filters: expected to be a rare occurrence. // #@#.tweet @@ -1451,7 +879,10 @@ FilterContainer.prototype.fromCompiledContent = function( FilterContainer.prototype.skipGenericCompiledContent = function(reader) { var fingerprint, args, db, filter, bucket; - while ( reader.next() === true ) { + // 1000 = cosmetic filtering + reader.select(1000); + + while ( reader.next() ) { this.acceptedCount += 1; fingerprint = reader.fingerprint(); if ( this.duplicateBuster.has(fingerprint) ) { @@ -1463,13 +894,6 @@ FilterContainer.prototype.skipGenericCompiledContent = function(reader) { switch ( args[0] ) { - // js, hash, example.com, script:contains(...) - // js, hash, example.com, script:inject(...) - case 6: - this.duplicateBuster.add(fingerprint); - this.createScriptFilter(args); - break; - // https://github.com/chrisaljoudi/uBlock/issues/497 // Generic exception filters: expected to be a rare occurrence. case 7: @@ -1504,268 +928,17 @@ FilterContainer.prototype.skipGenericCompiledContent = function(reader) { /******************************************************************************/ FilterContainer.prototype.skipCompiledContent = function(reader) { - var fingerprint, args; + // 1000 = cosmetic filtering + reader.select(1000); - while ( reader.next() === true ) { + while ( reader.next() ) { this.acceptedCount += 1; - - args = reader.args(); - - // js, hash, example.com, script:contains(...) - // js, hash, example.com, script:inject(...) - if ( args[0] === 6 ) { - fingerprint = reader.fingerprint(); - if ( this.duplicateBuster.has(fingerprint) === false ) { - this.duplicateBuster.add(fingerprint); - this.createScriptFilter(args); - } - continue; - } - this.discardedCount += 1; } }; /******************************************************************************/ -FilterContainer.prototype.createScriptFilter = function(args) { - if ( args[3].startsWith('script:inject') ) { - return this.createUserScriptRule(args); - } - if ( args[3].startsWith('script:contains') ) { - return this.createScriptTagFilter(args); - } -}; - -/******************************************************************************/ - -// 0123456789012345678901 -// script:contains(token) -// ^ ^ -// 16 -1 - -FilterContainer.prototype.createScriptTagFilter = function(args) { - var hostname = args[2], - token = args[3].slice(16, -1); - token = token.startsWith('/') && token.endsWith('/') - ? token.slice(1, -1) - : token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - - if ( this.scriptTagFilters.hasOwnProperty(hostname) ) { - this.scriptTagFilters[hostname] += '|' + token; - } else { - this.scriptTagFilters[hostname] = token; - } - - this.scriptTagFilterCount += 1; -}; - -/******************************************************************************/ - -FilterContainer.prototype.retrieveScriptTagHostnames = function() { - return Object.keys(this.scriptTagFilters); -}; - -/******************************************************************************/ - -FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) { - if ( this.scriptTagFilterCount === 0 ) { - return; - } - var out = [], hn = hostname, pos; - - // Hostname-based - for (;;) { - if ( this.scriptTagFilters.hasOwnProperty(hn) ) { - out.push(this.scriptTagFilters[hn]); - } - if ( hn === domain ) { - break; - } - pos = hn.indexOf('.'); - if ( pos === -1 ) { - break; - } - hn = hn.slice(pos + 1); - } - - // Entity-based - pos = domain.indexOf('.'); - if ( pos !== -1 ) { - hn = domain.slice(0, pos) + '.*'; - if ( this.scriptTagFilters.hasOwnProperty(hn) ) { - out.push(this.scriptTagFilters[hn]); - } - } - if ( out.length !== 0 ) { - return out.join('|'); - } -}; - -/******************************************************************************/ - -// userScripts{hash} => FilterHostname | FilterBucket - -FilterContainer.prototype.createUserScriptRule = function(args) { - var hash = args[1], - filter = new FilterHostname(args[3].slice(14, -1), args[2]); - var bucket = this.userScripts.get(hash); - if ( bucket === undefined ) { - this.userScripts.set(hash, filter); - } else if ( bucket instanceof FilterBucket ) { - bucket.add(filter); - } else { - this.userScripts.set(hash, new FilterBucket(bucket, filter)); - } -}; - -/******************************************************************************/ - -// https://github.com/gorhill/uBlock/issues/1954 - -// 01234567890123456789 -// script:inject(token[, arg[, ...]]) -// ^ ^ -// 14 -1 - -FilterContainer.prototype.retrieveUserScripts = function( - domain, - hostname, - details -) { - if ( this.userScripts.size === 0 ) { return; } - if ( µb.hiddenSettings.ignoreScriptInjectFilters === true ) { return; } - - var reng = µb.redirectEngine; - if ( !reng ) { return; } - - this.mapRegister0.clear(); - - var toInject = this.mapRegister0, - pos = domain.indexOf('.'), - entity = pos !== -1 ? domain.slice(0, pos) + '.*' : ''; - - // Implicit - var hn = hostname; - for (;;) { - this._lookupUserScript(hn + '.js', reng, toInject); - if ( hn === domain ) { break; } - pos = hn.indexOf('.'); - if ( pos === -1 ) { break; } - hn = hn.slice(pos + 1); - } - if ( entity !== '' ) { - this._lookupUserScript(entity + '.js', reng, toInject); - } - - // Explicit (hash is domain). - var selectors = new Set(), - bucket; - if ( (bucket = this.userScripts.get(domain)) ) { - bucket.retrieve(hostname, selectors); - } - if ( entity !== '' && (bucket = this.userScripts.get(entity)) ) { - bucket.retrieve(entity, selectors); - } - for ( var selector of selectors ) { - this._lookupUserScript(selector, reng, toInject); - } - - if ( toInject.size === 0 ) { return; } - - // https://github.com/gorhill/uBlock/issues/2835 - // Do not inject scriptlets if the site is under an `allow` rule. - if ( - µb.userSettings.advancedUserEnabled === true && - µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2 - ) { - return; - } - - // Exceptions should be rare, so we check for exception only if there are - // scriptlets returned. - var exceptions = new Set(); - if ( (bucket = this.userScripts.get('!' + domain)) ) { - bucket.retrieve(hostname, exceptions); - } - if ( entity !== '' && (bucket = this.userScripts.get('!' + entity)) ) { - bucket.retrieve(hostname, exceptions); - } - - // Return an array of scriptlets, and log results if needed. - var out = [], - logger = µb.logger.isEnabled() ? µb.logger : null, - isException; - - for ( var entry of toInject ) { - if ( (isException = exceptions.has(entry[0])) === false ) { - out.push(entry[1]); - } - if ( logger === null ) { continue; } - logger.writeOne( - details.tabId, - 'cosmetic', - { - source: 'cosmetic', - raw: (isException ? '#@#' : '##') + 'script:inject(' + entry[0] + ')' - }, - 'dom', - details.locationURL, - null, - hostname - ); - } - - return out.join('\n'); -}; - -FilterContainer.prototype._lookupUserScript = function(raw, reng, toInject) { - if ( toInject.has(raw) ) { return; } - if ( this.userScriptCache.resetTime < reng.modifyTime ) { - this.userScriptCache.reset(); - } - var content = this.userScriptCache.lookup(raw); - if ( content === undefined ) { - var token, args, - pos = raw.indexOf(','); - if ( pos === -1 ) { - token = raw; - } else { - token = raw.slice(0, pos).trim(); - args = raw.slice(pos + 1).trim(); - } - content = reng.resourceContentFromName(token, 'application/javascript'); - if ( !content ) { return; } - if ( args ) { - content = this._fillupUserScript(content, args); - if ( !content ) { return; } - } - this.userScriptCache.add(raw, content); - } - toInject.set(raw, content); -}; - -// Fill template placeholders. Return falsy if: -// - At least one argument contains anything else than /\w/ and `.` - -FilterContainer.prototype._fillupUserScript = function(content, args) { - var i = 1, - pos, arg; - while ( args !== '' ) { - pos = args.indexOf(','); - if ( pos === -1 ) { pos = args.length; } - arg = args.slice(0, pos).trim().replace(this._reEscapeScriptArg, '\\$&'); - content = content.replace('{{' + i + '}}', arg); - args = args.slice(pos + 1).trim(); - i++; - } - return content; -}; - -FilterContainer.prototype._reEscapeScriptArg = /[\\'"]/g; - -/******************************************************************************/ - FilterContainer.prototype.toSelfie = function() { var selfieFromMap = function(map) { var selfie = []; @@ -1788,10 +961,7 @@ FilterContainer.prototype.toSelfie = function() { lowlyGenericCCL: µb.arrayFrom(this.lowlyGeneric.cl.complex), highSimpleGenericHideArray: µb.arrayFrom(this.highlyGeneric.simple.dict), highComplexGenericHideArray: µb.arrayFrom(this.highlyGeneric.complex.dict), - genericDonthideArray: µb.arrayFrom(this.genericDonthideSet), - scriptTagFilters: this.scriptTagFilters, - scriptTagFilterCount: this.scriptTagFilterCount, - userScripts: selfieFromMap(this.userScripts) + genericDonthideArray: µb.arrayFrom(this.genericDonthideSet) }; }; @@ -1823,9 +993,6 @@ FilterContainer.prototype.fromSelfie = function(selfie) { this.highlyGeneric.complex.dict = new Set(selfie.highComplexGenericHideArray); this.highlyGeneric.complex.str = selfie.highComplexGenericHideArray.join(',\n'); this.genericDonthideSet = new Set(selfie.genericDonthideArray); - this.scriptTagFilters = selfie.scriptTagFilters; - this.scriptTagFilterCount = selfie.scriptTagFilterCount; - this.userScripts = mapFromSelfie(selfie.userScripts); this.frozen = true; }; @@ -2055,14 +1222,10 @@ FilterContainer.prototype.retrieveDomainSelectors = function( request, options ) { - if ( !request.locationURL ) { return; } - //console.time('cosmeticFilteringEngine.retrieveDomainSelectors'); - var hostname = this.µburi.hostnameFromURI(request.locationURL), - domain = this.µburi.domainFromHostname(hostname) || hostname, - pos = domain.indexOf('.'), - entity = pos === -1 ? '' : domain.slice(0, pos - domain.length) + '.*', + var hostname = request.hostname, + entity = request.entity, cacheEntry = this.selectorCache.get(hostname), entry; @@ -2076,8 +1239,7 @@ FilterContainer.prototype.retrieveDomainSelectors = function( var out = { ready: this.frozen, hostname: hostname, - domain: domain, - entity: entity, + domain: request.domain, declarativeFilters: [], exceptionFilters: [], hideNodeAttr: this.randomAlphaToken(), @@ -2087,12 +1249,11 @@ FilterContainer.prototype.retrieveDomainSelectors = function( injectedHideFilters: '', networkFilters: '', noDOMSurveying: this.hasGenericHide === false, - proceduralFilters: [], - scripts: undefined + proceduralFilters: [] }; if ( options.noCosmeticFiltering !== true ) { - var domainHash = makeHash(domain), + var domainHash = makeHash(request.domain), entityHash = entity !== '' ? makeHash(entity) : undefined, exception, bucket; @@ -2228,9 +1389,6 @@ FilterContainer.prototype.retrieveDomainSelectors = function( this.setRegister2.clear(); } - // Scriptlet injection. - out.scripts = this.retrieveUserScripts(domain, hostname, request); - // CSS selectors for collapsible blocked elements if ( cacheEntry ) { var networkFilters = []; diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js new file mode 100644 index 0000000000000..890c874d991f7 --- /dev/null +++ b/src/js/html-filtering.js @@ -0,0 +1,357 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2017 Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +'use strict'; + +/******************************************************************************/ + +µBlock.htmlFilteringEngine = (function() { + var api = {}; + + var µb = µBlock, + filterDB = new µb.staticExtFilteringEngine.HostnameBasedDB(), + pselectors = new Map(), + duplicates = new Set(), + docRegister, loggerRegister; + + var PSelectorHasTask = function(task) { + this.selector = task[1]; + }; + PSelectorHasTask.prototype.exec = function(input) { + var output = []; + for ( var node of input ) { + if ( node.querySelector(this.selector) !== null ) { + output.push(node); + } + } + return output; + }; + + var PSelectorHasTextTask = function(task) { + this.needle = new RegExp(task[1]); + }; + PSelectorHasTextTask.prototype.exec = function(input) { + var output = []; + for ( var node of input ) { + if ( this.needle.test(node.textContent) ) { + output.push(node); + } + } + return output; + }; + + var PSelectorIfTask = function(task) { + this.pselector = new PSelector(task[1]); + }; + PSelectorIfTask.prototype.target = true; + PSelectorIfTask.prototype.exec = function(input) { + var output = []; + for ( var node of input ) { + if ( this.pselector.test(node) === this.target ) { + output.push(node); + } + } + return output; + }; + + var PSelectorIfNotTask = function(task) { + PSelectorIfTask.call(this, task); + this.target = false; + }; + PSelectorIfNotTask.prototype = Object.create(PSelectorIfTask.prototype); + PSelectorIfNotTask.prototype.constructor = PSelectorIfNotTask; + + var PSelectorXpathTask = function(task) { + this.xpe = task[1]; + }; + PSelectorXpathTask.prototype.exec = function(input) { + var output = [], + xpe = docRegister.createExpression(this.xpe, null), + xpr = null; + for ( var node of input ) { + xpr = xpe.evaluate( + node, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + xpr + ); + var j = xpr.snapshotLength; + while ( j-- ) { + node = xpr.snapshotItem(j); + if ( node.nodeType === 1 ) { + output.push(node); + } + } + } + return output; + }; + + var PSelector = function(o) { + if ( PSelector.prototype.operatorToTaskMap === undefined ) { + PSelector.prototype.operatorToTaskMap = new Map([ + [ ':has', PSelectorHasTask ], + [ ':has-text', PSelectorHasTextTask ], + [ ':if', PSelectorIfTask ], + [ ':if-not', PSelectorIfNotTask ], + [ ':xpath', PSelectorXpathTask ] + ]); + } + this.invalid = false; + this.raw = o.raw; + this.selector = o.selector; + this.tasks = []; + var tasks = o.tasks; + if ( !tasks ) { return; } + for ( var task of tasks ) { + var ctor = this.operatorToTaskMap.get(task[0]); + if ( ctor === undefined ) { + this.invalid = true; + break; + } + this.tasks.push(new ctor(task)); + } + }; + PSelector.prototype.operatorToTaskMap = undefined; + PSelector.prototype.prime = function(input) { + var root = input || docRegister; + if ( this.selector !== '' ) { + return root.querySelectorAll(this.selector); + } + return [ root ]; + }; + PSelector.prototype.exec = function(input) { + if ( this.invalid ) { return; } + var nodes = this.prime(input); + for ( var task of this.tasks ) { + if ( nodes.length === 0 ) { break; } + nodes = task.exec(nodes); + } + return nodes; + }; + + var logOne = function(details, selector) { + loggerRegister.writeOne( + details.tabId, + 'cosmetic', + { source: 'cosmetic', raw: '##^' + selector }, + 'dom', + details.url, + null, + details.hostname + ); + }; + + var applyProceduralSelector = function(details, selector) { + var pselector = pselectors.get(selector); + if ( pselector === undefined ) { + pselector = new PSelector(JSON.parse(selector)); + pselectors.set(selector, pselector); + } + var nodes = pselector.exec(), + i = nodes.length, + modified = false; + while ( i-- ) { + var node = nodes[i]; + if ( node.parentNode !== null ) { + node.parentNode.removeChild(node); + modified = true; + } + } + if ( modified && loggerRegister.isEnabled() ) { + logOne(details, pselector.raw); + } + return modified; + }; + + var applyCSSSelector = function(details, selector) { + var nodes = docRegister.querySelectorAll(selector), + i = nodes.length, + modified = false; + while ( i-- ) { + var node = nodes[i]; + if ( node.parentNode !== null ) { + node.parentNode.removeChild(node); + modified = true; + } + } + if ( modified && loggerRegister.isEnabled() ) { + logOne(details, selector); + } + return modified; + }; + + api.reset = function() { + filterDB.clear(); + pselectors.clear(); + duplicates.clear(); + }; + + api.freeze = function() { + duplicates.clear(); + }; + + api.compile = function(parsed, writer) { + var selector = parsed.suffix.slice(1).trim(), + compiled = µb.staticExtFilteringEngine.compileSelector(selector); + if ( compiled === undefined ) { return; } + + // 1002 = html filtering + writer.select(1002); + + // TODO: Mind negated hostnames, they are currently discarded. + + for ( var hostname of parsed.hostnames ) { + if ( hostname.charCodeAt(0) === 0x7E /* '~' */ ) { continue; } + var domain = µb.URI.domainFromHostname(hostname); + writer.push([ + compiled.charCodeAt(0) !== 0x7B /* '{' */ ? 64 : 65, + parsed.exception ? '!' + domain : domain, + hostname, + compiled + ]); + } + }; + + api.fromCompiledContent = function(reader) { + // Don't bother loading filters if stream filtering is not supported. + //if ( µb.canFilterResponseBody === false ) { return; } + + // 1002 = html filtering + reader.select(1002); + + while ( reader.next() ) { + var fingerprint = reader.fingerprint(); + if ( duplicates.has(fingerprint) ) { continue; } + duplicates.add(fingerprint); + var args = reader.args(); + filterDB.add(args[1], { + type: args[0], + hostname: args[2], + selector: args[3] + }); + } + }; + + api.retrieve = function(request) { + var hostname = request.hostname; + + // https://github.com/gorhill/uBlock/issues/2835 + // Do not filter if the site is under an `allow` rule. + if ( + µb.userSettings.advancedUserEnabled && + µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2 + ) { + return; + } + + var out = []; + if ( request.domain !== '' ) { + filterDB.retrieve(request.domain, hostname, out); + filterDB.retrieve(request.entity, request.entity, out); + } + filterDB.retrieve('', hostname, out); + + // TODO: handle exceptions. + + if ( out.length !== 0 ) { + return out; + } + }; + + api.apply = function(doc, details) { + docRegister = doc; + loggerRegister = µb.logger; + var modified = false; + for ( var entry of details.selectors ) { + if ( entry.type === 64 ) { + if ( applyCSSSelector(details, entry.selector) ) { + modified = true; + } + } else { + if ( applyProceduralSelector(details, entry.selector) ) { + modified = true; + } + } + } + + docRegister = loggerRegister = undefined; + return modified; + }; + + api.toSelfie = function() { + return filterDB.toSelfie(); + }; + + api.fromSelfie = function(selfie) { + filterDB = new µb.staticExtFilteringEngine.HostnameBasedDB(selfie); + pselectors.clear(); + }; + + // TODO: Following methods is useful only to legacy Firefox. This can be + // removed once support for legacy Firefox is dropped. The only care + // at this point is for the code to work, not to be efficient. + // Only `script:has-text` selectors are considered. + + api.retrieveScriptTagHostnames = function() { + var out = new Set(); + for ( var entry of filterDB ) { + if ( entry.type !== 65 ) { continue; } + var o = JSON.parse(entry.selector); + if ( + o.tasks.length === 1 && + o.tasks[0].length === 2 && + o.tasks[0][0] === ':has-text' + ) { + out.add(entry.hostname); + } + } + if ( out.size !== 0 ) { + return Array.from(out); + } + }; + + api.retrieveScriptTagRegex = function(domain, hostname) { + var entries = api.retrieve({ + hostname: hostname, + domain: domain, + entity: µb.URI.entityFromDomain(domain) + }); + if ( entries === undefined ) { return; } + var out = new Set(); + for ( var entry of entries ) { + if ( entry.type !== 65 ) { continue; } + var o = JSON.parse(entry.selector); + if ( + o.tasks.length === 1 && + o.tasks[0].length === 2 && + o.tasks[0][0] === ':has-text' + ) { + out.add(o.tasks[0][1]); + } + } + if ( out.size !== 0 ) { + return Array.from(out).join('|'); + } + }; + + return api; +})(); + +/******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 40ccaee61f1d2..51c91606cccb5 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -102,7 +102,7 @@ var onMessage = function(request, sender, callback) { break; case 'compileCosmeticFilterSelector': - response = µb.cosmeticFilteringEngine.compileSelector(request.selector); + response = µb.staticExtFilteringEngine.compileSelector(request.selector); break; case 'cosmeticFiltersInjected': @@ -465,7 +465,7 @@ var onMessage = function(request, sender, callback) { var µb = µBlock, response, tabId, frameId, - pageStore; + pageStore = null; if ( sender && sender.tab ) { tabId = sender.tab.id; @@ -491,21 +491,33 @@ var onMessage = function(request, sender, callback) { break; case 'retrieveContentScriptParameters': - if ( pageStore && pageStore.getNetFilteringSwitch() ) { - response = { - collapseBlocked: µb.userSettings.collapseBlocked, - noCosmeticFiltering: pageStore.noCosmeticFiltering === true, - noGenericCosmeticFiltering: - pageStore.noGenericCosmeticFiltering === true - }; - request.tabId = tabId; - request.frameId = frameId; - response.specificCosmeticFilters = - µb.cosmeticFilteringEngine - .retrieveDomainSelectors(request, response); - if ( request.isRootFrame && µb.logger.isEnabled() ) { - µb.logCosmeticFilters(tabId); - } + if ( + pageStore === null || + pageStore.getNetFilteringSwitch() === false || + !request.url + ) { + break; + } + response = { + collapseBlocked: µb.userSettings.collapseBlocked, + noCosmeticFiltering: pageStore.noCosmeticFiltering === true, + noGenericCosmeticFiltering: + pageStore.noGenericCosmeticFiltering === true + }; + request.tabId = tabId; + request.frameId = frameId; + request.hostname = µb.URI.hostnameFromURI(request.url); + request.domain = µb.URI.domainFromHostname(request.hostname); + request.entity = µb.URI.entityFromDomain(request.domain); + response.specificCosmeticFilters = + µb.cosmeticFilteringEngine.retrieveDomainSelectors(request, response); + // If response body filtering is supported, than the scriptlets have + // already been injected. + if ( µb.canFilterResponseBody === false ) { + response.scriptlets = µb.scriptletFilteringEngine.retrieve(request); + } + if ( request.isRootFrame && µb.logger.isEnabled() ) { + µb.logCosmeticFilters(tabId); } break; diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index 7b2662c8782e5..2073f9b8cebef 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -26,7 +26,26 @@ /******************************************************************************/ var listEntries = Object.create(null), - filterClassSeparator = '\n/* end of network - start of cosmetic */\n'; + reBlockStart = /^#block-start-(\d+)\n/gm; + +/******************************************************************************/ + +var extractBlocks = function(content, begId, endId) { + reBlockStart.lastIndex = 0; + var out = []; + var match = reBlockStart.exec(content); + while ( match !== null ) { + var beg = match.index + match[0].length; + var blockId = parseInt(match[1], 10); + if ( blockId >= begId && blockId < endId ) { + var end = content.indexOf('#block-end-' + match[1], beg); + out.push(content.slice(beg, end)); + reBlockStart.lastIndex = end; + } + match = reBlockStart.exec(content); + } + return out.join('\n'); +}; /******************************************************************************/ @@ -34,13 +53,11 @@ var fromNetFilter = function(details) { var lists = [], compiledFilter = details.compiledFilter, entry, content, pos, notFound; + for ( var assetKey in listEntries ) { entry = listEntries[assetKey]; if ( entry === undefined ) { continue; } - content = entry.content.slice( - 0, - entry.content.indexOf(filterClassSeparator) - ); + content = extractBlocks(entry.content, 0, 1000); pos = 0; for (;;) { pos = content.indexOf(compiledFilter, pos); @@ -96,7 +113,7 @@ var fromNetFilter = function(details) { // the various compiled versions. var fromCosmeticFilter = function(details) { - var match = /^#@?#/.exec(details.rawFilter), + var match = /^#@?#\^?/.exec(details.rawFilter), prefix = match[0], selector = details.rawFilter.slice(prefix.length); @@ -138,15 +155,14 @@ var fromCosmeticFilter = function(details) { } var response = Object.create(null), - assetKey, entry, content, found, beg, end, fargs; + assetKey, entry, content, + found, beg, end, + fargs, isProcedural; for ( assetKey in listEntries ) { entry = listEntries[assetKey]; if ( entry === undefined ) { continue; } - content = entry.content.slice( - entry.content.indexOf(filterClassSeparator) + - filterClassSeparator.length - ); + content = extractBlocks(entry.content, 1000, 2000); found = undefined; while ( (match = reNeedle.exec(content)) !== null ) { beg = content.lastIndexOf('\n', match.index); @@ -194,12 +210,15 @@ var fromCosmeticFilter = function(details) { found = prefix + selector; } break; - case 6: case 8: case 9: + case 32: + case 64: + case 65: + isProcedural = fargs[3].charCodeAt(0) === 0x7B; if ( - fargs[0] !== 9 && fargs[3] !== selector || - fargs[0] === 9 && JSON.parse(fargs[3]).raw !== selector + isProcedural === false && fargs[3] !== selector || + isProcedural && JSON.parse(fargs[3]).raw !== selector ) { break; } diff --git a/src/js/rpcreceiver.js b/src/js/rpcreceiver.js index 681d0f7d017ef..86adbcb5960cf 100644 --- a/src/js/rpcreceiver.js +++ b/src/js/rpcreceiver.js @@ -35,22 +35,21 @@ if ( typeof vAPI.rpcReceiver !== 'object' ) { vAPI.rpcReceiver.getScriptTagHostnames = function() { var µb = µBlock; - var cfe = µb.cosmeticFilteringEngine; - if ( !cfe ) { return; } - return cfe.retrieveScriptTagHostnames(); + if ( µb.htmlFilteringEngine ) { + return µb.htmlFilteringEngine.retrieveScriptTagHostnames(); + } }; /******************************************************************************/ vAPI.rpcReceiver.getScriptTagFilters = function(details) { var µb = µBlock; - var cfe = µb.cosmeticFilteringEngine; - if ( !cfe ) { return; } + if ( !µb.htmlFilteringEngine ) { return; } // Fetching the script tag filters first: assuming it is faster than // checking whether the site is whitelisted. var hostname = details.frameHostname; - var r = cfe.retrieveScriptTagRegex( - µb.URI.domainFromHostname(hostname) || hostname, + var r = µb.htmlFilteringEngine.retrieveScriptTagRegex( + µb.URI.domainFromHostname(hostname), hostname ); // https://github.com/gorhill/uBlock/issues/838 diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js new file mode 100644 index 0000000000000..b10066cf11bd8 --- /dev/null +++ b/src/js/scriptlet-filtering.js @@ -0,0 +1,270 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2017 Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +'use strict'; + +/******************************************************************************/ + +µBlock.scriptletFilteringEngine = (function() { + var api = {}; + + var µb = µBlock, + scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(), + duplicates = new Set(), + scriptletCache = new µb.MRUCache(32), + exceptionsRegister = new Set(), + scriptletsRegister = new Map(), + reEscapeScriptArg = /[\\'"]/g; + + var scriptletRemover = [ + '(function() {', + ' var c = document.currentScript, p = c && c.parentNode;', + ' if ( p ) { p.removeChild(c); }', + '})();' + ].join('\n'); + + + var lookupScriptlet = function(raw, reng, toInject) { + if ( toInject.has(raw) ) { return; } + if ( scriptletCache.resetTime < reng.modifyTime ) { + scriptletCache.reset(); + } + var content = scriptletCache.lookup(raw); + if ( content === undefined ) { + var token, args, + pos = raw.indexOf(','); + if ( pos === -1 ) { + token = raw; + } else { + token = raw.slice(0, pos).trim(); + args = raw.slice(pos + 1).trim(); + } + content = reng.resourceContentFromName(token, 'application/javascript'); + if ( !content ) { return; } + if ( args ) { + content = patchScriptlet(content, args); + if ( !content ) { return; } + } + scriptletCache.add(raw, content); + } + toInject.set(raw, content); + }; + + // Fill template placeholders. Return falsy if: + // - At least one argument contains anything else than /\w/ and `.` + + var patchScriptlet = function(content, args) { + var i = 1, + pos, arg; + while ( args !== '' ) { + pos = args.indexOf(','); + if ( pos === -1 ) { pos = args.length; } + arg = args.slice(0, pos).trim().replace(reEscapeScriptArg, '\\$&'); + content = content.replace('{{' + i + '}}', arg); + args = args.slice(pos + 1).trim(); + i++; + } + return content; + }; + + var logOne = function(isException, token, details) { + µb.logger.writeOne( + details.tabId, + 'cosmetic', + { + source: 'cosmetic', + raw: (isException ? '#@#' : '##') + 'script:inject(' + token + ')' + }, + 'dom', + details.url, + null, + details.hostname + ); + }; + + api.reset = function() { + scriptletDB.clear(); + duplicates.clear(); + }; + + api.freeze = function() { + duplicates.clear(); + }; + + api.compile = function(parsed, writer) { + // 1001 = scriptlet injection + writer.select(1001); + + // Only exception filters are allowed to be global. + + if ( parsed.hostnames.length === 0 ) { + if ( parsed.exception ) { + writer.push([ 32, '!', '', parsed.suffix ]); + } + return; + } + + // https://github.com/gorhill/uBlock/issues/3375 + // Ignore instances of exception filter with negated hostnames, + // because there is no way to create an exception to an exception. + + var µburi = µb.URI; + + for ( var hostname of parsed.hostnames ) { + var negated = hostname.charCodeAt(0) === 0x7E /* '~' */; + if ( negated ) { + hostname = hostname.slice(1); + } + var hash = µburi.domainFromHostname(hostname); + if ( parsed.exception ) { + if ( negated ) { continue; } + hash = '!' + hash; + } else if ( negated ) { + hash = '!' + hash; + } + writer.push([ 32, hash, hostname, parsed.suffix ]); + } + }; + + // 01234567890123456789 + // script:inject(token[, arg[, ...]]) + // ^ ^ + // 14 -1 + + api.fromCompiledContent = function(reader) { + // 1001 = scriptlet injection + reader.select(1001); + + while ( reader.next() ) { + var fingerprint = reader.fingerprint(); + if ( duplicates.has(fingerprint) ) { continue; } + duplicates.add(fingerprint); + var args = reader.args(); + if ( args.length < 4 ) { continue; } + scriptletDB.add( + args[1], + { hostname: args[2], token: args[3].slice(14, -1) } + ); + } + }; + + api.retrieve = function(request) { + if ( scriptletDB.size === 0 ) { return; } + if ( µb.hiddenSettings.ignoreScriptInjectFilters ) { return; } + + var reng = µb.redirectEngine; + if ( !reng ) { return; } + + var hostname = request.hostname; + + // https://github.com/gorhill/uBlock/issues/2835 + // Do not inject scriptlets if the site is under an `allow` rule. + if ( + µb.userSettings.advancedUserEnabled && + µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2 + ) { + return; + } + + var domain = request.domain, + entity = request.entity, + entries, entry; + + // https://github.com/gorhill/uBlock/issues/1954 + // Implicit + var hn = hostname; + for (;;) { + lookupScriptlet(hn + '.js', reng, scriptletsRegister); + if ( hn === domain ) { break; } + var pos = hn.indexOf('.'); + if ( pos === -1 ) { break; } + hn = hn.slice(pos + 1); + } + if ( entity !== '' ) { + lookupScriptlet(entity + '.js', reng, scriptletsRegister); + } + + // Explicit + entries = []; + if ( domain !== '' ) { + scriptletDB.retrieve(domain, hostname, entries); + scriptletDB.retrieve(entity, entity, entries); + } + scriptletDB.retrieve('', hostname, entries); + for ( entry of entries ) { + lookupScriptlet(entry.token, reng, scriptletsRegister); + } + + if ( scriptletsRegister.size === 0 ) { return; } + + // Collect exception filters. + entries = []; + if ( domain !== '' ) { + scriptletDB.retrieve('!' + domain, hostname, entries); + scriptletDB.retrieve('!' + entity, entity, entries); + } + scriptletDB.retrieve('!', hostname, entries); + for ( entry of entries ) { + exceptionsRegister.add(entry.token); + } + + // Return an array of scriptlets, and log results if needed. + var out = [], + logger = µb.logger.isEnabled() ? µb.logger : null, + isException; + for ( entry of scriptletsRegister ) { + if ( (isException = exceptionsRegister.has(entry[0])) === false ) { + out.push(entry[1]); + } + if ( logger !== null ) { + logOne(isException, entry[0], request); + } + } + + scriptletsRegister.clear(); + exceptionsRegister.clear(); + + if ( out.length === 0 ) { return; } + + out.push(scriptletRemover); + + return out.join('\n'); + }; + + api.apply = function(doc, details) { + var script = doc.createElement('script'); + script.textContent = details.scriptlets; + doc.head.insertBefore(script, doc.head.firstChild); + return true; + }; + + api.toSelfie = function() { + return scriptletDB.toSelfie(); + }; + + api.fromSelfie = function(selfie) { + scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(selfie); + }; + + return api; +})(); + +/******************************************************************************/ diff --git a/src/js/start.js b/src/js/start.js index f28280cf735a0..57f340812c282 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -39,7 +39,7 @@ vAPI.app.onShutdown = function() { µb.staticFilteringReverseLookup.shutdown(); µb.assets.updateStop(); µb.staticNetFilteringEngine.reset(); - µb.cosmeticFilteringEngine.reset(); + µb.staticExtFilteringEngine.reset(); µb.sessionFirewall.reset(); µb.permanentFirewall.reset(); µb.permanentFirewall.reset(); @@ -139,7 +139,7 @@ var onSelfieReady = function(selfie) { µb.availableFilterLists = selfie.availableFilterLists; µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine); µb.redirectEngine.fromSelfie(selfie.redirectEngine); - µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmeticFilteringEngine); + µb.staticExtFilteringEngine.fromSelfie(selfie.staticExtFilteringEngine); return true; }; diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js new file mode 100644 index 0000000000000..da5224c9ead2d --- /dev/null +++ b/src/js/static-ext-filtering.js @@ -0,0 +1,680 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2017 Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/* global punycode */ + +'use strict'; + +/******************************************************************************* + + All static extended filters are of the form: + + field 1: one hostname, or a list of comma-separated hostnames + field 2: `##` or `#@#` + field 3: selector + + The purpose of the static extended filtering engine is to coarse-parse and + dispatch to appropriate specialized filtering engines. There are currently + three specialized filtering engines: + + - cosmetic filtering (aka "element hiding" in Adblock Plus) + - scriptlet injection: selector starts with `script:inject` + - html filtering: selector starts with `^` + + Depending on the specialized filtering engine, field 1 may or may not be + optional. + + The static extended filtering engine also offers parsing capabilities which + are available to all other specialized fitlering engines. For example, + cosmetic and html filtering can ask the extended filtering engine to + compile/validate selectors. + +**/ + +µBlock.staticExtFilteringEngine = (function() { + var µb = µBlock, + reHostnameSeparator = /\s*,\s*/, + reHasUnicode = /[^\x00-\x7F]/, + reIsRegexLiteral = /^\/.+\/$/, + emptyArray = [], + parsed = { + hostnames: [], + exception: false, + suffix: '' + }; + + var isValidCSSSelector = (function() { + var div = document.createElement('div'), + matchesFn; + // Keep in mind: + // https://github.com/gorhill/uBlock/issues/693 + // https://github.com/gorhill/uBlock/issues/1955 + if ( div.matches instanceof Function ) { + matchesFn = div.matches.bind(div); + } else if ( div.mozMatchesSelector instanceof Function ) { + matchesFn = div.mozMatchesSelector.bind(div); + } else if ( div.webkitMatchesSelector instanceof Function ) { + matchesFn = div.webkitMatchesSelector.bind(div); + } else if ( div.msMatchesSelector instanceof Function ) { + matchesFn = div.msMatchesSelector.bind(div); + } else { + matchesFn = div.querySelector.bind(div); + } + // https://github.com/gorhill/uBlock/issues/3111 + // Workaround until https://bugzilla.mozilla.org/show_bug.cgi?id=1406817 + // is fixed. + try { + matchesFn(':scope'); + } catch (ex) { + matchesFn = div.querySelector.bind(div); + } + return function(s) { + try { + matchesFn(s + ', ' + s + ':not(#foo)'); + } catch (ex) { + return false; + } + return true; + }; + })(); + + + var isBadRegex = function(s) { + try { + void new RegExp(s); + } catch (ex) { + isBadRegex.message = ex.toString(); + return true; + } + return false; + }; + + var translateAdguardCSSInjectionFilter = function(suffix) { + var matches = /^([^{]+)\{([^}]+)\}$/.exec(suffix); + if ( matches === null ) { return ''; } + return matches[1].trim() + ':style(' + matches[2].trim() + ')'; + }; + + var toASCIIHostname = function(hostname) { + if ( hostname.charCodeAt(0) === 0x7E /* '~' */ ) { + return '~' + punycode.toASCII(hostname.slice(1)); + } + return punycode.toASCII(hostname); + }; + + var compileProceduralSelector = (function() { + var reOperatorParser = new RegExp([ + '(:(?:', + [ + '-abp-contains', + '-abp-has', + 'contains', + 'has', + 'has-text', + 'if', + 'if-not', + 'matches-css', + 'matches-css-after', + 'matches-css-before', + 'xpath' + ].join('|'), + '))\\(.+\\)$' + ].join('')); + + var reFirstParentheses = /^\(*/, + reLastParentheses = /\)*$/, + reEscapeRegex = /[.*+?^${}()|[\]\\]/g, + reNeedScope = /^\s*[+>~]/; + + var lastProceduralSelector = '', + lastProceduralSelectorCompiled, + regexToRawValue = new Map(); + + var compileCSSSelector = function(s) { + // https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277 + // Prepend `:scope ` if needed. + if ( reNeedScope.test(s) ) { + s = ':scope ' + s; + } + if ( isValidCSSSelector(s) ) { + return s; + } + }; + + var compileText = function(s) { + var reText; + if ( reIsRegexLiteral.test(s) ) { + reText = s.slice(1, -1); + if ( isBadRegex(reText) ) { return; } + } else { + reText = s.replace(reEscapeRegex, '\\$&'); + regexToRawValue.set(reText, s); + } + return reText; + }; + + var compileCSSDeclaration = function(s) { + var name, value, reText, + pos = s.indexOf(':'); + if ( pos === -1 ) { return; } + name = s.slice(0, pos).trim(); + value = s.slice(pos + 1).trim(); + if ( reIsRegexLiteral.test(value) ) { + reText = value.slice(1, -1); + if ( isBadRegex(reText) ) { return; } + } else { + reText = '^' + value.replace(reEscapeRegex, '\\$&') + '$'; + regexToRawValue.set(reText, value); + } + return { name: name, value: reText }; + }; + + var compileConditionalSelector = function(s) { + // https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277 + // Prepend `:scope ` if needed. + if ( reNeedScope.test(s) ) { + s = ':scope ' + s; + } + return compile(s); + }; + + var compileXpathExpression = function(s) { + try { + document.createExpression(s, null); + } catch (e) { + return; + } + return s; + }; + + // https://github.com/gorhill/uBlock/issues/2793 + var normalizedOperators = new Map([ + [ ':-abp-contains', ':has-text' ], + [ ':-abp-has', ':if' ], + [ ':contains', ':has-text' ] + ]); + + var compileArgument = new Map([ + [ ':has', compileCSSSelector ], + [ ':has-text', compileText ], + [ ':if', compileConditionalSelector ], + [ ':if-not', compileConditionalSelector ], + [ ':matches-css', compileCSSDeclaration ], + [ ':matches-css-after', compileCSSDeclaration ], + [ ':matches-css-before', compileCSSDeclaration ], + [ ':xpath', compileXpathExpression ] + ]); + + // https://github.com/gorhill/uBlock/issues/2793#issuecomment-333269387 + // Normalize (somewhat) the stringified version of procedural + // cosmetic filters -- this increase the likelihood of detecting + // duplicates given that uBO is able to understand syntax specific + // to other blockers. + // The normalized string version is what is reported in the logger, + // by design. + var decompile = function(compiled) { + var raw = [ compiled.selector ], + tasks = compiled.tasks, + value; + if ( Array.isArray(tasks) ) { + for ( var i = 0, n = tasks.length, task; i < n; i++ ) { + task = tasks[i]; + switch ( task[0] ) { + case ':has': + case ':xpath': + raw.push(task[0], '(', task[1], ')'); + break; + case ':has-text': + value = regexToRawValue.get(task[1]); + if ( value === undefined ) { + value = '/' + task[1] + '/'; + } + raw.push(task[0], '(', value, ')'); + break; + case ':matches-css': + case ':matches-css-after': + case ':matches-css-before': + value = regexToRawValue.get(task[1].value); + if ( value === undefined ) { + value = '/' + task[1].value + '/'; + } + raw.push(task[0], '(', task[1].name, ': ', value, ')'); + break; + case ':if': + case ':if-not': + raw.push(task[0], '(', decompile(task[1]), ')'); + break; + } + } + } + return raw.join(''); + }; + + var compile = function(raw) { + var matches = reOperatorParser.exec(raw); + if ( matches === null ) { + if ( isValidCSSSelector(raw) ) { return { selector: raw }; } + return; + } + var tasks = [], + firstOperand = raw.slice(0, matches.index), + currentOperator = matches[1], + selector = raw.slice(matches.index + currentOperator.length), + currentArgument = '', nextOperand, nextOperator, + depth = 0, opening, closing; + if ( + firstOperand !== '' && + isValidCSSSelector(firstOperand) === false + ) { + return; + } + for (;;) { + matches = reOperatorParser.exec(selector); + if ( matches !== null ) { + nextOperand = selector.slice(0, matches.index); + nextOperator = matches[1]; + } else { + nextOperand = selector; + nextOperator = ''; + } + opening = reFirstParentheses.exec(nextOperand)[0].length; + closing = reLastParentheses.exec(nextOperand)[0].length; + if ( opening > closing ) { + if ( depth === 0 ) { currentArgument = ''; } + depth += 1; + } else if ( closing > opening && depth > 0 ) { + depth -= 1; + if ( depth === 0 ) { + nextOperand = currentArgument + nextOperand; + } + } + if ( depth !== 0 ) { + currentArgument += nextOperand + nextOperator; + } else { + currentOperator = + normalizedOperators.get(currentOperator) || + currentOperator; + currentArgument = + compileArgument.get(currentOperator)( + nextOperand.slice(1, -1) + ); + if ( currentArgument === undefined ) { return; } + tasks.push([ currentOperator, currentArgument ]); + currentOperator = nextOperator; + } + if ( nextOperator === '' ) { break; } + selector = selector.slice(matches.index + nextOperator.length); + } + if ( tasks.length === 0 || depth !== 0 ) { return; } + return { selector: firstOperand, tasks: tasks }; + }; + + var entryPoint = function(raw) { + if ( raw === lastProceduralSelector ) { + return lastProceduralSelectorCompiled; + } + lastProceduralSelector = raw; + var compiled = compile(raw); + if ( compiled !== undefined ) { + compiled.raw = decompile(compiled); + compiled = JSON.stringify(compiled); + } + lastProceduralSelectorCompiled = compiled; + return compiled; + }; + + entryPoint.reset = function() { + regexToRawValue = new Map(); + lastProceduralSelector = ''; + lastProceduralSelectorCompiled = undefined; + }; + + return entryPoint; + })(); + + //-------------------------------------------------------------------------- + // Public API + //-------------------------------------------------------------------------- + + var api = {}; + + //-------------------------------------------------------------------------- + // Public classes + //-------------------------------------------------------------------------- + + api.HostnameBasedDB = function(selfie) { + if ( selfie !== undefined ) { + this.db = new Map(selfie.map); + this.size = selfie.size; + } else { + this.db = new Map(); + this.size = 0; + } + }; + + api.HostnameBasedDB.prototype = { + add: function(hash, entry) { + var bucket = this.db.get(hash); + if ( bucket === undefined ) { + this.db.set(hash, entry); + } else if ( Array.isArray(bucket) ) { + bucket.push(entry); + } else { + this.db.set(hash, [ bucket, entry ]); + } + this.size += 1; + }, + clear: function() { + this.db.clear(); + this.size = 0; + }, + retrieve: function(hash, hostname, out) { + var bucket = this.db.get(hash); + if ( bucket === undefined ) { return; } + if ( Array.isArray(bucket) === false ) { + if ( hostname.endsWith(bucket.hostname) ) { out.push(bucket); } + return; + } + var i = bucket.length; + while ( i-- ) { + var entry = bucket[i]; + if ( hostname.endsWith(entry.hostname) ) { out.push(entry); } + } + }, + toSelfie: function() { + return { + map: Array.from(this.db), + size: this.size + }; + } + }; + + api.HostnameBasedDB.prototype[Symbol.iterator] = (function() { + var Iter = function(db) { + this.mapIter = db.values(); + this.arrayIter = undefined; + }; + Iter.prototype.next = function() { + var result; + if ( this.arrayIter !== undefined ) { + result = this.arrayIter.next(); + if ( result.done === false ) { return result; } + this.arrayIter = undefined; + } + result = this.mapIter.next(); + if ( result.done || Array.isArray(result.value) === false ) { + return result; + } + this.arrayIter = result.value[Symbol.iterator](); + return this.arrayIter.next(); // array should never be empty + }; + return function() { + return new Iter(this.db); + }; + })(); + + //-------------------------------------------------------------------------- + // Public methods + //-------------------------------------------------------------------------- + + api.reset = function() { + compileProceduralSelector.reset(); + µb.cosmeticFilteringEngine.reset(); + µb.scriptletFilteringEngine.reset(); + µb.htmlFilteringEngine.reset(); + }; + + api.freeze = function() { + compileProceduralSelector.reset(); + µb.cosmeticFilteringEngine.freeze(); + µb.scriptletFilteringEngine.freeze(); + µb.htmlFilteringEngine.freeze(); + }; + + // https://github.com/chrisaljoudi/uBlock/issues/1004 + // Detect and report invalid CSS selectors. + + // Discard new ABP's `-abp-properties` directive until it is + // implemented (if ever). Unlikely, see: + // https://github.com/gorhill/uBlock/issues/1752 + + // https://github.com/gorhill/uBlock/issues/2624 + // Convert Adguard's `-ext-has='...'` into uBO's `:has(...)`. + + api.compileSelector = (function() { + var reAfterBeforeSelector = /^(.+?)(::?after|::?before)$/, + reStyleSelector = /^(.+?):style\((.+?)\)$/, + reStyleBad = /url\([^)]+\)/, + reExtendedSyntax = /\[-(?:abp|ext)-[a-z-]+=(['"])(?:.+?)(?:\1)\]/, + reExtendedSyntaxParser = /\[-(?:abp|ext)-([a-z-]+)=(['"])(.+?)\2\]/, + div = document.createElement('div'); + + var normalizedExtendedSyntaxOperators = new Map([ + [ 'contains', ':has-text' ], + [ 'has', ':if' ], + [ 'matches-css', ':matches-css' ], + [ 'matches-css-after', ':matches-css-after' ], + [ 'matches-css-before', ':matches-css-before' ], + ]); + + var isValidStyleProperty = function(cssText) { + if ( reStyleBad.test(cssText) ) { return false; } + div.style.cssText = cssText; + if ( div.style.cssText === '' ) { return false; } + div.style.cssText = ''; + return true; + }; + + var entryPoint = function(raw) { + var extendedSyntax = reExtendedSyntax.test(raw); + if ( isValidCSSSelector(raw) && extendedSyntax === false ) { + return raw; + } + + // We rarely reach this point -- majority of selectors are plain + // CSS selectors. + + var matches, operator; + + // Supported Adguard/ABP advanced selector syntax: will translate into + // uBO's syntax before further processing. + // Mind unsupported advanced selector syntax, such as ABP's + // `-abp-properties`. + // Note: extended selector syntax has been deprecated in ABP, in favor + // of the procedural one (i.e. `:operator(...)`). See + // https://issues.adblockplus.org/ticket/5287 + if ( extendedSyntax ) { + while ( (matches = reExtendedSyntaxParser.exec(raw)) !== null ) { + operator = normalizedExtendedSyntaxOperators.get(matches[1]); + if ( operator === undefined ) { return; } + raw = raw.slice(0, matches.index) + + operator + '(' + matches[3] + ')' + + raw.slice(matches.index + matches[0].length); + } + return entryPoint(raw); + } + + var selector = raw, + pseudoclass, style; + + // `:style` selector? + if ( (matches = reStyleSelector.exec(selector)) !== null ) { + selector = matches[1]; + style = matches[2]; + } + + // https://github.com/gorhill/uBlock/issues/2448 + // :after- or :before-based selector? + if ( (matches = reAfterBeforeSelector.exec(selector)) ) { + selector = matches[1]; + pseudoclass = matches[2]; + } + + if ( style !== undefined || pseudoclass !== undefined ) { + if ( isValidCSSSelector(selector) === false ) { + return; + } + if ( pseudoclass !== undefined ) { + selector += pseudoclass; + } + if ( style !== undefined ) { + if ( isValidStyleProperty(style) === false ) { return; } + return JSON.stringify({ + raw: raw, + style: [ selector, style ] + }); + } + return JSON.stringify({ + raw: raw, + pseudoclass: true + }); + } + + // Procedural selector? + var compiled; + if ( (compiled = compileProceduralSelector(raw)) ) { + return compiled; + } + + µb.logger.writeOne( + '', + 'error', + 'Cosmetic filtering – invalid filter: ' + raw + ); + }; + + return entryPoint; + })(); + + api.compile = function(raw, writer) { + var lpos = raw.indexOf('#'); + if ( lpos === -1 ) { return false; } + var rpos = lpos + 1; + if ( raw.charCodeAt(rpos) !== 0x23 /* '#' */ ) { + rpos = raw.indexOf('#', rpos + 1); + if ( rpos === -1 ) { return false; } + } + + // Coarse-check that the anchor is valid. + // `##`: l = 1 + // `#@#`, `#$#`, `#%#`, `#?#`: l = 2 + // `#@$#`, `#@%#`, `#@?#`: l = 3 + if ( (rpos - lpos) > 3 ) { return false; } + + // Extract the selector. + var suffix = parsed.suffix = raw.slice(rpos + 1).trim(); + if ( suffix.length === 0 ) { return false; } + + // https://github.com/gorhill/uBlock/issues/952 + // Find out whether we are dealing with an Adguard-specific cosmetic + // filter, and if so, translate it if supported, or discard it if not + // supported. + // We have an Adguard/ABP cosmetic filter if and only if the + // character is `$`, `%` or `?`, otherwise it's not a cosmetic + // filter. + var cCode = raw.charCodeAt(rpos - 1); + if ( cCode !== 0x23 /* '#' */ && cCode !== 0x40 /* '@' */ ) { + // Adguard's scriptlet injection: not supported. + if ( cCode === 0x25 /* '%' */ ) { return true; } + // Not a known extended filter. + if ( cCode !== 0x24 /* '$' */ && cCode !== 0x3F /* '?' */ ) { + return false; + } + // Adguard's style injection: translate to uBO's format. + if ( cCode === 0x24 /* '$' */ ) { + suffix = translateAdguardCSSInjectionFilter(suffix); + if ( suffix === '' ) { return true; } + } + } + + // Exception filter? + parsed.exception = raw.charCodeAt(lpos + 1) === 0x40 /* '@' */; + + // Extract the hostname(s), punycode if required. + if ( lpos === 0 ) { + parsed.hostnames = emptyArray; + } else { + var prefix = raw.slice(0, lpos); + parsed.hostnames = prefix.split(reHostnameSeparator); + if ( reHasUnicode.test(prefix) ) { + for ( var hostname of parsed.hostnames ) { + parsed.hostnames = toASCIIHostname(hostname); + } + } + } + + if ( suffix.startsWith('script:') ) { + // Scriptlet injection engine. + if ( suffix.startsWith('script:inject') ) { + µb.scriptletFilteringEngine.compile(parsed, writer); + return true; + } + // Script tag filtering: courtesy-conversion to HTML filtering. + if ( parsed.suffix.startsWith('script:contains') ) { + console.info( + 'uBO: ##script:contains(...) is deprecated, ' + + 'converting to ##^script:has-text(...)' + ); + suffix = parsed.suffix = suffix.replace( + /^script:contains/, + '^script:has-text' + ); + } + } + + // HTML filtering engine. + // TODO: evaluate converting Adguard's `$$` syntax into uBO's HTML + // filtering syntax. + if ( suffix.charCodeAt(0) === 0x5E /* '^' */ ) { + µb.htmlFilteringEngine.compile(parsed, writer); + return true; + } + + // Cosmetic filtering engine. + µb.cosmeticFilteringEngine.compile(parsed, writer); + return true; + }; + + api.fromCompiledContent = function(reader, options) { + µb.cosmeticFilteringEngine.fromCompiledContent(reader, options); + µb.scriptletFilteringEngine.fromCompiledContent(reader, options); + µb.htmlFilteringEngine.fromCompiledContent(reader, options); + }; + + api.toSelfie = function() { + return { + cosmetic: µb.cosmeticFilteringEngine.toSelfie(), + scriptlets: µb.scriptletFilteringEngine.toSelfie(), + html: µb.htmlFilteringEngine.toSelfie() + + }; + }; + + api.fromSelfie = function(selfie) { + µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic); + µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets); + µb.htmlFilteringEngine.fromSelfie(selfie.html); + }; + + return api; +})(); + +/******************************************************************************/ diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index c32281520299c..d228282eebffe 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2116,6 +2116,9 @@ FilterContainer.prototype.compile = function(raw, writer) { return false; } + // 0 = network filters + writer.select(0); + // Pure hostnames, use more efficient dictionary lookup // https://github.com/chrisaljoudi/uBlock/issues/665 // Create a dict keyed on request type etc. @@ -2268,6 +2271,9 @@ FilterContainer.prototype.fromCompiledContent = function(reader) { args, bits, bucket, entry, tokenHash, fdata, fingerprint; + // 0 = network filters + reader.select(0); + while ( reader.next() === true ) { args = reader.args(); bits = args[0]; diff --git a/src/js/storage.js b/src/js/storage.js index 256e80d15298b..d8dfaf1d07bd9 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -347,7 +347,7 @@ vAPI.storage.set({ 'availableFilterLists': µb.availableFilterLists }); µb.staticNetFilteringEngine.freeze(); µb.redirectEngine.freeze(); - µb.cosmeticFilteringEngine.freeze(); + µb.staticExtFilteringEngine.freeze(); µb.selfieManager.destroy(); }; @@ -543,7 +543,7 @@ var onDone = function() { µb.staticNetFilteringEngine.freeze(); - µb.cosmeticFilteringEngine.freeze(); + µb.staticExtFilteringEngine.freeze(); µb.redirectEngine.freeze(); vAPI.storage.set({ 'availableFilterLists': µb.availableFilterLists }); @@ -586,7 +586,7 @@ µb.availableFilterLists = lists; µb.redirectEngine.reset(); - µb.cosmeticFilteringEngine.reset(); + µb.staticExtFilteringEngine.reset(); µb.staticNetFilteringEngine.reset(); µb.selfieManager.destroy(); µb.staticFilteringReverseLookup.resetLists(); @@ -703,23 +703,22 @@ /******************************************************************************/ µBlock.compileFilters = function(rawText) { - var networkFilters = new this.CompiledLineWriter(), - cosmeticFilters = new this.CompiledLineWriter(); + var writer = new this.CompiledLineWriter(); // Useful references: // https://adblockplus.org/en/filter-cheatsheet // https://adblockplus.org/en/filters var staticNetFilteringEngine = this.staticNetFilteringEngine, - cosmeticFilteringEngine = this.cosmeticFilteringEngine, + staticExtFilteringEngine = this.staticExtFilteringEngine, reIsWhitespaceChar = /\s/, reMaybeLocalIp = /^[\d:f]/, - reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)(?=\s|$)/, + reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)\b/, reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/, - line, lineRaw, c, pos, + line, c, pos, lineIter = new this.LineIterator(rawText); while ( lineIter.eot() === false ) { - line = lineRaw = lineIter.next().trim(); + line = lineIter.next().trim(); // rhill 2014-04-18: The trim is important here, as without it there // could be a lingering `\r` which would cause problems in the @@ -733,9 +732,7 @@ // Parse or skip cosmetic filters // All cosmetic filters are caught here - if ( cosmeticFilteringEngine.compile(line, cosmeticFilters) ) { - continue; - } + if ( staticExtFilteringEngine.compile(line, writer) ) { continue; } // Whatever else is next can be assumed to not be a cosmetic filter @@ -767,12 +764,10 @@ if ( line.length === 0 ) { continue; } - staticNetFilteringEngine.compile(line, networkFilters); + staticNetFilteringEngine.compile(line, writer); } - return networkFilters.toString() + - '\n/* end of network - start of cosmetic */\n' + - cosmeticFilters.toString(); + return writer.toString(); }; /******************************************************************************/ @@ -783,15 +778,12 @@ µBlock.applyCompiledFilters = function(rawText, firstparty) { if ( rawText === '' ) { return; } - var separator = '\n/* end of network - start of cosmetic */\n', - pos = rawText.indexOf(separator), - reader = new this.CompiledLineReader(rawText.slice(0, pos)); + var reader = new this.CompiledLineReader(rawText); this.staticNetFilteringEngine.fromCompiledContent(reader); - this.cosmeticFilteringEngine.fromCompiledContent( - reader.reset(rawText.slice(pos + separator.length)), - this.userSettings.ignoreGenericCosmeticFilters, - !firstparty && !this.userSettings.parseAllABPHideFilters - ); + this.staticExtFilteringEngine.fromCompiledContent(reader, { + skipGenericCosmetic: this.userSettings.ignoreGenericCosmeticFilters, + skipCosmetic: !firstparty && !this.userSettings.parseAllABPHideFilters + }); }; /******************************************************************************/ @@ -885,7 +877,7 @@ availableFilterLists: this.availableFilterLists, staticNetFilteringEngine: this.staticNetFilteringEngine.toSelfie(), redirectEngine: this.redirectEngine.toSelfie(), - cosmeticFilteringEngine: this.cosmeticFilteringEngine.toSelfie() + staticExtFilteringEngine: this.staticExtFilteringEngine.toSelfie() }; vAPI.cacheStorage.set({ selfie: selfie }); }.bind(µBlock); @@ -1068,7 +1060,7 @@ this.availableFilterLists.hasOwnProperty(details.assetKey) === false || this.selectedFilterLists.indexOf(details.assetKey) === -1 ) { - return false; + return; } } // https://github.com/gorhill/uBlock/issues/2594 @@ -1077,10 +1069,10 @@ this.hiddenSettings.ignoreRedirectFilters === true && this.hiddenSettings.ignoreScriptInjectFilters === true ) { - return false; + return; } } - return; + return true; } // Compile the list while we have the raw version in memory diff --git a/src/js/traffic.js b/src/js/traffic.js index eaea84158b4a6..1ff0c01d0913a 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -480,9 +480,10 @@ onBeforeMaybeSpuriousCSPReport.textDecoder = undefined; /******************************************************************************/ // To handle: -// - inline script tags -// - websockets -// - media elements larger than n kB +// - Media elements larger than n kB +// - Scriptlet injection (requires ability to modify response body) +// - HTML filtering (requires ability to modify response body) +// - CSP injection var onHeadersReceived = function(details) { // Do not interfere with behind-the-scene requests. @@ -490,15 +491,17 @@ var onHeadersReceived = function(details) { if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; } var µb = µBlock, - requestType = details.type; + requestType = details.type, + isRootDoc = requestType === 'main_frame', + isDoc = isRootDoc || requestType === 'sub_frame'; - if ( requestType === 'main_frame' ) { + if ( isRootDoc ) { µb.tabContextManager.push(tabId, details.url); } var pageStore = µb.pageStoreFromTabId(tabId); if ( pageStore === null ) { - if ( requestType !== 'main_frame' ) { return; } + if ( isRootDoc === false ) { return; } pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest'); } if ( pageStore.getNetFilteringSwitch() === false ) { return; } @@ -507,24 +510,283 @@ var onHeadersReceived = function(details) { return foilLargeMediaElement(pageStore, details); } + if ( isDoc && µb.canFilterResponseBody ) { + filterDocument(details); + } + // https://github.com/gorhill/uBlock/issues/2813 // Disable the blocking of large media elements if the document is itself // a media element: the resource was not prevented from loading so no // point to further block large media elements for the current document. - if ( requestType === 'main_frame' ) { + if ( isRootDoc ) { if ( reMediaContentTypes.test(headerValueFromName('content-type', details.responseHeaders)) ) { pageStore.allowLargeMediaElementsUntil = Date.now() + 86400000; } return injectCSP(pageStore, details); } - if ( requestType === 'sub_frame' ) { + if ( isDoc ) { return injectCSP(pageStore, details); } }; var reMediaContentTypes = /^(?:audio|image|video)\//; +/******************************************************************************* + + The response body filterer is responsible for: + + - Scriptlet filtering + - HTML filtering + + In the spirit of efficiency, the response body filterer works this way: + + If: + - HTML filtering: no. + - Scriptlet filtering: no. + Then: + No response body filtering is initiated. + + If: + - HTML filtering: no. + - Scriptlet filtering: yes. + Then: + Inject scriptlets before first chunk of response body data reported + then immediately disconnect response body data listener. + + If: + - HTML filtering: yes. + - Scriptlet filtering: no/yes. + Then: + Assemble all response body data into a single buffer. Once all the + response data has been received, create a document from it. Then: + - Inject scriptlets in the resulting DOM. + - Remove all DOM elements matching HTML filters. + Then serialize the resulting modified document as the new response + body. + + This way, the overhead is minimal for when only scriptlets need to be + injected. + + If the platform does not support response body filtering, the scriptlets + will be injected the old way, through the content script. + +**/ + +var filterDocument = (function() { + var µb = µBlock, + filterers = new Map(), + reDoctype = /^\s*]+?>/, + reJustASCII = /^[\x00-\x7E]*$/, + domParser, xmlSerializer, + textDecoderCharset, textDecoder, textEncoder; + + var streamJobDone = function(filterer, responseBytes) { + if ( + filterer.scriptlets === undefined || + filterer.selectors !== undefined || + filterer.charset !== undefined + ) { + return false; + } + if ( textDecoder === undefined ) { + textDecoder = new TextDecoder(); + } + // We need to insert after DOCTYPE, or else the browser may falls into + // quirks mode. + var responseStr = textDecoder.decode(responseBytes); + var match = reDoctype.exec(responseStr); + if ( match === null ) { return false; } + filterers.delete(filterer.stream); + if ( textEncoder === undefined ) { + textEncoder = new TextEncoder(); + } + var beforeByteLength = match.index + match[0].length; + var beforeBytes = reJustASCII.test(match[0]) ? + new Uint8Array(responseBytes, 0, beforeByteLength) : + textEncoder.encode(responseStr.slice(0, beforeByteLength)); + filterer.stream.write(beforeBytes); + filterer.stream.write( + textEncoder.encode('') + ); + filterer.stream.write( + new Uint8Array(responseBytes, beforeBytes.byteLength) + ); + filterer.stream.disconnect(); + return true; + }; + + var streamClose = function(filterer, buffer) { + if ( buffer !== undefined ) { + filterer.stream.write(buffer); + } else if ( filterer.buffer !== undefined ) { + filterer.stream.write(filterer.buffer); + } + filterer.stream.close(); + }; + + var onStreamData = function(ev) { + var filterer = filterers.get(this); + if ( filterer === undefined ) { + this.write(ev.data); + this.disconnect(); + return; + } + if ( + this.status !== 'transferringdata' && + this.status !== 'finishedtransferringdata' + ) { + filterers.delete(this); + this.disconnect(); + return; + } + // TODO: possibly improve buffer growth, if benchmarking shows it's + // worth it. + if ( filterer.buffer === null ) { + if ( streamJobDone(filterer, ev.data) ) { return; } + filterer.buffer = new Uint8Array(ev.data); + return; + } + var buffer = new Uint8Array( + filterer.buffer.byteLength + + ev.data.byteLength + ); + buffer.set(filterer.buffer); + buffer.set(new Uint8Array(ev.data), filterer.buffer.byteLength); + filterer.buffer = buffer; + }; + + var onStreamStop = function() { + var filterer = filterers.get(this); + filterers.delete(this); + if ( filterer === undefined || filterer.buffer === null ) { + this.close(); + return; + } + if ( this.status !== 'finishedtransferringdata' ) { return; } + + if ( domParser === undefined ) { + domParser = new DOMParser(); + xmlSerializer = new XMLSerializer(); + } + if ( textEncoder === undefined ) { + textEncoder = new TextEncoder(); + } + + // In case of unknown charset, assume utf-8. + if ( filterer.charset !== textDecoderCharset ) { + textDecoder = undefined; + } + if ( textDecoder === undefined ) { + try { + textDecoder = new TextDecoder(filterer.charset); + textDecoderCharset = filterer.charset; + } catch(ex) { + textDecoder = new TextDecoder(); + textDecoderCharset = undefined; + } + } + + var doc = domParser.parseFromString( + textDecoder.decode(filterer.buffer), + 'text/html' + ); + + var modified = false; + if ( filterer.selectors !== undefined ) { + if ( µb.htmlFilteringEngine.apply(doc, filterer) ) { + modified = true; + } + } + if ( filterer.scriptlets !== undefined ) { + if ( µb.scriptletFilteringEngine.apply(doc, filterer) ) { + modified = true; + } + } + + if ( modified === false ) { + streamClose(filterer); + return; + } + + // If the charset of the document was not utf-8, we need to change it + // to utf-8. + if ( textDecoderCharset !== undefined ) { + var meta = doc.createElement('meta'); + meta.setAttribute('charset', 'utf-8'); + doc.head.insertBefore(meta, doc.head.firstChild); + } + + // https://stackoverflow.com/questions/6088972/get-doctype-of-an-html-as-string-with-javascript/10162353#10162353 + var doctypeStr = doc.doctype instanceof Object ? + xmlSerializer.serializeToString(doc.doctype) + '\n' : + ''; + + streamClose( + filterer, + textEncoder.encode(doctypeStr + doc.documentElement.outerHTML) + ); + }; + + var onStreamError = function() { + filterers.delete(this); + }; + + return function(details) { + var hostname = µb.URI.hostnameFromURI(details.url); + if ( hostname === '' ) { return; } + + var domain = µb.URI.domainFromHostname(hostname); + + var request = { + stream: undefined, + tabId: details.tabId, + url: details.url, + hostname: hostname, + domain: domain, + entity: µb.URI.entityFromDomain(domain), + selectors: undefined, + scriptlets: undefined, + buffer: null, + charset: undefined + }; + request.selectors = µb.htmlFilteringEngine.retrieve(request); + request.scriptlets = µb.scriptletFilteringEngine.retrieve(request); + + if ( + request.selectors === undefined && + request.scriptlets === undefined + ) { + return; + } + + var headers = details.responseHeaders, + contentType = headerValueFromName('content-type', headers); + if ( contentType !== '' ) { + if ( reContentTypeDocument.test(contentType) === false ) { return; } + var match = reContentTypeCharset.exec(contentType); + if ( match !== null ) { + var charset = match[1].toLowerCase(); + if ( charset !== 'utf-8' ) { + request.charset = charset; + } + } + } + // https://bugzilla.mozilla.org/show_bug.cgi?id=1426789 + if ( headerValueFromName('content-disposition', headers) ) { return; } + + var stream = request.stream = + vAPI.net.webRequest.filterResponseData(details.requestId); + stream.ondata = onStreamData; + stream.onstop = onStreamStop; + stream.onerror = onStreamError; + filterers.set(stream, request); + }; +})(); + +var reContentTypeDocument = /^(?:text\/html|application\/xhtml+xml)/i; +var reContentTypeCharset = /charset=['"]?([^'" ]+)/i; + /******************************************************************************/ var injectCSP = function(pageStore, details) { diff --git a/src/js/uritools.js b/src/js/uritools.js index eaca7731d51d7..a988af4e0053d 100644 --- a/src/js/uritools.js +++ b/src/js/uritools.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 Raymond Hill + Copyright (C) 2014-2017 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -309,6 +309,13 @@ var psl = publicSuffixList; /******************************************************************************/ +URI.entityFromDomain = function(domain) { + var pos = domain.indexOf('.'); + return pos !== -1 ? domain.slice(0, pos) + '.*' : ''; +}; + +/******************************************************************************/ + URI.pathFromURI = function(uri) { var matches = rePathFromURI.exec(uri); return matches !== null ? matches[1] : ''; diff --git a/src/js/utils.js b/src/js/utils.js index 709937524fe6c..918a49b1b4f9e 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -225,7 +225,9 @@ /******************************************************************************/ µBlock.CompiledLineWriter = function() { - this.output = []; + this.blockId = undefined; + this.block = undefined; + this.blocks = new Map(); this.stringifier = JSON.stringify; }; @@ -235,46 +237,81 @@ µBlock.CompiledLineWriter.prototype = { push: function(args) { - this.output[this.output.length] = this.stringifier(args); + this.block[this.block.length] = this.stringifier(args); + }, + select: function(blockId) { + if ( blockId === this.blockId ) { return; } + this.blockId = blockId; + this.block = this.blocks.get(blockId); + if ( this.block === undefined ) { + this.blocks.set(blockId, (this.block = [])); + } }, toString: function() { - return this.output.join('\n'); + var result = []; + for ( var entry of this.blocks ) { + if ( entry[1].length === 0 ) { continue; } + result.push( + '#block-start-' + entry[0], + entry[1].join('\n'), + '#block-end-' + entry[0] + ); + } + return result.join('\n'); } }; -µBlock.CompiledLineReader = function(raw) { - this.reset(raw); +/******************************************************************************/ + +µBlock.CompiledLineReader = function(raw, blockId) { + this.block = ''; + this.len = 0; + this.offset = 0; + this.line = ''; this.parser = JSON.parse; + this.blocks = new Map(); + var reBlockStart = /^#block-start-(\d+)\n/gm, + match = reBlockStart.exec(raw), + beg, end; + while ( match !== null ) { + beg = match.index + match[0].length; + end = raw.indexOf('#block-end-' + match[1], beg); + this.blocks.set(parseInt(match[1], 10), raw.slice(beg, end)); + reBlockStart.lastIndex = end; + match = reBlockStart.exec(raw); + } + if ( blockId !== undefined ) { + this.select(blockId); + } }; µBlock.CompiledLineReader.prototype = { - reset: function(raw) { - this.input = raw; - this.len = raw.length; - this.offset = 0; - this.s = ''; - return this; - }, next: function() { if ( this.offset === this.len ) { - this.s = ''; + this.line = ''; return false; } - var pos = this.input.indexOf('\n', this.offset); + var pos = this.block.indexOf('\n', this.offset); if ( pos !== -1 ) { - this.s = this.input.slice(this.offset, pos); + this.line = this.block.slice(this.offset, pos); this.offset = pos + 1; } else { - this.s = this.input.slice(this.offset); + this.line = this.block.slice(this.offset); this.offset = this.len; } return true; }, + select: function(blockId) { + this.block = this.blocks.get(blockId) || ''; + this.len = this.block.length; + this.offset = 0; + return this; + }, fingerprint: function() { - return this.s; + return this.line; }, args: function() { - return this.parser(this.s); + return this.parser(this.line); } }; From 8d4f2a2d11f7362dca94318759ca88cf3f424be4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 28 Dec 2017 14:24:04 -0500 Subject: [PATCH 022/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 28765ebc107eb..1dfbaeb156e4f 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.2", + "version": "1.14.23.3", "commands": { "launch-element-zapper": { From fd1410ac9f4ba1931ca850d191e6c2c96e979e83 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 28 Dec 2017 14:26:03 -0500 Subject: [PATCH 023/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/eu/messages.json | 22 +++++++++++----------- src/_locales/hu/messages.json | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index 983e2e50ed25b..4c204b278c76d 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -48,11 +48,11 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "Klik: Gaitu\/ezgaitu uBlock₀ gune honetan.\n\nKtrl+klik: Desgaitu uBlock₀ orri honetan soilik.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Click to enable uBlock₀ for this site.", + "message": "Kilk: Gaitu uBlock₀ gune honetan.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -96,11 +96,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "Click: Blokeatu gune honetako laster leiho guztiak", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "Click: Utzi gune honetako laster-leihoak blokeatzeari", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -108,11 +108,11 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "Klik: Blokeatu gune honetako tamaina handiko elementuak", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "Klik: Utzi gune honetako tamaina handiko elementuak blokeatzeari", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Click to disable cosmetic filtering on this site", + "message": "Klik: Desgaitu iragazki kosmetikoak gune honetan", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Click to enable cosmetic filtering on this site", + "message": "Klik: Gaitu iragazki kosmetikoak gune honetan", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -132,11 +132,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Click to block remote fonts on this site", + "message": "Klik: Blokeatu urruneko letra-tipoak gune honetan", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Click to no longer block remote fonts on this site", + "message": "Klik: Utzi urruneko letra-tipoak gune honetan blokeatzeari", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { @@ -292,7 +292,7 @@ "description": "" }, "settingsNoCSPReportsPrompt": { - "message": "Block CSP reports", + "message": "Blokeatu CSP txostenak", "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" }, "settingsStorageUsed": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 71551b31babb2..0aada52077dcf 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -48,11 +48,11 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Kattints a uBlock₀ letiltásához ezen a webhelyen.\n\nCtrl+kattintás a uBlock₀ letiltásához csak a jelenlegi oldalon.", + "message": "Kattints a uBlock₀ letiltásához ezen a webhelyen.\n\nCtrl+klikk a uBlock₀ letiltásához csak a jelenlegi oldalon.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Klikk a uBlock₀ engedélyezéséhez ezen a webhelyen.", + "message": "Kattints az uBlock₀ engedélyezéséhez ezen a webhelyen.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { From a8d79feecd5a31c21be1966f6cbd1cb91134f636 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 08:05:50 -0500 Subject: [PATCH 024/106] fix #3379 (regression from a9f68fe02f40) --- src/js/static-ext-filtering.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index da5224c9ead2d..73310fc37a174 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -580,8 +580,9 @@ if ( (rpos - lpos) > 3 ) { return false; } // Extract the selector. - var suffix = parsed.suffix = raw.slice(rpos + 1).trim(); + var suffix = raw.slice(rpos + 1).trim(); if ( suffix.length === 0 ) { return false; } + parsed.suffix = suffix; // https://github.com/gorhill/uBlock/issues/952 // Find out whether we are dealing with an Adguard-specific cosmetic @@ -602,6 +603,7 @@ if ( cCode === 0x24 /* '$' */ ) { suffix = translateAdguardCSSInjectionFilter(suffix); if ( suffix === '' ) { return true; } + parsed.suffix = suffix; } } @@ -628,15 +630,13 @@ return true; } // Script tag filtering: courtesy-conversion to HTML filtering. - if ( parsed.suffix.startsWith('script:contains') ) { + if ( suffix.startsWith('script:contains') ) { console.info( 'uBO: ##script:contains(...) is deprecated, ' + 'converting to ##^script:has-text(...)' ); - suffix = parsed.suffix = suffix.replace( - /^script:contains/, - '^script:has-text' - ); + suffix = suffix.replace(/^script:contains/, '^script:has-text'); + parsed.suffix = suffix; } } From a25166be92f0aecaeecabd5b4d317276a484369d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 08:06:40 -0500 Subject: [PATCH 025/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 1dfbaeb156e4f..498b6bfc234de 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.3", + "version": "1.14.23.4", "commands": { "launch-element-zapper": { From 31791f2dd2db4eb17969dc9dab8f7034aad00c57 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 09:02:26 -0500 Subject: [PATCH 026/106] code review: caller always expect an array as return value --- src/js/html-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 890c874d991f7..5f93aaadc0665 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -137,7 +137,7 @@ return [ root ]; }; PSelector.prototype.exec = function(input) { - if ( this.invalid ) { return; } + if ( this.invalid ) { return []; } var nodes = this.prime(input); for ( var task of this.tasks ) { if ( nodes.length === 0 ) { break; } From 707d7708a1ec201231df0322b88d4e5b02f60804 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 10:26:50 -0500 Subject: [PATCH 027/106] code review: fix recursivity in HTML filtering's procedural selectors --- src/js/html-filtering.js | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 5f93aaadc0665..0ce949d0d3f54 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -62,6 +62,11 @@ this.pselector = new PSelector(task[1]); }; PSelectorIfTask.prototype.target = true; + Object.defineProperty(PSelectorIfTask.prototype, 'invalid', { + get: function() { + return this.pselector.invalid; + } + }); PSelectorIfTask.prototype.exec = function(input) { var output = []; for ( var node of input ) { @@ -113,7 +118,6 @@ [ ':xpath', PSelectorXpathTask ] ]); } - this.invalid = false; this.raw = o.raw; this.selector = o.selector; this.tasks = []; @@ -125,10 +129,16 @@ this.invalid = true; break; } - this.tasks.push(new ctor(task)); + var pselector = new ctor(task); + if ( pselector instanceof PSelectorIfTask && pselector.invalid ) { + this.invalid = true; + break; + } + this.tasks.push(pselector); } }; PSelector.prototype.operatorToTaskMap = undefined; + PSelector.prototype.invalid = false; PSelector.prototype.prime = function(input) { var root = input || docRegister; if ( this.selector !== '' ) { @@ -145,6 +155,19 @@ } return nodes; }; + PSelector.prototype.test = function(input) { + if ( this.invalid ) { return false; } + var nodes = this.prime(input), AA = [ null ], aa; + for ( var node of nodes ) { + AA[0] = node; aa = AA; + for ( var task of this.tasks ) { + aa = task.exec(aa); + if ( aa.length === 0 ) { break; } + } + if ( aa.length !== 0 ) { return true; } + } + return false; + }; var logOne = function(details, selector) { loggerRegister.writeOne( From 5c20182948c8a92ab4243fc65f7ec08e6c228f76 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 13:31:37 -0500 Subject: [PATCH 028/106] fix regression in per-list filter counts (reported by @mapx-) --- src/js/html-filtering.js | 23 ++++++++++++++++++++++- src/js/scriptlet-filtering.js | 23 ++++++++++++++++++++++- src/js/static-ext-filtering.js | 17 +++++++++++++++++ src/js/storage.js | 12 +++++++----- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 0ce949d0d3f54..6bde40b3c0a22 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -30,6 +30,8 @@ filterDB = new µb.staticExtFilteringEngine.HostnameBasedDB(), pselectors = new Map(), duplicates = new Set(), + acceptedCount = 0, + discardedCount = 0, docRegister, loggerRegister; var PSelectorHasTask = function(task) { @@ -224,6 +226,8 @@ filterDB.clear(); pselectors.clear(); duplicates.clear(); + acceptedCount = 0; + discardedCount = 0; }; api.freeze = function() { @@ -260,8 +264,12 @@ reader.select(1002); while ( reader.next() ) { + acceptedCount += 1; var fingerprint = reader.fingerprint(); - if ( duplicates.has(fingerprint) ) { continue; } + if ( duplicates.has(fingerprint) ) { + discardedCount += 1; + continue; + } duplicates.add(fingerprint); var args = reader.args(); filterDB.add(args[1], { @@ -374,6 +382,19 @@ } }; + Object.defineProperties(api, { + acceptedCount: { + get: function() { + return acceptedCount; + } + }, + discardedCount: { + get: function() { + return discardedCount; + } + } + }); + return api; })(); diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index b10066cf11bd8..3ace313ff4853 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -29,6 +29,8 @@ var µb = µBlock, scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(), duplicates = new Set(), + acceptedCount = 0, + discardedCount = 0, scriptletCache = new µb.MRUCache(32), exceptionsRegister = new Set(), scriptletsRegister = new Map(), @@ -103,6 +105,8 @@ api.reset = function() { scriptletDB.clear(); duplicates.clear(); + acceptedCount = 0; + discardedCount = 0; }; api.freeze = function() { @@ -154,8 +158,12 @@ reader.select(1001); while ( reader.next() ) { + acceptedCount += 1; var fingerprint = reader.fingerprint(); - if ( duplicates.has(fingerprint) ) { continue; } + if ( duplicates.has(fingerprint) ) { + discardedCount += 1; + continue; + } duplicates.add(fingerprint); var args = reader.args(); if ( args.length < 4 ) { continue; } @@ -264,6 +272,19 @@ scriptletDB = new µb.staticExtFilteringEngine.HostnameBasedDB(selfie); }; + Object.defineProperties(api, { + acceptedCount: { + get: function() { + return acceptedCount; + } + }, + discardedCount: { + get: function() { + return discardedCount; + } + } + }); + return api; })(); diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index 73310fc37a174..ec274eeaf03c3 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -668,6 +668,23 @@ }; }; + Object.defineProperties(api, { + acceptedCount: { + get: function() { + return µb.cosmeticFilteringEngine.acceptedCount + + µb.scriptletFilteringEngine.acceptedCount + + µb.htmlFilteringEngine.acceptedCount; + } + }, + discardedCount: { + get: function() { + return µb.cosmeticFilteringEngine.discardedCount + + µb.scriptletFilteringEngine.discardedCount + + µb.htmlFilteringEngine.discardedCount; + } + } + }); + api.fromSelfie = function(selfie) { µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmetic); µb.scriptletFilteringEngine.fromSelfie(selfie.scriptlets); diff --git a/src/js/storage.js b/src/js/storage.js index d8dfaf1d07bd9..f172bf172ac56 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -562,14 +562,16 @@ var applyCompiledFilters = function(assetKey, compiled) { var snfe = µb.staticNetFilteringEngine, - cfe = µb.cosmeticFilteringEngine, - acceptedCount = snfe.acceptedCount + cfe.acceptedCount, - discardedCount = snfe.discardedCount + cfe.discardedCount; + sxfe = µb.staticExtFilteringEngine, + acceptedCount = snfe.acceptedCount + sxfe.acceptedCount, + discardedCount = snfe.discardedCount + sxfe.discardedCount; µb.applyCompiledFilters(compiled, assetKey === µb.userFiltersPath); if ( µb.availableFilterLists.hasOwnProperty(assetKey) ) { var entry = µb.availableFilterLists[assetKey]; - entry.entryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount; - entry.entryUsedCount = entry.entryCount - (snfe.discardedCount + cfe.discardedCount - discardedCount); + entry.entryCount = snfe.acceptedCount + sxfe.acceptedCount - + acceptedCount; + entry.entryUsedCount = entry.entryCount - + (snfe.discardedCount + sxfe.discardedCount - discardedCount); } loadedListKeys.push(assetKey); }; From 6f8099fb90c68b95256bbe87ede20d0e3bacd526 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 15:56:15 -0500 Subject: [PATCH 029/106] fix regression in scriptlet injection: mind BOM sequence in response data filtering --- src/js/traffic.js | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 1ff0c01d0913a..21cfc9c2d4d5f 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -577,7 +577,6 @@ var filterDocument = (function() { var µb = µBlock, filterers = new Map(), reDoctype = /^\s*]+?>/, - reJustASCII = /^[\x00-\x7E]*$/, domParser, xmlSerializer, textDecoderCharset, textDecoder, textEncoder; @@ -592,25 +591,37 @@ var filterDocument = (function() { if ( textDecoder === undefined ) { textDecoder = new TextDecoder(); } + if ( textEncoder === undefined ) { + textEncoder = new TextEncoder(); + } // We need to insert after DOCTYPE, or else the browser may falls into // quirks mode. - var responseStr = textDecoder.decode(responseBytes); - var match = reDoctype.exec(responseStr); + var firstResponseBytes = new Uint8Array(responseBytes, 0, 512), + haystack = textDecoder.decode(firstResponseBytes), + match = reDoctype.exec(haystack); if ( match === null ) { return false; } filterers.delete(filterer.stream); - if ( textEncoder === undefined ) { - textEncoder = new TextEncoder(); + // Output bytes may be different than response bytes: the BOM sequence + // if present is removed by the decoder. + var firstOutputBytes = textEncoder.encode( + haystack.slice(0, match.index + match[0].length) + ); + var insertAt = firstOutputBytes.byteLength; + // Mind BOM if present: + // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 + if ( + firstResponseBytes[0] === 0xEF && + firstResponseBytes[0] === 0xBB && + firstResponseBytes[0] === 0xBF + ) { + insertAt += 3; } - var beforeByteLength = match.index + match[0].length; - var beforeBytes = reJustASCII.test(match[0]) ? - new Uint8Array(responseBytes, 0, beforeByteLength) : - textEncoder.encode(responseStr.slice(0, beforeByteLength)); - filterer.stream.write(beforeBytes); + filterer.stream.write(firstOutputBytes); filterer.stream.write( textEncoder.encode('') ); filterer.stream.write( - new Uint8Array(responseBytes, beforeBytes.byteLength) + new Uint8Array(responseBytes, insertAt) ); filterer.stream.disconnect(); return true; From ff67cf5ada31459f7b92010005fd2ac2d0317752 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 15:56:53 -0500 Subject: [PATCH 030/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 498b6bfc234de..31d7ec9c1a16d 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.4", + "version": "1.14.23.5", "commands": { "launch-element-zapper": { From b36320c643f5b09cad2ed574c4c456aabc9cb8dd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 20:26:03 -0500 Subject: [PATCH 031/106] code review: improve scriptlet inject code in stream data listener --- src/js/traffic.js | 76 +++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 21cfc9c2d4d5f..b894b8d269a62 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -576,10 +576,23 @@ var reMediaContentTypes = /^(?:audio|image|video)\//; var filterDocument = (function() { var µb = µBlock, filterers = new Map(), - reDoctype = /^\s*]+?>/, domParser, xmlSerializer, textDecoderCharset, textDecoder, textEncoder; + // Purpose of following helper is to disconnect from watching the stream + // if all the following conditions are fulfilled: + // - Only need to inject scriptlets. + // - Charset of resource is utf-8. + // - A well-formed doc type declaration is found at the top. + // + // When all the conditions are fulfilled, then the scriptlets are safely + // injected after the doc type declaration, and the stream listener is + // disconnected, thus removing overhead from future streaming data. + // + // If at least one of the condition is not fulfilled and scriptlets need + // to be injected, it will be done the longer way when the whole stream + // has been collated in memory. + var streamJobDone = function(filterer, responseBytes) { if ( filterer.scriptlets === undefined || @@ -588,40 +601,51 @@ var filterDocument = (function() { ) { return false; } - if ( textDecoder === undefined ) { - textDecoder = new TextDecoder(); - } - if ( textEncoder === undefined ) { - textEncoder = new TextEncoder(); - } // We need to insert after DOCTYPE, or else the browser may falls into // quirks mode. - var firstResponseBytes = new Uint8Array(responseBytes, 0, 512), - haystack = textDecoder.decode(firstResponseBytes), - match = reDoctype.exec(haystack); - if ( match === null ) { return false; } - filterers.delete(filterer.stream); - // Output bytes may be different than response bytes: the BOM sequence - // if present is removed by the decoder. - var firstOutputBytes = textEncoder.encode( - haystack.slice(0, match.index + match[0].length) - ); - var insertAt = firstOutputBytes.byteLength; - // Mind BOM if present: - // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 + var bb = new Uint8Array(responseBytes, 0, 256), + i = 0, b; + // Skip BOM if present. + if ( bb[0] === 0xEF && bb[1] === 0xBB && bb[2] === 0xBF ) { i += 3; } + // Scan for '<' + for (;;) { + b = bb[i++]; + if ( b === 0x3C /* '<' */ ) { break; } + if ( b > 0x20 || b > 0x7F || i > 240 ) { return false; } + } + // Test for '!DOCTYPE' if ( - firstResponseBytes[0] === 0xEF && - firstResponseBytes[0] === 0xBB && - firstResponseBytes[0] === 0xBF + bb[i++] !== 0x21 /* '!' */ || + bb[i++] !== 0x44 /* 'D' */ || + bb[i++] !== 0x4F /* 'O' */ || + bb[i++] !== 0x43 /* 'C' */ || + bb[i++] !== 0x54 /* 'T' */ || + bb[i++] !== 0x59 /* 'Y' */ || + bb[i++] !== 0x50 /* 'P' */ || + bb[i++] !== 0x45 /* 'E' */ ) { - insertAt += 3; + return false; } - filterer.stream.write(firstOutputBytes); + // Scan for '>'. + var qcount = 0; + for (;;) { + b = bb[i++]; + if ( b === 0x3E /* '>' */ ) { break; } + if ( b === 0x22 /* '"' */ || b === 0x27 /* "'" */ ) { qcount += 1; } + if ( b > 127 || i > 240 ) { return false; } + } + // Bail out if mismatched quotes. + if ( (qcount & 1) !== 0 ) { return false; } + // We found a valid insertion point. + if ( textEncoder === undefined ) { textEncoder = new TextEncoder(); } + filterer.stream.write( + new Uint8Array(responseBytes, 0, i) + ); filterer.stream.write( textEncoder.encode('') ); filterer.stream.write( - new Uint8Array(responseBytes, insertAt) + new Uint8Array(responseBytes, i) ); filterer.stream.disconnect(); return true; From a3f7392f068fd997d4732da075eb778497b27763 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 20:33:24 -0500 Subject: [PATCH 032/106] code review: mind length of data available --- src/js/traffic.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index b894b8d269a62..825e5149501c9 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -603,6 +603,7 @@ var filterDocument = (function() { } // We need to insert after DOCTYPE, or else the browser may falls into // quirks mode. + if ( responseBytes.byteLength < 256 ) { return false; } var bb = new Uint8Array(responseBytes, 0, 256), i = 0, b; // Skip BOM if present. @@ -638,15 +639,11 @@ var filterDocument = (function() { if ( (qcount & 1) !== 0 ) { return false; } // We found a valid insertion point. if ( textEncoder === undefined ) { textEncoder = new TextEncoder(); } - filterer.stream.write( - new Uint8Array(responseBytes, 0, i) - ); + filterer.stream.write(new Uint8Array(responseBytes, 0, i)); filterer.stream.write( textEncoder.encode('') ); - filterer.stream.write( - new Uint8Array(responseBytes, i) - ); + filterer.stream.write(new Uint8Array(responseBytes, i)); filterer.stream.disconnect(); return true; }; From 93e76ecf5d08e9373fa4cf3b1c9d6cbc7f583960 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 20:36:31 -0500 Subject: [PATCH 033/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 31d7ec9c1a16d..6c15ac7b53d50 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.5", + "version": "1.14.23.6", "commands": { "launch-element-zapper": { From 3ec9377c3eda60dcff52c47158d52290c0296b3b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 21:29:57 -0500 Subject: [PATCH 034/106] code review: disregard case sensitivity --- src/js/traffic.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 825e5149501c9..587f8576625f4 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -614,16 +614,16 @@ var filterDocument = (function() { if ( b === 0x3C /* '<' */ ) { break; } if ( b > 0x20 || b > 0x7F || i > 240 ) { return false; } } - // Test for '!DOCTYPE' + // Case insensitively test for '!doctype'. if ( - bb[i++] !== 0x21 /* '!' */ || - bb[i++] !== 0x44 /* 'D' */ || - bb[i++] !== 0x4F /* 'O' */ || - bb[i++] !== 0x43 /* 'C' */ || - bb[i++] !== 0x54 /* 'T' */ || - bb[i++] !== 0x59 /* 'Y' */ || - bb[i++] !== 0x50 /* 'P' */ || - bb[i++] !== 0x45 /* 'E' */ + bb[i++] !== 0x21 /* '!' */ || + ( bb[i++] | 0x20 ) !== 0x64 /* 'd' */ || + ( bb[i++] | 0x20 ) !== 0x6F /* 'o' */ || + ( bb[i++] | 0x20 ) !== 0x63 /* 'c' */ || + ( bb[i++] | 0x20 ) !== 0x74 /* 't' */ || + ( bb[i++] | 0x20 ) !== 0x79 /* 'y' */ || + ( bb[i++] | 0x20 ) !== 0x70 /* 'p' */ || + ( bb[i++] | 0x20 ) !== 0x65 /* 'e' */ ) { return false; } From 6ab34efe4486f8848e0cd013df36fa338d3e5439 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 29 Dec 2017 21:54:03 -0500 Subject: [PATCH 035/106] minor code review: remove pointless test --- src/js/traffic.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 587f8576625f4..d54bd161ac488 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -601,7 +601,7 @@ var filterDocument = (function() { ) { return false; } - // We need to insert after DOCTYPE, or else the browser may falls into + // We need to insert after DOCTYPE, or else the browser may fall into // quirks mode. if ( responseBytes.byteLength < 256 ) { return false; } var bb = new Uint8Array(responseBytes, 0, 256), @@ -612,7 +612,7 @@ var filterDocument = (function() { for (;;) { b = bb[i++]; if ( b === 0x3C /* '<' */ ) { break; } - if ( b > 0x20 || b > 0x7F || i > 240 ) { return false; } + if ( b > 0x20 || i > 240 ) { return false; } } // Case insensitively test for '!doctype'. if ( @@ -633,7 +633,7 @@ var filterDocument = (function() { b = bb[i++]; if ( b === 0x3E /* '>' */ ) { break; } if ( b === 0x22 /* '"' */ || b === 0x27 /* "'" */ ) { qcount += 1; } - if ( b > 127 || i > 240 ) { return false; } + if ( b > 0x7F || i > 240 ) { return false; } } // Bail out if mismatched quotes. if ( (qcount & 1) !== 0 ) { return false; } From f7c02e237f7e3a76b985c95f5deafe3df11172d3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 30 Dec 2017 11:05:15 -0500 Subject: [PATCH 036/106] code review for #3331: increase restrictions Only resources from within current directory will be allowed, everything else will be silently rejected. For example, this will forbid pulling lists from different repos on GitHub, despite the lists being same origin. --- src/js/assets.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 845ef68b47416..07255364fc93e 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -190,22 +190,22 @@ api.fetchFilterList = function(mainlistURL, onLoad, onError) { if ( isSublist ) { content.push('\n! ' + '>>>>>>>> ' + details.url); } content.push(details.content.trim()); if ( isSublist ) { content.push('! <<<<<<<< ' + details.url); } - - if ( parsedMainURL !== undefined ) { + if ( + parsedMainURL !== undefined && + parsedMainURL.pathname.length > 0 + ) { var reInclude = /^!#include +(\S+)/gm, + match, subURL; + for (;;) { match = reInclude.exec(details.content); - while ( match !== null ) { - var parsedSubURL = toParsedURL(match[1]); - if ( parsedSubURL === undefined ) { - parsedSubURL = toParsedURL( - parsedMainURL.href.replace(/[^/?]+(?:\?.*)?$/, match[1]) - ); - if ( parsedSubURL === undefined ) { continue; } - } - if ( parsedSubURL.origin !== parsedMainURL.origin ) { continue; } - if ( loadedSublistURLs.has(parsedSubURL.href) ) { continue; } - pendingSublistURLs.add(parsedSubURL.href); - match = reInclude.exec(details.content); + if ( match === null ) { break; } + if ( toParsedURL(match[1]) !== undefined ) { continue; } + if ( match[1].indexOf('..') !== -1 ) { continue; } + subURL = + parsedMainURL.origin + + parsedMainURL.pathname.replace(/[^/]+$/, match[1]); + if ( loadedSublistURLs.has(subURL) ) { continue; } + pendingSublistURLs.add(subURL); } } From f659dc8e4923ddd5acedd764fcc0929937143a49 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 30 Dec 2017 11:21:23 -0500 Subject: [PATCH 037/106] add TODOs comments --- src/js/traffic.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index d54bd161ac488..4d334a59d5e21 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -672,8 +672,17 @@ var filterDocument = (function() { this.disconnect(); return; } - // TODO: possibly improve buffer growth, if benchmarking shows it's - // worth it. + // TODO: + // - Possibly improve buffer growth, if benchmarking shows it's worth + // it. + // - Also evaluate whether keeping a list of buffers and then decoding + // them in sequence using TextDecoder's "stream" option is more + // efficient. Can the data buffers be safely kept around for later + // use? + // - Informal, quick benchmarks seem to show most of the overhead is + // from calling TextDecoder.decode() and TextEncoder.encode(), and if + // confirmed, there is nothing which can be done uBO-side to reduce + // overhead. if ( filterer.buffer === null ) { if ( streamJobDone(filterer, ev.data) ) { return; } filterer.buffer = new Uint8Array(ev.data); From 96576df1e4591e261ce24a94e7e3eaa902e15268 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 30 Dec 2017 11:22:03 -0500 Subject: [PATCH 038/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 6c15ac7b53d50..631ae989896a0 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.6", + "version": "1.14.23.7", "commands": { "launch-element-zapper": { From 9f89c67f3f9b917bdd148681299388878f73d6cd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 30 Dec 2017 11:26:06 -0500 Subject: [PATCH 039/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/da/messages.json | 24 ++++++++++++------------ src/_locales/hi/messages.json | 6 +++--- src/_locales/te/messages.json | 22 +++++++++++----------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index ab3f2c14b8f17..1f0dafdb4eb44 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -48,11 +48,11 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "Klik for at deaktivere uBlock₀ på dette websted.\n\nCtrl+Klik for at deaktivere uBlock₀ kun på denne side.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Click to enable uBlock₀ for this site.", + "message": "Klik for at aktivere uBlock₀ på dette websted.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -96,11 +96,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "Klik for at blokere alle popups på dette websted", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "Klik for at stoppe med at blokere alle popups på dette websted", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -108,11 +108,11 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "Klik for at blokere store medie-elementer på dette websted", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "Klik for at stoppe med at blokere store medie-elementer på dette websted", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Click to disable cosmetic filtering on this site", + "message": "Klik for at deaktivere kosmetisk filtre på dette websted", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Click to enable cosmetic filtering on this site", + "message": "Klik for at aktivere kosmetisk filtre på dette websted", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -132,11 +132,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Click to block remote fonts on this site", + "message": "Klik for at blokere skrifttyper fra nettet på dette websted", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Click to no longer block remote fonts on this site", + "message": "Klik for at stoppe med at blokere skrifttyper på nettet på dette websted", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { @@ -292,7 +292,7 @@ "description": "" }, "settingsNoCSPReportsPrompt": { - "message": "Block CSP reports", + "message": "Blokér CSP rapporter", "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" }, "settingsStorageUsed": { @@ -364,7 +364,7 @@ "description": "English: Malware domains" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "Irriterende", "description": "The header identifying the filter lists in the category 'annoyances'" }, "3pGroupMultipurpose": { diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 9f02e4d59c2bc..e60f8dd21e6c3 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -48,11 +48,11 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "दबाए: uBlock को इस साईट में बंद करने के लिए।\n\nCtrl के साथ दबाए: uBlock को केवल इस पेज में बंद करने के लिए।", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Click to enable uBlock₀ for this site.", + "message": "इस साईट में uBlock₀ को चालू करने के लिए क्लिक कीजिये।", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -80,7 +80,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "Enter element zapper mode", + "message": "अन्श मिटाने की प्रक्रिया आरम्भ करे", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index 2538f75b7be3f..dce0e80174d68 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -92,15 +92,15 @@ "description": "Tooltip used for the logger icon in the panel" }, "popupTipNoPopups": { - "message": "ఈ వెబ్సైట్ లో అన్ని పాప్అప్స్ ని నిషేధించు", + "message": "ఈ వెబ్సైట్ లో అన్ని పాప్అప్స్ ని నిషేధించు\/అనుమతించు", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "ఇకపై ఈ సైట్లో అన్ని పాపప్లు బ్లాక్ చేయుటకు క్లిక్ చేయండి", + "message": "ఇకపై ఈ సైట్లో అన్ని పపప్లను నిరోధించుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "ఇకపై ఈ సైట్లో అన్ని పాపప్లు బ్లాక్ చేయకుండా ఉండుటకు క్లిక్ చేయండి", + "message": "ఇకపై ఈ సైట్లో అన్ని పపప్లను అనుమతించుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -108,11 +108,11 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "ఈ సైట్లో పెద్ద పరిమాణం మీడియా అంశాలు బ్లాక్ చేయుటకు క్లిక్ చేయండి", + "message": "ఈ సైట్లో పెద్ద పరిమాణం మీడియా అంశాలను నిరోధించుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "ఇకపై ఈ సైట్లో పెద్ద పరిమాణం మీడియా అంశాలు బ్లాక్ చేయకుండా ఉండుటకు క్లిక్ చేయండి", + "message": "ఇకపై ఈ సైట్లో పెద్ద పరిమాణం మీడియా అంశాలను అనుమతించుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "ఈ వెబ్ సైట్ లో కాస్మెటిక్ ఫిల్టరింగ్ ని అచేతనంచేయుటకు క్లిక్ చేయండి", + "message": "ఈ వెబ్ సైట్ లో కాస్మెటిక్ ఫిల్టరింగ్ ని అచేతనపరచుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "ఈ వెబ్ సైట్ లో కాస్మెటిక్ ఫిల్టరింగ్ ని చేతనంచేయుటకు క్లిక్ చేయండి", + "message": "ఈ వెబ్ సైట్ లో కాస్మెటిక్ ఫిల్టరింగ్ ని చేతపరచుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -132,11 +132,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "ఈ సైట్లో రిమోట్ ఫాంట్లను బ్లాక్ చేయుటకు క్లిక్ చేయండి", + "message": "ఈ సైట్లోని రిమోట్ ఫాంట్లను నిరోధించుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "ఇకపై ఈ సైట్లో రిమోట్ ఫాంట్లను బ్లాక్ చేయకుండా ఉండుటకు క్లిక్ చేయండి", + "message": "ఇకపై ఈ సైట్లో రిమోట్ ఫాంట్లను అనుమతించుటకు క్లిక్ చేయండి", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { @@ -152,7 +152,7 @@ "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." }, "popupTipRevertRules": { - "message": "మీ మార్పులను తిరిగరాయుటకు క్లిక్ చేయండి.", + "message": "మీ మార్పులను తిరిగి పూర్వావస్థకు చేర్చుటకు క్లిక్ చేయండి.", "description": "Tooltip when hovering over the eraser in the dynamic filtering pane." }, "popupAnyRulePrompt": { @@ -292,7 +292,7 @@ "description": "" }, "settingsNoCSPReportsPrompt": { - "message": "CSP నివేదికలను బ్లాక్ చేయండి", + "message": "CSP నివేదికలను నిరోధించు", "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" }, "settingsStorageUsed": { From e84e79f96eff2b0d2e0cda806907a504dace25da Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 30 Dec 2017 16:02:34 -0500 Subject: [PATCH 040/106] fix #3367 --- src/js/static-ext-filtering.js | 113 +++++++++++++++++---------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index ec274eeaf03c3..f7b98684f2eef 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -121,8 +121,8 @@ }; var compileProceduralSelector = (function() { - var reOperatorParser = new RegExp([ - '(:(?:', + var reProceduralOperator = new RegExp([ + '^(?:', [ '-abp-contains', '-abp-has', @@ -136,12 +136,10 @@ 'matches-css-before', 'xpath' ].join('|'), - '))\\(.+\\)$' + ')\\(' ].join('')); - var reFirstParentheses = /^\(*/, - reLastParentheses = /\)*$/, - reEscapeRegex = /[.*+?^${}()|[\]\\]/g, + var reEscapeRegex = /[.*+?^${}()|[\]\\]/g, reNeedScope = /^\s*[+>~]/; var lastProceduralSelector = '', @@ -269,62 +267,65 @@ }; var compile = function(raw) { - var matches = reOperatorParser.exec(raw); - if ( matches === null ) { - if ( isValidCSSSelector(raw) ) { return { selector: raw }; } - return; - } - var tasks = [], - firstOperand = raw.slice(0, matches.index), - currentOperator = matches[1], - selector = raw.slice(matches.index + currentOperator.length), - currentArgument = '', nextOperand, nextOperator, - depth = 0, opening, closing; - if ( - firstOperand !== '' && - isValidCSSSelector(firstOperand) === false - ) { - return; - } + if ( raw === '' ) { return; } + var prefix = '', + tasks = []; for (;;) { - matches = reOperatorParser.exec(selector); - if ( matches !== null ) { - nextOperand = selector.slice(0, matches.index); - nextOperator = matches[1]; - } else { - nextOperand = selector; - nextOperator = ''; + var i = 0, + n = raw.length, + c, match; + // Advance to next operator. + while ( i < n ) { + c = raw.charCodeAt(i++); + if ( c === 0x3A /* ':' */ ) { + match = reProceduralOperator.exec(raw.slice(i)); + if ( match !== null ) { break; } + } } - opening = reFirstParentheses.exec(nextOperand)[0].length; - closing = reLastParentheses.exec(nextOperand)[0].length; - if ( opening > closing ) { - if ( depth === 0 ) { currentArgument = ''; } - depth += 1; - } else if ( closing > opening && depth > 0 ) { - depth -= 1; - if ( depth === 0 ) { - nextOperand = currentArgument + nextOperand; + if ( i === n ) { break; } + var opNameBeg = i - 1; + var opNameEnd = i + match[0].length - 1; + i += match[0].length; + // Find end of argument: first balanced closing parenthesis. + // Note: unbalanced parenthesis can be used in a regex literal + // when they are escaped using `\`. + var pcnt = 1; + while ( i < n ) { + c = raw.charCodeAt(i++); + if ( c === 0x5C /* '\\' */ ) { + if ( i < n ) { i += 1; } + } else if ( c === 0x28 /* '(' */ ) { + pcnt +=1 ; + } else if ( c === 0x29 /* ')' */ ) { + pcnt -= 1; + if ( pcnt === 0 ) { break; } } } - if ( depth !== 0 ) { - currentArgument += nextOperand + nextOperator; - } else { - currentOperator = - normalizedOperators.get(currentOperator) || - currentOperator; - currentArgument = - compileArgument.get(currentOperator)( - nextOperand.slice(1, -1) - ); - if ( currentArgument === undefined ) { return; } - tasks.push([ currentOperator, currentArgument ]); - currentOperator = nextOperator; + // Unbalanced parenthesis? + if ( pcnt !== 0 ) { return; } + // Extract and remember operator details. + var operator = raw.slice(opNameBeg, opNameEnd); + operator = normalizedOperators.get(operator) || operator; + var args = raw.slice(opNameEnd + 1, i - 1); + args = compileArgument.get(operator)(args); + if ( args === undefined ) { return; } + if ( tasks.length === 0 ) { + prefix = raw.slice(0, opNameBeg); + } else if ( opNameBeg !== 0 ) { + return; } - if ( nextOperator === '' ) { break; } - selector = selector.slice(matches.index + nextOperator.length); + tasks.push([ operator, args ]); + if ( i === n ) { break; } + raw = raw.slice(i); + } + if ( tasks.length === 0 ) { + prefix = raw; + tasks = undefined; + } + if ( prefix !== '' && isValidCSSSelector(prefix) === false ) { + return; } - if ( tasks.length === 0 || depth !== 0 ) { return; } - return { selector: firstOperand, tasks: tasks }; + return { selector: prefix, tasks: tasks }; }; var entryPoint = function(raw) { From 1cd61063fae39d4e8d55ad2b94589bc7735a6216 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 30 Dec 2017 17:38:07 -0500 Subject: [PATCH 041/106] fix #3380 --- src/js/assets.js | 4 +++- src/js/messaging.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 07255364fc93e..d0db484496163 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -1011,7 +1011,9 @@ var updateDone = function() { api.updateStart = function(details) { var oldUpdateDelay = updaterAssetDelay, - newUpdateDelay = details.delay || updaterAssetDelayDefault; + newUpdateDelay = typeof details.delay === 'number' ? + details.delay : + updaterAssetDelayDefault; updaterAssetDelay = Math.min(oldUpdateDelay, newUpdateDelay); if ( updaterStatus !== undefined ) { if ( newUpdateDelay < oldUpdateDelay ) { diff --git a/src/js/messaging.js b/src/js/messaging.js index 51c91606cccb5..eefb957885328 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -121,7 +121,9 @@ var onMessage = function(request, sender, callback) { case 'forceUpdateAssets': µb.scheduleAssetUpdater(0); - µb.assets.updateStart({ delay: µb.hiddenSettings.manualUpdateAssetFetchPeriod || 2000 }); + µb.assets.updateStart({ + delay: µb.hiddenSettings.manualUpdateAssetFetchPeriod + }); break; case 'getAppData': From 17dfec5759fa76372683262f6241084473ab40e0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 30 Dec 2017 18:55:01 -0500 Subject: [PATCH 042/106] fix #3372 --- src/js/contentscript.js | 12 ++++- src/js/html-filtering.js | 6 ++- src/js/static-ext-filtering.js | 91 ++++++++++++++++++++-------------- 3 files changed, 69 insertions(+), 40 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 1ba69ad5c1494..dcc3f14828849 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -388,7 +388,11 @@ vAPI.DOMFilterer = (function() { }; var PSelectorHasTextTask = function(task) { - this.needle = new RegExp(task[1]); + var arg0 = task[1], arg1; + if ( Array.isArray(task[1]) ) { + arg1 = arg0[1]; arg0 = arg0[0]; + } + this.needle = new RegExp(arg0, arg1); }; PSelectorHasTextTask.prototype.exec = function(input) { var output = []; @@ -423,7 +427,11 @@ vAPI.DOMFilterer = (function() { var PSelectorMatchesCSSTask = function(task) { this.name = task[1].name; - this.value = new RegExp(task[1].value); + var arg0 = task[1].value, arg1; + if ( Array.isArray(arg0) ) { + arg1 = arg0[1]; arg0 = arg0[0]; + } + this.value = new RegExp(arg0, arg1); }; PSelectorMatchesCSSTask.prototype.pseudo = null; PSelectorMatchesCSSTask.prototype.exec = function(input) { diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 6bde40b3c0a22..1dec988cf0f99 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -48,7 +48,11 @@ }; var PSelectorHasTextTask = function(task) { - this.needle = new RegExp(task[1]); + var arg0 = task[1], arg1; + if ( Array.isArray(task[1]) ) { + arg1 = arg0[1]; arg0 = arg0[0]; + } + this.needle = new RegExp(arg0, arg1); }; PSelectorHasTextTask.prototype.exec = function(input) { var output = []; diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index f7b98684f2eef..f8ccff91d9e00 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -53,7 +53,7 @@ var µb = µBlock, reHostnameSeparator = /\s*,\s*/, reHasUnicode = /[^\x00-\x7F]/, - reIsRegexLiteral = /^\/.+\/$/, + reParseRegexLiteral = /^\/(.+)\/([im]+)?$/, emptyArray = [], parsed = { hostnames: [], @@ -158,31 +158,39 @@ }; var compileText = function(s) { - var reText; - if ( reIsRegexLiteral.test(s) ) { - reText = s.slice(1, -1); - if ( isBadRegex(reText) ) { return; } + var regexDetails, + match = reParseRegexLiteral.exec(s); + if ( match !== null ) { + regexDetails = match[1]; + if ( isBadRegex(regexDetails) ) { return; } + if ( match[2] ) { + regexDetails = [ regexDetails, match[2] ]; + } } else { - reText = s.replace(reEscapeRegex, '\\$&'); - regexToRawValue.set(reText, s); + regexDetails = s.replace(reEscapeRegex, '\\$&'); + regexToRawValue.set(regexDetails, s); } - return reText; + return regexDetails; }; var compileCSSDeclaration = function(s) { - var name, value, reText, + var name, value, regexDetails, pos = s.indexOf(':'); if ( pos === -1 ) { return; } name = s.slice(0, pos).trim(); value = s.slice(pos + 1).trim(); - if ( reIsRegexLiteral.test(value) ) { - reText = value.slice(1, -1); - if ( isBadRegex(reText) ) { return; } + var match = reParseRegexLiteral.exec(value); + if ( match !== null ) { + regexDetails = match[1]; + if ( isBadRegex(regexDetails) ) { return; } + if ( match[2] ) { + regexDetails = [ regexDetails, match[2] ]; + } } else { - reText = '^' + value.replace(reEscapeRegex, '\\$&') + '$'; - regexToRawValue.set(reText, value); + regexDetails = '^' + value.replace(reEscapeRegex, '\\$&') + '$'; + regexToRawValue.set(regexDetails, value); } - return { name: name, value: reText }; + return { name: name, value: regexDetails }; }; var compileConditionalSelector = function(s) { @@ -229,38 +237,47 @@ // The normalized string version is what is reported in the logger, // by design. var decompile = function(compiled) { + var tasks = compiled.tasks; + if ( Array.isArray(tasks) === false ) { + return compiled.selector; + } var raw = [ compiled.selector ], - tasks = compiled.tasks, - value; - if ( Array.isArray(tasks) ) { - for ( var i = 0, n = tasks.length, task; i < n; i++ ) { - task = tasks[i]; - switch ( task[0] ) { - case ':has': - case ':xpath': - raw.push(task[0], '(', task[1], ')'); - break; - case ':has-text': + value; + for ( var i = 0, n = tasks.length, task; i < n; i++ ) { + task = tasks[i]; + switch ( task[0] ) { + case ':has': + case ':xpath': + raw.push(task[0], '(', task[1], ')'); + break; + case ':has-text': + if ( Array.isArray(task[1]) ) { + value = '/' + task[1][0] + '/' + task[1][1]; + } else { value = regexToRawValue.get(task[1]); if ( value === undefined ) { value = '/' + task[1] + '/'; } - raw.push(task[0], '(', value, ')'); - break; - case ':matches-css': - case ':matches-css-after': - case ':matches-css-before': + } + raw.push(task[0], '(', value, ')'); + break; + case ':matches-css': + case ':matches-css-after': + case ':matches-css-before': + if ( Array.isArray(task[1].value) ) { + value = '/' + task[1].value[0] + '/' + task[1].value[1]; + } else { value = regexToRawValue.get(task[1].value); if ( value === undefined ) { value = '/' + task[1].value + '/'; } - raw.push(task[0], '(', task[1].name, ': ', value, ')'); - break; - case ':if': - case ':if-not': - raw.push(task[0], '(', decompile(task[1]), ')'); - break; } + raw.push(task[0], '(', task[1].name, ': ', value, ')'); + break; + case ':if': + case ':if-not': + raw.push(task[0], '(', decompile(task[1]), ')'); + break; } } return raw.join(''); From 37fde84a45ae5b96128c4a272f012223356d8134 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 31 Dec 2017 08:44:29 -0500 Subject: [PATCH 043/106] code review #3367 + improve compatibility with Adguard filters --- src/js/static-ext-filtering.js | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index f8ccff91d9e00..685927227cfc6 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -140,23 +140,13 @@ ].join('')); var reEscapeRegex = /[.*+?^${}()|[\]\\]/g, - reNeedScope = /^\s*[+>~]/; + reNeedScope = /^\s*[+>~]/, + reIsDanglingSelector = /(?:[+>~]\s*|\s+)$/; var lastProceduralSelector = '', lastProceduralSelectorCompiled, regexToRawValue = new Map(); - var compileCSSSelector = function(s) { - // https://github.com/AdguardTeam/ExtendedCss/issues/31#issuecomment-302391277 - // Prepend `:scope ` if needed. - if ( reNeedScope.test(s) ) { - s = ':scope ' + s; - } - if ( isValidCSSSelector(s) ) { - return s; - } - }; - var compileText = function(s) { var regexDetails, match = reParseRegexLiteral.exec(s); @@ -219,7 +209,7 @@ ]); var compileArgument = new Map([ - [ ':has', compileCSSSelector ], + [ ':has', compileConditionalSelector ], [ ':has-text', compileText ], [ ':if', compileConditionalSelector ], [ ':if-not', compileConditionalSelector ], @@ -332,16 +322,22 @@ return; } tasks.push([ operator, args ]); - if ( i === n ) { break; } raw = raw.slice(i); + if ( i === n ) { break; } } + // No task found: then we have a CSS selector. + // At least one task found: nothing should be left to parse. if ( tasks.length === 0 ) { prefix = raw; tasks = undefined; - } - if ( prefix !== '' && isValidCSSSelector(prefix) === false ) { + } else if ( raw.length !== 0 ) { return; } + // https://github.com/NanoAdblocker/NanoCore/issues/1#issuecomment-354394894 + if ( prefix !== '' ) { + if ( reIsDanglingSelector.test(prefix) ) { prefix += '*'; } + if ( isValidCSSSelector(prefix) === false ) { return; } + } return { selector: prefix, tasks: tasks }; }; From 35eaf267e6d39ffd2dd046667157424bf2d80651 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 31 Dec 2017 08:46:58 -0500 Subject: [PATCH 044/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 631ae989896a0..9c98d9ce737c0 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.7", + "version": "1.14.23.8", "commands": { "launch-element-zapper": { From ec29c9a127342f6d250c41572017329c45aca0e5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 31 Dec 2017 16:05:23 -0500 Subject: [PATCH 045/106] fix #3382 --- src/js/contentscript.js | 15 +-------------- src/js/html-filtering.js | 15 +-------------- src/js/scriptlets/dom-inspector.js | 7 ++++++- src/js/static-ext-filtering.js | 2 +- 4 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index dcc3f14828849..4c50ceda2b45a 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -374,19 +374,6 @@ vAPI.DOMFilterer = (function() { // 'P' stands for 'Procedural' - var PSelectorHasTask = function(task) { - this.selector = task[1]; - }; - PSelectorHasTask.prototype.exec = function(input) { - var output = []; - for ( var node of input ) { - if ( node.querySelector(this.selector) !== null ) { - output.push(node); - } - } - return output; - }; - var PSelectorHasTextTask = function(task) { var arg0 = task[1], arg1; if ( Array.isArray(task[1]) ) { @@ -486,7 +473,7 @@ vAPI.DOMFilterer = (function() { var PSelector = function(o) { if ( PSelector.prototype.operatorToTaskMap === undefined ) { PSelector.prototype.operatorToTaskMap = new Map([ - [ ':has', PSelectorHasTask ], + [ ':has', PSelectorIfTask ], [ ':has-text', PSelectorHasTextTask ], [ ':if', PSelectorIfTask ], [ ':if-not', PSelectorIfNotTask ], diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 1dec988cf0f99..f4d2c39126809 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -34,19 +34,6 @@ discardedCount = 0, docRegister, loggerRegister; - var PSelectorHasTask = function(task) { - this.selector = task[1]; - }; - PSelectorHasTask.prototype.exec = function(input) { - var output = []; - for ( var node of input ) { - if ( node.querySelector(this.selector) !== null ) { - output.push(node); - } - } - return output; - }; - var PSelectorHasTextTask = function(task) { var arg0 = task[1], arg1; if ( Array.isArray(task[1]) ) { @@ -117,7 +104,7 @@ var PSelector = function(o) { if ( PSelector.prototype.operatorToTaskMap === undefined ) { PSelector.prototype.operatorToTaskMap = new Map([ - [ ':has', PSelectorHasTask ], + [ ':has', PSelectorIfTask ], [ ':has-text', PSelectorHasTextTask ], [ ':if', PSelectorIfTask ], [ ':if-not', PSelectorIfNotTask ], diff --git a/src/js/scriptlets/dom-inspector.js b/src/js/scriptlets/dom-inspector.js index ec02f8f072f13..5b2f4df73f938 100644 --- a/src/js/scriptlets/dom-inspector.js +++ b/src/js/scriptlets/dom-inspector.js @@ -589,7 +589,12 @@ var elementsFromSpecialSelector = function(selector) { var out = [], i; var matches = /^(.+?):has\((.+?)\)$/.exec(selector); if ( matches !== null ) { - var nodes = document.querySelectorAll(matches[1]); + var nodes; + try { + nodes = document.querySelectorAll(matches[1]); + } catch(ex) { + nodes = []; + } i = nodes.length; while ( i-- ) { var node = nodes[i]; diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index 685927227cfc6..d84adb5a109a4 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -236,7 +236,6 @@ for ( var i = 0, n = tasks.length, task; i < n; i++ ) { task = tasks[i]; switch ( task[0] ) { - case ':has': case ':xpath': raw.push(task[0], '(', task[1], ')'); break; @@ -264,6 +263,7 @@ } raw.push(task[0], '(', task[1].name, ': ', value, ')'); break; + case ':has': case ':if': case ':if-not': raw.push(task[0], '(', decompile(task[1]), ')'); From d8d1fa52210ab125a0f9ef8865cb39068ff65bcb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 31 Dec 2017 16:06:45 -0500 Subject: [PATCH 046/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 9c98d9ce737c0..b2cd993c09768 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.8", + "version": "1.14.23.9", "commands": { "launch-element-zapper": { From ce696e5fbee12093d7e4c3177cac9ded73aa864b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 31 Dec 2017 21:13:06 -0500 Subject: [PATCH 047/106] fix #3386 --- src/js/static-ext-filtering.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/js/static-ext-filtering.js b/src/js/static-ext-filtering.js index d84adb5a109a4..6d90a03b80a16 100644 --- a/src/js/static-ext-filtering.js +++ b/src/js/static-ext-filtering.js @@ -113,11 +113,14 @@ return matches[1].trim() + ':style(' + matches[2].trim() + ')'; }; - var toASCIIHostname = function(hostname) { - if ( hostname.charCodeAt(0) === 0x7E /* '~' */ ) { - return '~' + punycode.toASCII(hostname.slice(1)); + var toASCIIHostnames = function(hostnames) { + var i = hostnames.length; + while ( i-- ) { + var hostname = hostnames[i]; + hostnames[i] = hostname.charCodeAt(0) === 0x7E /* '~' */ ? + '~' + punycode.toASCII(hostname.slice(1)) : + punycode.toASCII(hostname); } - return punycode.toASCII(hostname); }; var compileProceduralSelector = (function() { @@ -631,9 +634,7 @@ var prefix = raw.slice(0, lpos); parsed.hostnames = prefix.split(reHostnameSeparator); if ( reHasUnicode.test(prefix) ) { - for ( var hostname of parsed.hostnames ) { - parsed.hostnames = toASCIIHostname(hostname); - } + toASCIIHostnames(parsed.hostnames); } } From e675ccf73e2d1ed2233029cfda6c8c82916b34bc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 31 Dec 2017 21:13:46 -0500 Subject: [PATCH 048/106] fix improper reporting of internal filter in logger [Firefox] --- platform/webext/vapi-usercss.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/platform/webext/vapi-usercss.js b/platform/webext/vapi-usercss.js index b9ef068e341ce..10e7d371e4b56 100644 --- a/platform/webext/vapi-usercss.js +++ b/platform/webext/vapi-usercss.js @@ -204,9 +204,16 @@ vAPI.DOMFilterer.prototype = { var out = { declarative: [] }; + var selectors; for ( var entry of this.filterset ) { - if ( all === false && entry.internal ) { continue; } - out.declarative.push([ entry.selectors, entry.declarations ]); + selectors = entry.selectors; + if ( all !== true && this.hideNodeAttr !== undefined ) { + selectors = selectors + .replace('[' + this.hideNodeAttr + ']', '') + .replace(/^,\n|^\n/, ''); + if ( selectors === '' ) { continue; } + } + out.declarative.push([ selectors, entry.declarations ]); } return out; }, From fc26b5d22725b44d7e83f5ad52812c1877294a75 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 31 Dec 2017 21:15:22 -0500 Subject: [PATCH 049/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index b2cd993c09768..958081972ed27 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.9", + "version": "1.14.23.10", "commands": { "launch-element-zapper": { From 636dcf7ee4a15596b36e6f6a42bd9330c08a7374 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 1 Jan 2018 07:52:03 -0500 Subject: [PATCH 050/106] fix #3383 --- src/js/storage.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/js/storage.js b/src/js/storage.js index f172bf172ac56..e0968e83ff1d0 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -172,11 +172,16 @@ // Select default filter lists if first-time launch. if ( !bin || Array.isArray(bin.selectedFilterLists) === false ) { µb.assets.metadata(function(availableLists) { - µb.saveSelectedFilterLists(µb.autoSelectRegionalFilterLists(availableLists)); + µb.saveSelectedFilterLists( + µb.autoSelectRegionalFilterLists(availableLists) + ); callback(); }); return; } + // TODO: Removes once 1.1.15 is in widespread use. + // https://github.com/gorhill/uBlock/issues/3383 + vAPI.storage.remove('remoteBlacklists'); µb.selectedFilterLists = bin.selectedFilterLists; callback(); }); From 455bf281f007e7f900b1e4edac15a40cfbf665cd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 2 Jan 2018 23:06:16 -0500 Subject: [PATCH 051/106] fix #3391 --- src/background.html | 1 + src/js/text-encode.js | 117 ++++++++++++++++++++++++++++++++++++++++++ src/js/traffic.js | 30 ++++++----- 3 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 src/js/text-encode.js diff --git a/src/background.html b/src/background.html index 168e9ae2044d4..603027fe1c7a3 100644 --- a/src/background.html +++ b/src/background.html @@ -34,6 +34,7 @@ + diff --git a/src/js/text-encode.js b/src/js/text-encode.js new file mode 100644 index 0000000000000..12568cfedf27c --- /dev/null +++ b/src/js/text-encode.js @@ -0,0 +1,117 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2018 Raymond Hill + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +'use strict'; + +/******************************************************************************/ + +µBlock.textEncode = (function() { + + var cp1251_range0 = new Uint8Array([ + /* 0x0400 */ 0x00, 0xA8, 0x80, 0x81, 0xAA, 0xBD, 0xB2, 0xAF, + /* 0x0408 */ 0xA3, 0x8A, 0x8C, 0x8E, 0x8D, 0x00, 0xA1, 0x8F, + /* 0x0410 */ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /* 0x0418 */ 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + /* 0x0420 */ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + /* 0x0428 */ 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + /* 0x0430 */ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + /* 0x0438 */ 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + /* 0x0440 */ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /* 0x0448 */ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF, + /* 0x0450 */ 0x00, 0xB8, 0x90, 0x83, 0xBA, 0xBE, 0xB3, 0xBF, + /* 0x0458 */ 0xBC, 0x9A, 0x9C, 0x9E, 0x9D, 0x00, 0xA2, 0x9F, + /* 0x0460 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0468 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0470 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0478 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0480 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0488 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0490 */ 0xA5, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]); + + var cp1251_range1 = new Uint8Array([ + /* 0x2010 */ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, + /* 0x2018 */ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, + /* 0x2020 */ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, + /* 0x2028 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x2030 */ 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x2038 */ 0x00, 0x8B, 0x9B, 0x00, 0x00, 0x00, 0x00, 0x00 + ]); + + var encoders = { + 'windows-1251': function(buf) { + var i = 0, n = buf.byteLength, o = 0, c; + while ( i < n ) { + c = buf[i++]; + if ( c < 0x80 ) { + buf[o++] = c; + } else { + if ( (c & 0xE0) === 0xC0 ) { + c = (c & 0x1F) << 6; + c |= (buf[i++] & 0x3F); + } else if ( (c & 0xF0) === 0xE0 ) { + c = (c & 0x0F) << 12; + c |= (buf[i++] & 0x3F) << 6; + c |= (buf[i++] & 0x3F); + } else if ( (c & 0xF0) === 0xF0 ) { + c = (c & 0x07) << 18; + c |= (buf[i++] & 0x3F) << 12; + c |= (buf[i++] & 0x3F) << 6; + c |= (buf[i++] & 0x3F); + } + if ( c >= 0x400 && c < 0x4A0 ) { + buf[o++] = cp1251_range0[c - 0x400]; + } else if ( c >= 0x2010 && c < 0x2040 ) { + buf[o++] = cp1251_range1[c - 0x2010]; + } else if ( c === 0x20AC ) { + buf[o++] = 0x88; + } else if ( c === 0x2116 ) { + buf[o++] = 0xB9; + } else if ( c === 0x2122 ) { + buf[o++] = 0x99; + } else if ( c < 0xD800 || c >= 0xE000 ) { + buf[o++] = c; + } + } + } + return buf.slice(0, o); + } + }; + + var api = {}; + + api.normalizedCharset = new Map([ + [ 'utf8', 'utf-8' ], + [ 'unicode-1-1-utf-8', 'utf-8' ], + [ 'utf-8', 'utf-8' ], + [ 'windows-1251', 'windows-1251' ], + [ 'cp1251', 'windows-1251' ], + [ 'x-cp1251', 'windows-1251' ], + ]); + + api.encode = function(charset, buf) { + return encoders.hasOwnProperty(charset) ? + encoders[charset](buf) : + buf; + }; + + return api; +})(); diff --git a/src/js/traffic.js b/src/js/traffic.js index 4d334a59d5e21..0734c1e1ab196 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2017 Raymond Hill + Copyright (C) 2014-2018 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -750,23 +750,24 @@ var filterDocument = (function() { return; } - // If the charset of the document was not utf-8, we need to change it - // to utf-8. - if ( textDecoderCharset !== undefined ) { - var meta = doc.createElement('meta'); - meta.setAttribute('charset', 'utf-8'); - doc.head.insertBefore(meta, doc.head.firstChild); - } - // https://stackoverflow.com/questions/6088972/get-doctype-of-an-html-as-string-with-javascript/10162353#10162353 var doctypeStr = doc.doctype instanceof Object ? xmlSerializer.serializeToString(doc.doctype) + '\n' : ''; - streamClose( - filterer, - textEncoder.encode(doctypeStr + doc.documentElement.outerHTML) + // https://github.com/gorhill/uBlock/issues/3391 + var encodedStream = textEncoder.encode( + doctypeStr + + doc.documentElement.outerHTML ); + if ( textDecoderCharset !== undefined ) { + encodedStream = µb.textEncode.encode( + textDecoderCharset, + encodedStream + ); + } + + streamClose(filterer, encodedStream); }; var onStreamError = function() { @@ -809,7 +810,10 @@ var filterDocument = (function() { if ( match !== null ) { var charset = match[1].toLowerCase(); if ( charset !== 'utf-8' ) { - request.charset = charset; + request.charset = µb.textEncode.normalizedCharset.get(charset); + if ( request.charset === 'utf-8' ) { + request.charset = undefined; + } } } } From 9049909d2d85b2bd56b9ade6025e53a7bb659828 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 2 Jan 2018 23:22:40 -0500 Subject: [PATCH 052/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 958081972ed27..c5b5100d67535 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.10", + "version": "1.14.23.11", "commands": { "launch-element-zapper": { From 5a468be66147dbe3493d2b69e5a64e937cfa0902 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 2 Jan 2018 23:25:42 -0500 Subject: [PATCH 053/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/ro/messages.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index ac9d254cece5a..4e5c6e7a5012c 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -48,11 +48,11 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Dați clic pentru a dezactiva uBlock₀ pentru acest sait.\n\nDați Ctrl+clic pentru a dezactiva uBlock₀ doar pe această pagină.", + "message": "Clic ca să dezactivezi uBo pentru acest site.\n\nCtrl+clic ca sa dezactivezi uBo doar pe această pagină.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Dați clic pentru a activa uBlock₀ pentru acest sait.", + "message": "Dați clic pentru a activa uBlock₀ pentru acest site.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -108,11 +108,11 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Dați clic pentru a bloca elementele media de mari dimensiuni pentru acest sait", + "message": "Dați clic pentru a bloca elementele media de mari dimensiuni pentru acest site", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Dați clic pentru a nu mai bloca elementele media de mari dimensiuni pentru acest sait", + "message": "Dați clic pentru a nu mai bloca elementele media de mari dimensiuni pentru acest site", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Dați clic pentru a dezactiva filtrele vizuale pentru acest sait", + "message": "Dați clic pentru a dezactiva filtrele vizuale pentru acest site", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Dați clic pentru a activa filtrele vizuale pentru acest sait", + "message": "Dați clic pentru a activa filtrele vizuale pentru acest site", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -136,7 +136,7 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Dați clic pentru a nu mai bloca fonturile externe pentru acest sait", + "message": "Dați clic pentru a nu mai bloca fonturile externe pentru acest site", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { From 04d84cf92aeb40be1093b33f0383cd3705f8b169 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 3 Jan 2018 13:59:38 -0500 Subject: [PATCH 054/106] fix #3397 --- src/js/contentscript.js | 3 +- src/js/messaging.js | 5 +- src/js/text-encode.js | 119 ++++++++++++++++++++++++++++++++-------- src/js/traffic.js | 18 +++--- 4 files changed, 110 insertions(+), 35 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 4c50ceda2b45a..eb97b75fabd14 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -1403,7 +1403,8 @@ vAPI.domSurveyor = (function() { { what: 'retrieveContentScriptParameters', url: window.location.href, - isRootFrame: window === window.top + isRootFrame: window === window.top, + charset: document.characterSet }, bootstrapPhase1 ); diff --git a/src/js/messaging.js b/src/js/messaging.js index eefb957885328..4eefbeb9c035e 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -515,7 +515,10 @@ var onMessage = function(request, sender, callback) { µb.cosmeticFilteringEngine.retrieveDomainSelectors(request, response); // If response body filtering is supported, than the scriptlets have // already been injected. - if ( µb.canFilterResponseBody === false ) { + if ( + µb.canFilterResponseBody === false || + µb.textEncode.normalizeCharset(request.charset) === undefined + ) { response.scriptlets = µb.scriptletFilteringEngine.retrieve(request); } if ( request.isRootFrame && µb.logger.isEnabled() ) { diff --git a/src/js/text-encode.js b/src/js/text-encode.js index 12568cfedf27c..60a4c8d948c07 100644 --- a/src/js/text-encode.js +++ b/src/js/text-encode.js @@ -25,6 +25,39 @@ µBlock.textEncode = (function() { + var normalizedCharset = new Map([ + [ 'utf8', 'utf-8' ], + [ 'unicode-1-1-utf-8', 'utf-8' ], + [ 'utf-8', 'utf-8' ], + [ 'windows-1250', 'windows-1250' ], + [ 'cp1250', 'windows-1250' ], + [ 'x-cp1250', 'windows-1250' ], + [ 'windows-1251', 'windows-1251' ], + [ 'cp1251', 'windows-1251' ], + [ 'x-cp1251', 'windows-1251' ], + ]); + + // http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1250.TXT + var cp1250_range0 = new Uint8Array([ + /* 0x0100 */ 0x00, 0x00, 0xC3, 0xE3, 0xA5, 0xB9, 0xC6, 0xE6, + /* 0x0108 */ 0x00, 0x00, 0x00, 0x00, 0xC8, 0xE8, 0xCF, 0xEF, + /* 0x0110 */ 0xD0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0118 */ 0xCA, 0xEA, 0xCC, 0xEC, 0x00, 0x00, 0x00, 0x00, + /* 0x0120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0130 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0138 */ 0x00, 0xC5, 0xE5, 0x00, 0x00, 0xBC, 0xBE, 0x00, + /* 0x0140 */ 0x00, 0xA3, 0xB3, 0xD1, 0xF1, 0x00, 0x00, 0xD2, + /* 0x0148 */ 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0150 */ 0xD5, 0xF5, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x00, + /* 0x0158 */ 0xD8, 0xF8, 0x8C, 0x9C, 0x00, 0x00, 0xAA, 0xBA, + /* 0x0160 */ 0x8A, 0x9A, 0xDE, 0xFE, 0x8D, 0x9D, 0x00, 0x00, + /* 0x0168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD9, 0xF9, + /* 0x0170 */ 0xDB, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0178 */ 0x00, 0x8F, 0x9F, 0xAF, 0xBF, 0x8E, 0x9E, 0x00 + ]); + + // http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT var cp1251_range0 = new Uint8Array([ /* 0x0400 */ 0x00, 0xA8, 0x80, 0x81, 0xAA, 0xBD, 0xB2, 0xAF, /* 0x0408 */ 0xA3, 0x8A, 0x8C, 0x8E, 0x8D, 0x00, 0xA1, 0x8F, @@ -47,7 +80,7 @@ /* 0x0490 */ 0xA5, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]); - var cp1251_range1 = new Uint8Array([ + var cp125x_range0 = new Uint8Array([ /* 0x2010 */ 0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, /* 0x2018 */ 0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, /* 0x2020 */ 0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, @@ -57,6 +90,51 @@ ]); var encoders = { + 'windows-1250': function(buf) { + var i = 0, n = buf.byteLength, o = 0, c; + while ( i < n ) { + c = buf[i++]; + if ( c < 0x80 ) { + buf[o++] = c; + } else { + if ( (c & 0xE0) === 0xC0 ) { + c = (c & 0x1F) << 6; + c |= (buf[i++] & 0x3F); + } else if ( (c & 0xF0) === 0xE0 ) { + c = (c & 0x0F) << 12; + c |= (buf[i++] & 0x3F) << 6; + c |= (buf[i++] & 0x3F); + } else if ( (c & 0xF8) === 0xF0 ) { + c = (c & 0x07) << 18; + c |= (buf[i++] & 0x3F) << 12; + c |= (buf[i++] & 0x3F) << 6; + c |= (buf[i++] & 0x3F); + } + if ( c < 0x100 ) { + buf[o++] = c; + } else if ( c >= 0x100 && c < 0x180 ) { + buf[o++] = cp1250_range0[c - 0x100]; + } else if ( c >= 0x2010 && c < 0x2040 ) { + buf[o++] = cp125x_range0[c - 0x2010]; + } else if ( c === 0x02C7 ) { + buf[o++] = 0xA1; + } else if ( c === 0x02D8 ) { + buf[o++] = 0xA2; + } else if ( c === 0x02D9 ) { + buf[o++] = 0xFF; + } else if ( c === 0x02DB ) { + buf[o++] = 0xB2; + } else if ( c === 0x02DD ) { + buf[o++] = 0xBD; + } else if ( c === 0x20AC ) { + buf[o++] = 0x88; + } else if ( c === 0x2122 ) { + buf[o++] = 0x99; + } + } + } + return buf.slice(0, o); + }, 'windows-1251': function(buf) { var i = 0, n = buf.byteLength, o = 0, c; while ( i < n ) { @@ -71,24 +149,24 @@ c = (c & 0x0F) << 12; c |= (buf[i++] & 0x3F) << 6; c |= (buf[i++] & 0x3F); - } else if ( (c & 0xF0) === 0xF0 ) { + } else if ( (c & 0xF8) === 0xF0 ) { c = (c & 0x07) << 18; c |= (buf[i++] & 0x3F) << 12; c |= (buf[i++] & 0x3F) << 6; c |= (buf[i++] & 0x3F); } - if ( c >= 0x400 && c < 0x4A0 ) { + if ( c < 0x100 ) { + buf[o++] = c; + } else if ( c >= 0x400 && c < 0x4A0 ) { buf[o++] = cp1251_range0[c - 0x400]; } else if ( c >= 0x2010 && c < 0x2040 ) { - buf[o++] = cp1251_range1[c - 0x2010]; + buf[o++] = cp125x_range0[c - 0x2010]; } else if ( c === 0x20AC ) { buf[o++] = 0x88; } else if ( c === 0x2116 ) { buf[o++] = 0xB9; } else if ( c === 0x2122 ) { buf[o++] = 0x99; - } else if ( c < 0xD800 || c >= 0xE000 ) { - buf[o++] = c; } } } @@ -96,22 +174,17 @@ } }; - var api = {}; - - api.normalizedCharset = new Map([ - [ 'utf8', 'utf-8' ], - [ 'unicode-1-1-utf-8', 'utf-8' ], - [ 'utf-8', 'utf-8' ], - [ 'windows-1251', 'windows-1251' ], - [ 'cp1251', 'windows-1251' ], - [ 'x-cp1251', 'windows-1251' ], - ]); - - api.encode = function(charset, buf) { - return encoders.hasOwnProperty(charset) ? - encoders[charset](buf) : - buf; + return { + encode: function(charset, buf) { + return encoders.hasOwnProperty(charset) ? + encoders[charset](buf) : + buf; + }, + normalizeCharset: function(charset) { + if ( charset === undefined ) { + return 'utf-8'; + } + return normalizedCharset.get(charset.toLowerCase()); + } }; - - return api; })(); diff --git a/src/js/traffic.js b/src/js/traffic.js index 0734c1e1ab196..548caa5a9aa91 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -511,7 +511,7 @@ var onHeadersReceived = function(details) { } if ( isDoc && µb.canFilterResponseBody ) { - filterDocument(details); + filterDocument(pageStore, details); } // https://github.com/gorhill/uBlock/issues/2813 @@ -579,6 +579,9 @@ var filterDocument = (function() { domParser, xmlSerializer, textDecoderCharset, textDecoder, textEncoder; + var reContentTypeDocument = /^(?:text\/html|application\/xhtml+xml)/i, + reContentTypeCharset = /charset=['"]?([^'" ]+)/i; + // Purpose of following helper is to disconnect from watching the stream // if all the following conditions are fulfilled: // - Only need to inject scriptlets. @@ -774,7 +777,7 @@ var filterDocument = (function() { filterers.delete(this); }; - return function(details) { + return function(pageStore, details) { var hostname = µb.URI.hostnameFromURI(details.url); if ( hostname === '' ) { return; } @@ -808,12 +811,10 @@ var filterDocument = (function() { if ( reContentTypeDocument.test(contentType) === false ) { return; } var match = reContentTypeCharset.exec(contentType); if ( match !== null ) { - var charset = match[1].toLowerCase(); + var charset = µb.textEncode.normalizeCharset(match[1]); + if ( charset === undefined ) { return; } if ( charset !== 'utf-8' ) { - request.charset = µb.textEncode.normalizedCharset.get(charset); - if ( request.charset === 'utf-8' ) { - request.charset = undefined; - } + request.charset = charset; } } } @@ -829,9 +830,6 @@ var filterDocument = (function() { }; })(); -var reContentTypeDocument = /^(?:text\/html|application\/xhtml+xml)/i; -var reContentTypeCharset = /charset=['"]?([^'" ]+)/i; - /******************************************************************************/ var injectCSP = function(pageStore, details) { From 1c1fdde4d2dc35c5cd9df10bdbf9e01a60570c03 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 3 Jan 2018 14:07:57 -0500 Subject: [PATCH 055/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index c5b5100d67535..6ff50ed6349e9 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.11", + "version": "1.14.23.12", "commands": { "launch-element-zapper": { From e4783bf68dd0e759cd0fda6a8ebea40cd4d1a95d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 4 Jan 2018 14:51:33 -0500 Subject: [PATCH 056/106] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9c7df8a1a0af3..3c5283c79dc25 100644 --- a/README.md +++ b/README.md @@ -141,12 +141,16 @@ Stable version available in [Microsoft Store](https://www.microsoft.com/store/p/ Development version available at . +Note that any issues specific to the Edge fork are the responsibility of the current maintainer, I have no control over the code base of the fork. + #### Safari (macOS) Developer: [@el1t](https://github.com/el1t). Development version available at . +Note that any issues specific to the Safari fork are the responsibility of the current maintainer, I have no control over the code base of the fork. + #### Note for all browsers To benefit from uBlock Origin's higher efficiency, it's advised that you don't use other inefficient blockers at the same time (such as AdBlock or Adblock Plus). uBlock₀ will do [as well or better](#blocking) than most popular ad blockers. Other blockers can also prevent uBlock₀'s privacy or anti-blocker features from working properly. From 053476949c00422702d51018f90cb19eceef2bf1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 4 Jan 2018 15:15:41 -0500 Subject: [PATCH 057/106] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3c5283c79dc25..8b17ee5314e90 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ Stable version available in [Microsoft Store](https://www.microsoft.com/store/p/ Development version available at . -Note that any issues specific to the Edge fork are the responsibility of the current maintainer, I have no control over the code base of the fork. +Note that issues specific to the Edge fork are the responsibility of the current maintainer, I have no control over the code base of the fork. #### Safari (macOS) @@ -149,7 +149,7 @@ Developer: [@el1t](https://github.com/el1t). Development version available at . -Note that any issues specific to the Safari fork are the responsibility of the current maintainer, I have no control over the code base of the fork. +Note that issues specific to the Safari fork are the responsibility of the current maintainer, I have no control over the code base of the fork. #### Note for all browsers From 21b52ec10bf0483331e8c7027c52db9556e69a0f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 4 Jan 2018 17:55:59 -0500 Subject: [PATCH 058/106] code review: remove pointless test --- src/js/text-encode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/text-encode.js b/src/js/text-encode.js index 60a4c8d948c07..7b2823aa3a380 100644 --- a/src/js/text-encode.js +++ b/src/js/text-encode.js @@ -112,7 +112,7 @@ } if ( c < 0x100 ) { buf[o++] = c; - } else if ( c >= 0x100 && c < 0x180 ) { + } else if ( c < 0x180 ) { buf[o++] = cp1250_range0[c - 0x100]; } else if ( c >= 0x2010 && c < 0x2040 ) { buf[o++] = cp125x_range0[c - 0x2010]; From 4812ac9b68729fcba05065a21330c3fc8144dc9e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 4 Jan 2018 18:26:52 -0500 Subject: [PATCH 059/106] fix #3399 (part 1) --- src/js/traffic.js | 53 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 548caa5a9aa91..fe6c598637ac6 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -582,6 +582,26 @@ var filterDocument = (function() { var reContentTypeDocument = /^(?:text\/html|application\/xhtml+xml)/i, reContentTypeCharset = /charset=['"]?([^'" ]+)/i; + var charsetFromContentType = function(contentType) { + var match = reContentTypeCharset.exec(contentType); + if ( match !== null ) { + return match[1].toLowerCase(); + } + }; + + var charsetFromDoc = function(doc) { + var meta = doc.querySelector('meta[charset]'); + if ( meta !== null ) { + return meta.getAttribute('charset').toLowerCase(); + } + meta = doc.querySelector( + 'meta[http-equiv="content-type" i][content]' + ); + if ( meta !== null ) { + return charsetFromContentType(meta.getAttribute('content')); + } + }; + // Purpose of following helper is to disconnect from watching the stream // if all the following conditions are fulfilled: // - Only need to inject scriptlets. @@ -600,7 +620,7 @@ var filterDocument = (function() { if ( filterer.scriptlets === undefined || filterer.selectors !== undefined || - filterer.charset !== undefined + filterer.charset === undefined ) { return false; } @@ -718,16 +738,19 @@ var filterDocument = (function() { } // In case of unknown charset, assume utf-8. - if ( filterer.charset !== textDecoderCharset ) { + if ( + filterer.charset === undefined && textDecoderCharset !== 'utf-8' || + filterer.charset !== undefined && filterer.charset !== textDecoderCharset + ) { textDecoder = undefined; } if ( textDecoder === undefined ) { try { textDecoder = new TextDecoder(filterer.charset); - textDecoderCharset = filterer.charset; + textDecoderCharset = filterer.charset || 'utf-8'; } catch(ex) { textDecoder = new TextDecoder(); - textDecoderCharset = undefined; + textDecoderCharset = 'utf-8'; } } @@ -736,6 +759,14 @@ var filterDocument = (function() { 'text/html' ); + if ( filterer.charset === undefined ) { + filterer.charset = µb.textEncode.normalizeCharset(charsetFromDoc(doc)); + if ( filterer.charset === undefined ) { + streamClose(filterer); + return; + } + } + var modified = false; if ( filterer.selectors !== undefined ) { if ( µb.htmlFilteringEngine.apply(doc, filterer) ) { @@ -763,9 +794,9 @@ var filterDocument = (function() { doctypeStr + doc.documentElement.outerHTML ); - if ( textDecoderCharset !== undefined ) { + if ( filterer.charset !== 'utf-8' ) { encodedStream = µb.textEncode.encode( - textDecoderCharset, + filterer.charset, encodedStream ); } @@ -809,13 +840,11 @@ var filterDocument = (function() { contentType = headerValueFromName('content-type', headers); if ( contentType !== '' ) { if ( reContentTypeDocument.test(contentType) === false ) { return; } - var match = reContentTypeCharset.exec(contentType); - if ( match !== null ) { - var charset = µb.textEncode.normalizeCharset(match[1]); + var charset = charsetFromContentType(contentType); + if ( charset !== undefined ) { + charset = µb.textEncode.normalizeCharset(charset); if ( charset === undefined ) { return; } - if ( charset !== 'utf-8' ) { - request.charset = charset; - } + request.charset = charset; } } // https://bugzilla.mozilla.org/show_bug.cgi?id=1426789 From fcd2124ad334426eb74be33d1152c9060ea9b969 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 4 Jan 2018 18:30:23 -0500 Subject: [PATCH 060/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 6ff50ed6349e9..c1b3ee92e1937 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.12", + "version": "1.14.23.13", "commands": { "launch-element-zapper": { From a0375bb6a3aae0af2f0b5509d0bec237877facaf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jan 2018 13:15:56 -0500 Subject: [PATCH 061/106] add support for iso-8859-1/windows-1252 encoding (https://github.com/gorhill/uBlock/issues/3391#issuecomment-354868704) --- src/js/messaging.js | 1 + src/js/text-encode.js | 77 ++++++++++++++++++++++++++++++++++++++++++- src/js/traffic.js | 43 +++++++++++++----------- 3 files changed, 100 insertions(+), 21 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index 4eefbeb9c035e..5855baf9f41b0 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -517,6 +517,7 @@ var onMessage = function(request, sender, callback) { // already been injected. if ( µb.canFilterResponseBody === false || + µb.textEncode === undefined || µb.textEncode.normalizeCharset(request.charset) === undefined ) { response.scriptlets = µb.scriptletFilteringEngine.retrieve(request); diff --git a/src/js/text-encode.js b/src/js/text-encode.js index 7b2823aa3a380..93ac0ff62b3c9 100644 --- a/src/js/text-encode.js +++ b/src/js/text-encode.js @@ -25,16 +25,40 @@ µBlock.textEncode = (function() { + if ( µBlock.canFilterResponseBody !== true ) { return; } + + // charset aliases extracted from: + // https://github.com/inexorabletash/text-encoding/blob/b4e5bc26e26e51f56e3daa9f13138c79f49d3c34/lib/encoding.js#L342 var normalizedCharset = new Map([ [ 'utf8', 'utf-8' ], [ 'unicode-1-1-utf-8', 'utf-8' ], [ 'utf-8', 'utf-8' ], + [ 'windows-1250', 'windows-1250' ], [ 'cp1250', 'windows-1250' ], [ 'x-cp1250', 'windows-1250' ], + [ 'windows-1251', 'windows-1251' ], [ 'cp1251', 'windows-1251' ], [ 'x-cp1251', 'windows-1251' ], + + [ 'windows-1252', 'windows-1252' ], + [ 'ansi_x3.4-1968', 'windows-1252' ], + [ 'ascii', 'windows-1252' ], + [ 'cp1252', 'windows-1252' ], + [ 'cp819', 'windows-1252' ], + [ 'csisolatin1', 'windows-1252' ], + [ 'ibm819', 'windows-1252' ], + [ 'iso-8859-1', 'windows-1252' ], + [ 'iso-ir-100', 'windows-1252' ], + [ 'iso8859-1', 'windows-1252' ], + [ 'iso88591', 'windows-1252' ], + [ 'iso_8859-1', 'windows-1252' ], + [ 'iso_8859-1:1987', 'windows-1252' ], + [ 'l1', 'windows-1252' ], + [ 'latin1', 'windows-1252' ], + [ 'us-ascii', 'windows-1252' ], + [ 'x-cp1252', 'windows-1252' ], ]); // http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1250.TXT @@ -77,7 +101,17 @@ /* 0x0478 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0480 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0488 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 0x0490 */ 0xA5, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0490 */ 0xA5, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ]); + + // https://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT + var cp1252_range0 = new Uint8Array([ + /* 0x0150 */ 0x00, 0x00, 0x8C, 0x9C, 0x00, 0x00, 0x00, 0x00, + /* 0x0158 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0160 */ 0x8A, 0x9A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0170 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 0x0178 */ 0x9F, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x9E, 0x00 ]); var cp125x_range0 = new Uint8Array([ @@ -171,6 +205,47 @@ } } return buf.slice(0, o); + }, + 'windows-1252': function(buf) { + var i = 0, n = buf.byteLength, o = 0, c; + while ( i < n ) { + c = buf[i++]; + if ( c < 0x80 ) { + buf[o++] = c; + } else { + if ( (c & 0xE0) === 0xC0 ) { + c = (c & 0x1F) << 6; + c |= (buf[i++] & 0x3F); + } else if ( (c & 0xF0) === 0xE0 ) { + c = (c & 0x0F) << 12; + c |= (buf[i++] & 0x3F) << 6; + c |= (buf[i++] & 0x3F); + } else if ( (c & 0xF8) === 0xF0 ) { + c = (c & 0x07) << 18; + c |= (buf[i++] & 0x3F) << 12; + c |= (buf[i++] & 0x3F) << 6; + c |= (buf[i++] & 0x3F); + } + if ( c < 0x100 ) { + buf[o++] = c; + } else if ( c >= 0x150 && c < 0x180 ) { + buf[o++] = cp1252_range0[c - 0x150]; + } else if ( c >= 0x2010 && c < 0x2040 ) { + buf[o++] = cp125x_range0[c - 0x2010]; + } else if ( c === 0x192 ) { + buf[o++] = 0x83; + } else if ( c === 0x2C6 ) { + buf[o++] = 0x88; + } else if ( c === 0x2DC ) { + buf[o++] = 0x98; + } else if ( c === 0x20AC ) { + buf[o++] = 0x80; + } else if ( c === 0x2122 ) { + buf[o++] = 0x99; + } + } + } + return buf.slice(0, o); } }; diff --git a/src/js/traffic.js b/src/js/traffic.js index fe6c598637ac6..9a7dbf97ba95a 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -577,7 +577,7 @@ var filterDocument = (function() { var µb = µBlock, filterers = new Map(), domParser, xmlSerializer, - textDecoderCharset, textDecoder, textEncoder; + utf8TextDecoder, textDecoder, textEncoder; var reContentTypeDocument = /^(?:text\/html|application\/xhtml+xml)/i, reContentTypeCharset = /charset=['"]?([^'" ]+)/i; @@ -737,36 +737,39 @@ var filterDocument = (function() { textEncoder = new TextEncoder(); } - // In case of unknown charset, assume utf-8. + var doc; + + // If stream encoding is still unknnown, try to extract from document. + if ( filterer.charset === undefined ) { + if ( utf8TextDecoder === undefined ) { + utf8TextDecoder = new TextDecoder(); + } + doc = domParser.parseFromString( + utf8TextDecoder.decode(filterer.buffer.slice(0, 1024)), + 'text/html' + ); + filterer.charset = µb.textEncode.normalizeCharset(charsetFromDoc(doc)); + if ( filterer.charset === undefined ) { + streamClose(filterer); + return; + } + } + if ( - filterer.charset === undefined && textDecoderCharset !== 'utf-8' || - filterer.charset !== undefined && filterer.charset !== textDecoderCharset + textDecoder !== undefined && + textDecoder.encoding !== filterer.charset ) { textDecoder = undefined; } if ( textDecoder === undefined ) { - try { - textDecoder = new TextDecoder(filterer.charset); - textDecoderCharset = filterer.charset || 'utf-8'; - } catch(ex) { - textDecoder = new TextDecoder(); - textDecoderCharset = 'utf-8'; - } + textDecoder = new TextDecoder(filterer.charset); } - var doc = domParser.parseFromString( + doc = domParser.parseFromString( textDecoder.decode(filterer.buffer), 'text/html' ); - if ( filterer.charset === undefined ) { - filterer.charset = µb.textEncode.normalizeCharset(charsetFromDoc(doc)); - if ( filterer.charset === undefined ) { - streamClose(filterer); - return; - } - } - var modified = false; if ( filterer.selectors !== undefined ) { if ( µb.htmlFilteringEngine.apply(doc, filterer) ) { From 7684bfde53db24062a5e32ccfbb66d9e72f03a8c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jan 2018 13:24:53 -0500 Subject: [PATCH 062/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index c1b3ee92e1937..f525650867781 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.13", + "version": "1.14.23.14", "commands": { "launch-element-zapper": { From 7cd8185f3a01c25108d7f767a6eec854d230a130 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 5 Jan 2018 13:26:16 -0500 Subject: [PATCH 063/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 2 +- src/_locales/sl/messages.json | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 84ed339035d35..6bf2be0141b7a 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -136,7 +136,7 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "أضغط للسماح للخطوط الخارجية لهذا الموقع", + "message": "أضغط للوقف منع خطوط الطباعة الخارجية لهذا الموقع", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index 13d30f2baae57..e2f354f371b38 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -48,11 +48,11 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "Kliknite, da onemogočite uBlock₀ za to stran.\n\nPritisnite Ctrl in kliknite, da onemogočite uBlock₀ samo na tej strani.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Click to enable uBlock₀ for this site.", + "message": "Kliknite, da omogočite uBlock₀ za to stran.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -96,11 +96,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "Kliknite, da omogičite blokiranje vseh pojavnih oken za to stran", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "Kliknite, da onemogočite blokiranje vsah pojavnih oken na tej strani", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -108,11 +108,11 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "Kliknite, da omogičite blokiranje večjih medijev za to stran", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "Kliknite, da onemogočite blokiranje večjih medijev za to stran", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Click to disable cosmetic filtering on this site", + "message": "Kliknite, da onemogočite kozmetično filtriranje na tej strani", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Click to enable cosmetic filtering on this site", + "message": "Kliknite, da omogočite kozmetično filtriranje na tej strani", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -132,11 +132,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Click to block remote fonts on this site", + "message": "Kliknite, da omogočite blokiranje oddaljenih pisav na tej strani", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Click to no longer block remote fonts on this site", + "message": "Kliknite, da onemogočite blokiranje oddaljenih pisav na tej strani", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { @@ -164,7 +164,7 @@ "description": "" }, "popup3pAnyRulePrompt": { - "message": "tretje-osebne", + "message": "tretjih oseb", "description": "" }, "popup3pPassiveRulePrompt": { @@ -292,7 +292,7 @@ "description": "" }, "settingsNoCSPReportsPrompt": { - "message": "Block CSP reports", + "message": "Blokiraj CSP poročila", "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" }, "settingsStorageUsed": { @@ -364,7 +364,7 @@ "description": "English: Malware domains" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "\t\nNadlegovanje", "description": "The header identifying the filter lists in the category 'annoyances'" }, "3pGroupMultipurpose": { From 178b82eb6aa5e7cf7caf6bb891c9733718c7e330 Mon Sep 17 00:00:00 2001 From: jspenguin2017 Date: Fri, 5 Jan 2018 14:52:04 -0700 Subject: [PATCH 064/106] https://github.com/NanoAdblocker/NanoCore/issues/40 --- src/js/cloud-ui.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/js/cloud-ui.js b/src/js/cloud-ui.js index 45a1f73a0b7a2..d0a76bb7a6268 100644 --- a/src/js/cloud-ui.js +++ b/src/js/cloud-ui.js @@ -188,8 +188,6 @@ var onInitialize = function(options) { } self.cloud.options = options; - fetchCloudData(); - var xhr = new XMLHttpRequest(); xhr.open('GET', 'cloud-ui.html', true); xhr.overrideMimeType('text/html;charset=utf-8'); @@ -214,6 +212,10 @@ var onInitialize = function(options) { uDom('#cloudCog').on('click', openOptions); uDom('#cloudOptions').on('click', closeOptions); uDom('#cloudOptionsSubmit').on('click', submitOptions); + + // Patch 2018-01-05: Must not assume this XHR will always be faster + // than messaging + fetchCloudData(); }; xhr.send(); }; From 71009cf83d7b09ba67a0090861dd998956178e98 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Jan 2018 15:14:32 -0500 Subject: [PATCH 065/106] update minimum opera version (#3411) --- platform/opera/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/opera/manifest.json b/platform/opera/manifest.json index 316e49036f957..c8f8d8cf02389 100644 --- a/platform/opera/manifest.json +++ b/platform/opera/manifest.json @@ -44,7 +44,7 @@ }, "incognito": "split", "manifest_version": 2, - "minimum_opera_version": "25.0", + "minimum_opera_version": "32.0", "name": "uBlock Origin", "optional_permissions": [ "file:///*" ], "options_page": "dashboard.html", From b7155a0e0f0f4563a476af49aed44b3bed3ba7ba Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Jan 2018 17:53:33 -0500 Subject: [PATCH 066/106] fix #3408 --- platform/chromium/vapi-background.js | 14 ++++++++------ src/js/messaging.js | 4 ++-- src/js/popup.js | 7 ++++--- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 83bae3229e9f4..2c569f32d1e43 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2017 The uBlock Origin authors + Copyright (C) 2014-2018 The uBlock Origin authors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -579,11 +579,9 @@ vAPI.tabs.remove = function(tabId) { /******************************************************************************/ -vAPI.tabs.reload = function(tabId /*, flags*/) { +vAPI.tabs.reload = function(tabId, bypassCache) { tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - return; - } + if ( tabId === 0 ) { return; } var onReloaded = function() { // https://code.google.com/p/chromium/issues/detail?id=410868#c8 @@ -592,7 +590,11 @@ vAPI.tabs.reload = function(tabId /*, flags*/) { } }; - chrome.tabs.reload(tabId, onReloaded); + chrome.tabs.reload( + tabId, + { bypassCache: bypassCache === true }, + onReloaded + ); }; /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 5855baf9f41b0..34980672e806a 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2017 Raymond Hill + Copyright (C) 2014-2018 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -157,7 +157,7 @@ var onMessage = function(request, sender, callback) { case 'reloadTab': if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) { - vAPI.tabs.reload(request.tabId); + vAPI.tabs.reload(request.tabId, request.bypassCache === true); if ( request.select && vAPI.tabs.select ) { vAPI.tabs.select(request.tabId); } diff --git a/src/js/popup.js b/src/js/popup.js index 457a6c502b386..e1ee975341e88 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2017 Raymond Hill + Copyright (C) 2014-2018 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -831,13 +831,14 @@ var setFirewallRuleHandler = function(ev) { /******************************************************************************/ -var reloadTab = function() { +var reloadTab = function(ev) { messaging.send( 'popupPanel', { what: 'reloadTab', tabId: popupData.tabId, - select: true + select: true, + bypassCache: ev.ctrlKey || ev.metaKey } ); From 233afcdcac48d6aea7083ca492103338e282fc49 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Jan 2018 18:04:12 -0500 Subject: [PATCH 067/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index f525650867781..94e5638433c3f 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.14", + "version": "1.14.23.15", "commands": { "launch-element-zapper": { From 1e7e63de342cb196e12694eacb002b929aaa78ad Mon Sep 17 00:00:00 2001 From: PL_user <33114475+userOperaFF@users.noreply.github.com> Date: Mon, 8 Jan 2018 00:58:59 +0100 Subject: [PATCH 068/106] xpinstall.signatures.required support (#3404) Links when `xpinstall.signatures.required` does not work - propse change build Firefox to Developer / Nightly / unbranded . Possible unlock in stable version if find working code config.js + config-prefs.js for Firefox 57+ - https://www.ghacks.net/2016/08/14/override-firefox-add-on-signing-requirement/ --- dist/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dist/README.md b/dist/README.md index 6932c8e95f96c..77ec0f518991f 100644 --- a/dist/README.md +++ b/dist/README.md @@ -22,7 +22,7 @@ Remember that you have to update manually also. For some users, updating manuall ### Firefox webext -Compatible with Firefox 52 and beyond. This works only if you set `xpinstall.signatures.required` to `false` in `about:config`. +Compatible with Firefox 52 and beyond[*][*]. This works only if you set `xpinstall.signatures.required` to `false` in `about:config`. - Download `ublock0.webext.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). - Right-click and choose _"Save As..."_. @@ -32,6 +32,8 @@ On Linux, the settings are saved in a JSON file located at `~/.mozilla/firefox/[ When you uninstall the extension, Firefox deletes that file, so all your settings are lost when you uninstall. +\* - [Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?][*] + ### Firefox legacy Compatible with Firefox 24 to Firefox 56. @@ -39,7 +41,7 @@ Compatible with Firefox 24 to Firefox 56. - Download `ublock0.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). - Drag and drop the previously downloaded `ublock0.firefox.xpi` into Firefox -With Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`. +With Firefox 43 and beyond[*][*], you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`. Your uBlock Origin settings are kept intact even after you uninstall the addon. @@ -47,6 +49,10 @@ On Linux, the settings are saved in a SQlite file located at `~/.mozilla/firefox On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\ublock0.sqlite`. +\* - [Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?][*] + +[*]: https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox "Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?" + ### Build instructions (for developers) - Clone [uBlock](https://github.com/gorhill/uBlock) and [uAssets](https://github.com/uBlockOrigin/uAssets) repositories in the same parent directory From c3a5edeb52cbb0940c7fd5d8a4d56ae94d95f1c3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Jan 2018 19:04:49 -0500 Subject: [PATCH 069/106] Update README.md --- dist/README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dist/README.md b/dist/README.md index 77ec0f518991f..1a0686cded0b2 100644 --- a/dist/README.md +++ b/dist/README.md @@ -22,7 +22,7 @@ Remember that you have to update manually also. For some users, updating manuall ### Firefox webext -Compatible with Firefox 52 and beyond[*][*]. This works only if you set `xpinstall.signatures.required` to `false` in `about:config`. +Compatible with Firefox 52 and beyond. This works only if you set `xpinstall.signatures.required` to `false` in `about:config`.[see note 1 at the bottom] - Download `ublock0.webext.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). - Right-click and choose _"Save As..."_. @@ -32,8 +32,6 @@ On Linux, the settings are saved in a JSON file located at `~/.mozilla/firefox/[ When you uninstall the extension, Firefox deletes that file, so all your settings are lost when you uninstall. -\* - [Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?][*] - ### Firefox legacy Compatible with Firefox 24 to Firefox 56. @@ -41,7 +39,7 @@ Compatible with Firefox 24 to Firefox 56. - Download `ublock0.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). - Drag and drop the previously downloaded `ublock0.firefox.xpi` into Firefox -With Firefox 43 and beyond[*][*], you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`. +With Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`.[see note 1 at the bottom] Your uBlock Origin settings are kept intact even after you uninstall the addon. @@ -49,10 +47,6 @@ On Linux, the settings are saved in a SQlite file located at `~/.mozilla/firefox On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\ublock0.sqlite`. -\* - [Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?][*] - -[*]: https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox "Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?" - ### Build instructions (for developers) - Clone [uBlock](https://github.com/gorhill/uBlock) and [uAssets](https://github.com/uBlockOrigin/uAssets) repositories in the same parent directory @@ -66,3 +60,6 @@ On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozill - Chromium: load the unpacked extension folder `/uBlock/dist/build/uBlock0.chromium/` in Chromium to use the extension. - Firefox: drag-and-drop `/uBlock/dist/build/uBlock0.firefox.xpi` or `/uBlock/dist/build/uBlock0.webext.xpi` into Firefox. +*** + +[1] [Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) From 99aec0e6d449335e47dc12825997ef1de3418fd6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Jan 2018 19:07:12 -0500 Subject: [PATCH 070/106] Update README.md --- dist/README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/dist/README.md b/dist/README.md index 1a0686cded0b2..49acc042a3b43 100644 --- a/dist/README.md +++ b/dist/README.md @@ -22,7 +22,7 @@ Remember that you have to update manually also. For some users, updating manuall ### Firefox webext -Compatible with Firefox 52 and beyond. This works only if you set `xpinstall.signatures.required` to `false` in `about:config`.[see note 1 at the bottom] +Compatible with Firefox 52 and beyond. This works only if you set `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) - Download `ublock0.webext.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). - Right-click and choose _"Save As..."_. @@ -39,7 +39,7 @@ Compatible with Firefox 24 to Firefox 56. - Download `ublock0.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)). - Drag and drop the previously downloaded `ublock0.firefox.xpi` into Firefox -With Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`.[see note 1 at the bottom] +With Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`.[see "Add-on signing in Firefox"](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) Your uBlock Origin settings are kept intact even after you uninstall the addon. @@ -60,6 +60,3 @@ On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozill - Chromium: load the unpacked extension folder `/uBlock/dist/build/uBlock0.chromium/` in Chromium to use the extension. - Firefox: drag-and-drop `/uBlock/dist/build/uBlock0.firefox.xpi` or `/uBlock/dist/build/uBlock0.webext.xpi` into Firefox. -*** - -[1] [Add-on signing in Firefox - What are my options if I want to use an unsigned add-on?](https://support.mozilla.org/en-US/kb/add-on-signing-in-firefox) From f53b3e39306a5e050e730f0aacca10aa05e74231 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 8 Jan 2018 07:33:38 -0500 Subject: [PATCH 071/106] fix #3416 --- src/js/popup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/popup.js b/src/js/popup.js index e1ee975341e88..dfd38d2d004ea 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -838,7 +838,7 @@ var reloadTab = function(ev) { what: 'reloadTab', tabId: popupData.tabId, select: true, - bypassCache: ev.ctrlKey || ev.metaKey + bypassCache: ev.ctrlKey || ev.metaKey || ev.shiftKey } ); From 689d18c54f65c2df29f412fad960473fee6dc888 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 8 Jan 2018 14:29:39 -0500 Subject: [PATCH 072/106] fix #2854 --- platform/chromium/vapi-background.js | 10 +++--- platform/webext/manifest.json | 8 +++++ src/css/logger-ui.css | 10 +++--- src/js/logger-ui-inspector.js | 7 +++- src/js/logger-ui.js | 49 ++++++++++++++++++++++++++-- src/js/logger.js | 11 +++++-- src/js/messaging.js | 33 +++++++++++-------- 7 files changed, 99 insertions(+), 29 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 2c569f32d1e43..b7f9a2b1c816e 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -779,8 +779,7 @@ vAPI.messaging.onPortMessage = (function() { var toFramework = function(request, port, callback) { var sender = port && port.sender; if ( !sender ) { return; } - var tabId = sender.tab && sender.tab.id; - if ( !tabId ) { return; } + var tabId = sender.tab && sender.tab.id || undefined; var msg = request.msg, toPort; switch ( msg.what ) { @@ -788,7 +787,7 @@ vAPI.messaging.onPortMessage = (function() { case 'connectionRefused': toPort = messaging.ports.get(msg.fromToken); if ( toPort !== undefined ) { - msg.tabId = tabId.toString(); + msg.tabId = tabId && tabId.toString(); toPort.postMessage(request); } else { msg.what = 'connectionBroken'; @@ -796,7 +795,7 @@ vAPI.messaging.onPortMessage = (function() { } break; case 'connectionRequested': - msg.tabId = '' + tabId.toString(); + msg.tabId = tabId && tabId.toString(); for ( toPort of messaging.ports.values() ) { toPort.postMessage(request); } @@ -808,7 +807,7 @@ vAPI.messaging.onPortMessage = (function() { port.name === msg.fromToken ? msg.toToken : msg.fromToken ); if ( toPort !== undefined ) { - msg.tabId = tabId.toString(); + msg.tabId = tabId && tabId.toString(); toPort.postMessage(request); } else { msg.what = 'connectionBroken'; @@ -816,6 +815,7 @@ vAPI.messaging.onPortMessage = (function() { } break; case 'userCSS': + if ( tabId === undefined ) { break; } var details = { code: undefined, frameId: sender.frameId, diff --git a/platform/webext/manifest.json b/platform/webext/manifest.json index 70e97de5f9bd6..679ddbb1e8b4e 100644 --- a/platform/webext/manifest.json +++ b/platform/webext/manifest.json @@ -80,5 +80,13 @@ "" ], "short_name":"uBlock₀", + "sidebar_action":{ + "default_title":"__MSG_statsPageName__", + "default_panel":"logger-ui.html", + "default_icon":{ + "16":"img/ublock.svg", + "48":"img/ublock.svg" + } + }, "version":"1.9.15.101" } diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index a7c5252586ed6..f68c05055ebef 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -128,19 +128,19 @@ textarea { width: 4.6em; } #netInspector table > colgroup > col:nth-of-type(2) { - width: 2.2em; + width: 2.1em; } #netInspector table > colgroup > col:nth-of-type(3) { width: 20%; } #netInspector table > colgroup > col:nth-of-type(4) { - width: 2.2em; + width: 2.1em; } #netInspector table > colgroup > col:nth-of-type(5) { - width: 6em; + width: 5.8em; } #netInspector table > colgroup > col:nth-of-type(6) { - width: calc(100% - 15em - 20%); + width: calc(100% - 14.6em - 20%); } #netInspector.f table tr.f { display: none; @@ -200,7 +200,7 @@ body #netInspector td { #netInspector tr td:last-of-type { border-right: none; } -#netInspector.vCompact td { +#netInspector.vCompact tr:not(.vExpanded) td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/src/js/logger-ui-inspector.js b/src/js/logger-ui-inspector.js index f598ec2db293b..a1471eb3a2f23 100644 --- a/src/js/logger-ui-inspector.js +++ b/src/js/logger-ui-inspector.js @@ -79,7 +79,12 @@ messaging.addChannelListener('loggerUI', function(msg) { break; case 'connectionRequested': if ( msg.from !== 'domInspector' ) { return false; } - if ( msg.tabId !== inspectedTabId ) { return false; } + if ( + msg.tabId === undefined || + msg.tabId !== inspectedTabId + ) { + return false; + } filterToIdMap.clear(); logger.removeAllChildren(domTree); inspectorConnectionId = msg.id; diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 218f44fb31bb3..7a0a2a2af011b 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -31,6 +31,7 @@ var logger = self.logger = {}; var messaging = vAPI.messaging; +var ownerId = Date.now(); /******************************************************************************/ @@ -486,6 +487,11 @@ var truncateLog = function(size) { /******************************************************************************/ var onLogBufferRead = function(response) { + if ( !response || response.unavailable ) { + readLogBufferAsync(); + return; + } + // This tells us the behind-the-scene tab id noTabId = response.noTabId; @@ -521,7 +527,7 @@ var onLogBufferRead = function(response) { tbody.querySelector('tr') === null ); - vAPI.setTimeout(readLogBuffer, 1200); + readLogBufferAsync(); }; /******************************************************************************/ @@ -531,9 +537,19 @@ var onLogBufferRead = function(response) { // require a bit more code to ensure no multi time out events. var readLogBuffer = function() { - messaging.send('loggerUI', { what: 'readAll' }, onLogBufferRead); + if ( ownerId === undefined ) { return; } + vAPI.messaging.send( + 'loggerUI', + { what: 'readAll', ownerId: ownerId }, + onLogBufferRead + ); }; +var readLogBufferAsync = function() { + if ( ownerId === undefined ) { return; } + vAPI.setTimeout(readLogBuffer, 1200); +}; + /******************************************************************************/ var pageSelectorChanged = function() { @@ -1511,6 +1527,11 @@ var cleanBuffer = function() { var toggleVCompactView = function() { uDom.nodeFromId('netInspector').classList.toggle('vCompact'); + uDom('#netInspector .vExpanded').toggleClass('vExpanded'); +}; + +var toggleVCompactRow = function(ev) { + ev.target.parentElement.classList.toggle('vExpanded'); }; /******************************************************************************/ @@ -1634,6 +1655,29 @@ var popupManager = (function() { /******************************************************************************/ +var grabView = function() { + if ( ownerId === undefined ) { + ownerId = Date.now(); + } + readLogBufferAsync(); +}; + +var releaseView = function() { + if ( ownerId === undefined ) { return; } + vAPI.messaging.send( + 'loggerUI', + { what: 'releaseView', ownerId: ownerId } + ); + ownerId = undefined; +}; + +window.addEventListener('pagehide', releaseView); +window.addEventListener('pageshow', grabView); +// https://bugzilla.mozilla.org/show_bug.cgi?id=1398625 +window.addEventListener('beforeunload', releaseView); + +/******************************************************************************/ + readLogBuffer(); uDom('#pageSelector').on('change', pageSelectorChanged); @@ -1644,6 +1688,7 @@ uDom('#netInspector .vCompactToggler').on('click', toggleVCompactView); uDom('#clean').on('click', cleanBuffer); uDom('#clear').on('click', clearBuffer); uDom('#maxEntries').on('change', onMaxEntriesChanged); +uDom('#netInspector table').on('click', 'tr > td:nth-of-type(1)', toggleVCompactRow); uDom('#netInspector table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn); uDom('#netInspector').on('click', 'tr.canLookup > td:nth-of-type(3)', reverseLookupManager.toggleOn); uDom('#netInspector').on('click', 'tr.cat_net > td:nth-of-type(4)', netFilteringManager.toggleOn); diff --git a/src/js/logger.js b/src/js/logger.js index 37da162a75d33..f4e33f5c84c2c 100644 --- a/src/js/logger.js +++ b/src/js/logger.js @@ -47,7 +47,7 @@ // After 60 seconds without being read, a buffer will be considered // unused, and thus removed from memory. - var logBufferObsoleteAfter = 60 * 1000; + var logBufferObsoleteAfter = 30 * 1000; var janitor = function() { if ( @@ -56,6 +56,7 @@ ) { buffer = null; writePtr = 0; + api.ownerId = undefined; vAPI.messaging.broadcast({ what: 'loggerDisabled' }); } if ( buffer !== null ) { @@ -63,7 +64,8 @@ } }; - return { + var api = { + ownerId: undefined, writeOne: function() { if ( buffer === null ) { return; } if ( writePtr === buffer.length ) { @@ -73,7 +75,8 @@ } writePtr += 1; }, - readAll: function() { + readAll: function(ownerId) { + this.ownerId = ownerId; if ( buffer === null ) { buffer = []; vAPI.setTimeout(janitor, logBufferObsoleteAfter); @@ -88,6 +91,8 @@ } }; + return api; + })(); /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 34980672e806a..ced2781e6594a 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1010,7 +1010,8 @@ vAPI.messaging.listen('dashboard', onMessage); /******************************************************************************/ -var µb = µBlock; +var µb = µBlock, + extensionPageURL = vAPI.getURL(''); /******************************************************************************/ @@ -1057,21 +1058,23 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'readAll': - var tabIds = {}, pageStore; - var loggerURL = vAPI.getURL('logger-ui.html'); + if ( + µb.logger.ownerId !== undefined && + µb.logger.ownerId !== request.ownerId + ) { + response = { unavailable: true }; + break; + } + var tabIds = {}; for ( var tabId in µb.pageStores ) { - pageStore = µb.pageStoreFromTabId(tabId); - if ( pageStore === null ) { - continue; - } - if ( pageStore.rawURL.startsWith(loggerURL) ) { - continue; - } + var pageStore = µb.pageStoreFromTabId(tabId); + if ( pageStore === null ) { continue; } + if ( pageStore.rawURL.startsWith(extensionPageURL) ) { continue; } tabIds[tabId] = pageStore.title; } response = { colorBlind: µb.userSettings.colorBlindFriendly, - entries: µb.logger.readAll(), + entries: µb.logger.readAll(request.ownerId), maxEntries: µb.userSettings.requestLogMaxEntries, noTabId: vAPI.noTabId, tabIds: tabIds, @@ -1079,6 +1082,12 @@ var onMessage = function(request, sender, callback) { }; break; + case 'releaseView': + if ( request.ownerId === µb.logger.ownerId ) { + µb.logger.ownerId = undefined; + } + break; + case 'saveURLFilteringRules': response = µb.permanentURLFiltering.copyRules( µb.sessionURLFiltering, @@ -1112,8 +1121,6 @@ vAPI.messaging.listen('loggerUI', onMessage); })(); -// https://www.youtube.com/watch?v=3_WcygKJP1k - /******************************************************************************/ /******************************************************************************/ From 225bab9550a7ac3b13008ae73c0dc3b21252511b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 8 Jan 2018 14:33:25 -0500 Subject: [PATCH 073/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 94e5638433c3f..85c7a98424ae0 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.15", + "version": "1.14.23.16", "commands": { "launch-element-zapper": { From 098f3baadd500d2f4f33ac042d15083a320c0721 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 8 Jan 2018 15:03:55 -0500 Subject: [PATCH 074/106] code review: fix regex used to report cosmetic filters in logger --- platform/webext/vapi-usercss.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/webext/vapi-usercss.js b/platform/webext/vapi-usercss.js index 10e7d371e4b56..9db9bce1b542a 100644 --- a/platform/webext/vapi-usercss.js +++ b/platform/webext/vapi-usercss.js @@ -210,7 +210,7 @@ vAPI.DOMFilterer.prototype = { if ( all !== true && this.hideNodeAttr !== undefined ) { selectors = selectors .replace('[' + this.hideNodeAttr + ']', '') - .replace(/^,\n|^\n/, ''); + .replace(/^,\n|,\n$/gm, ''); if ( selectors === '' ) { continue; } } out.declarative.push([ selectors, entry.declarations ]); From 6cdb20dffea3425b721a4882d67f9370175767d7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jan 2018 08:08:17 -0500 Subject: [PATCH 075/106] improve logger convenience following fix for #2854 --- platform/chromium/vapi-background.js | 43 +++++----- src/_locales/en/messages.json | 4 + src/css/logger-ui.css | 1 + src/js/logger-ui-inspector.js | 9 +- src/js/logger-ui.js | 120 +++++++++++++++------------ src/js/messaging.js | 63 ++++++++------ src/logger-ui.html | 1 + 7 files changed, 138 insertions(+), 103 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index b7f9a2b1c816e..b32e8af9a62d6 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -384,32 +384,33 @@ vAPI.tabs.registerListeners = function() { /******************************************************************************/ +// Caller must be prepared to deal with nil tab argument. + +// https://code.google.com/p/chromium/issues/detail?id=410868#c8 + vAPI.tabs.get = function(tabId, callback) { - var onTabReady = function(tab) { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - if ( chrome.runtime.lastError ) { - /* noop */ - } - // Caller must be prepared to deal with nil tab value - callback(tab); - }; + if ( tabId === null ) { + chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { + if ( chrome.runtime.lastError ) { /* noop */ } + var tab = tabs && tabs[0]; + if ( tab ) { + tab.id = tab.id.toString(); + } + callback(tab); + }); + return; + } - if ( tabId !== null ) { - tabId = toChromiumTabId(tabId); - if ( tabId === 0 ) { - onTabReady(null); - } else { - chrome.tabs.get(tabId, onTabReady); - } + tabId = toChromiumTabId(tabId); + if ( tabId === 0 ) { + callback(null); return; } - var onTabReceived = function(tabs) { - // https://code.google.com/p/chromium/issues/detail?id=410868#c8 - void chrome.runtime.lastError; - callback(tabs[0]); - }; - chrome.tabs.query({ active: true, currentWindow: true }, onTabReceived); + chrome.tabs.get(tabId, function(tab) { + if ( chrome.runtime.lastError ) { /* noop */ } + callback(tab); + }); }; /******************************************************************************/ diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 0d02224110534..c0716de42ed35 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -511,6 +511,10 @@ "message":"Behind the scene", "description":"Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab":{ + "message":"Current tab", + "description":"Appears in the logger's tab selector" + }, "logFilterPrompt":{ "message":"filter log entries", "description": "English: filter log entries" diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index f68c05055ebef..5a90028afd8b0 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -207,6 +207,7 @@ body #netInspector td { } #netInspector tr td:nth-of-type(1) { + cursor: default; text-align: right; white-space: nowrap; } diff --git a/src/js/logger-ui-inspector.js b/src/js/logger-ui-inspector.js index a1471eb3a2f23..2cceec1f33b18 100644 --- a/src/js/logger-ui-inspector.js +++ b/src/js/logger-ui-inspector.js @@ -50,7 +50,6 @@ var inspectedURL = ''; var inspectedHostname = ''; var inspector = uDom.nodeFromId('domInspector'); var domTree = uDom.nodeFromId('domTree'); -var tabSelector = uDom.nodeFromId('pageSelector'); var uidGenerator = 1; var filterToIdMap = new Map(); @@ -562,7 +561,7 @@ var onMouseOver = (function() { var currentTabId = function() { if ( showdomButton.classList.contains('active') === false ) { return ''; } - var tabId = logger.tabIdFromClassName(tabSelector.value) || ''; + var tabId = logger.tabIdFromPageSelector(); return tabId !== 'bts' ? tabId : ''; }; @@ -636,7 +635,7 @@ var revert = function() { var toggleOn = function() { window.addEventListener('beforeunload', toggleOff); - tabSelector.addEventListener('change', onTabIdChanged); + document.addEventListener('tabIdChanged', onTabIdChanged); domTree.addEventListener('click', onClicked, true); domTree.addEventListener('mouseover', onMouseOver, true); uDom.nodeFromSelector('#domInspector .vCompactToggler').addEventListener('click', toggleVCompactView); @@ -652,7 +651,7 @@ var toggleOn = function() { var toggleOff = function() { shutdownInspector(); window.removeEventListener('beforeunload', toggleOff); - tabSelector.removeEventListener('change', onTabIdChanged); + document.removeEventListener('tabIdChanged', onTabIdChanged); domTree.removeEventListener('click', onClicked, true); domTree.removeEventListener('mouseover', onMouseOver, true); uDom.nodeFromSelector('#domInspector .vCompactToggler').removeEventListener('click', toggleVCompactView); @@ -680,5 +679,3 @@ showdomButton.addEventListener('click', toggle); /******************************************************************************/ })(); - - diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 7a0a2a2af011b..b3cad3b5175fd 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -29,9 +29,12 @@ /******************************************************************************/ -var logger = self.logger = {}; +var logger = self.logger = { + ownerId: Date.now() +}; + var messaging = vAPI.messaging; -var ownerId = Date.now(); +var activeTabId; /******************************************************************************/ @@ -44,11 +47,16 @@ var removeAllChildren = logger.removeAllChildren = function(node) { /******************************************************************************/ var tabIdFromClassName = logger.tabIdFromClassName = function(className) { - var matches = className.match(/(?:^| )tab_([^ ]+)(?: |$)/); - if ( matches === null ) { - return ''; - } - return matches[1]; + var matches = className.match(/\btab_([^ ]+)\b/); + return matches !== null ? matches[1] : ''; +}; + +var tabIdFromPageSelector = logger.tabIdFromPageSelector = function() { + var tabClass = uDom.nodeFromId('pageSelector').value; + if ( tabClass === '' ) { return ''; } + if ( tabClass === 'tab_bts' ) { return noTabId; } + if ( tabClass === 'tab_active' ) { return activeTabId; } + return tabClass.slice(4); }; /******************************************************************************/ @@ -408,15 +416,17 @@ var renderLogEntries = function(response) { /******************************************************************************/ var synchronizeTabIds = function(newTabIds) { + var select = uDom.nodeFromId('pageSelector'); + var selectValue = select.value; + var oldTabIds = allTabIds; - var autoDeleteVoidRows = !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows'); + var autoDeleteVoidRows = selectValue === 'tab_active'; var rowVoided = false; - var trs; for ( var tabId in oldTabIds ) { if ( oldTabIds.hasOwnProperty(tabId) === false ) { continue; } if ( newTabIds.hasOwnProperty(tabId) ) { continue; } // Mark or remove voided rows - trs = uDom('.tab_' + tabId); + var trs = uDom('.tab_' + tabId); if ( autoDeleteVoidRows ) { toJunkyard(trs); } else { @@ -429,13 +439,11 @@ var synchronizeTabIds = function(newTabIds) { } } - var select = uDom.nodeFromId('pageSelector'); - var selectValue = select.value; var tabIds = Object.keys(newTabIds).sort(function(a, b) { return newTabIds[a].localeCompare(newTabIds[b]); }); var option; - for ( var i = 0, j = 2; i < tabIds.length; i++ ) { + for ( var i = 0, j = 3; i < tabIds.length; i++ ) { tabId = tabIds[i]; if ( tabId === noTabId ) { continue; } option = select.options[j]; @@ -495,6 +503,11 @@ var onLogBufferRead = function(response) { // This tells us the behind-the-scene tab id noTabId = response.noTabId; + // Tab id of currently active tab + if ( response.activeTabId ) { + activeTabId = response.activeTabId; + } + // This may have changed meanwhile if ( response.maxEntries !== maxEntries ) { maxEntries = response.maxEntries; @@ -537,62 +550,70 @@ var onLogBufferRead = function(response) { // require a bit more code to ensure no multi time out events. var readLogBuffer = function() { - if ( ownerId === undefined ) { return; } + if ( logger.ownerId === undefined ) { return; } vAPI.messaging.send( 'loggerUI', - { what: 'readAll', ownerId: ownerId }, + { what: 'readAll', ownerId: logger.ownerId }, onLogBufferRead ); }; var readLogBufferAsync = function() { - if ( ownerId === undefined ) { return; } + if ( logger.ownerId === undefined ) { return; } vAPI.setTimeout(readLogBuffer, 1200); }; /******************************************************************************/ var pageSelectorChanged = function() { - window.location.replace('#' + uDom.nodeFromId('pageSelector').value); + var select = uDom.nodeFromId('pageSelector'); + window.location.replace('#' + select.value); pageSelectorFromURLHash(); }; -/******************************************************************************/ - var pageSelectorFromURLHash = (function() { - var lastHash = ''; + var lastTabClass = ''; + var lastEffectiveTabClass = ''; - return function() { - var hash = window.location.hash; - if ( hash === lastHash ) { - return; + var selectRows = function(tabClass) { + if ( tabClass === 'tab_active' ) { + if ( activeTabId === undefined ) { return; } + tabClass = 'tab_' + activeTabId; + } + if ( tabClass === lastEffectiveTabClass ) { return; } + lastEffectiveTabClass = tabClass; + + document.dispatchEvent(new Event('tabIdChanged')); + + var style = uDom.nodeFromId('tabFilterer'); + var sheet = style.sheet; + while ( sheet.cssRules.length !== 0 ) { + sheet.deleteRule(0); } + if ( tabClass === '' ) { return; } + sheet.insertRule( + '#netInspector tr:not(.' + tabClass + '):not(.tab_bts) ' + + '{display:none;}' + ); + }; + + return function() { + var tabClass = window.location.hash.slice(1); + selectRows(tabClass); + if ( tabClass === lastTabClass ) { return; } + lastTabClass = tabClass; - var tabClass = hash.slice(1); var select = uDom.nodeFromId('pageSelector'); var option = select.querySelector('option[value="' + tabClass + '"]'); if ( option === null ) { - hash = window.location.hash = ''; + window.location.hash = ''; tabClass = ''; option = select.options[0]; } - lastHash = hash; - select.selectedIndex = option.index; select.value = option.value; - var style = uDom.nodeFromId('tabFilterer'); - var sheet = style.sheet; - while ( sheet.cssRules.length !== 0 ) { - sheet.deleteRule(0); - } - if ( tabClass !== '' ) { - sheet.insertRule( - '#netInspector tr:not(.' + tabClass + ') { display: none; }', - 0 - ); - } uDom('.needtab').toggleClass( 'disabled', tabClass === '' || tabClass === 'tab_bts' @@ -603,11 +624,8 @@ var pageSelectorFromURLHash = (function() { /******************************************************************************/ var reloadTab = function() { - var tabClass = uDom.nodeFromId('pageSelector').value; - var tabId = tabIdFromClassName(tabClass); - if ( tabId === 'bts' || tabId === '' ) { - return; - } + var tabId = tabIdFromPageSelector(); + if ( tabId === '' || tabId === noTabId ) { return; } messaging.send('loggerUI', { what: 'reloadTab', tabId: tabId }); }; @@ -1491,13 +1509,13 @@ var toJunkyard = function(trs) { /******************************************************************************/ var clearBuffer = function() { - var tabId = uDom.nodeFromId('pageSelector').value || null; + var tabClass = uDom.nodeFromId('pageSelector').value || ''; var tbody = document.querySelector('#netInspector tbody'); var tr = tbody.lastElementChild; var trPrevious; while ( tr !== null ) { trPrevious = tr.previousElementSibling; - if ( tabId === null || tr.classList.contains(tabId) ) { + if ( tabClass === '' || tr.classList.contains(tabClass) ) { trJunkyard.push(tbody.removeChild(tr)); } tr = trPrevious; @@ -1656,19 +1674,19 @@ var popupManager = (function() { /******************************************************************************/ var grabView = function() { - if ( ownerId === undefined ) { - ownerId = Date.now(); + if ( logger.ownerId === undefined ) { + logger.ownerId = Date.now(); } readLogBufferAsync(); }; var releaseView = function() { - if ( ownerId === undefined ) { return; } + if ( logger.ownerId === undefined ) { return; } vAPI.messaging.send( 'loggerUI', - { what: 'releaseView', ownerId: ownerId } + { what: 'releaseView', ownerId: logger.ownerId } ); - ownerId = undefined; + logger.ownerId = undefined; }; window.addEventListener('pagehide', releaseView); diff --git a/src/js/messaging.js b/src/js/messaging.js index ced2781e6594a..73801ffc987bd 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1015,6 +1015,31 @@ var µb = µBlock, /******************************************************************************/ +var getLoggerData = function(ownerId, tab, callback) { + var tabIds = {}; + for ( var tabId in µb.pageStores ) { + var pageStore = µb.pageStoreFromTabId(tabId); + if ( pageStore === null ) { continue; } + if ( pageStore.rawURL.startsWith(extensionPageURL) ) { continue; } + tabIds[tabId] = pageStore.title; + } + var activeTabId; + if ( tabIds.hasOwnProperty(tab.id) ) { + activeTabId = tab.id; + } + callback({ + colorBlind: µb.userSettings.colorBlindFriendly, + entries: µb.logger.readAll(ownerId), + maxEntries: µb.userSettings.requestLogMaxEntries, + noTabId: vAPI.noTabId, + activeTabId: activeTabId, + tabIds: tabIds, + tabIdsToken: µb.pageStoresToken + }); +}; + +/******************************************************************************/ + var getURLFilteringData = function(details) { var colors = {}; var response = { @@ -1048,40 +1073,28 @@ var getURLFilteringData = function(details) { var onMessage = function(request, sender, callback) { // Async - switch ( request.what ) { - default: - break; - } - - // Sync - var response; - switch ( request.what ) { case 'readAll': if ( µb.logger.ownerId !== undefined && µb.logger.ownerId !== request.ownerId ) { - response = { unavailable: true }; - break; + callback({ unavailable: true }); + return; } - var tabIds = {}; - for ( var tabId in µb.pageStores ) { - var pageStore = µb.pageStoreFromTabId(tabId); - if ( pageStore === null ) { continue; } - if ( pageStore.rawURL.startsWith(extensionPageURL) ) { continue; } - tabIds[tabId] = pageStore.title; - } - response = { - colorBlind: µb.userSettings.colorBlindFriendly, - entries: µb.logger.readAll(request.ownerId), - maxEntries: µb.userSettings.requestLogMaxEntries, - noTabId: vAPI.noTabId, - tabIds: tabIds, - tabIdsToken: µb.pageStoresToken - }; + vAPI.tabs.get(null, function(tab) { + getLoggerData(request.ownerId, tab, callback); + }); + return; + + default: break; + } + // Sync + var response; + + switch ( request.what ) { case 'releaseView': if ( request.ownerId === µb.logger.ownerId ) { µb.logger.ownerId = undefined; diff --git a/src/logger-ui.html b/src/logger-ui.html index 9539f44a90e79..804a1363faeaa 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -15,6 +15,7 @@ From 4ed8184d2ccb2b15131012cfccc14920233f7775 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jan 2018 09:40:14 -0500 Subject: [PATCH 076/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 4 ++ src/_locales/bg/messages.json | 4 ++ src/_locales/bn/messages.json | 4 ++ src/_locales/ca/messages.json | 4 ++ src/_locales/cs/messages.json | 4 ++ src/_locales/cv/messages.json | 4 ++ src/_locales/da/messages.json | 4 ++ src/_locales/de/messages.json | 4 ++ src/_locales/el/messages.json | 4 ++ src/_locales/eo/messages.json | 4 ++ src/_locales/es/messages.json | 4 ++ src/_locales/et/messages.json | 4 ++ src/_locales/eu/messages.json | 4 ++ src/_locales/fa/messages.json | 78 +++++++++++++++++--------------- src/_locales/fi/messages.json | 4 ++ src/_locales/fil/messages.json | 4 ++ src/_locales/fr/messages.json | 4 ++ src/_locales/fy/messages.json | 4 ++ src/_locales/gl/messages.json | 4 ++ src/_locales/he/messages.json | 4 ++ src/_locales/hi/messages.json | 4 ++ src/_locales/hr/messages.json | 4 ++ src/_locales/hu/messages.json | 4 ++ src/_locales/id/messages.json | 4 ++ src/_locales/it/messages.json | 4 ++ src/_locales/ja/messages.json | 6 ++- src/_locales/ka/messages.json | 4 ++ src/_locales/kn/messages.json | 4 ++ src/_locales/ko/messages.json | 6 ++- src/_locales/lt/messages.json | 4 ++ src/_locales/lv/messages.json | 4 ++ src/_locales/ml/messages.json | 4 ++ src/_locales/mr/messages.json | 4 ++ src/_locales/ms/messages.json | 4 ++ src/_locales/nb/messages.json | 4 ++ src/_locales/nl/messages.json | 4 ++ src/_locales/pl/messages.json | 4 ++ src/_locales/pt_BR/messages.json | 4 ++ src/_locales/pt_PT/messages.json | 4 ++ src/_locales/ro/messages.json | 4 ++ src/_locales/ru/messages.json | 4 ++ src/_locales/sk/messages.json | 4 ++ src/_locales/sl/messages.json | 4 ++ src/_locales/sq/messages.json | 4 ++ src/_locales/sr/messages.json | 4 ++ src/_locales/sv/messages.json | 4 ++ src/_locales/ta/messages.json | 4 ++ src/_locales/te/messages.json | 4 ++ src/_locales/tr/messages.json | 4 ++ src/_locales/uk/messages.json | 4 ++ src/_locales/vi/messages.json | 4 ++ src/_locales/zh_CN/messages.json | 4 ++ src/_locales/zh_TW/messages.json | 4 ++ 53 files changed, 251 insertions(+), 39 deletions(-) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 6bf2be0141b7a..bf045e0a5518e 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -511,6 +511,10 @@ "message": "خلف الكواليس", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "فلتر سجل الإدخالات", "description": "English: filter log entries" diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index efdce303c4fb4..b17e2dc356a2c 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -511,6 +511,10 @@ "message": "Скрити", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "филтриране на записи", "description": "English: filter log entries" diff --git a/src/_locales/bn/messages.json b/src/_locales/bn/messages.json index c4241726d980b..f2f97c13088f4 100644 --- a/src/_locales/bn/messages.json +++ b/src/_locales/bn/messages.json @@ -511,6 +511,10 @@ "message": "পর্দার আড়ালে", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "ফিল্টার লগের ভুক্তি", "description": "English: filter log entries" diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 1e74724165ab2..a92f68e80a249 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -511,6 +511,10 @@ "message": "Peticions ocultes", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Pestanya actual", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrar entrades del registre", "description": "English: filter log entries" diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index f3b7fc81fb639..c3f91e3c06de2 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -511,6 +511,10 @@ "message": "Za oponou", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrovat záznamy", "description": "English: filter log entries" diff --git a/src/_locales/cv/messages.json b/src/_locales/cv/messages.json index 002c2cf52100c..1c37d33b5b20f 100644 --- a/src/_locales/cv/messages.json +++ b/src/_locales/cv/messages.json @@ -511,6 +511,10 @@ "message": "Behind the scene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filter log entries", "description": "English: filter log entries" diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 1f0dafdb4eb44..e0921ec056944 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -511,6 +511,10 @@ "message": "Bag kulisserne", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "Filtrér elementer i log", "description": "English: filter log entries" diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 75a975d2f4c7a..c0528c9173c59 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -511,6 +511,10 @@ "message": "Hintergrundanfragen", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "Protokoll-Einträge filtern", "description": "English: filter log entries" diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index d43b84a73415f..6fb66ac467174 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -511,6 +511,10 @@ "message": "Παρασκήνιο", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "φιλτράρισμα των καταχωρήσεων", "description": "English: filter log entries" diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index f6ed3e96f83b1..e88d8007568fe 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -511,6 +511,10 @@ "message": "Fonaj petoj", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtri protokolerojn", "description": "English: filter log entries" diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index cb47c4b825dfe..661157b011e45 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -511,6 +511,10 @@ "message": "Peticiones ocultas", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Pestaña actual", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrar entradas del registro", "description": "English: filter log entries" diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 9de2b2462ebb8..8bbd1eb9e3395 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -511,6 +511,10 @@ "message": "Telgitagus", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtreeri logikirjeid", "description": "English: filter log entries" diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index 4c204b278c76d..b3d508ef101bc 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -511,6 +511,10 @@ "message": "Atzeko planoan", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "iragazi egunkariko sarrerak", "description": "English: filter log entries" diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index 0b11de1688da6..342f62e0d597a 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -48,11 +48,11 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "کلیک کنید تا uBlock₀ برای این سایت غیر فعال شود.\n\nهمزمان کلیدکنترل را نگهدارید و کلیک کنید تا uBlock₀ فقط برای این صفحه غیر فعال شود.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Click to enable uBlock₀ for this site.", + "message": "کلیک کنید تا uBlock₀ برای این سایت فعال شود.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -92,15 +92,15 @@ "description": "Tooltip used for the logger icon in the panel" }, "popupTipNoPopups": { - "message": "تغییر وضعیت انسداد همه ی پنجره های این سایت", + "message": "تغییر وضعیت انسداد همه‌ی پنجره های بالاپَر برای این سایت", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "کلیک کنید تا همۀ پنجره های بالاپَر در این سایت مسدود شوند", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "کلیک کنید تا دیگر هیچ کدام از پنجره های بالاپَر این سایت مسدود نشوند", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -108,23 +108,23 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "کلیک کنید تا عناصر رسانه ای حجیم بر روی این سایت مسدود شوند", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "کلیک کنید تا دیگر عناصر رسانه ای حجیم روی این سایت مسدود نشوند", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { - "message": "تغییر وضعیت فیلترزیباسازی در این صفحه", + "message": "تغییر وضعیت فیلترزیباسازی برای این سایت", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Click to disable cosmetic filtering on this site", + "message": "کلیک کنید تا فیلتر زیبا سازی بر روی این سایت غیر فعال شود", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Click to enable cosmetic filtering on this site", + "message": "کلیک کنید تا فیلتر زیبا سازی بر روی این سایت فعال شود", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -132,11 +132,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Click to block remote fonts on this site", + "message": "کلیک کنید تا فونت های راه دور در این سایت مسدود شوند", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Click to no longer block remote fonts on this site", + "message": "کلیک کنید تا دیگر فونت های راه دور در این سایت مسدود نشوند", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { @@ -264,11 +264,11 @@ "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { - "message": "غیر فعال سازی پیوندهای نظارتی \/ هدایتی", + "message": "غیرفعال کردن حسابرسی پیوند", "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "جلوگیری WebRTC از افشاسازی آدرس های IP محلی", + "message": "ممنوع کردن WebRTC از افشاسازی آدرس های IP محلی", "description": "English: " }, "settingPerSiteSwitchGroup": { @@ -276,23 +276,23 @@ "description": "" }, "settingPerSiteSwitchGroupSynopsis": { - "message": "این رفتارهای پیش فرض بسته به هر وب‌گاهیی می تواند باطل شود", + "message": "تنظیمات مخصوص هر سایت این تنظیمات پیش فرض را لغو می‌کند", "description": "" }, "settingsNoCosmeticFilteringPrompt": { - "message": "غیر فعال‌سازی انسداد تزیینی", + "message": "غیر فعال‌سازی فیلتر زیبا سازی", "description": "" }, "settingsNoLargeMediaPrompt": { - "message": "انسداد عناصر مدیایی حجیم تر از {{input:number}} کیلوبایت", + "message": "انسداد عناصر رسانه ای حجیم تر از {{input:number}} کیلوبایت", "description": "" }, "settingsNoRemoteFontsPrompt": { - "message": "انسداد فونت های از راه دور", + "message": "انسداد فونت های راه دور", "description": "" }, "settingsNoCSPReportsPrompt": { - "message": "Block CSP reports", + "message": "مسدود کردن گزارشات سیاست امنیت محتوا", "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" }, "settingsStorageUsed": { @@ -336,11 +336,11 @@ "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { - "message": "Ignore generic cosmetic filters", + "message": "نادیده گرفتن فیلترهای زیبا سازی عمومی", "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "

Generic cosmetic filters are those cosmetic filters which are meant to apply on all web sites.

Though handled efficiently by uBlock₀, generic cosmetic filters may still contribute measurable memory and CPU overhead on some web pages, especially for large and long-lived ones.

Enabling this option will eliminate the memory and CPU overhead added to web pages as a result of handling generic cosmetic filters, and also lower the memory footprint of uBlock₀ itself.

It is recommended to enable this option on less powerful devices.", + "message": "

فیلترهای زیبا سازی عمومی، آن دسته از فیلترهای زیبا سازی هستند که در همه وب سایت ها کاربرد دارند.\n

اگر چه بصورت موثر توسط uBlock₀ انجام شده، ولی فیلترهای عمومی زیبا سازی هنوز هم ممکن است سربار حافظه قابل اندازه گیری و سربار پردازنده در برخی از صفحات وب به جای بگذارد، به ویژه برای صفحات وب بزرگ و طولانی مدت.

فعال کردن این گزینه سربارهای حافظه و پردازنده را که به صفحات اضافه شده به عنوان نتیجۀ اجرای فیلترهای عمومی زیبا سازی برطرف می‌کند، و همچنین ردپای حافظۀ خود uBlock₀ را کمتر می‌کند

توصیه می شود این گزینه را در دستگاه های با قدرت کمتر فعال کنید.", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pListsOfBlockedHostsHeader": { @@ -364,7 +364,7 @@ "description": "English: Malware domains" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "مزاحم‌ها", "description": "The header identifying the filter lists in the category 'annoyances'" }, "3pGroupMultipurpose": { @@ -384,11 +384,11 @@ "description": "English: One URL per line. Lines prefixed with ‘!’ will be ignored. Invalid URLs will be silently ignored." }, "3pExternalListObsolete": { - "message": "منقضی شده", + "message": "منقضی شده.", "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pLastUpdate": { - "message": "آخرین به روز رسانی: {{ago}}", + "message": "آخرین به روز رسانی: {{ago}}.\nبرای یک به روز رسانی اجباری کلیک کنید.", "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { @@ -468,7 +468,7 @@ "description": "English: dynamic rule syntax and full documentation." }, "whitelistPrompt": { - "message": "لیست شما از هاست هایی که uBlock₀ در آنها غیرفعال خواهد شد. یک ورودی در هر خط. هاست های نامعتبر بدون اشاره ای نادیده گرفته می شوند.", + "message": "دستور العمل‌های لیست سفید حکم می‌کند که uBlock₀ باید بر روی کدام یک از صفحات وب غیر فعال باشد. در هر خط فقط یک مورد. دستور العمل‌های نامعتبر بدون اشاره ای نادیده گرفته شده و بیرون انداخته می‌شوند.", "description": "English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport": { @@ -511,6 +511,10 @@ "message": "پشت صحنه", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "برگۀ فعلی", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "فیلتر کردن مطالب لاگ", "description": "English: filter log entries" @@ -520,7 +524,7 @@ "description": "Tooltip informaing that the input field is to set the maximum number of entries in the log" }, "loggerURLFilteringContextLabel": { - "message": "زمینه:", + "message": "محتوی:", "description": "Label for the context selector" }, "loggerURLFilteringTypeLabel": { @@ -528,7 +532,7 @@ "description": "Label for the type selector" }, "loggerURLFilteringHeader": { - "message": "فیلترینگ آدرس اینترنتی پویا", + "message": "فیلترینگ پویای آدرس اینترنتی", "description": "Small header to identify the dynamic URL filtering section" }, "loggerStaticFilteringHeader": { @@ -536,7 +540,7 @@ "description": "Small header to identify the static filtering section" }, "loggerStaticFilteringSentence": { - "message": "{{action}} درخواست های شبکه از {{type}} {{br}} هر آدرس اینترنتی مطابقت داده می شود {{url}} {{br}}و هر چه شد{{origin}},{{br}}{{importance}} یک فیلتر تطبیق استثنا وجود دارد.", + "message": "{{action}} درخواست های شبکه از نوع {{type}}{{br}} که مطابقت دارد با آدرس اینترنی {{url}}{{br}} و سرچشمه می‌گیرد از {{origin}}،{{br}}{{importance}} اینها یک فیلتر استثناء از تطبیق هستند.", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartBlock": { @@ -620,11 +624,11 @@ "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "تمام تنظیمات شده حذف و uBlock دوباره راه اندازی خواهد شد.\n\nتنظیم مجدد uBlock به تنظیمات کارخانه؟", + "message": "تمام تنظیمات شده حذف و uBlock₀ دوباره راه اندازی خواهد شد.\n\nتنظیم مجدد uBlock₀ به تنظیمات کارخانه؟", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { - "message": "قادر به اتصال به {{url}}", + "message": "خطای شبکه: {{msg}}", "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { @@ -668,7 +672,7 @@ "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedPrompt1": { - "message": "ublock از بارگذاری این صفحات جلوگیری کرده:", + "message": "uBlock₀ از بارگذاری این صفحات جلوگیری کرده:", "description": "English: uBlock₀ has prevented the following page from loading:" }, "docblockedPrompt2": { @@ -676,7 +680,7 @@ "description": "English: Because of the following filter" }, "docblockedNoParamsPrompt": { - "message": "بدون پارامترها", + "message": "بدون پارامتر", "description": "label to be used for the parameter-less URL: https:\/\/cloud.githubusercontent.com\/assets\/585534\/9832014\/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { @@ -708,11 +712,11 @@ "description": "tooltip" }, "cloudPull": { - "message": "ورود از فضای ذخیره سازی ابری", + "message": "وارد کردن از فضای ذخیره سازی ابری", "description": "tooltip" }, "cloudPullAndMerge": { - "message": "ورود از فضای ذخیره سازی ابری و ادغام با تنظیمات فعلی", + "message": "وارد کردن از فضای ذخیره سازی ابری و ادغام با تنظیمات فعلی", "description": "tooltip" }, "cloudNoData": { @@ -728,7 +732,7 @@ "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { - "message": "ثبت", + "message": "ارسال", "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { @@ -736,7 +740,7 @@ "description": "for generic 'Apply changes' buttons" }, "genericRevert": { - "message": "بازگشت", + "message": "بازگرداندن", "description": "for generic 'Revert' buttons" }, "genericBytes": { @@ -744,7 +748,7 @@ "description": "" }, "contextMenuTemporarilyAllowLargeMediaElements": { - "message": "اجازه موقت عناصر مدیای حجیم", + "message": "اجازه موقت عناصر رسانه‌ای حجیم", "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "dummy": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index a00212a955a18..9aec3a8859ff3 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -511,6 +511,10 @@ "message": "Kulissien takana", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "suodata lokimerkinnät", "description": "English: filter log entries" diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index bebc7f404bdce..39786e77cd7a7 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -511,6 +511,10 @@ "message": "Behind the scene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filter log entries", "description": "English: filter log entries" diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index 1b84357596230..5e7eebd468549 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -511,6 +511,10 @@ "message": "Requêtes en coulisses", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Onglet courant", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "Filtrer les entrées du journal", "description": "English: filter log entries" diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index 44e5a14314fae..27cf13485da71 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -511,6 +511,10 @@ "message": "Achter de skermen", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Aktuele ljepblêd", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "lochboekitems filterje", "description": "English: filter log entries" diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index 40cee338de996..c0f122e08c047 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -511,6 +511,10 @@ "message": "Peticións ocultas", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Lapela activa", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtras as entradas de rexistro", "description": "English: filter log entries" diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index bc00586e8764f..3e111dd59b372 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -511,6 +511,10 @@ "message": "מאחורי הקלעים", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "סנן רשומות", "description": "English: filter log entries" diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index e60f8dd21e6c3..779b8322ef8b1 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -511,6 +511,10 @@ "message": "पर्दे के पीछे", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filter log entries", "description": "English: filter log entries" diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index b09cc5867c810..bfae69207d568 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -511,6 +511,10 @@ "message": "Iza scene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrirajte zabilješke", "description": "English: filter log entries" diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 0aada52077dcf..94ebc0d7f151c 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -511,6 +511,10 @@ "message": "Hálózati lekérések a háttérben", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "naplóbejegyzések szűrése", "description": "English: filter log entries" diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index 9bf1fc25b4135..44965129a9635 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -511,6 +511,10 @@ "message": "Di balik layar", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Tab saat ini", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "saring entri catatan", "description": "English: filter log entries" diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 1c11c43ed2031..4506d3f996566 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -511,6 +511,10 @@ "message": "Dietro le quinte", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtra voci del registro", "description": "English: filter log entries" diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 20ca6aa68dc24..09290416dcb4d 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -340,7 +340,7 @@ "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { - "message": "

要素隠蔽フィルターは、ほぼすべてのウェブサイトに適用することができるフィルターです。

これはuBlock₀によって効率的に処理されますが、特に大規模で長期運営されている一部のウェブページ上ではメモリやCPUの処理に時間がかかる可能性があります。

このオプションを有効にすると、要素隠蔽フィルターの処理結果としてウェブページに追加されたメモリやCPUの処理時間を減らし、uBlock₀自体のメモリ使用量を軽減できます。

低スペックの環境では、このオプションを有効にすることを推奨します。", + "message": "

汎用的な要素隠蔽フィルターとは、すべてのウェブサイトに適用される要素隠蔽フィルターのことです。

これはuBlock₀によって効率的に処理されますが、特に大規模で長期運営されている一部のウェブページ上では、メモリ使用量や処理時間に目に見える影響を与えることがあります。

このオプションを有効にすると、汎用的な要素隠蔽フィルターを処理するためにウェブページごとに追加されるメモリ使用量や処理時間を減らし、さらにuBlock₀自体のメモリ使用量を軽減できます。

低スペックの環境では、このオプションを有効にすることを推奨します。", "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pListsOfBlockedHostsHeader": { @@ -511,6 +511,10 @@ "message": "バックグラウンド通信", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "絞り込み", "description": "English: filter log entries" diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 8f4765e65377d..370a75e98ee24 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -511,6 +511,10 @@ "message": "სცენის უკან", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filter log entries", "description": "English: filter log entries" diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json index f68f732d35d39..8e6a240f98ca8 100644 --- a/src/_locales/kn/messages.json +++ b/src/_locales/kn/messages.json @@ -511,6 +511,10 @@ "message": "Behind the scene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filter log entries", "description": "English: filter log entries" diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 4a05331e7bab0..49c2928b57447 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -364,7 +364,7 @@ "description": "English: Malware domains" }, "3pGroupAnnoyances": { - "message": "방해되는 것", + "message": "골칫거리", "description": "The header identifying the filter lists in the category 'annoyances'" }, "3pGroupMultipurpose": { @@ -511,6 +511,10 @@ "message": "숨겨진 구성 요소", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "필터 로그 항목", "description": "English: filter log entries" diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index fdeda38d1c542..6ba10cabb9cbd 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -511,6 +511,10 @@ "message": "Užkulisiai", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Dabartinė kortelė", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtruoti žurnalo įrašus", "description": "English: filter log entries" diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index 06e3b91bf807d..bb25920d54811 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -511,6 +511,10 @@ "message": "Aizkulisēs", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "žurnāla ierakstu filtrs", "description": "English: filter log entries" diff --git a/src/_locales/ml/messages.json b/src/_locales/ml/messages.json index 2ecb4877067a4..ffdb6c36aaba5 100644 --- a/src/_locales/ml/messages.json +++ b/src/_locales/ml/messages.json @@ -511,6 +511,10 @@ "message": "സീനിനു പിന്നില്‍", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "ലോഗ് എന്‍ട്രി ഫില്‍ട്ടര്‍ ചെയ്യുക", "description": "English: filter log entries" diff --git a/src/_locales/mr/messages.json b/src/_locales/mr/messages.json index 16b43a6d6653b..1e5d11b5b32d4 100644 --- a/src/_locales/mr/messages.json +++ b/src/_locales/mr/messages.json @@ -511,6 +511,10 @@ "message": "Behind the scene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filter log entries", "description": "English: filter log entries" diff --git a/src/_locales/ms/messages.json b/src/_locales/ms/messages.json index 900670bcf5535..ae44caa169fd3 100644 --- a/src/_locales/ms/messages.json +++ b/src/_locales/ms/messages.json @@ -511,6 +511,10 @@ "message": "Behind the scene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filter log entries", "description": "English: filter log entries" diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 75de1f938b5e3..445bfbadfe2be 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -511,6 +511,10 @@ "message": "Bak kulissene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrer loggoppføringer", "description": "English: filter log entries" diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 3804f7b674c00..e6cc15201721c 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -511,6 +511,10 @@ "message": "Achter de schermen", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Huidige tabblad", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "logboekvermeldingen filteren", "description": "English: filter log entries" diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index e17038f4b5d41..1579b681b2f4f 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -511,6 +511,10 @@ "message": "Ukryte żądania", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Aktualna karta", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtruj wpisy", "description": "English: filter log entries" diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 1bb458ace4dce..df55c1cb2d174 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -511,6 +511,10 @@ "message": "Por trás da cena", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Aba atual", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrar entradas de registro", "description": "English: filter log entries" diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index e726a731308a6..d447bfc2236b0 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -511,6 +511,10 @@ "message": "Bastidores", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Separador atual", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrar entradas de registo", "description": "English: filter log entries" diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index 4e5c6e7a5012c..c2fb049b28e2a 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -511,6 +511,10 @@ "message": "În spatele scenei", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrează intrările din jurnal", "description": "English: filter log entries" diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index a13072c283fd5..a78286753858a 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -511,6 +511,10 @@ "message": "Скрытые запросы", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "фильтр записей в журнале", "description": "English: filter log entries" diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 85036319fb588..3cdbbd2436152 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -511,6 +511,10 @@ "message": "Za oponou", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrovať položky v zázname", "description": "English: filter log entries" diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index e2f354f371b38..3f224153ef794 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -511,6 +511,10 @@ "message": "Za zaveso", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtriraj vnose dnevnika", "description": "English: filter log entries" diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 811c799634b20..00b980ada26a7 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -511,6 +511,10 @@ "message": "Në prapaskenë", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Skeda aktuale", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtroni elementet në regjistër", "description": "English: filter log entries" diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index 08add989bab32..04d7021a8acdb 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -511,6 +511,10 @@ "message": "Иза сцене", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Тренутна картица", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "филтрирај уносе дневника", "description": "English: filter log entries" diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 6861d3929e06a..883d40ca90482 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -511,6 +511,10 @@ "message": "Under huven", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "filtrera loggposter", "description": "English: filter log entries" diff --git a/src/_locales/ta/messages.json b/src/_locales/ta/messages.json index f03bda05bd428..317afca55c8ae 100644 --- a/src/_locales/ta/messages.json +++ b/src/_locales/ta/messages.json @@ -511,6 +511,10 @@ "message": "திறக்கப்பட்ட இ.தளங்களின் பின்னால் நடப்பவை", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "பதிகை உருப்படிகளை வடிகட்டு", "description": "English: filter log entries" diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index dce0e80174d68..f75ede7aa90ec 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -511,6 +511,10 @@ "message": "తేర వెనుక", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "చిట్టాలోని నమోదులను జల్లెడపరచు", "description": "English: filter log entries" diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 8e3bffd636c40..ca981561212c4 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -511,6 +511,10 @@ "message": "Perde arkası", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Geçerli sekme", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "günlük girişlerini süz", "description": "English: filter log entries" diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index f412b4f4856de..253db74238ce8 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -511,6 +511,10 @@ "message": "За лаштунками", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Поточна вкладка", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "фільтр журналу записів", "description": "English: filter log entries" diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index b9707f6604746..bc38e23b85766 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -511,6 +511,10 @@ "message": "Behind the scene", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "lọc mục ghi nhận", "description": "English: filter log entries" diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 6204a79418f9c..7ae9d449437d7 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -511,6 +511,10 @@ "message": "后台", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "过滤日志条目", "description": "English: filter log entries" diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 76c8401581979..feeb8dd67474f 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -511,6 +511,10 @@ "message": "背景的網路連線請求", "description": "Pretty name for behind-the-scene network requests" }, + "loggerCurrentTab": { + "message": "目前分頁", + "description": "Appears in the logger's tab selector" + }, "logFilterPrompt": { "message": "過濾日誌項目", "description": "English: filter log entries" From abcea432a47b2177181c83b4d7c29c4a13c361d8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 9 Jan 2018 17:57:40 -0500 Subject: [PATCH 077/106] shallow clone uAssets in travis build --- tools/make-assets.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/make-assets.sh b/tools/make-assets.sh index 0ce141c1088dc..4e9e26a28f095 100755 --- a/tools/make-assets.sh +++ b/tools/make-assets.sh @@ -8,7 +8,7 @@ printf "*** Packaging assets in $DES... " if [ -n "${TRAVIS_TAG}" ]; then pushd .. > /dev/null - git clone https://github.com/uBlockOrigin/uAssets.git + git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git popd > /dev/null fi From 9fa1c37183a69a906659479a8c87b9db319fd90b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jan 2018 11:50:08 -0500 Subject: [PATCH 078/106] code review: fix eraser when using "Current tab" view in logger --- src/js/logger-ui-inspector.js | 5 ++--- src/js/logger-ui.js | 30 +++++++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/js/logger-ui-inspector.js b/src/js/logger-ui-inspector.js index 2cceec1f33b18..8b5e3f3816c24 100644 --- a/src/js/logger-ui-inspector.js +++ b/src/js/logger-ui-inspector.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2015-2016 Raymond Hill + Copyright (C) 2015-2018 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -561,8 +561,7 @@ var onMouseOver = (function() { var currentTabId = function() { if ( showdomButton.classList.contains('active') === false ) { return ''; } - var tabId = logger.tabIdFromPageSelector(); - return tabId !== 'bts' ? tabId : ''; + return logger.tabIdFromPageSelector(); }; /******************************************************************************/ diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index b3cad3b5175fd..9690cafec27bd 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2015-2017 Raymond Hill + Copyright (C) 2015-2018 Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -46,17 +46,17 @@ var removeAllChildren = logger.removeAllChildren = function(node) { /******************************************************************************/ -var tabIdFromClassName = logger.tabIdFromClassName = function(className) { +var tabIdFromClassName = function(className) { var matches = className.match(/\btab_([^ ]+)\b/); return matches !== null ? matches[1] : ''; }; var tabIdFromPageSelector = logger.tabIdFromPageSelector = function() { var tabClass = uDom.nodeFromId('pageSelector').value; - if ( tabClass === '' ) { return ''; } - if ( tabClass === 'tab_bts' ) { return noTabId; } - if ( tabClass === 'tab_active' ) { return activeTabId; } - return tabClass.slice(4); + if ( tabClass === 'tab_active' && activeTabId !== undefined ) { + return activeTabId; + } + return /^tab_\d+$/.test(tabClass) ? tabClass.slice(4) : ''; }; /******************************************************************************/ @@ -623,10 +623,14 @@ var pageSelectorFromURLHash = (function() { /******************************************************************************/ -var reloadTab = function() { +var reloadTab = function(ev) { var tabId = tabIdFromPageSelector(); - if ( tabId === '' || tabId === noTabId ) { return; } - messaging.send('loggerUI', { what: 'reloadTab', tabId: tabId }); + if ( tabId === '' ) { return; } + messaging.send('loggerUI', { + what: 'reloadTab', + tabId: tabId, + bypassCache: ev && (ev.ctrlKey || ev.metaKey || ev.shiftKey) + }); }; /******************************************************************************/ @@ -1509,13 +1513,17 @@ var toJunkyard = function(trs) { /******************************************************************************/ var clearBuffer = function() { - var tabClass = uDom.nodeFromId('pageSelector').value || ''; + var tabClass = uDom.nodeFromId('pageSelector').value; + var btsAlso = tabClass === '' || tabClass === 'tab_bts'; var tbody = document.querySelector('#netInspector tbody'); var tr = tbody.lastElementChild; var trPrevious; while ( tr !== null ) { trPrevious = tr.previousElementSibling; - if ( tabClass === '' || tr.classList.contains(tabClass) ) { + if ( + (tr.clientHeight > 0) && + (tr.classList.contains('tab_bts') === false || btsAlso) + ) { trJunkyard.push(tbody.removeChild(tr)); } tr = trPrevious; From 41c2c33ce2434d66ad302951bf1ffa28337b2b96 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jan 2018 11:55:16 -0500 Subject: [PATCH 079/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 20 ++++++++++---------- src/_locales/bg/messages.json | 4 ++-- src/_locales/de/messages.json | 2 +- src/_locales/et/messages.json | 2 +- src/_locales/eu/messages.json | 2 +- src/_locales/he/messages.json | 2 +- src/_locales/hi/messages.json | 8 ++++---- src/_locales/hr/messages.json | 2 +- src/_locales/hu/messages.json | 2 +- src/_locales/ja/messages.json | 2 +- src/_locales/ko/messages.json | 2 +- src/_locales/nb/messages.json | 2 +- src/_locales/pl/messages.json | 4 ++-- src/_locales/ro/messages.json | 2 +- src/_locales/ru/messages.json | 2 +- src/_locales/sk/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- src/_locales/zh_CN/messages.json | 2 +- 18 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index bf045e0a5518e..a6527c22c9402 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -48,7 +48,7 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "اضغط: لتعطيل\/تشغيل uBlock₀ لهذا الموقع.\n\nCtrl+click لتعطيل uBlock₀ لهذه الصفحة فقط.", + "message": "اضغط لتعطيل uBlock₀ لهذا الموقع.\n\nCtrl+click لتعطيل uBlock₀ لهذه الصفحة فقط.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { @@ -76,7 +76,7 @@ "description": "English: or" }, "popupTipDashboard": { - "message": "إضغط لفتح قائمة التحكم", + "message": "إضغط لفتح لوحة التحكم", "description": "English: Click to open the dashboard" }, "popupTipZapper": { @@ -100,7 +100,7 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "اضغط للسماح لجميع النوافذ المنبثقة لهذا الموقع", + "message": "أضغط للوقف منع جميع النوافذ المنبثقة لهذا الموقع", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -112,7 +112,7 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "اضغط للسماح لعناصر الوسائط الكبيرة لهذا الموقع", + "message": "أضغط للوقف منع عناصر الوسائط الكبيرة لهذا الموقع", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "اضغط لتعطيل الترشيح الجمالي لهذا الموقع", + "message": "اضغط لتعطيل الفلترة التجميلية لهذا الموقع", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "اضغط لتشغيل الترشيح الجمالي لهذا الموقع", + "message": "اضغط لتشغيل الفلترة التجميلية لهذا الموقع", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -132,11 +132,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "أضغط للسماح للخطوط الخارجية لهذا الموقع", + "message": "أضغط لحجب الخطوط الخارجية لهذا الموقع", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "أضغط للوقف منع خطوط الطباعة الخارجية لهذا الموقع", + "message": "أضغط للسماح للخطوط الخارجية لهذا الموقع", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { @@ -384,7 +384,7 @@ "description": "English: One URL per line. Lines prefixed with ‘!’ will be ignored. Invalid URLs will be silently ignored." }, "3pExternalListObsolete": { - "message": "إنتهت صلاحيته.", + "message": "متقادم.", "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pLastUpdate": { @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "علامة التبويب الحالية", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index b17e2dc356a2c..d50bd913b97c0 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Последно активен раздел", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { @@ -720,7 +720,7 @@ "description": "tooltip" }, "cloudNoData": { - "message": "...\n...", + "message": "...", "description": "" }, "cloudDeviceNamePrompt": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index c0528c9173c59..e5b0ca59d6120 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Aktueller Tab", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 8bbd1eb9e3395..509beede24814 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Praegune kaart", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index b3d508ef101bc..c40aa30d3139c 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Uneko fitxa", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index 3e111dd59b372..ab66313334d76 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "לשונית נוכחית", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 779b8322ef8b1..653be5e2ce5a2 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -96,11 +96,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "इस वेबसाइट पर सभी पॉप-अप्स को ब्लाक करने के लिए क्लिक करें", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "इस वेबसाइट पर सभी पॉप-अप्स को ब्लाक न करने के लिए क्लिक करें", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -108,11 +108,11 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "इस वेबसाइट पर बड़े मीडिया ऐलिमेंटस को ब्लाक करने के लिए क्लिक करें", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "इस वेबसाइट पर बड़े मीडिया ऐलिमेंटस को ब्लाक न करने के लिए क्लिक करें", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index bfae69207d568..fb888cb69b4c7 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Trenutna kartica", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 94ebc0d7f151c..a1b3efd1d2e00 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Jelenlegi lap", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 09290416dcb4d..c8d3b4a9e2740 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "現在のタブ", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 49c2928b57447..d66723336497e 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "현재 탭", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 445bfbadfe2be..0e686d8245617 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Gjeldende fane", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index 1579b681b2f4f..572f986aad40b 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Nareszcie, skuteczny bloker, charakteryzujący się niskim użyciem procesora i pamięci.", + "message": "Nareszcie skuteczny bloker charakteryzujący się niskim użyciem procesora i pamięci.", "description": "this will be in the chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Aktualna karta", + "message": "Aktywna karta", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index c2fb049b28e2a..c3537f0816c62 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Tab-ul curent", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index a78286753858a..3c926ea169fdf 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Текущая вкладка", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 3cdbbd2436152..4cacc2add524d 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Aktívna karta", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 883d40ca90482..b2e1b5b19ed6f 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Aktuell flik", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 7ae9d449437d7..dee4c5fa3d2bb 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "当前标签页", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { From d7064158900630b60992d7dad70525bf1077a17a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jan 2018 12:04:56 -0500 Subject: [PATCH 080/106] new revision for dev build --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 85c7a98424ae0..f0ff183ca9018 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.16", + "version": "1.14.23.17", "commands": { "launch-element-zapper": { From 36b1eac88e8afb9ee2b10f7c6088ee50d48dc6fc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 10 Jan 2018 17:59:07 -0500 Subject: [PATCH 081/106] fix #3425 (regression from 6cdb20df) --- platform/chromium/vapi-background.js | 15 ++++++++------- src/js/messaging.js | 13 ++++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index b32e8af9a62d6..21c3dbcfb1004 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -390,14 +390,15 @@ vAPI.tabs.registerListeners = function() { vAPI.tabs.get = function(tabId, callback) { if ( tabId === null ) { - chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) { - if ( chrome.runtime.lastError ) { /* noop */ } - var tab = tabs && tabs[0]; - if ( tab ) { - tab.id = tab.id.toString(); + chrome.tabs.query( + { active: true, currentWindow: true }, + function(tabs) { + if ( chrome.runtime.lastError ) { /* noop */ } + callback( + Array.isArray(tabs) && tabs.length !== 0 ? tabs[0] : null + ); } - callback(tab); - }); + ); return; } diff --git a/src/js/messaging.js b/src/js/messaging.js index 73801ffc987bd..7ce4476c65a7d 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1015,7 +1015,7 @@ var µb = µBlock, /******************************************************************************/ -var getLoggerData = function(ownerId, tab, callback) { +var getLoggerData = function(ownerId, activeTabId, callback) { var tabIds = {}; for ( var tabId in µb.pageStores ) { var pageStore = µb.pageStoreFromTabId(tabId); @@ -1023,9 +1023,8 @@ var getLoggerData = function(ownerId, tab, callback) { if ( pageStore.rawURL.startsWith(extensionPageURL) ) { continue; } tabIds[tabId] = pageStore.title; } - var activeTabId; - if ( tabIds.hasOwnProperty(tab.id) ) { - activeTabId = tab.id; + if ( activeTabId && tabIds.hasOwnProperty(activeTabId) === false ) { + activeTabId = undefined; } callback({ colorBlind: µb.userSettings.colorBlindFriendly, @@ -1083,7 +1082,11 @@ var onMessage = function(request, sender, callback) { return; } vAPI.tabs.get(null, function(tab) { - getLoggerData(request.ownerId, tab, callback); + getLoggerData( + request.ownerId, + tab && tab.id.toString(), + callback + ); }); return; From 295cf563ee809af144afea9c3ce94037a927eab3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 11 Jan 2018 09:08:09 -0500 Subject: [PATCH 082/106] add sidebar support for Opera --- platform/opera/manifest.json | 138 ++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 58 deletions(-) diff --git a/platform/opera/manifest.json b/platform/opera/manifest.json index c8f8d8cf02389..9aa58f523ff6b 100644 --- a/platform/opera/manifest.json +++ b/platform/opera/manifest.json @@ -1,64 +1,86 @@ { - "author": "All uBlock Origin contributors", - "browser_action": { - "default_icon": { - "19": "img/browsericons/icon19.png", - "38": "img/browsericons/icon19.png" - }, - "default_title": "uBlock Origin", - "default_popup": "popup.html" + "author": "All uBlock Origin contributors", + "background": { + "page": "background.html" + }, + "browser_action": { + "default_icon": { + "19": "img/browsericons/icon19.png", + "38": "img/browsericons/icon19.png" }, - "background": { - "page": "background.html" + "default_popup": "popup.html", + "default_title": "uBlock Origin" + }, + "commands": { + "launch-element-picker": { + "description": "__MSG_popupTipPicker__" }, - "commands": { - "launch-element-zapper": { - "description": "__MSG_popupTipZapper__" - }, - "launch-element-picker": { - "description": "__MSG_popupTipPicker__" - }, - "launch-logger": { - "description": "__MSG_popupTipLog__" - } + "launch-element-zapper": { + "description": "__MSG_popupTipZapper__" }, - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*"], - "js": ["js/vapi.js", "js/vapi-client.js", "js/contentscript.js"], - "run_at": "document_start", - "all_frames": true - }, - { - "matches": ["http://*/*", "https://*/*"], - "js": ["js/scriptlets/subscriber.js"], - "run_at": "document_idle", - "all_frames": false - } - ], - "default_locale": "en", - "description": "__MSG_extShortDesc__", - "icons": { - "16": "img/icon_16.png", - "128": "img/icon_128.png" + "launch-logger": { + "description": "__MSG_popupTipLog__" + } + }, + "content_scripts": [ + { + "all_frames": true, + "js": [ + "js/vapi.js", + "js/vapi-client.js", + "js/contentscript.js" + ], + "matches": [ + "http://*/*", + "https://*/*" + ], + "run_at": "document_start" }, - "incognito": "split", - "manifest_version": 2, - "minimum_opera_version": "32.0", - "name": "uBlock Origin", - "optional_permissions": [ "file:///*" ], - "options_page": "dashboard.html", - "permissions": [ - "contextMenus", - "privacy", - "storage", - "tabs", - "unlimitedStorage", - "webNavigation", - "webRequest", - "webRequestBlocking", - "" - ], - "short_name": "uBlock₀", - "version": "1.9.15.101" + { + "all_frames": false, + "js": [ + "js/scriptlets/subscriber.js" + ], + "matches": [ + "http://*/*", + "https://*/*" + ], + "run_at": "document_idle" + } + ], + "default_locale": "en", + "description": "__MSG_extShortDesc__", + "icons": { + "128": "img/icon_128.png", + "16": "img/icon_16.png" + }, + "incognito": "split", + "manifest_version": 2, + "minimum_opera_version": "32.0", + "name": "uBlock Origin", + "optional_permissions": [ + "file:///*" + ], + "options_page": "dashboard.html", + "permissions": [ + "contextMenus", + "privacy", + "storage", + "tabs", + "unlimitedStorage", + "webNavigation", + "webRequest", + "webRequestBlocking", + "" + ], + "short_name": "uBlock\u2080", + "sidebar_action": { + "default_icon": { + "19": "img/browsericons/icon19.png", + "38": "img/browsericons/icon19.png" + }, + "default_panel": "logger-ui.html", + "default_title": "__MSG_statsPageName__" + }, + "version": "1.14.23.17" } From a73e1460b8d51e09915be140fabf9426d3f61598 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 11 Jan 2018 09:09:56 -0500 Subject: [PATCH 083/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/cs/messages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index c3f91e3c06de2..b75365c96c411 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -48,7 +48,7 @@ "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { - "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "message": "Kliknutí: trvale zakázat uBlock₀ pro celý tento web.\n\nCtrl+kliknutí: zakázat uBlock₀ pouze pro tuto stránku.", "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { @@ -100,7 +100,7 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Click to no longer block all popups on this site", + "message": "Zhbllokoj të gjitha dritaret automatike të faqes", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { From 0b684bd419fe4ea9ebff0291e2cdf222180a482d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 11 Jan 2018 11:09:15 -0500 Subject: [PATCH 084/106] new revision for release candidate --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index f0ff183ca9018..ce2b5dd0114fc 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.17", + "version": "1.14.23.100", "commands": { "launch-element-zapper": { From 6219866b53b3c545785864ca8903e10ed3ae4b63 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 12 Jan 2018 09:33:01 -0500 Subject: [PATCH 085/106] fix #3430 (regression) --- src/js/cosmetic-filtering.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 2561b8c37f50b..06380d6fc1341 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -398,6 +398,7 @@ var FilterContainer = function() { '.+?:if-not', '.+?:matches-css(?:-before|-after)?', '.*?:xpath', + '.+?:style', '.+?:-abp-contains', // ABP-specific for `:has-text` '.+?:-abp-has', // ABP-specific for `:if` '.+?:contains' // Adguard-specific for `:has-text` @@ -623,6 +624,8 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, writer) { /******************************************************************************/ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) { + var selector = parsed.suffix; + // For some selectors, it is mandatory to have a hostname or entity: // ##.foo:-abp-contains(...) // ##.foo:-abp-has(...) @@ -635,10 +638,17 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, writer) // ##.foo:matches-css-after(...) // ##.foo:matches-css-before(...) // ##:xpath(...) - if ( this.reNeedHostname.test(selector) ) { return; } + // ##.foo:style(...) + if ( this.reNeedHostname.test(selector) ) { + µb.logger.writeOne( + '', + 'error', + 'Cosmetic filtering – invalid generic filter: ##' + selector + ); + return; + } - var selector = parsed.suffix, - type = selector.charCodeAt(0), + var type = selector.charCodeAt(0), key; if ( type === 0x23 /* '#' */ ) { From a217ca42c9551ce622765c11c2c4fd3e0de394b3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jan 2018 08:31:13 -0500 Subject: [PATCH 086/106] fix https://github.com/nikrolls/uBlock-Edge/issues/101 --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index d228282eebffe..3f5a0c855fd35 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -954,7 +954,7 @@ FilterOriginMixedSet.prototype = Object.create(FilterOrigin.prototype, { i = hostnames.length, hostname; while ( i-- ) { - hostname = hostnames[i].replace(/\./g, '\\.'); + hostname = hostnames[i]; if ( hostname.charCodeAt(0) === 0x7E /* '~' */ ) { noneOf.push(hostname.slice(1)); } else { From 605dcc9199e6d4c0c0ad0ce51ae6a60dcb64b196 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jan 2018 09:42:04 -0500 Subject: [PATCH 087/106] fix #2283 --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 3f5a0c855fd35..bf1da6f634c77 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1735,7 +1735,7 @@ FilterParser.prototype.parse = function(raw) { // Abort if type is only for unsupported types, otherwise // toggle off `unsupported` bit. if ( this.types & this.unsupportedTypeBit ) { - this.types &= ~(this.unsupportedTypeBit | this.allNetRequestTypeBits); + this.types &= ~this.unsupportedTypeBit; if ( this.types === 0 ) { this.unsupported = true; return this; From 888d36fd066acb590454a475c0dcd8dfa20a67dd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jan 2018 09:42:04 -0500 Subject: [PATCH 088/106] fix #3433 --- src/js/static-net-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 3f5a0c855fd35..bf1da6f634c77 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1735,7 +1735,7 @@ FilterParser.prototype.parse = function(raw) { // Abort if type is only for unsupported types, otherwise // toggle off `unsupported` bit. if ( this.types & this.unsupportedTypeBit ) { - this.types &= ~(this.unsupportedTypeBit | this.allNetRequestTypeBits); + this.types &= ~this.unsupportedTypeBit; if ( this.types === 0 ) { this.unsupported = true; return this; From f7d7657293272c84249561e896b236f0a41314eb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jan 2018 09:45:44 -0500 Subject: [PATCH 089/106] new revision for release candidate --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index ce2b5dd0114fc..e5f41c79106a3 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.23.100", + "version": "1.14.25.100", "commands": { "launch-element-zapper": { From ecb5ca393e84f110c496d1cabe081d2ae18fbac4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jan 2018 09:48:58 -0500 Subject: [PATCH 090/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/it/messages.json | 2 +- src/_locales/nb/messages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 4506d3f996566..ad4ab65ca84d1 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Scheda corrente", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 0e686d8245617..364bc3df86e6f 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -344,7 +344,7 @@ "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pListsOfBlockedHostsHeader": { - "message": "Lister over blokkerte verter", + "message": "Lists of blocked hosts", "description": "English: Lists of blocked hosts" }, "3pApplyChanges": { From 9fc8b546eeee0a8d83a1d17c99019dffd4b59ee9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 13 Jan 2018 09:50:26 -0500 Subject: [PATCH 091/106] replace "no" locale by "nb" --- dist/description/{description-no.txt => description-nb.txt} | 0 tools/import-crowdin.sh | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename dist/description/{description-no.txt => description-nb.txt} (100%) diff --git a/dist/description/description-no.txt b/dist/description/description-nb.txt similarity index 100% rename from dist/description/description-no.txt rename to dist/description/description-nb.txt diff --git a/tools/import-crowdin.sh b/tools/import-crowdin.sh index f12a800b5ee48..e635e9a923029 100755 --- a/tools/import-crowdin.sh +++ b/tools/import-crowdin.sh @@ -43,7 +43,7 @@ cp $SRC/lv/messages.json $DES/lv/messages.json cp $SRC/ml-IN/messages.json $DES/ml/messages.json cp $SRC/mr/messages.json $DES/mr/messages.json cp $SRC/ms/messages.json $DES/ms/messages.json -cp $SRC/no/messages.json $DES/nb/messages.json +cp $SRC/nb/messages.json $DES/nb/messages.json cp $SRC/nl/messages.json $DES/nl/messages.json cp $SRC/pl/messages.json $DES/pl/messages.json cp $SRC/pt-BR/messages.json $DES/pt_BR/messages.json @@ -100,7 +100,7 @@ cp $SRC/lv/description.txt $DES/description-lv.txt cp $SRC/ml-IN/description.txt $DES/description-ml.txt cp $SRC/ms/description.txt $DES/description-ms.txt cp $SRC/mr/description.txt $DES/description-mr.txt -cp $SRC/no/description.txt $DES/description-no.txt +cp $SRC/nb/description.txt $DES/description-nb.txt cp $SRC/nl/description.txt $DES/description-nl.txt cp $SRC/pl/description.txt $DES/description-pl.txt cp $SRC/pt-BR/description.txt $DES/description-pt_BR.txt From 32bde274b959cc4054dca9b562a7c11d5a1e84af Mon Sep 17 00:00:00 2001 From: Babak Farrokhi Date: Mon, 15 Jan 2018 16:54:43 +0330 Subject: [PATCH 092/106] Use rawgit.com to download filter list (#3438) - (githubusercontent.com cannot be accessed from iran --- assets/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/assets.json b/assets/assets.json index edbba5e5a1803..9fd1a81645120 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -424,7 +424,7 @@ "off": true, "title": "IRN: Adblock-Iran", "lang": "fa", - "contentURL": "https://raw.githubusercontent.com/farrokhi/adblock-iran/master/filter.txt", + "contentURL": "https://cdn.rawgit.com/farrokhi/adblock-iran/master/filter.txt", "supportURL": "https://github.com/farrokhi/adblock-iran" }, "ISL-0": { From 1c468035ddfbac527306a4522cec764356b72871 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 16 Jan 2018 09:13:51 -0500 Subject: [PATCH 093/106] fix #3441 --- platform/webext/vapi-webrequest.js | 47 +++++++++++++----------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/platform/webext/vapi-webrequest.js b/platform/webext/vapi-webrequest.js index c2551ffbc366a..e25b8d164e7c9 100644 --- a/platform/webext/vapi-webrequest.js +++ b/platform/webext/vapi-webrequest.js @@ -61,46 +61,41 @@ vAPI.net.registerListeners = function() { var wrApi = browser.webRequest; // legacy Chromium understands only these network request types. - var validTypes = { - main_frame: true, - sub_frame: true, - stylesheet: true, - script: true, - image: true, - object: true, - xmlhttprequest: true, - other: true - }; + var validTypes = new Set([ + 'image', + 'main_frame', + 'object', + 'other', + 'script', + 'stylesheet', + 'sub_frame', + 'xmlhttprequest', + ]); // modern Chromium/WebExtensions: more types available. if ( wrApi.ResourceType ) { for ( let typeKey in wrApi.ResourceType ) { if ( wrApi.ResourceType.hasOwnProperty(typeKey) ) { - validTypes[wrApi.ResourceType[typeKey]] = true; + validTypes.add(wrApi.ResourceType[typeKey]); } } } var denormalizeTypes = function(aa) { if ( aa.length === 0 ) { - return Object.keys(validTypes); + return Array.from(validTypes); } - var out = []; - var i = aa.length, - type, - needOther = true; + var out = new Set(), + i = aa.length; while ( i-- ) { - type = aa[i]; - if ( validTypes[type] ) { - out.push(type); + var type = aa[i]; + if ( validTypes.has(type) ) { + out.add(type); } - if ( type === 'other' ) { - needOther = false; + if ( type === 'image' && validTypes.has('imageset') ) { + out.add('imageset'); } } - if ( needOther ) { - out.push('other'); - } - return out; + return Array.from(out); }; var punycode = self.punycode; @@ -144,7 +139,7 @@ vAPI.net.registerListeners = function() { let urls = this.onBeforeRequest.urls || ['']; let types = this.onBeforeRequest.types || undefined; if ( - (validTypes.websocket) && + (validTypes.has('websocket')) && (types === undefined || types.indexOf('websocket') !== -1) && (urls.indexOf('') === -1) ) { From 568c1d46e49f68f6be904eb187badd03ee836d5b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 16 Jan 2018 09:20:39 -0500 Subject: [PATCH 094/106] new revision for release candidate --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index e5f41c79106a3..0e637577433ec 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.25.100", + "version": "1.14.25.101", "commands": { "launch-element-zapper": { From 55be87223eb9ba5cb8183f345b2ba1b3cd597ed6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 16 Jan 2018 09:22:37 -0500 Subject: [PATCH 095/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/cs/messages.json | 22 +++++++++++----------- src/_locales/fa/messages.json | 12 ++++++------ src/_locales/tr/messages.json | 26 +++++++++++++------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index b75365c96c411..d928a65981c48 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "Konečně efektivní blokovač, který nezatěžuje CPU a paměť.", + "message": "Konečně efektivní blokovač. Nezatěžuje CPU a paměť.", "description": "this will be in the chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -52,7 +52,7 @@ "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Click to enable uBlock₀ for this site.", + "message": "Kliknutím povolíte uBlock₀ pro tento web.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -96,11 +96,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Click to block all popups on this site", + "message": "Kliknutím zablokujete všechny popupy pro tento web", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Zhbllokoj të gjitha dritaret automatike të faqes", + "message": "Kliknutím vypnete blokování všech popupů pro tento web", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -108,11 +108,11 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { - "message": "Click to block large media elements on this site", + "message": "Kliknutím zablokujete velké multimediální prvky na tomto webu", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "Click to no longer block large media elements on this site", + "message": "Kliknutím vypnete blokování velkých multimediálních prvků na tomto webu", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Click to disable cosmetic filtering on this site", + "message": "Kliknutím zakážete kosmetické filtrování na tomto webu", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Click to enable cosmetic filtering on this site", + "message": "Kliknutím povolíte kosmetické filtrování na tomto webu", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { @@ -132,11 +132,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Click to block remote fonts on this site", + "message": "Kliknutím zablokujete externí\/vzdálené fonty na tomto webu", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Click to no longer block remote fonts on this site", + "message": "Kliknutím vypnete blokování externích\/vzdálených fontů na tomto webu", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "Aktivní list", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index 342f62e0d597a..29c580a7ce57d 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -268,7 +268,7 @@ "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { - "message": "ممنوع کردن WebRTC از افشاسازی آدرس های IP محلی", + "message": "جلوگیری کردن از WebRTC از افشاسازی آدرس های IP محلی", "description": "English: " }, "settingPerSiteSwitchGroup": { @@ -292,7 +292,7 @@ "description": "" }, "settingsNoCSPReportsPrompt": { - "message": "مسدود کردن گزارشات سیاست امنیت محتوا", + "message": "مسدود کردن براساس گزارشات سیاست امنیت محتوا", "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" }, "settingsStorageUsed": { @@ -332,7 +332,7 @@ "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "

این گزینه Adblock Plus-compatible “element hiding” filters<\/a> را تجزیه و اجرا میکند. این فیلتر ها اساساً فنی هستند، آنها اشیاء موجود در صفحه وب را که مزاحم تلقی میشوند و با موتور فیلترینگ شبکه قابل بلاک کردن نیستند را حذف میکنند.<\/p>

فعال کردن این قابلیت مصرف حافظه ی uBlock<\/i>را افزایش میدهد.<\/p>", + "message": "

این گزینه فیلترهای “مخفی‌کردن اشیا” سازگار با ادبلاک پلاس<\/a> را تجزیه و اجرا میکند. این فیلتر ها اساساً فنی هستند، آنها اشیاء موجود در صفحه وب را که مزاحم تلقی میشوند و با موتور فیلترینگ شبکه قابل بلاک کردن نیستند را حذف میکنند.<\/p>

فعال کردن این قابلیت مصرف حافظه ی uBlock را افزایش میدهد.<\/p>", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -524,7 +524,7 @@ "description": "Tooltip informaing that the input field is to set the maximum number of entries in the log" }, "loggerURLFilteringContextLabel": { - "message": "محتوی:", + "message": "زمینه:", "description": "Label for the context selector" }, "loggerURLFilteringTypeLabel": { @@ -624,7 +624,7 @@ "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "تمام تنظیمات شده حذف و uBlock₀ دوباره راه اندازی خواهد شد.\n\nتنظیم مجدد uBlock₀ به تنظیمات کارخانه؟", + "message": "تمام تنظیمات شما حذف شده و uBlock₀ دوباره راه اندازی خواهد شد.\n\nتنظیم مجدد uBlock₀ به تنظیمات کارخانه؟", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { @@ -632,7 +632,7 @@ "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { - "message": "uBlock₀: لینک زیر به لیست فیلتر ها اضافه شود؟\n\nعنوان: \"{{title}}\"\nآدرس: {{url}}", + "message": "uBlock₀: آدرس اینترنتی زیر به فهرست فیلتر های سفارشی شما اضافه شود؟\n\nعنوان: \"{{title}}\"\nآدرس: {{url}}", "description": "English: The message seen by the user to confirm subscription to a ABP filter list" }, "elapsedOneMinuteAgo": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index ca981561212c4..c68fa1c7daf1d 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -44,7 +44,7 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "Tıklama: uBlock₀'i bu site için devre dışı bırak\/etkinleştir.\n\nCtrl+tıklama: uBlock₀'i sadece bu sayfa için devre dışı bırak.", + "message": "Tıklama: uBlock₀'i bu site için devre dışı bırak\/etkinleştir.\n\nCtrl+tıklama: uBlock₀'i yalnızca bu sayfada devre dışı bırak.", "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { @@ -68,7 +68,7 @@ "description": "Example: 15 or 13%" }, "popupBlockedSinceInstallPrompt": { - "message": "yüklendiğinden beri", + "message": "kurulumdan bu yana", "description": "English: since install" }, "popupOr": { @@ -228,11 +228,11 @@ "description": "English: Block element" }, "settingsCollapseBlockedPrompt": { - "message": "Engellenen reklamların yerlerini gizle", + "message": "Engellenmiş ögelerin yertutucularını gizle", "description": "English: Hide placeholders of blocked elements" }, "settingsIconBadgePrompt": { - "message": "Engellenmiş reklam sayısını simge üzerinde göster", + "message": "Engellenen istek sayısını simge üstünde göster", "description": "English: Show the number of blocked requests on the icon" }, "settingsTooltipsPrompt": { @@ -252,7 +252,7 @@ "description": "" }, "settingsAdvancedUserPrompt": { - "message": "Deneyimli kullanıcıyım (Okunması gerekir<\/a>)", + "message": "Deneyimli kullanıcıyım (okunması gerekir<\/a>)", "description": "" }, "settingsAdvancedUserSettings": { @@ -284,7 +284,7 @@ "description": "" }, "settingsNoLargeMediaPrompt": { - "message": "Belirlenenden büyük medya ögelerini engelle {{input:number}} kB", + "message": "{{input:number}} kB'tan büyük medya ögelerini engelle", "description": "" }, "settingsNoRemoteFontsPrompt": { @@ -328,7 +328,7 @@ "description": "A button in the in the _3rd-party filters_ pane" }, "3pParseAllABPHideFiltersPrompt1": { - "message": "Kozmetik süzgeçleri incele ve uygula.", + "message": "Kozmetik süzgeçleri incele ve uygula", "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { @@ -448,7 +448,7 @@ "description": "Will discard manually-edited content and exit manual-edit mode" }, "rulesImport": { - "message": "Dosyadan içe aktar...", + "message": "Dosyadan al...", "description": "" }, "rulesExport": { @@ -468,7 +468,7 @@ "description": "English: dynamic rule syntax and full documentation." }, "whitelistPrompt": { - "message": "Hangi alan adları için uBlock₀'in devre dışı olacağını belirten listeniz. Satır başına bir girdi. Geçersiz alan adları sessizce yok sayılır.", + "message": "Beyaz liste yönergeleri, uBlock₀'in devre dışı bırakılması gerektiği web sayfalarını belirler. Satır başına bir girdi. Geçersiz yönergeler sessizce yok sayılır ve yoruma dönüştürülür.", "description": "English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport": { @@ -516,11 +516,11 @@ "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { - "message": "günlük girişlerini süz", + "message": "günlük girdilerini süz", "description": "English: filter log entries" }, "logMaxEntriesTip": { - "message": "Maksimum günlük giriş sayısı", + "message": "En fazla günlük girdi sayısı", "description": "Tooltip informaing that the input field is to set the maximum number of entries in the log" }, "loggerURLFilteringContextLabel": { @@ -624,7 +624,7 @@ "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "Tüm ayarlarınızı silinecek, ve uBlock₀ yeniden başlayacak.\n\nuBlock₀ fabrika ayarlarına geri dönsün mü?", + "message": "Tüm ayarlarınızı silinecek, ve uBlock₀ yeniden başlayacak.\n\nuBlock₀ fabrika ayarlarına sıfırlansın mı?", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { @@ -728,7 +728,7 @@ "description": "used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning": { - "message": "Uyarı! Bu ayarları değiştirme sorumluluğu size aittir.", + "message": "Uyarı! Bu gelişmiş ayarları değiştirmenin sorumluluğu size aittir.", "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { From dada3fe6bcbcc9a687e00d073f3d02e6fec67238 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 18 Jan 2018 12:32:43 -0500 Subject: [PATCH 096/106] import translation work from https://crowdin.com/project/ublock --- dist/description/description-fa.txt | 4 ++-- src/_locales/fa/messages.json | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dist/description/description-fa.txt b/dist/description/description-fa.txt index b204bd8b4c5d0..1fba6ef815c7b 100644 --- a/dist/description/description-fa.txt +++ b/dist/description/description-fa.txt @@ -2,11 +2,11 @@ بررسی تصویری از کارایی این محصول: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared -کاربرد: دکمه ی پاور بزرگ در پنجره برای فعال یا غیر فعال کردن uBlock برای صفحه ی جاری است. فقط برای همین سایت اعمال میشود، دکمه ی پاوری برای تمام سایت ها نیست. +روش استفاده: دکمۀ قدرت بزرگ در پنجرۀ بالاپَر برای فعال یا غیرفعال کردن دائمی یوبلاک برای وب‌سایت فعلی می‌باشد. این فقط برای همین سایت اعمال میشود، این دکمه ی قدرتی برای تمام سایت ها نیست. *** -انعطاف پذیری آن بیشتر از "ad blocker" است: همچنین می تواند فیلتر ها را از هاست میزبان، بخواند و بسازد. +انعطاف پذیری آن بیشتر از "ad blocker" است: این یکی همچنین می تواند فیلتر‌هایی را از فایل‌های هاست‌های میزبان، خوانده و بسازد. بیرون از جعبه، این لیست فیلترها بارگذاری و اجرا میشوند: diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index 29c580a7ce57d..f2fd41ba3a86d 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -32,7 +32,7 @@ "description": "appears as tab name in dashboard" }, "statsPageName": { - "message": "uBlock₀ — لاگ درخواست های شبکه", + "message": "uBlock₀ — واقعه‌نگار", "description": "Title for the logger window" }, "aboutPageName": { @@ -80,7 +80,7 @@ "description": "English: Click to open the dashboard" }, "popupTipZapper": { - "message": "ورود به حالت انتخاب اشیاء", + "message": "ورود به حالت له کردن اشیاء", "description": "Tooltip for the element-zapper icon in the popup panel" }, "popupTipPicker": { @@ -88,7 +88,7 @@ "description": "English: Enter element picker mode" }, "popupTipLog": { - "message": "رفتن به لاگ درخواست", + "message": "بازکردن واقعه‌نگار", "description": "Tooltip used for the logger icon in the panel" }, "popupTipNoPopups": { @@ -104,7 +104,7 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { - "message": "تغییر وضعیت انسداد عناصر مدیای حجیم برای این سایت", + "message": "تغییر وضعیت مسدود کردن عناصر رسانه ای حجیم برای این سایت", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia1": { @@ -112,7 +112,7 @@ "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoLargeMedia2": { - "message": "کلیک کنید تا دیگر عناصر رسانه ای حجیم روی این سایت مسدود نشوند", + "message": "کلیک کنید تا از این به بعد عناصر رسانه ای حجیم روی این سایت مسدود نشوند", "description": "Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering": { @@ -128,7 +128,7 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { - "message": "تغییر وضعیت انسداد فونت های از راه دور برای این سایت", + "message": "تغییر وضعیت انسداد فونت های راه دور برای این سایت", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { @@ -136,11 +136,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "کلیک کنید تا دیگر فونت های راه دور در این سایت مسدود نشوند", + "message": "کلیک کنید تا از این به بعد فونت های راه دور در این سایت مسدود نشوند", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipGlobalRules": { - "message": "قوانین همگانی: این ستون برای قوانینی است که به تمامی سایت ها اعمال می شود.", + "message": "قوانین همگانی: این ستون برای قوانینی است که برای همۀ سایت ها اعمال می شوند.", "description": "Tooltip when hovering the top-most cell of the global-rules column." }, "popupTipLocalRules": { @@ -152,7 +152,7 @@ "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." }, "popupTipRevertRules": { - "message": "برای برگشت تغییرات کلیک کنید.", + "message": "برای دور انداختن تغییراتی که اعمال کرده اید کلیک کنید.", "description": "Tooltip when hovering over the eraser in the dynamic filtering pane." }, "popupAnyRulePrompt": { @@ -260,7 +260,7 @@ "description": "For the tooltip of a link which gives access to advanced settings" }, "settingsPrefetchingDisabledPrompt": { - "message": "غیر فعال کردن واکشی اولیه (برای جلوگیری از هر گونه اتصال برای درخواست های شبکه مسدود شده)", + "message": "غیر فعال کردن واکشی اولیه (برای جلوگیری از هر گونه اتصال برای درخواست های مسدود شدۀ شبکه)", "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { @@ -396,7 +396,7 @@ "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { - "message": "یک خطای شبکه از بروزشدن منابع جلوگیری کرد.", + "message": "یک خطای شبکه از بروزشدن این منبع جلوگیری کرد.", "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { @@ -576,7 +576,7 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringFinderSentence1": { - "message": "انسداد ایستا {{filter}} یاقت شد در:", + "message": "انسداد ایستا {{filter}} یافت شد در:", "description": "Below this sentence, the filter lists in which the filter was found" }, "aboutChangelog": { @@ -664,7 +664,7 @@ "description": "Firefox\/Fennec-specific: Show Dashboard" }, "showNetworkLogButton": { - "message": "نمایش درخواست ثبت شبکه", + "message": "نمایش واقعه‌نگار", "description": "Firefox\/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { @@ -680,7 +680,7 @@ "description": "English: Because of the following filter" }, "docblockedNoParamsPrompt": { - "message": "بدون پارامتر", + "message": "بدون پارامترها", "description": "label to be used for the parameter-less URL: https:\/\/cloud.githubusercontent.com\/assets\/585534\/9832014\/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { From 51fc60223aeacc62d2d29a001f901038da7129d7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 20 Jan 2018 09:10:23 -0500 Subject: [PATCH 097/106] fix #3450 --- src/js/cosmetic-filtering.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 06380d6fc1341..d634b93a105b4 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -107,7 +107,9 @@ var FilterHostname = function(s, hostname) { FilterHostname.prototype.fid = 8; FilterHostname.prototype.retrieve = function(hostname, out) { - if ( hostname.endsWith(this.hostname) ) { + if ( hostname.endsWith(this.hostname) === false ) { return; } + var i = hostname.length - this.hostname.length; + if ( i === 0 || hostname.charCodeAt(i-1) === 0x2E /* '.' */ ) { out.add(this.s); } }; From 36da71f5476ca53e744d9856e5de9a6879cccd7c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 30 Jan 2018 09:18:02 -0500 Subject: [PATCH 098/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/bn/messages.json | 2 +- src/_locales/hi/messages.json | 50 ++++++++++++++++---------------- src/_locales/ja/messages.json | 2 +- src/_locales/pt_BR/messages.json | 2 +- src/_locales/sv/messages.json | 10 +++---- src/_locales/te/messages.json | 2 +- 6 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/_locales/bn/messages.json b/src/_locales/bn/messages.json index f2f97c13088f4..e99ea6f254ea4 100644 --- a/src/_locales/bn/messages.json +++ b/src/_locales/bn/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "বর্তমান ট্যাব", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 653be5e2ce5a2..efe448d4aafe0 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -68,7 +68,7 @@ "description": "Example: 15 or 13%" }, "popupBlockedSinceInstallPrompt": { - "message": "इंस्टॉल से अब तक", + "message": "स्थापना से अब तक", "description": "English: since install" }, "popupOr": { @@ -296,11 +296,11 @@ "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" }, "settingsStorageUsed": { - "message": "Storage used: {{value}} bytes", + "message": "प्रचलित मैमोरी: {{value}} बाइट्स", "description": "English: Storage used: {{}} bytes" }, "settingsLastRestorePrompt": { - "message": "Last restore:", + "message": "अंतिम बहाल:", "description": "English: Last restore:" }, "settingsLastBackupPrompt": { @@ -320,11 +320,11 @@ "description": "A checkbox in the _3rd-party filters_ pane" }, "3pUpdateNow": { - "message": "अभी अपडेट करें", + "message": "अभी नवीकृत करें", "description": "A button in the in the _3rd-party filters_ pane" }, "3pPurgeAll": { - "message": "Purge all caches", + "message": "सभी अस्थायी मेमोरी को शुद्ध करे", "description": "A button in the in the _3rd-party filters_ pane" }, "3pParseAllABPHideFiltersPrompt1": { @@ -364,7 +364,7 @@ "description": "English: Malware domains" }, "3pGroupAnnoyances": { - "message": "Annoyances", + "message": "सतानेवाले विज्ञापन", "description": "The header identifying the filter lists in the category 'annoyances'" }, "3pGroupMultipurpose": { @@ -388,11 +388,11 @@ "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pLastUpdate": { - "message": "Last update: {{ago}}.\nClick to force an update.", + "message": "अन्तिम अद्यातन: {{ago}}. कृत्रिम नवीकरण के लिए क्लिक की जिए", "description": "used as a tooltip for the clock icon beside a list" }, "3pUpdating": { - "message": "Updating...", + "message": "नवीकरण प्रगति में हैं...", "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { @@ -448,7 +448,7 @@ "description": "Will discard manually-edited content and exit manual-edit mode" }, "rulesImport": { - "message": "Import from file...", + "message": "फाइल से आयात करे...", "description": "" }, "rulesExport": { @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "वर्तमान टैब", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { @@ -524,11 +524,11 @@ "description": "Tooltip informaing that the input field is to set the maximum number of entries in the log" }, "loggerURLFilteringContextLabel": { - "message": "Context:", + "message": "सन्दर्भ:", "description": "Label for the context selector" }, "loggerURLFilteringTypeLabel": { - "message": "प्रकार", + "message": "प्रकार:", "description": "Label for the type selector" }, "loggerURLFilteringHeader": { @@ -568,11 +568,11 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant": { - "message": "except when", + "message": "सिवाय", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartImportant": { - "message": "even if", + "message": "भले ही", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringFinderSentence1": { @@ -580,7 +580,7 @@ "description": "Below this sentence, the filter lists in which the filter was found" }, "aboutChangelog": { - "message": "Change log", + "message": "परिवर्तन सूची", "description": "English: Change log" }, "aboutWiki": { @@ -668,11 +668,11 @@ "description": "Firefox\/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { - "message": "off", + "message": "बंद", "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedPrompt1": { - "message": "uBlock Origin has prevented the following page from loading:", + "message": "uBlock Origin ने इस पेज को चालु होने से रोका हैं:", "description": "English: uBlock₀ has prevented the following page from loading:" }, "docblockedPrompt2": { @@ -684,15 +684,15 @@ "description": "label to be used for the parameter-less URL: https:\/\/cloud.githubusercontent.com\/assets\/585534\/9832014\/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { - "message": "Found in:", + "message": "इन में पायी है:", "description": "English: List of filter list names follows" }, "docblockedBack": { - "message": "Go back", + "message": "वापस जाएँ", "description": "English: Go back" }, "docblockedClose": { - "message": "Close this window", + "message": "इस विंडो को बंद करे", "description": "English: Close this window" }, "docblockedProceed": { @@ -700,11 +700,11 @@ "description": "English: Disable strict blocking for {{hostname}} ..." }, "docblockedDisableTemporary": { - "message": "Temporarily", + "message": "कुछ समय के लिए", "description": "English: Temporarily" }, "docblockedDisablePermanent": { - "message": "हमेशा", + "message": "स्थायी", "description": "English: Permanently" }, "cloudPush": { @@ -724,7 +724,7 @@ "description": "" }, "cloudDeviceNamePrompt": { - "message": "This device name:", + "message": "इस साधन का नाम:", "description": "used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning": { @@ -736,7 +736,7 @@ "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { - "message": "Apply changes", + "message": "परिवर्तन लागू करें", "description": "for generic 'Apply changes' buttons" }, "genericRevert": { @@ -748,7 +748,7 @@ "description": "" }, "contextMenuTemporarilyAllowLargeMediaElements": { - "message": "Temporarily allow large media elements", + "message": "कुछ समय के लिए विशाल तत्वोंको चलने की अनुमति दे", "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "dummy": { diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index c8d3b4a9e2740..3832289bc498e 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -336,7 +336,7 @@ "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { - "message": "要素隠蔽フィルターを無視する", + "message": "汎用的な要素隠蔽フィルターを無視する", "description": "This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo": { diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index df55c1cb2d174..ff2c5a6a2c18f 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -432,7 +432,7 @@ "description": "This will remove all temporary rules" }, "rulesCommit": { - "message": "Confirmar", + "message": "Aplicar", "description": "This will persist temporary rules" }, "rulesEdit": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index b2e1b5b19ed6f..d0c2e63aa1057 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -52,7 +52,7 @@ "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Klicka för att aktivera uBlock₀ för den här webbplatsen.", + "message": "Klicka för att aktivera uBlock₀ på den här webbplatsen.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -96,11 +96,11 @@ "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups1": { - "message": "Klicka för att blockera alla popup-fönster på denna sida", + "message": "Klicka för att blockera alla poppupp-fönster på den här webbplatsen", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoPopups2": { - "message": "Klicka för att inte längre blockera alla popup-fönster på den här webbplatsen", + "message": "Klicka för att inte längre blockera alla poppupp-fönster på den här webbplatsen", "description": "Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia": { @@ -120,11 +120,11 @@ "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering1": { - "message": "Klicka för att inaktivera kosmetiska filter för den här webbplatsen", + "message": "Klicka för att inaktivera kosmetiska filter på den här webbplatsen", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoCosmeticFiltering2": { - "message": "Klicka för att aktivera kosmetisk filtrering på denna sida", + "message": "Klicka för att aktivera kosmetisk filtrering på den här webbplatsen", "description": "Tooltip for the no-cosmetic-filtering per-site switch" }, "popupTipNoRemoteFonts": { diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index f75ede7aa90ec..9870d807c69a2 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -512,7 +512,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Current tab", + "message": "ప్రస్తుత ట్యాబ్", "description": "Appears in the logger's tab selector" }, "logFilterPrompt": { From 6602ed27e02bf81122c69cd7aa0f6ed0bff5f020 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 31 Jan 2018 12:29:01 -0500 Subject: [PATCH 099/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/id/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index 44965129a9635..97201ef9c761d 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -228,7 +228,7 @@ "description": "English: Block element" }, "settingsCollapseBlockedPrompt": { - "message": "Sembunyikan tempat elemen yang diblokir", + "message": "Sembunyikan wadah elemen yang diblokir", "description": "English: Hide placeholders of blocked elements" }, "settingsIconBadgePrompt": { From fb4d412cf6cb222fde601ab9192d3c307978e6ee Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 31 Jan 2018 12:32:07 -0500 Subject: [PATCH 100/106] new revision for release --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 0e637577433ec..50d56b7bd2170 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.14.25.101", + "version": "1.15.0", "commands": { "launch-element-zapper": { From 7f9435f8b225288cbf3c9a311ce72a785671ef85 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 31 Jan 2018 12:49:23 -0500 Subject: [PATCH 101/106] import Kazakh translation work --- dist/description/description-kk.txt | 49 ++ src/_locales/kk/messages.json | 758 ++++++++++++++++++++++++++++ tools/import-crowdin.sh | 2 + tools/make-opera.sh | 1 + 4 files changed, 810 insertions(+) create mode 100644 dist/description/description-kk.txt create mode 100644 src/_locales/kk/messages.json diff --git a/dist/description/description-kk.txt b/dist/description/description-kk.txt new file mode 100644 index 0000000000000..0407a694837a8 --- /dev/null +++ b/dist/description/description-kk.txt @@ -0,0 +1,49 @@ +An efficient blocker: easy on memory and CPU footprint, and yet can load and enforce thousands more filters than other popular blockers out there. + +Illustrated overview of its efficiency: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared + +Usage: The big power button in the popup is to permanently disable/enable uBlock for the current web site. It applies to the current web site only, it is not a global power button. + +*** + +Flexible, it's more than an "ad blocker": it can also read and create filters from hosts files. + +Out of the box, these lists of filters are loaded and enforced: + +- EasyList +- Peter Lowe’s Ad server list +- EasyPrivacy +- Malware domains + +More lists are available for you to select if you wish: + +- Fanboy’s Enhanced Tracking List +- Dan Pollock’s hosts file +- hpHosts’s Ad and tracking servers +- MVPS HOSTS +- Spam404 +- And many others + +Of course, the more filters enabled, the higher the memory footprint. Yet, even after adding Fanboy's two extra lists, hpHosts’s Ad and tracking servers, uBlock still has a lower memory footprint than other very popular blockers out there. + +Also, be aware that selecting some of these extra lists may lead to higher likelihood of web site breakage -- especially those lists which are normally used as hosts file. + +*** + +Without the preset lists of filters, this extension is nothing. So if ever you really do want to contribute something, think about the people working hard to maintain the filter lists you are using, which were made available to use by all for free. + +*** + +Еркін. +Open source with public license (GPLv3) +For users by users. + +Contributors @ Github: https://github.com/gorhill/uBlock/graphs/contributors +Contributors @ Crowdin: https://crowdin.net/project/ublock + +*** + +It's quite an early version, keep this in mind when you review. + +Project change log: +https://github.com/gorhill/uBlock/releases diff --git a/src/_locales/kk/messages.json b/src/_locales/kk/messages.json new file mode 100644 index 0000000000000..5a3f91d20c621 --- /dev/null +++ b/src/_locales/kk/messages.json @@ -0,0 +1,758 @@ +{ + "extName": { + "message": "uBlock₀", + "description": "extension name." + }, + "extShortDesc": { + "message": "Finally, an efficient blocker. Easy on CPU and memory.", + "description": "this will be in the chrome web store: must be 132 characters or less" + }, + "dashboardName": { + "message": "uBlock₀ — Dashboard", + "description": "English: uBlock₀ — Dashboard" + }, + "settingsPageName": { + "message": "Баптаулар", + "description": "appears as tab name in dashboard" + }, + "3pPageName": { + "message": "3-ші жақты сүзгілер", + "description": "appears as tab name in dashboard" + }, + "1pPageName": { + "message": "Менің сүзгілерім", + "description": "appears as tab name in dashboard" + }, + "rulesPageName": { + "message": "Менің ережелерім", + "description": "appears as tab name in dashboard" + }, + "whitelistPageName": { + "message": "Рұқсат тізімі", + "description": "appears as tab name in dashboard" + }, + "statsPageName": { + "message": "uBlock₀ — Logger", + "description": "Title for the logger window" + }, + "aboutPageName": { + "message": "Осы туралы", + "description": "appears as tab name in dashboard" + }, + "advancedSettingsPageName": { + "message": "Кеңейтілген баптаулар", + "description": "Title for the advanced settings page" + }, + "popupPowerSwitchInfo": { + "message": "Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page.", + "description": "English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." + }, + "popupPowerSwitchInfo1": { + "message": "Click to disable uBlock₀ for this site.\n\nCtrl+click to disable uBlock₀ only on this page.", + "description": "Message to be read by screen readers" + }, + "popupPowerSwitchInfo2": { + "message": "Click to enable uBlock₀ for this site.", + "description": "Message to be read by screen readers" + }, + "popupBlockedRequestPrompt": { + "message": "сұраным блокталды", + "description": "English: requests blocked" + }, + "popupBlockedOnThisPagePrompt": { + "message": "бұл парақта", + "description": "English: on this page" + }, + "popupBlockedStats": { + "message": "{{count}} немесе {{percent}}%", + "description": "Example: 15 or 13%" + }, + "popupBlockedSinceInstallPrompt": { + "message": "орнатылғаннан бастап", + "description": "English: since install" + }, + "popupOr": { + "message": "немесе", + "description": "English: or" + }, + "popupTipDashboard": { + "message": "Open the dashboard", + "description": "English: Click to open the dashboard" + }, + "popupTipZapper": { + "message": "Enter element zapper mode", + "description": "Tooltip for the element-zapper icon in the popup panel" + }, + "popupTipPicker": { + "message": "Enter element picker mode", + "description": "English: Enter element picker mode" + }, + "popupTipLog": { + "message": "Open the logger", + "description": "Tooltip used for the logger icon in the panel" + }, + "popupTipNoPopups": { + "message": "Toggle the blocking of all popups for this site", + "description": "Tooltip for the no-popups per-site switch" + }, + "popupTipNoPopups1": { + "message": "Click to block all popups on this site", + "description": "Tooltip for the no-popups per-site switch" + }, + "popupTipNoPopups2": { + "message": "Click to no longer block all popups on this site", + "description": "Tooltip for the no-popups per-site switch" + }, + "popupTipNoLargeMedia": { + "message": "Toggle the blocking of large media elements for this site", + "description": "Tooltip for the no-large-media per-site switch" + }, + "popupTipNoLargeMedia1": { + "message": "Click to block large media elements on this site", + "description": "Tooltip for the no-large-media per-site switch" + }, + "popupTipNoLargeMedia2": { + "message": "Click to no longer block large media elements on this site", + "description": "Tooltip for the no-large-media per-site switch" + }, + "popupTipNoCosmeticFiltering": { + "message": "Toggle cosmetic filtering for this site", + "description": "Tooltip for the no-cosmetic-filtering per-site switch" + }, + "popupTipNoCosmeticFiltering1": { + "message": "Click to disable cosmetic filtering on this site", + "description": "Tooltip for the no-cosmetic-filtering per-site switch" + }, + "popupTipNoCosmeticFiltering2": { + "message": "Click to enable cosmetic filtering on this site", + "description": "Tooltip for the no-cosmetic-filtering per-site switch" + }, + "popupTipNoRemoteFonts": { + "message": "Toggle the blocking of remote fonts for this site", + "description": "Tooltip for the no-remote-fonts per-site switch" + }, + "popupTipNoRemoteFonts1": { + "message": "Click to block remote fonts on this site", + "description": "Tooltip for the no-remote-fonts per-site switch" + }, + "popupTipNoRemoteFonts2": { + "message": "Click to no longer block remote fonts on this site", + "description": "Tooltip for the no-remote-fonts per-site switch" + }, + "popupTipGlobalRules": { + "message": "Global rules: this column is for rules which apply to all sites.", + "description": "Tooltip when hovering the top-most cell of the global-rules column." + }, + "popupTipLocalRules": { + "message": "Local rules: this column is for rules which apply to the current site only.\nLocal rules override global rules.", + "description": "Tooltip when hovering the top-most cell of the local-rules column." + }, + "popupTipSaveRules": { + "message": "Click to make your changes permanent.", + "description": "Tooltip when hovering over the padlock in the dynamic filtering pane." + }, + "popupTipRevertRules": { + "message": "Click to revert your changes.", + "description": "Tooltip when hovering over the eraser in the dynamic filtering pane." + }, + "popupAnyRulePrompt": { + "message": "барлығы", + "description": "" + }, + "popupImageRulePrompt": { + "message": "суреттер", + "description": "" + }, + "popup3pAnyRulePrompt": { + "message": "3-ші жақты", + "description": "" + }, + "popup3pPassiveRulePrompt": { + "message": "3-ші жақты css\/суреттер", + "description": "" + }, + "popupInlineScriptRulePrompt": { + "message": "inline scripts", + "description": "" + }, + "popup1pScriptRulePrompt": { + "message": "1st-party scripts", + "description": "" + }, + "popup3pScriptRulePrompt": { + "message": "3rd-party scripts", + "description": "" + }, + "popup3pFrameRulePrompt": { + "message": "3rd-party frames", + "description": "" + }, + "popupHitDomainCountPrompt": { + "message": "domains connected", + "description": "appears in popup" + }, + "popupHitDomainCount": { + "message": "{{count}} out of {{total}}", + "description": "appears in popup" + }, + "pickerCreate": { + "message": "Жасау", + "description": "English: Create" + }, + "pickerPick": { + "message": "Таңдау", + "description": "English: Pick" + }, + "pickerQuit": { + "message": "Шығу", + "description": "English: Quit" + }, + "pickerPreview": { + "message": "Алдын-ала қарау", + "description": "Element picker preview mode: will cause the elements matching the current filter to be removed from the page" + }, + "pickerNetFilters": { + "message": "Network filters", + "description": "English: header for a type of filter in the element picker dialog" + }, + "pickerCosmeticFilters": { + "message": "Cosmetic filters", + "description": "English: Cosmetic filters" + }, + "pickerCosmeticFiltersHint": { + "message": "Шерту, Ctrl-шерту", + "description": "English: Click, Ctrl-click" + }, + "pickerContextMenuEntry": { + "message": "Элементті блоктау", + "description": "English: Block element" + }, + "settingsCollapseBlockedPrompt": { + "message": "Hide placeholders of blocked elements", + "description": "English: Hide placeholders of blocked elements" + }, + "settingsIconBadgePrompt": { + "message": "Show the number of blocked requests on the icon", + "description": "English: Show the number of blocked requests on the icon" + }, + "settingsTooltipsPrompt": { + "message": "Disable tooltips", + "description": "A checkbox in the Settings pane" + }, + "settingsContextMenuPrompt": { + "message": "Make use of context menu where appropriate", + "description": "English: Make use of context menu where appropriate" + }, + "settingsColorBlindPrompt": { + "message": "Color-blind friendly", + "description": "English: Color-blind friendly" + }, + "settingsCloudStorageEnabledPrompt": { + "message": "Enable cloud storage support", + "description": "" + }, + "settingsAdvancedUserPrompt": { + "message": "I am an advanced user (required reading<\/a>)", + "description": "" + }, + "settingsAdvancedUserSettings": { + "message": "advanced settings", + "description": "For the tooltip of a link which gives access to advanced settings" + }, + "settingsPrefetchingDisabledPrompt": { + "message": "Disable pre-fetching (to prevent any connection for blocked network requests)", + "description": "English: " + }, + "settingsHyperlinkAuditingDisabledPrompt": { + "message": "Disable hyperlink auditing", + "description": "English: " + }, + "settingsWebRTCIPAddressHiddenPrompt": { + "message": "Prevent WebRTC from leaking local IP addresses", + "description": "English: " + }, + "settingPerSiteSwitchGroup": { + "message": "Default behavior", + "description": "" + }, + "settingPerSiteSwitchGroupSynopsis": { + "message": "These default behaviors can be overridden on a per-site basis", + "description": "" + }, + "settingsNoCosmeticFilteringPrompt": { + "message": "Disable cosmetic filtering", + "description": "" + }, + "settingsNoLargeMediaPrompt": { + "message": "Block media elements larger than {{input:number}} kB", + "description": "" + }, + "settingsNoRemoteFontsPrompt": { + "message": "Block remote fonts", + "description": "" + }, + "settingsNoCSPReportsPrompt": { + "message": "Block CSP reports", + "description": "background information: https:\/\/github.com\/gorhill\/uBlock\/issues\/3150" + }, + "settingsStorageUsed": { + "message": "Storage used: {{value}} bytes", + "description": "English: Storage used: {{}} bytes" + }, + "settingsLastRestorePrompt": { + "message": "Last restore:", + "description": "English: Last restore:" + }, + "settingsLastBackupPrompt": { + "message": "Last backup:", + "description": "English: Last backup:" + }, + "3pListsOfBlockedHostsPrompt": { + "message": "{{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:", + "description": "Appears at the top of the _3rd-party filters_ pane" + }, + "3pListsOfBlockedHostsPerListStats": { + "message": "{{used}} used out of {{total}}", + "description": "Appears aside each filter list in the _3rd-party filters_ pane" + }, + "3pAutoUpdatePrompt1": { + "message": "Auto-update filter lists", + "description": "A checkbox in the _3rd-party filters_ pane" + }, + "3pUpdateNow": { + "message": "Update now", + "description": "A button in the in the _3rd-party filters_ pane" + }, + "3pPurgeAll": { + "message": "Purge all caches", + "description": "A button in the in the _3rd-party filters_ pane" + }, + "3pParseAllABPHideFiltersPrompt1": { + "message": "Parse and enforce cosmetic filters", + "description": "English: Parse and enforce Adblock+ element hiding filters." + }, + "3pParseAllABPHideFiltersInfo": { + "message": "

This option enables the parsing and enforcing of Adblock Plus-compatible “element hiding” filters<\/a>. These filters are essentially cosmetic, they serve to hide elements in a web page which are deemed to be a visual nuisance, and which can't be blocked by the net request-based filtering engine.<\/p>

Enabling this feature increases uBlock₀'s memory footprint.<\/p>", + "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." + }, + "3pIgnoreGenericCosmeticFilters": { + "message": "Ignore generic cosmetic filters", + "description": "This will cause uBO to ignore all generic cosmetic filters." + }, + "3pIgnoreGenericCosmeticFiltersInfo": { + "message": "

Generic cosmetic filters are those cosmetic filters which are meant to apply on all web sites.

Though handled efficiently by uBlock₀, generic cosmetic filters may still contribute measurable memory and CPU overhead on some web pages, especially for large and long-lived ones.

Enabling this option will eliminate the memory and CPU overhead added to web pages as a result of handling generic cosmetic filters, and also lower the memory footprint of uBlock₀ itself.

It is recommended to enable this option on less powerful devices.", + "description": "Describes the purpose of the 'Ignore generic cosmetic filters' feature." + }, + "3pListsOfBlockedHostsHeader": { + "message": "Lists of blocked hosts", + "description": "English: Lists of blocked hosts" + }, + "3pApplyChanges": { + "message": "Өзерістерді іске асыру", + "description": "English: Apply changes" + }, + "3pGroupAds": { + "message": "Жарнамалар", + "description": "English: Ads" + }, + "3pGroupPrivacy": { + "message": "Жекелік", + "description": "English: Privacy" + }, + "3pGroupMalware": { + "message": "Malware domains", + "description": "English: Malware domains" + }, + "3pGroupAnnoyances": { + "message": "Annoyances", + "description": "The header identifying the filter lists in the category 'annoyances'" + }, + "3pGroupMultipurpose": { + "message": "Multipurpose", + "description": "English: Multipurpose" + }, + "3pGroupRegions": { + "message": "Regions, languages", + "description": "English: Regions, languages" + }, + "3pGroupCustom": { + "message": "Custom", + "description": "English: Custom" + }, + "3pExternalListsHint": { + "message": "One URL per line. Lines prefixed with ‘!’ will be ignored. Invalid URLs will be silently ignored.", + "description": "English: One URL per line. Lines prefixed with ‘!’ will be ignored. Invalid URLs will be silently ignored." + }, + "3pExternalListObsolete": { + "message": "Out of date.", + "description": "used as a tooltip for the out-of-date icon beside a list" + }, + "3pLastUpdate": { + "message": "Last update: {{ago}}.\nClick to force an update.", + "description": "used as a tooltip for the clock icon beside a list" + }, + "3pUpdating": { + "message": "Updating...", + "description": "used as a tooltip for the spinner icon beside a list" + }, + "3pNetworkError": { + "message": "A network error prevented the resource from being updated.", + "description": "used as a tooltip for error icon beside a list" + }, + "1pFormatHint": { + "message": "One filter per line. A filter can be a plain hostname, or an Adblock Plus-compatible filter. Lines prefixed with ‘!’ will be ignored.", + "description": "English: One filter per line. A filter can be a plain hostname, or an Adblock Plus-compatible filter. Lines prefixed with ‘!’ will be ignored." + }, + "1pImport": { + "message": "Import and append", + "description": "English: Import and append" + }, + "1pExport": { + "message": "Export", + "description": "English: Export" + }, + "1pExportFilename": { + "message": "my-ublock-static-filters_{{datetime}}.txt", + "description": "English: my-ublock-static-filters_{{datetime}}.txt" + }, + "1pApplyChanges": { + "message": "Apply changes", + "description": "English: Apply changes" + }, + "rulesPermanentHeader": { + "message": "Permanent rules", + "description": "header" + }, + "rulesTemporaryHeader": { + "message": "Temporary rules", + "description": "header" + }, + "rulesRevert": { + "message": "Қайтару", + "description": "This will remove all temporary rules" + }, + "rulesCommit": { + "message": "Commit", + "description": "This will persist temporary rules" + }, + "rulesEdit": { + "message": "Түзету", + "description": "Will enable manual-edit mode (textarea)" + }, + "rulesEditSave": { + "message": "Сақтау", + "description": "Will save manually-edited content and exit manual-edit mode" + }, + "rulesEditDiscard": { + "message": "Елемеу", + "description": "Will discard manually-edited content and exit manual-edit mode" + }, + "rulesImport": { + "message": "Файлдан импорттау...", + "description": "" + }, + "rulesExport": { + "message": "Файлға экспорттау", + "description": "" + }, + "rulesDefaultFileName": { + "message": "my-ublock-dynamic-rules_{{datetime}}.txt", + "description": "default file name to use" + }, + "rulesHint": { + "message": "List of your dynamic filtering rules.", + "description": "English: List of your dynamic filtering rules." + }, + "rulesFormatHint": { + "message": "Rule syntax: source destination type action<\/code> (full documentation<\/a>).", + "description": "English: dynamic rule syntax and full documentation." + }, + "whitelistPrompt": { + "message": "The whitelist directives dictate on which web pages uBlock Origin should be disabled. One entry per line. Invalid directives will be silently ignored and commented out.", + "description": "English: An overview of the content of the dashboard's Whitelist pane." + }, + "whitelistImport": { + "message": "Import and append", + "description": "English: Import and append" + }, + "whitelistExport": { + "message": "Экспорт", + "description": "English: Export" + }, + "whitelistExportFilename": { + "message": "my-ublock-whitelist_{{datetime}}.txt", + "description": "English: my-ublock-whitelist_{{datetime}}.txt" + }, + "whitelistApply": { + "message": "Өзерістерді іске асыру", + "description": "English: Apply changes" + }, + "logRequestsHeaderType": { + "message": "Түрі", + "description": "English: Type" + }, + "logRequestsHeaderDomain": { + "message": "Домен", + "description": "English: Domain" + }, + "logRequestsHeaderURL": { + "message": "URL", + "description": "English: URL" + }, + "logRequestsHeaderFilter": { + "message": "Сүзгі", + "description": "English: Filter" + }, + "logAll": { + "message": "Барлығы", + "description": "Appears in the logger's tab selector" + }, + "logBehindTheScene": { + "message": "Behind the scene", + "description": "Pretty name for behind-the-scene network requests" + }, + "loggerCurrentTab": { + "message": "Current tab", + "description": "Appears in the logger's tab selector" + }, + "logFilterPrompt": { + "message": "filter log entries", + "description": "English: filter log entries" + }, + "logMaxEntriesTip": { + "message": "Maximum number of log entries", + "description": "Tooltip informaing that the input field is to set the maximum number of entries in the log" + }, + "loggerURLFilteringContextLabel": { + "message": "Context:", + "description": "Label for the context selector" + }, + "loggerURLFilteringTypeLabel": { + "message": "Түрі:", + "description": "Label for the type selector" + }, + "loggerURLFilteringHeader": { + "message": "Dynamic URL filtering", + "description": "Small header to identify the dynamic URL filtering section" + }, + "loggerStaticFilteringHeader": { + "message": "Static filtering", + "description": "Small header to identify the static filtering section" + }, + "loggerStaticFilteringSentence": { + "message": "{{action}} network requests of {{type}} {{br}}which URL address matches {{url}} {{br}}and which originates {{origin}},{{br}}{{importance}} there is a matching exception filter.", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartBlock": { + "message": "Block", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartAllow": { + "message": "Allow", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartType": { + "message": "type “{{type}}”", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartAnyType": { + "message": "any type", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartOrigin": { + "message": "from “{{origin}}”", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartAnyOrigin": { + "message": "from anywhere", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartNotImportant": { + "message": "except when", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartImportant": { + "message": "even if", + "description": "Used in the static filtering wizard" + }, + "loggerStaticFilteringFinderSentence1": { + "message": "Static filter {{filter}} found in:", + "description": "Below this sentence, the filter lists in which the filter was found" + }, + "aboutChangelog": { + "message": "Change log", + "description": "English: Change log" + }, + "aboutWiki": { + "message": "Wiki", + "description": "English: project' wiki on Github" + }, + "aboutSupport": { + "message": "Support", + "description": "A link for where to get support" + }, + "aboutCode": { + "message": "Source code (GPLv3)", + "description": "English: Source code (GPLv3)" + }, + "aboutContributors": { + "message": "Contributors", + "description": "English: Contributors" + }, + "aboutBackupDataButton": { + "message": "Back up to file", + "description": "Text for button to create a backup of all settings" + }, + "aboutBackupFilename": { + "message": "my-ublock-backup_{{datetime}}.txt", + "description": "English: my-ublock-backup_{{datetime}}.txt" + }, + "aboutRestoreDataButton": { + "message": "Restore from file...", + "description": "English: Restore from file..." + }, + "aboutResetDataButton": { + "message": "Reset to default settings...", + "description": "English: Reset to default settings..." + }, + "aboutRestoreDataConfirm": { + "message": "All your settings will be overwritten using data backed up on {{time}}, and uBlock₀ will restart.\n\nOverwrite all existing settings using backed up data?", + "description": "Message asking user to confirm restore" + }, + "aboutRestoreDataError": { + "message": "The data could not be read or is invalid", + "description": "Message to display when an error occurred during restore" + }, + "aboutResetDataConfirm": { + "message": "All your settings will be removed, and uBlock₀ will restart.\n\nReset uBlock₀ to factory settings?", + "description": "Message asking user to confirm reset" + }, + "errorCantConnectTo": { + "message": "Network error: {{msg}}", + "description": "English: Network error: {{msg}}" + }, + "subscriberConfirm": { + "message": "uBlock₀: Add the following URL to your custom filter lists?\n\nTitle: \"{{title}}\"\nURL: {{url}}", + "description": "English: The message seen by the user to confirm subscription to a ABP filter list" + }, + "elapsedOneMinuteAgo": { + "message": "a minute ago", + "description": "English: a minute ago" + }, + "elapsedManyMinutesAgo": { + "message": "{{value}} minutes ago", + "description": "English: {{value}} minutes ago" + }, + "elapsedOneHourAgo": { + "message": "an hour ago", + "description": "English: an hour ago" + }, + "elapsedManyHoursAgo": { + "message": "{{value}} hours ago", + "description": "English: {{value}} hours ago" + }, + "elapsedOneDayAgo": { + "message": "a day ago", + "description": "English: a day ago" + }, + "elapsedManyDaysAgo": { + "message": "{{value}} days ago", + "description": "English: {{value}} days ago" + }, + "showDashboardButton": { + "message": "Show Dashboard", + "description": "Firefox\/Fennec-specific: Show Dashboard" + }, + "showNetworkLogButton": { + "message": "Show Logger", + "description": "Firefox\/Fennec-specific: Show Logger" + }, + "fennecMenuItemBlockingOff": { + "message": "off", + "description": "Firefox-specific: appears as 'uBlock₀ (off)'" + }, + "docblockedPrompt1": { + "message": "uBlock Origin has prevented the following page from loading:", + "description": "English: uBlock₀ has prevented the following page from loading:" + }, + "docblockedPrompt2": { + "message": "Because of the following filter", + "description": "English: Because of the following filter" + }, + "docblockedNoParamsPrompt": { + "message": "without parameters", + "description": "label to be used for the parameter-less URL: https:\/\/cloud.githubusercontent.com\/assets\/585534\/9832014\/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" + }, + "docblockedFoundIn": { + "message": "Found in:", + "description": "English: List of filter list names follows" + }, + "docblockedBack": { + "message": "Go back", + "description": "English: Go back" + }, + "docblockedClose": { + "message": "Close this window", + "description": "English: Close this window" + }, + "docblockedProceed": { + "message": "Disable strict blocking for {{hostname}}", + "description": "English: Disable strict blocking for {{hostname}} ..." + }, + "docblockedDisableTemporary": { + "message": "Temporarily", + "description": "English: Temporarily" + }, + "docblockedDisablePermanent": { + "message": "Permanently", + "description": "English: Permanently" + }, + "cloudPush": { + "message": "Export to cloud storage", + "description": "tooltip" + }, + "cloudPull": { + "message": "Import from cloud storage", + "description": "tooltip" + }, + "cloudPullAndMerge": { + "message": "Import from cloud storage and merge with current settings", + "description": "tooltip" + }, + "cloudNoData": { + "message": "...\n...", + "description": "" + }, + "cloudDeviceNamePrompt": { + "message": "This device name:", + "description": "used as a prompt for the user to provide a custom device name" + }, + "advancedSettingsWarning": { + "message": "Warning! Change these advanced settings at your own risk.", + "description": "A warning to users at the top of 'Advanced settings' page" + }, + "genericSubmit": { + "message": "Submit", + "description": "for generic 'Submit' buttons" + }, + "genericApplyChanges": { + "message": "Apply changes", + "description": "for generic 'Apply changes' buttons" + }, + "genericRevert": { + "message": "Revert", + "description": "for generic 'Revert' buttons" + }, + "genericBytes": { + "message": "bytes", + "description": "" + }, + "contextMenuTemporarilyAllowLargeMediaElements": { + "message": "Temporarily allow large media elements", + "description": "A context menu entry, present when large media elements have been blocked on the current site" + }, + "dummy": { + "message": "This entry must be the last one", + "description": "so we dont need to deal with comma for last entry" + } +} \ No newline at end of file diff --git a/tools/import-crowdin.sh b/tools/import-crowdin.sh index e635e9a923029..06c8af6e2366f 100755 --- a/tools/import-crowdin.sh +++ b/tools/import-crowdin.sh @@ -36,6 +36,7 @@ cp $SRC/id/messages.json $DES/id/messages.json cp $SRC/it/messages.json $DES/it/messages.json cp $SRC/ja/messages.json $DES/ja/messages.json cp $SRC/ka/messages.json $DES/ka/messages.json +cp $SRC/kk/messages.json $DES/kk/messages.json cp $SRC/kn/messages.json $DES/kn/messages.json cp $SRC/ko/messages.json $DES/ko/messages.json cp $SRC/lt/messages.json $DES/lt/messages.json @@ -93,6 +94,7 @@ cp $SRC/id/description.txt $DES/description-id.txt cp $SRC/it/description.txt $DES/description-it.txt cp $SRC/ja/description.txt $DES/description-ja.txt cp $SRC/ka/description.txt $DES/description-ka.txt +cp $SRC/kk/description.txt $DES/description-kk.txt cp $SRC/ko/description.txt $DES/description-ko.txt cp $SRC/kn/description.txt $DES/description-kn.txt cp $SRC/lt/description.txt $DES/description-lt.txt diff --git a/tools/make-opera.sh b/tools/make-opera.sh index 5dd9c91e3b4d2..7e00d3287511e 100755 --- a/tools/make-opera.sh +++ b/tools/make-opera.sh @@ -35,6 +35,7 @@ cp platform/opera/manifest.json $DES/ rm -r $DES/_locales/cv rm -r $DES/_locales/hi rm -r $DES/_locales/ka +rm -r $DES/_locales/kk rm -r $DES/_locales/mr rm -r $DES/_locales/ta From 026286977e4ea81f676558f5cd25f9702b164b55 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Feb 2018 07:04:04 -0500 Subject: [PATCH 102/106] new revision for release --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 50d56b7bd2170..f9db02ccb5543 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.15.0", + "version": "1.15.2", "commands": { "launch-element-zapper": { From 613ff9cc9b4c5f86c83de9eac71194aa056111b6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Feb 2018 09:34:27 -0500 Subject: [PATCH 103/106] tentatively fix #3478: need feedback from reporter --- src/js/traffic.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/js/traffic.js b/src/js/traffic.js index 9a7dbf97ba95a..456dfa8b16492 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -812,6 +812,12 @@ var filterDocument = (function() { }; return function(pageStore, details) { + // https://github.com/gorhill/uBlock/issues/3478 + var statusCode = details.statusCode || 0; + if ( statusCode !== 0 && (statusCode < 200 || statusCode >= 300) ) { + return; + } + var hostname = µb.URI.hostnameFromURI(details.url); if ( hostname === '' ) { return; } From ae4677c0c7a30fffb1065fa0024d4bbff68e2210 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Feb 2018 09:35:29 -0500 Subject: [PATCH 104/106] new revision for release candidate --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index f9db02ccb5543..d379a6e1a5df6 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.15.2", + "version": "1.15.3.100", "commands": { "launch-element-zapper": { From d1d96ba30f8308f3c72bd30018395cd1a00046ae Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Feb 2018 12:06:52 -0500 Subject: [PATCH 105/106] new revision for stable release --- platform/chromium/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index d379a6e1a5df6..ab31d6feddaf8 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.15.3.100", + "version": "1.15.4", "commands": { "launch-element-zapper": { From 43eab7aff7026bbc20a882557f64241e94d4237b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 3 Feb 2018 12:10:01 -0500 Subject: [PATCH 106/106] import translation work from https://crowdin.com/project/ublock --- src/_locales/ka/messages.json | 68 +++++++++++++++++------------------ src/_locales/sq/messages.json | 14 ++++---- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 370a75e98ee24..522a86169472b 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -356,11 +356,11 @@ "description": "English: Ads" }, "3pGroupPrivacy": { - "message": "კონფიდენციალურობა", + "message": "პირადულობა", "description": "English: Privacy" }, "3pGroupMalware": { - "message": "მავნე დომეინები", + "message": "მავნე დომენები", "description": "English: Malware domains" }, "3pGroupAnnoyances": { @@ -408,7 +408,7 @@ "description": "English: Import and append" }, "1pExport": { - "message": "გამოტანა", + "message": "შენახვა", "description": "English: Export" }, "1pExportFilename": { @@ -436,7 +436,7 @@ "description": "This will persist temporary rules" }, "rulesEdit": { - "message": "რედაქტირება", + "message": "შეცვლა", "description": "Will enable manual-edit mode (textarea)" }, "rulesEditSave": { @@ -444,7 +444,7 @@ "description": "Will save manually-edited content and exit manual-edit mode" }, "rulesEditDiscard": { - "message": "უკუგდება", + "message": "გაუქმება", "description": "Will discard manually-edited content and exit manual-edit mode" }, "rulesImport": { @@ -508,7 +508,7 @@ "description": "Appears in the logger's tab selector" }, "logBehindTheScene": { - "message": "სცენის უკან", + "message": "ფარული მოთხოვნები", "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { @@ -636,87 +636,87 @@ "description": "English: The message seen by the user to confirm subscription to a ABP filter list" }, "elapsedOneMinuteAgo": { - "message": "a minute ago", + "message": "ერთი წუთის წინ", "description": "English: a minute ago" }, "elapsedManyMinutesAgo": { - "message": "{{value}} minutes ago", + "message": "{{value}} წუთის წინ", "description": "English: {{value}} minutes ago" }, "elapsedOneHourAgo": { - "message": "an hour ago", + "message": "ერთი საათის წინ", "description": "English: an hour ago" }, "elapsedManyHoursAgo": { - "message": "{{value}} hours ago", + "message": "{{value}} საათის წინ", "description": "English: {{value}} hours ago" }, "elapsedOneDayAgo": { - "message": "a day ago", + "message": "ერთი დღის წინ", "description": "English: a day ago" }, "elapsedManyDaysAgo": { - "message": "{{value}} days ago", + "message": "{{value}} დღის წინ", "description": "English: {{value}} days ago" }, "showDashboardButton": { - "message": "Show Dashboard", + "message": "ხელსაწყოების გვერდის ჩვენება", "description": "Firefox\/Fennec-specific: Show Dashboard" }, "showNetworkLogButton": { - "message": "Show Logger", + "message": "აღრიცხვის ჩვენება", "description": "Firefox\/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff": { - "message": "off", + "message": "გამორთული", "description": "Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedPrompt1": { - "message": "uBlock Origin has prevented the following page from loading:", + "message": "uBlock Origin-მა შეზღუდა მოცემული გვერდის ჩატვირთვა:", "description": "English: uBlock₀ has prevented the following page from loading:" }, "docblockedPrompt2": { - "message": "Because of the following filter", + "message": "მოცემული ფილტრიდან გამომდინარე", "description": "English: Because of the following filter" }, "docblockedNoParamsPrompt": { - "message": "without parameters", + "message": "პარამეტრების გარეშე", "description": "label to be used for the parameter-less URL: https:\/\/cloud.githubusercontent.com\/assets\/585534\/9832014\/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png" }, "docblockedFoundIn": { - "message": "Found in:", + "message": "პოვნა:", "description": "English: List of filter list names follows" }, "docblockedBack": { - "message": "Go back", + "message": "უკან დაბრუნება", "description": "English: Go back" }, "docblockedClose": { - "message": "Close this window", + "message": "ფანჯრის დახურვა", "description": "English: Close this window" }, "docblockedProceed": { - "message": "Disable strict blocking for {{hostname}}", + "message": "მკაცრი შეზღუდვის მოხსნა საიტისთვის: {{hostname}}", "description": "English: Disable strict blocking for {{hostname}} ..." }, "docblockedDisableTemporary": { - "message": "Temporarily", + "message": "დროებით", "description": "English: Temporarily" }, "docblockedDisablePermanent": { - "message": "Permanently", + "message": "მუდმივად", "description": "English: Permanently" }, "cloudPush": { - "message": "Export to cloud storage", + "message": "ღრუბლოვან საცავში შენახვა", "description": "tooltip" }, "cloudPull": { - "message": "Import from cloud storage", + "message": "ღრუბლოვანი საცავიდან გადმოტანა", "description": "tooltip" }, "cloudPullAndMerge": { - "message": "Import from cloud storage and merge with current settings", + "message": "ღრუბლოვანი საცავიდან გადმოტანა და არსებულ პარამეტრებთან მისადაგება", "description": "tooltip" }, "cloudNoData": { @@ -724,31 +724,31 @@ "description": "" }, "cloudDeviceNamePrompt": { - "message": "This device name:", + "message": "ამ მოწყობილობის დასახელება:", "description": "used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning": { - "message": "Warning! Change these advanced settings at your own risk.", + "message": "გაფრთხილება! დამატებითი პარამეტრებს ცვლილების შედეგებზე, თავად ხართ პასუხისმგებელი.", "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { - "message": "Submit", + "message": "მიღება", "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { - "message": "Apply changes", + "message": "ცვლილებების მისადაგება", "description": "for generic 'Apply changes' buttons" }, "genericRevert": { - "message": "Revert", + "message": "დაბრუნება", "description": "for generic 'Revert' buttons" }, "genericBytes": { - "message": "bytes", + "message": "ბაიტი", "description": "" }, "contextMenuTemporarilyAllowLargeMediaElements": { - "message": "Temporarily allow large media elements", + "message": "დიდი მედია-ელემენტების დროებით დაშვება", "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "dummy": { diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 00b980ada26a7..a2a402a1fa3e9 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -276,7 +276,7 @@ "description": "" }, "settingPerSiteSwitchGroupSynopsis": { - "message": "Këto vlera bëhen të panevojshme, sipas rastit", + "message": "Këto vlera mund të ndryshohen në bazë të faqeve", "description": "" }, "settingsNoCosmeticFilteringPrompt": { @@ -440,7 +440,7 @@ "description": "Will enable manual-edit mode (textarea)" }, "rulesEditSave": { - "message": "Ruaj", + "message": "Regjistroj", "description": "Will save manually-edited content and exit manual-edit mode" }, "rulesEditDiscard": { @@ -460,7 +460,7 @@ "description": "default file name to use" }, "rulesHint": { - "message": "Lista e rregullave tuaja për filtrimin dinamik.", + "message": "Lista e rregullave për filtrimin dinamik.", "description": "English: List of your dynamic filtering rules." }, "rulesFormatHint": { @@ -612,11 +612,11 @@ "description": "English: Restore from file..." }, "aboutResetDataButton": { - "message": "Kthej parametrat e mëparshëm...", + "message": "Kthej parametrat fillestarë...", "description": "English: Reset to default settings..." }, "aboutRestoreDataConfirm": { - "message": "Të gjithë parametrat do të mbishkruhen me të dhënat e kopjuara më {{time}}, dhe uBlock₀ do të hapet përsëri.\n\nDo i mbishkruani parametrat aktualë?", + "message": "Të gjithë parametrat do të mbishkruhen me të dhënat e kopjuara më {{time}}, dhe uBlock₀ do të hapet përsëri.\n\nTë mbishkruhen parametrat aktualë?", "description": "Message asking user to confirm restore" }, "aboutRestoreDataError": { @@ -624,7 +624,7 @@ "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { - "message": "Të gjithë parametrat do të fshihen dhe uBlock₀ do të hapet përsëri.\n\nDo i ktheni parametrat origjinalë?", + "message": "Të gjithë parametrat do të fshihen dhe uBlock₀ do të hapet përsëri.\n\nTë kthehen parametrat origjinalë?", "description": "Message asking user to confirm reset" }, "errorCantConnectTo": { @@ -632,7 +632,7 @@ "description": "English: Network error: {{msg}}" }, "subscriberConfirm": { - "message": "uBlock₀: Do e shtoni këtë adresën në listën e filtrave tuaj?\n\nTitulli: \"{{title}}\"\nURL: {{url}}", + "message": "uBlock₀: Të shtohet adresa në listën e filtrave tuaj?\n\nTitulli: \"{{title}}\"\nURL: {{url}}", "description": "English: The message seen by the user to confirm subscription to a ABP filter list" }, "elapsedOneMinuteAgo": {