From 235d1266e8283c8afc27e9b20ad1657d5a92d995 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 3 Dec 2016 09:21:31 -0500 Subject: [PATCH 0001/4093] fix #2210 --- src/js/logger-ui.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 9bf8509f66bb7..ba05a6bd19b30 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -540,8 +540,9 @@ var renderLogEntry = function(entry) { } // Fields common to all rows. - var time = new Date(entry.tstamp); - tr.cells[0].textContent = padTo2(time.getHours()) + ':' + + var time = logDate; + time.setTime(entry.tstamp - logDateTimezoneOffset); + tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' + padTo2(time.getMinutes()) + ':' + padTo2(time.getSeconds()); @@ -561,6 +562,10 @@ var renderLogEntry = function(entry) { return tr; }; +// Reuse date objects. +var logDate = new Date(), + logDateTimezoneOffset = logDate.getTimezoneOffset() * 60000; + /******************************************************************************/ var renderLogEntries = function(response) { From 4837b15895f0da7d4b0de51bc79f058dda1b36d8 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 3 Dec 2016 14:03:28 -0500 Subject: [PATCH 0002/4093] fix #2206 --- src/js/dashboard.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/dashboard.js b/src/js/dashboard.js index b3cdee4689b19..052c838ebfb89 100644 --- a/src/js/dashboard.js +++ b/src/js/dashboard.js @@ -40,7 +40,9 @@ var resizeFrame = function() { var loadDashboardPanel = function() { var pane = window.location.hash.slice(1); if ( pane === '' ) { - pane = 'settings.html'; + pane = vAPI.localStorage.getItem('dashboardLastVisitedPane') || 'settings.html'; + } else { + vAPI.localStorage.setItem('dashboardLastVisitedPane', pane); } var tabButton = uDom('[href="#' + pane + '"]'); if ( !tabButton || tabButton.hasClass('selected') ) { From d1ac1286b7b234c7e221f34638b37e304608d02c Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 7 Dec 2016 09:43:11 -0500 Subject: [PATCH 0003/4093] addendum to fix for #2206: auto scroll to the end of user filters text box --- src/js/1p-filters.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 307a70e4f4bad..77ba955815796 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -36,22 +36,29 @@ var cachedUserFilters = ''; // This is to give a visual hint that the content of user blacklist has changed. -function userFiltersChanged() { - var changed = uDom.nodeFromId('userFilters').value.trim() !== cachedUserFilters; +function userFiltersChanged(changed) { + if ( typeof changed !== 'boolean' ) { + changed = uDom.nodeFromId('userFilters').value.trim() !== cachedUserFilters; + } uDom.nodeFromId('userFiltersApply').disabled = !changed; uDom.nodeFromId('userFiltersRevert').disabled = !changed; } /******************************************************************************/ -function renderUserFilters() { +function renderUserFilters(first) { var onRead = function(details) { - if ( details.error ) { - return; - } + if ( details.error ) { return; } + var textarea = uDom.nodeFromId('userFilters'); cachedUserFilters = details.content.trim(); - uDom.nodeFromId('userFilters').value = details.content; - userFiltersChanged(); + textarea.value = details.content; + if ( first ) { + textarea.value += '\n'; + var textlen = textarea.value.length; + textarea.setSelectionRange(textlen, textlen); + textarea.focus(); + } + userFiltersChanged(false); }; messaging.send('dashboard', { what: 'readUserFilters' }, onRead); } @@ -195,7 +202,7 @@ uDom('#userFilters').on('input', userFiltersChanged); uDom('#userFiltersApply').on('click', applyChanges); uDom('#userFiltersRevert').on('click', revertChanges); -renderUserFilters(); +renderUserFilters(true); /******************************************************************************/ From cfa70bde7b3afe37aacd4c4ff7b23cc38d89774f Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 7 Dec 2016 09:49:43 -0500 Subject: [PATCH 0004/4093] minor code review --- platform/chromium/vapi-background.js | 76 +++++++++++++--------------- 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 573fc0b1d53fb..3aac3c4a1fcab 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -21,12 +21,12 @@ // For background page +'use strict'; + /******************************************************************************/ (function() { -'use strict'; - /******************************************************************************/ var vAPI = self.vAPI = self.vAPI || {}; @@ -873,20 +873,26 @@ vAPI.net.registerListeners = function() { is_v49_55 = /\bChrom[a-z]+\/(?:49|5[012345])\b/.test(navigator.userAgent); // Chromium-based browsers understand 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 = [ + 'main_frame', + 'sub_frame', + 'stylesheet', + 'script', + 'image', + 'object', + 'xmlhttprequest', + 'other' + ]; + + var extToTypeMap = new Map([ + ['eot','font'],['otf','font'],['svg','font'],['ttf','font'],['woff','font'],['woff2','font'], + ['mp3','media'],['mp4','media'],['webm','media'], + ['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image'] + ]); var denormalizeTypes = function(aa) { if ( aa.length === 0 ) { - return Object.keys(validTypes); + return validTypes; } var out = []; var i = aa.length, @@ -894,7 +900,7 @@ vAPI.net.registerListeners = function() { needOther = true; while ( i-- ) { type = aa[i]; - if ( validTypes.hasOwnProperty(type) ) { + if ( validTypes.indexOf(type) !== -1 ) { out.push(type); } if ( type === 'other' ) { @@ -933,49 +939,35 @@ vAPI.net.registerListeners = function() { return; } - var path = µburi.pathFromURI(details.url); - var pos = path.indexOf('.', path.length - 6); - - // https://github.com/chrisaljoudi/uBlock/issues/862 - // If no transposition possible, transpose to `object` as per - // Chromium bug 410382 (see below) - if ( pos !== -1 ) { - var needle = path.slice(pos) + '.'; - if ( '.eot.ttf.otf.svg.woff.woff2.'.indexOf(needle) !== -1 ) { - details.type = 'font'; - return; - } - - if ( '.mp3.mp4.webm.'.indexOf(needle) !== -1 ) { - details.type = 'media'; - return; - } - - // Still need this because often behind-the-scene requests are wrongly - // categorized as 'other' - if ( '.ico.png.gif.jpg.jpeg.webp.'.indexOf(needle) !== -1 ) { - details.type = 'image'; - return; - } + // Try to map known "extension" part of URL to request type. + var path = µburi.pathFromURI(details.url), + pos = path.indexOf('.', path.length - 6), + type; + if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) { + details.type = type; + return; } // Try to extract type from response headers if present. if ( details.responseHeaders ) { - var contentType = headerValue(details.responseHeaders, 'content-type'); - if ( contentType.startsWith('font/') ) { + type = headerValue(details.responseHeaders, 'content-type'); + if ( type.startsWith('font/') ) { details.type = 'font'; return; } - if ( contentType.startsWith('image/') ) { + if ( type.startsWith('image/') ) { details.type = 'image'; return; } - if ( contentType.startsWith('audio/') || contentType.startsWith('video/') ) { + if ( type.startsWith('audio/') || type.startsWith('video/') ) { details.type = 'media'; return; } } + // https://github.com/chrisaljoudi/uBlock/issues/862 + // If no transposition possible, transpose to `object` as per + // Chromium bug 410382 // https://code.google.com/p/chromium/issues/detail?id=410382 if ( is_v38_48 ) { details.type = 'object'; From ce0b5ab5fb28c21c2bdc9c03055431e13bf9df7d Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 7 Dec 2016 09:51:46 -0500 Subject: [PATCH 0005/4093] translation work from https://crowdin.com/project/ublock --- src/_locales/bg/messages.json | 2 +- src/_locales/et/messages.json | 2 +- src/_locales/hi/messages.json | 14 +++++++------- src/_locales/pl/messages.json | 2 +- src/_locales/vi/messages.json | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 25dedb371de70..782775f42dbc6 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -680,7 +680,7 @@ "description":"used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning":{ - "message":"Внимание! Промяната на тези разширени настройки е на ваш риск.", + "message":"Внимание! Променяте тези настройки на свой собствен риск.", "description":"A warning to users at the top of 'Advanced settings' page" }, "genericSubmit":{ diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 9683ded94e910..edd521c2f94f1 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Nimekiri domeenidest, millel uBlock₀ keelatakse. Üks domeen rea kohta, vigaseid domeeninimesid eiratakse.", + "message":"Nimekiri domeenidest, millel uBlock₀ keelatakse. Üks domeen rea kohta, vigased domeeninimed eiratakse vaikimisi ning kommenteeritakse välja.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 8cccf9ac3a8a2..2777ccbb282bc 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -256,7 +256,7 @@ "description":"English: Last restore:" }, "settingsLastBackupPrompt":{ - "message":"Last backup:", + "message":"पिछला बेकप", "description":"English: Last backup:" }, "3pListsOfBlockedHostsPrompt":{ @@ -496,11 +496,11 @@ "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartBlock":{ - "message":"Block", + "message":"रोको", "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAllow":{ - "message":"Allow", + "message":"छोडो", "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartType":{ @@ -508,7 +508,7 @@ "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyType":{ - "message":"any type", + "message":"हर टइप", "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartOrigin":{ @@ -516,7 +516,7 @@ "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyOrigin":{ - "message":"from anywhere", + "message":"कहि से भी", "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant":{ @@ -540,7 +540,7 @@ "description":"English: project' wiki on Github" }, "aboutSupport":{ - "message":"Support", + "message":"मदद", "description":"A link for where to get support" }, "aboutCode":{ @@ -560,7 +560,7 @@ "description":"English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton":{ - "message":"Restore from file...", + "message":"फाइल से वापस करो", "description":"English: Restore from file..." }, "aboutResetDataButton":{ diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index 3820731dfd4b0..942351821b327 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Twoja lista nazw hostów, na których uBlock zostanie wyłączony. Jeden wpis na linię. Błędne nazwy hostów zostaną zignorowane.", + "message":"Wytyczne wyjątków nakazują, na których stronach uBlock Origin powinien zostać wyłączony. Jeden wpis na linię. Nieprawidłowe wytyczne zostaną bez powiadomienia zignorowane i wykomentowane.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index d8a3ae58bf21f..f112d6f706eeb 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -40,7 +40,7 @@ "description":"appears as tab name in dashboard" }, "advancedSettingsPageName":{ - "message":"Advanced settings", + "message":"Cài đặt nâng cao", "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ @@ -212,7 +212,7 @@ "description":"" }, "settingsAdvancedUserSettings":{ - "message":"advanced settings", + "message":"cài đặt nâng cao", "description":"For the tooltip of a link which gives access to advanced settings" }, "settingsPrefetchingDisabledPrompt":{ @@ -680,7 +680,7 @@ "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":"Cảnh báo! Bạn phải chịu trách nhiệm cho những nguy cơ khi thay đổi các cài đặt nâng cao này.", "description":"A warning to users at the top of 'Advanced settings' page" }, "genericSubmit":{ @@ -688,7 +688,7 @@ "description":"for generic 'Submit' buttons" }, "genericApplyChanges":{ - "message":"Apply changes", + "message":"Áp dụng các thay đổi", "description":"for generic 'Apply changes' buttons" }, "genericRevert":{ From a727a99e3538af68dbc573e4e76670054248daa9 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 7 Dec 2016 09:52:42 -0500 Subject: [PATCH 0006/4093] 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 b56dd00eccf1e..528b32d2df112 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.1.0", + "version": "1.10.1.1", "default_locale": "en", "description": "__MSG_extShortDesc__", From 51c73cfd494ebf017eb505df20b191a0137247a3 Mon Sep 17 00:00:00 2001 From: euf Date: Wed, 7 Dec 2016 18:01:46 +0300 Subject: [PATCH 0007/4093] Added a link to Safari version at Readme.md (#2222) Safari version was announced at #1596 and now available --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f216c8d3809e4..0aed9e4bb4a0e 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,11 @@ The Firefox version of uBlock Origin has [an extra feature](https://github.com/g Also of interest: [Deploying uBlock Origin for Firefox with CCK2 and Group Policy](http://decentsecurity.com/ublock-for-firefox-deployment/). -##### Debian/Ubuntu +#### Safari (macOS) + +Early development version: . + +#### Debian / Ubuntu Thanks to Debian contributor [Sean Whitton](https://wiki.debian.org/SeanWhitton), users of Debian 9 or later or Ubuntu 16.04 or later may simply `apt-get install xul-ext-ublock-origin`. From 6aa5e430399bc24196567dba288a823c77357cf5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 7 Dec 2016 10:05:29 -0500 Subject: [PATCH 0008/4093] Update README.md --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0aed9e4bb4a0e..a19148e4148fa 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ uBlock Origin * [Chromium](#chromium) * [Firefox](#firefox--firefox-for-android) * [Microsoft Edge](#microsoft-edge) + - [Safari (macOS)](#safari-macos) * [Release History](#release-history) * [Privacy policy](https://github.com/gorhill/uBlock/wiki/Privacy-policy) * [Wiki](https://github.com/gorhill/uBlock/wiki) @@ -133,10 +134,6 @@ The Firefox version of uBlock Origin has [an extra feature](https://github.com/g Also of interest: [Deploying uBlock Origin for Firefox with CCK2 and Group Policy](http://decentsecurity.com/ublock-for-firefox-deployment/). -#### Safari (macOS) - -Early development version: . - #### Debian / Ubuntu Thanks to Debian contributor [Sean Whitton](https://wiki.debian.org/SeanWhitton), users of Debian 9 or later or Ubuntu 16.04 or later may simply @@ -146,6 +143,10 @@ Thanks to Debian contributor [Sean Whitton](https://wiki.debian.org/SeanWhitton) Early development version by [@nikrolls](https://github.com/nikrolls): . +#### Safari (macOS) + +Early development versionby [@el1t](https://github.com/el1t): . + #### 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. From b45b916a7c8f8a3a019380ec76c6a1af7652d36d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 7 Dec 2016 10:07:39 -0500 Subject: [PATCH 0009/4093] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index a19148e4148fa..62330fcbe7c1c 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,6 @@ The Firefox version of uBlock Origin has [an extra feature](https://github.com/g Also of interest: [Deploying uBlock Origin for Firefox with CCK2 and Group Policy](http://decentsecurity.com/ublock-for-firefox-deployment/). -#### Debian / Ubuntu - Thanks to Debian contributor [Sean Whitton](https://wiki.debian.org/SeanWhitton), users of Debian 9 or later or Ubuntu 16.04 or later may simply `apt-get install xul-ext-ublock-origin`. From 2f4c6777867726ea5b0a6a9de44f27d2dbdae5f0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 7 Dec 2016 10:08:11 -0500 Subject: [PATCH 0010/4093] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62330fcbe7c1c..6a11cb3d8c920 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Early development version by [@nikrolls](https://github.com/nikrolls): . +Early development version by [@el1t](https://github.com/el1t): . #### Note for all browsers From df7dedfed1bad1e5d803d7f0d06d61ecd08f089c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 7 Dec 2016 12:56:20 -0500 Subject: [PATCH 0011/4093] Update CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6545dfed9195b..b3ef10f656f5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ For **support/discussions**, there is [Mozilla Discourse](https://discourse.mozi For **filter-related issues**, report on the respective filter list support site, or at [uBlockOrigin/uAssets](https://github.com/uBlockOrigin/uAssets/issues). Use [the logger](https://github.com/gorhill/uBlock/wiki/The-logger) to diagnose/confirm filter-related issues. If something does not work properly with uBO enabled, the **first step** is to rule out filter-related issues. -Ignorance of the above rules is no excuse: **Opening an issue for purpose of support or discussion, or opening a filter-related issue will result in the user being immediately blocked.** Given the [amount of invalid issues being opened](https://github.com/gorhill/uBlock/issues?q=is%3Aissue+label%3Ainvalid+is%3Aclosed), I have no choice but to resort to such a drastic measure. +Ignorance of the above rules is no excuse: **Opening an issue for purpose of support or discussion, or opening a filter-related issue will result in the user being immediately blocked.** Given the [amount of invalid issues being opened](https://github.com/gorhill/uBlock/issues?q=is%3Aissue+label%3Ainvalid+is%3Aclosed), I have no choice but to resort to such a drastic measure. You will still be able to open filter list issues at [uBlockOrigin/uAssets](https://github.com/uBlockOrigin/uAssets/issues). **The issue tracker is for provable issues only:** You will have to make the case that the issue is really with uBlock Origin and not something else on your side. To make a case means to provide detailed steps so that anybody can reproduce the issue. Be sure to rule out that the issue is not caused by something specific on your side. From c3c92f85ff569d0601c3fec67d932abde189e1f4 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 7 Dec 2016 20:18:58 -0500 Subject: [PATCH 0012/4093] fix #2225 --- src/js/static-net-filtering.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index d62036bbe391b..4d6550b4b9fef 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2201,6 +2201,10 @@ FilterContainer.prototype.matchTokens = function(bucket, url) { FilterContainer.prototype.matchStringGenericHide = function(context, requestURL) { var url = this.urlTokenizer.setURL(requestURL); + // https://github.com/gorhill/uBlock/issues/2225 + // Important: this is used by FilterHostnameDict.match(). + requestHostnameRegister = µb.URI.hostnameFromURI(url); + var bucket = this.categories.get(toHex(genericHideException)); if ( !bucket || this.matchTokens(bucket, url) === false ) { this.fRegister = null; @@ -2282,7 +2286,7 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r // If there is no block filter, no need to test against allow filters if ( this.fRegister === null ) { - return undefined; + return; } // Test against allow filters @@ -2419,7 +2423,7 @@ FilterContainer.prototype.matchString = function(context) { // If there is no block filter, no need to test against allow filters if ( this.fRegister === null ) { - return undefined; + return; } // Test against allow filters From 9bc6b5b2feb25c06018c8757c9d4248db8af03a2 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 7 Dec 2016 20:22:07 -0500 Subject: [PATCH 0013/4093] 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 528b32d2df112..f32c7633335a3 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.1.1", + "version": "1.10.1.100", "default_locale": "en", "description": "__MSG_extShortDesc__", From 4a4de32bba617a72bd02ed49c289863b365bfb2c Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 7 Dec 2016 23:59:10 -0500 Subject: [PATCH 0014/4093] fix #2226 --- platform/firefox/vapi-background.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index d01eb9a3637db..36964825e0652 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -1900,6 +1900,7 @@ var httpObserver = { 16: 'websocket', 17: 'csp_report', 19: 'beacon', + 20: 'xmlhttprequest', 21: 'image' }, onBeforeRequest: function(){}, From 289611173c03a5e43a66d9b708d863fd7b542e54 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 8 Dec 2016 00:03:57 -0500 Subject: [PATCH 0015/4093] 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 f32c7633335a3..923de765bb35e 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.1.100", + "version": "1.10.1.101", "default_locale": "en", "description": "__MSG_extShortDesc__", From b256a48bd100922f8173993c20189114633585de Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 13 Dec 2016 14:03:43 -0500 Subject: [PATCH 0016/4093] fix https://github.com/nikrolls/uBlock-Edge/issues/34 --- src/css/popup.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/css/popup.css b/src/css/popup.css index 2e8dfefc96749..b647aa3d10f70 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -134,6 +134,7 @@ p { } #switch { margin: 8px 0; + user-select: none; } #switch .fa { color: #0046ff; From a29b76f3adfa17f5a6681937965ae7a44f2aba8f Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 14 Dec 2016 07:34:36 -0500 Subject: [PATCH 0017/4093] fix https://github.com/nikrolls/uBlock-Edge/issues/34 --- src/css/common.css | 3 +++ src/css/popup.css | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/css/common.css b/src/css/common.css index ab51b32d87ff7..255d1d18e245d 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -10,6 +10,9 @@ font-style: normal; font-weight: normal; line-height: 1; + user-select: none; + -moz-user-select: none; + -webkit-user-select: none; vertical-align: middle; } body { diff --git a/src/css/popup.css b/src/css/popup.css index b647aa3d10f70..2e8dfefc96749 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -134,7 +134,6 @@ p { } #switch { margin: 8px 0; - user-select: none; } #switch .fa { color: #0046ff; From acd27b07ec67799b79fa9a09013e2c528aa77027 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 14 Dec 2016 07:50:52 -0500 Subject: [PATCH 0018/4093] trabslation work from https://crowdin.com/project/ublock --- src/_locales/el/messages.json | 16 ++++++++-------- src/_locales/zh_CN/messages.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index f7ec275ab552e..ad9818a10af18 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -44,7 +44,7 @@ "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ - "message":"Κλικ: απενεργοποίηση\/ενεργοποίηση του uBlock για αυτόν τον ιστότοπο.\n\nCtrl+κλικ: απενεργοποίηση του µBlock μόνο για αυτήν την σελίδα.", + "message":"Κλικ: απενεργοποίηση\/ενεργοποίηση του uBlock₀ για αυτόν τον ιστότοπο.\n\nCtrl+κλικ: απενεργοποίηση του uBlock₀ μόνο για αυτήν την σελίδα.", "description":"English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupBlockedRequestPrompt":{ @@ -100,7 +100,7 @@ "description":"Tooltip when hovering the top-most cell of the global-rules column." }, "popupTipLocalRules":{ - "message":"Τοπικοί κανόνες: αυτή η στήλη είναι για κανόνες με εφαρμογή στην τρέχουσα τοποθεσία μόνον.\nΟι τοπικοί κανόνες παραμερίζουν τους καθολικούς κανόνες.", + "message":"Τοπικοί κανόνες: αυτή η στήλη είναι για κανόνες με εφαρμογή στην τρέχουσα τοποθεσία μόνο.\nΟι τοπικοί κανόνες παραμερίζουν τους καθολικούς κανόνες.", "description":"Tooltip when hovering the top-most cell of the local-rules column." }, "popupTipSaveRules":{ @@ -124,7 +124,7 @@ "description":"" }, "popup3pPassiveRulePrompt":{ - "message":"Css\/Εικόνες τρίτου μέρους", + "message":"3ου μέρους css\/Εικόνες", "description":"" }, "popupInlineScriptRulePrompt":{ @@ -200,7 +200,7 @@ "description":"English: Make use of context menu where appropriate" }, "settingsColorBlindPrompt":{ - "message":"Λειτουργία φιλική πρως χρήστες με αχρωματοψία", + "message":"Λειτουργία φιλική προς χρήστες με αχρωματοψία", "description":"English: Color-blind friendly" }, "settingsCloudStorageEnabledPrompt":{ @@ -240,7 +240,7 @@ "description":"" }, "settingsNoLargeMediaPrompt":{ - "message":"Φραγή στοιχείων πολυμέσων μεγαλύτερων από {{εισαγωγή:αριθμός}} kB", + "message":"Φραγή στοιχείων πολυμέσων μεγαλύτερων από {{input:number}} kB", "description":"" }, "settingsNoRemoteFontsPrompt":{ @@ -492,7 +492,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":{ @@ -520,7 +520,7 @@ "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant":{ - "message":"εκτός εάν", + "message":"εκτός όταν", "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartImportant":{ @@ -676,7 +676,7 @@ "description":"" }, "cloudDeviceNamePrompt":{ - "message":"Το όνομα της συσκευής:", + "message":"Το όνομα αυτής της συσκευής:", "description":"used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning":{ diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 26bc31775e73b..9a753bad9c379 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -424,7 +424,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":{ From f30b31ff1e354db16dc1ffc0920ef1f112d1fb90 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 14 Dec 2016 07:51:20 -0500 Subject: [PATCH 0019/4093] 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 923de765bb35e..2d6015ddab486 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.1.101", + "version": "1.10.2", "default_locale": "en", "description": "__MSG_extShortDesc__", From 66de9a3b5c3534cd5add38d49d110b7017c10365 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 15 Dec 2016 09:53:53 -0500 Subject: [PATCH 0020/4093] 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 2d6015ddab486..eafc0b33441f5 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.2", + "version": "1.10.3.0", "default_locale": "en", "description": "__MSG_extShortDesc__", From 94637ee4ff410f1f61664c06befc010e8bff1250 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 15 Dec 2016 10:47:32 -0500 Subject: [PATCH 0021/4093] fix https://github.com/nikrolls/uBlock-Edge/issues/30 --- platform/firefox/frameModule.js | 39 +++++---- platform/firefox/vapi-client.js | 1 + src/js/contentscript.js | 143 +++++++++++++++++++------------- 3 files changed, 111 insertions(+), 72 deletions(-) diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 77f99a5615738..66c75fa90ad3d 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -384,23 +384,34 @@ var contentObserver = { svc.scriptloader.loadSubScript(script, sandbox); }; - sandbox.injectCSS = function(sheetURI) { + let canUserStyles = (function() { try { - let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - wu.loadSheetUsingURIString(sheetURI, wu.USER_SHEET); + return win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .loadSheetUsingURIString instanceof Function; } catch(ex) { } - }; - - sandbox.removeCSS = function(sheetURI) { - try { - let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - wu.removeSheetUsingURIString(sheetURI, wu.USER_SHEET); - } catch (ex) { - } - }; + return false; + })(); + + if ( canUserStyles ) { + sandbox.injectCSS = function(sheetURI) { + try { + let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + wu.loadSheetUsingURIString(sheetURI, wu.USER_SHEET); + } catch(ex) { + } + }; + sandbox.removeCSS = function(sheetURI) { + try { + let wu = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + wu.removeSheetUsingURIString(sheetURI, wu.USER_SHEET); + } catch (ex) { + } + }; + } sandbox.topContentScript = win === win.top; diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index c9f8ccf1291ba..b34f2183a718f 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -505,6 +505,7 @@ if ( self.injectCSS ) { return state ? this._load() : this._unload(); } }; + vAPI.hideNode = vAPI.unhideNode = function(){}; } /******************************************************************************/ diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 6106d6a332753..ae0a993038826 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -126,10 +126,6 @@ vAPI.domFilterer = (function() { /******************************************************************************/ -var shadowId = document.documentElement.shadowRoot !== undefined ? - vAPI.randomToken(): - undefined; - var jobQueue = [ { t: 'css-hide', _0: [] }, // to inject in style tag { t: 'css-style', _0: [] }, // to inject in style tag @@ -165,6 +161,87 @@ var cosmeticFiltersActivated = function() { /******************************************************************************/ +// If a platform does not provide its own (improved) vAPI.hideNode, we assign +// a default one to try to override author styles as best as can be. + +var platformHideNode = vAPI.hideNode, + platformUnhideNode = vAPI.unhideNode; + +(function() { + if ( platformHideNode instanceof Function ) { + return; + } + + var uid, + timerId, + observer, + changedNodes = []; + var observerOptions = { + attributes: true, + attributeFilter: [ 'style' ] + }; + + var overrideInlineStyle = function(node) { + var style = window.getComputedStyle(node), + display = style.getPropertyValue('display'), + attr = node.getAttribute('style') || ''; + if ( node[uid] === undefined ) { + node[uid] = node.hasAttribute('style') && attr; + } + if ( display !== '' && display !== 'none' ) { + if ( attr !== '' ) { attr += '; '; } + node.setAttribute('style', attr + 'display: none !important;'); + } + }; + + var timerHandler = function() { + timerId = undefined; + var nodes = changedNodes, + i = nodes.length, node; + while ( i-- ) { + node = nodes[i]; + if ( node[uid] !== undefined ) { + overrideInlineStyle(node); + } + } + nodes.length = 0; + }; + + var observerHandler = function(mutations) { + var i = mutations.length; + while ( i-- ) { + changedNodes.push(mutations[i].target); + } + if ( timerId === undefined ) { + timerId = vAPI.setTimeout(timerHandler, 1); + } + }; + + platformHideNode = function(node) { + if ( uid === undefined ) { + uid = vAPI.randomToken(); + } + overrideInlineStyle(node); + if ( observer === undefined ) { + observer = new MutationObserver(observerHandler); + } + observer.observe(node, observerOptions); + }; + + platformUnhideNode = function(node) { + if ( uid === undefined ) { return; } + var attr = node[uid]; + if ( attr === false ) { + node.removeAttribute('style'); + } else if ( typeof attr === 'string' ) { + node.setAttribute('style', attr); + } + delete node[uid]; + }; +})(); + +/******************************************************************************/ + var runSimpleSelectorJob = function(job, root, fn) { if ( job._1 === undefined ) { job._1 = job._0.join(cssNotHiddenId + ','); @@ -515,30 +592,7 @@ var domFilterer = { this.hiddenNodeCount += 1; node.hidden = true; node[this.hiddenId] = null; - var style = window.getComputedStyle(node), - display = style.getPropertyValue('display'); - if ( display !== '' && display !== 'none' ) { - var styleAttr = node.getAttribute('style') || ''; - node[this.hiddenId] = node.hasAttribute('style') && styleAttr; - if ( styleAttr !== '' ) { styleAttr += '; '; } - node.setAttribute('style', styleAttr + 'display: none !important;'); - } - if ( shadowId === undefined ) { return; } - var shadow = node.shadowRoot; - if ( shadow ) { - if ( shadow[shadowId] && shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } - return; - } - // https://github.com/gorhill/uBlock/pull/555 - // Not all nodes can be shadowed: - // https://github.com/w3c/webcomponents/issues/102 - try { - shadow = node.createShadowRoot(); - shadow[shadowId] = true; - } catch (ex) { - } + platformHideNode(node); }, init: function() { @@ -561,19 +615,7 @@ var domFilterer = { showNode: function(node) { node.hidden = false; - var styleAttr = node[this.hiddenId]; - if ( styleAttr === false ) { - node.removeAttribute('style'); - } else if ( typeof styleAttr === 'string' ) { - node.setAttribute('style', node[this.hiddenId]); - } - var shadow = node.shadowRoot; - if ( shadow && shadow[shadowId] ) { - if ( shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } - shadow.appendChild(document.createElement('content')); - } + platformUnhideNode(node); }, toggleLogging: function(state) { @@ -601,27 +643,12 @@ var domFilterer = { node.removeAttribute(this.hiddenId); node[this.hiddenId] = undefined; node.hidden = false; - var shadow = node.shadowRoot; - if ( shadow && shadow[shadowId] ) { - if ( shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } - shadow.appendChild(document.createElement('content')); - } + platformUnhideNode(node); }, unshowNode: function(node) { node.hidden = true; - var styleAttr = node[this.hiddenId]; - if ( styleAttr === false ) { - node.setAttribute('style', 'display: none !important;'); - } else if ( typeof styleAttr === 'string' ) { - node.setAttribute('style', node[this.hiddenId] + '; display: none !important;'); - } - var shadow = node.shadowRoot; - if ( shadow && shadow[shadowId] && shadow.firstElementChild !== null ) { - shadow.removeChild(shadow.firstElementChild); - } + platformHideNode(node); }, domChangedHandler: function(addedNodes, removedNodes) { From 786db5a6a41b74851f3b54feea76e6e492574def Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 15 Dec 2016 13:41:38 -0500 Subject: [PATCH 0022/4093] Update README.md --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6a11cb3d8c920..e3eb1167eb2bb 100644 --- a/README.md +++ b/README.md @@ -139,11 +139,16 @@ Thanks to Debian contributor [Sean Whitton](https://wiki.debian.org/SeanWhitton) #### Microsoft Edge -Early development version by [@nikrolls](https://github.com/nikrolls): . +Developer: [@nikrolls](https://github.com/nikrolls). + +Stable version available in [Microsoft Store](https://www.microsoft.com/store/p/app/9nblggh444l4). +Development version available at . #### Safari (macOS) -Early development version by [@el1t](https://github.com/el1t): . +Developer: [@el1t](https://github.com/el1t). + +Development version available at . #### Note for all browsers From 34b359aa92821f034237c73ea92490ce11f30061 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 15 Dec 2016 13:42:11 -0500 Subject: [PATCH 0023/4093] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e3eb1167eb2bb..72b4f7f996708 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ Thanks to Debian contributor [Sean Whitton](https://wiki.debian.org/SeanWhitton) Developer: [@nikrolls](https://github.com/nikrolls). Stable version available in [Microsoft Store](https://www.microsoft.com/store/p/app/9nblggh444l4). + Development version available at . #### Safari (macOS) From c39adacc5082891f07e6dda2f11a8f8b01bb0bf5 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 16 Dec 2016 16:25:36 -0500 Subject: [PATCH 0024/4093] better abstraction of user styles --- src/js/contentscript.js | 154 +++++++++++++++-------------- src/js/scriptlets/cosmetic-off.js | 14 +-- src/js/scriptlets/cosmetic-on.js | 14 +-- src/js/scriptlets/dom-inspector.js | 26 +---- 4 files changed, 82 insertions(+), 126 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index ae0a993038826..d297caaa22908 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -66,6 +66,17 @@ Additionally, the domSurveyor can turn itself off once it decides that it has become pointless (repeatedly not finding new cosmetic filters). + The domFilterer makes use of platform-dependent user styles[1] code, or + provide a default generic implementation if none is present. + At time of writing, only modern Firefox provides a custom implementation, + which makes for solid, reliable and low overhead cosmetic filtering on + Firefox. + The generic implementation[2] performs as best as can be, but won't ever be + as reliable as real user styles. + [1] "user styles" refer to local CSS rules which have priority over, and + can't be overriden by a web page's own CSS rules. + [2] below, see platformUserCSS / platformHideNode / platformUnhideNode + */ /******************************************************************************/ @@ -138,8 +149,7 @@ var reParserEx = /:(?:has|matches-css|matches-css-before|matches-css-after|style var allExceptions = createSet(), allSelectors = createSet(), stagedNodes = [], - matchesProp = vAPI.matchesProp, - userCSS = vAPI.userCSS; + matchesProp = vAPI.matchesProp; // Complex selectors, due to their nature may need to be "de-committed". A // Set() is used to implement this functionality. @@ -161,6 +171,62 @@ var cosmeticFiltersActivated = function() { /******************************************************************************/ +// If a platform does not support its own vAPI.userCSS (user styles), we +// provide a default (imperfect) implementation. + +// Probably no longer need to watch for style tags removal/tampering with fix +// to https://github.com/gorhill/uBlock/issues/963 + +var platformUserCSS = (function() { + if ( vAPI.userCSS instanceof Object ) { + return vAPI.userCSS; + } + + return { + enabled: true, + styles: [], + add: function(css) { + var style = document.createElement('style'); + style.setAttribute('type', 'text/css'); + style.textContent = css; + if ( document.head ) { + document.head.appendChild(style); + } + this.styles.push(style); + if ( style.sheet ) { + style.sheet.disabled = !this.enabled; + } + }, + remove: function(css) { + var i = this.styles.length, + style, parent; + while ( i-- ) { + style = this.styles[i]; + if ( style.textContent !== css ) { continue; } + parent = style.parentNode; + if ( parent !== null ) { + parent.removeChild(style); + } + this.styles.splice(i, 1); + } + }, + toggle: function(state) { + if ( this.styles.length === '' ) { return; } + if ( state === undefined ) { + state = !this.enabled; + } + var i = this.styles.length, style; + while ( i-- ) { + style = this.styles[i]; + if ( style.sheet !== null ) { + style.sheet.disabled = !state; + } + } + this.enabled = state; + } + }; +})(); + // If a platform does not provide its own (improved) vAPI.hideNode, we assign // a default one to try to override author styles as best as can be. @@ -344,7 +410,6 @@ var runXpathJob = function(job, fn) { var domFilterer = { addedNodesHandlerMissCount: 0, - removedNodesHandlerMissCount: 0, commitTimer: null, disabledId: vAPI.randomToken(), enabled: true, @@ -428,54 +493,6 @@ var domFilterer = { } }, - addStyleTag: function(text) { - var styleTag = document.createElement('style'); - styleTag.setAttribute('type', 'text/css'); - styleTag.textContent = text; - if ( document.head ) { - document.head.appendChild(styleTag); - } - this.styleTags.push(styleTag); - if ( userCSS ) { - userCSS.add(text); - } - }, - - checkStyleTags_: function() { - var doc = document, - html = doc.documentElement, - head = doc.head, - newParent = head || html; - if ( newParent === null ) { return; } - this.removedNodesHandlerMissCount += 1; - var styles = this.styleTags, - style, oldParent; - for ( var i = 0; i < styles.length; i++ ) { - style = styles[i]; - oldParent = style.parentNode; - // https://github.com/gorhill/uBlock/issues/1031 - // If our style tag was disabled, re-insert into the page. - if ( - style.disabled && - oldParent !== null && - style.hasAttribute(this.disabledId) === false - ) { - oldParent.removeChild(style); - oldParent = null; - } - if ( oldParent === head || oldParent === html ) { continue; } - style.disabled = false; - newParent.appendChild(style); - this.removedNodesHandlerMissCount = 0; - } - }, - - checkStyleTags: function() { - if ( this.removedNodesHandlerMissCount < 16 ) { - this.checkStyleTags_(); - } - }, - commit_: function() { this.commitTimer.clear(); @@ -538,7 +555,7 @@ var domFilterer = { } if ( styleText !== '' ) { - this.addStyleTag(styleText); + platformUserCSS.add(styleText); } // Un-hide nodes previously hidden. @@ -623,19 +640,17 @@ var domFilterer = { }, toggleOff: function() { - if ( userCSS ) { - userCSS.toggle(false); - } + platformUserCSS.toggle(false); this.enabled = false; }, toggleOn: function() { - if ( userCSS ) { - userCSS.toggle(true); - } + platformUserCSS.toggle(true); this.enabled = true; }, + userCSS: platformUserCSS, + unhideNode: function(node) { if ( node[this.hiddenId] !== undefined ) { this.hiddenNodeCount--; @@ -651,13 +666,8 @@ var domFilterer = { platformHideNode(node); }, - domChangedHandler: function(addedNodes, removedNodes) { + domChangedHandler: function(addedNodes) { this.commit(addedNodes); - // https://github.com/gorhill/uBlock/issues/873 - // This will ensure our style elements will stay in the DOM. - if ( removedNodes ) { - domFilterer.checkStyleTags(); - } }, start: function() { @@ -818,9 +828,9 @@ vAPI.domWatcher = (function() { } addedNodeLists.length = 0; if ( addedNodes.length !== 0 || removedNodes ) { - listeners[0](addedNodes, removedNodes); + listeners[0](addedNodes); if ( listeners[1] ) { - listeners[1](addedNodes, removedNodes); + listeners[1](addedNodes); } addedNodes.length = 0; removedNodes = false; @@ -1485,19 +1495,16 @@ vAPI.domSurveyor = (function() { surveyPhase2(addedNodes); }; - var domChangedHandler = function(addedNodes, removedNodes) { + var domChangedHandler = function(addedNodes) { if ( cosmeticSurveyingMissCount > 255 ) { vAPI.domWatcher.removeListener(domChangedHandler); vAPI.domSurveyor = null; - domFilterer.domChangedHandler(addedNodes, removedNodes); + domFilterer.domChangedHandler(addedNodes); domFilterer.start(); return; } surveyPhase1(addedNodes); - if ( removedNodes ) { - domFilterer.checkStyleTags(); - } }; var start = function() { @@ -1535,11 +1542,6 @@ vAPI.domIsLoaded = function(ev) { vAPI.domCollapser.start(); if ( vAPI.domFilterer ) { - // https://github.com/chrisaljoudi/uBlock/issues/789 - // https://github.com/gorhill/uBlock/issues/873 - // Be sure our style tags used for cosmetic filtering are still - // applied. - vAPI.domFilterer.checkStyleTags(); // To avoid neddless CPU overhead, we commit existing cosmetic filters // only if the page loaded "slowly", i.e. if the code here had to wait // for a DOMContentLoaded event -- in which case the DOM may have diff --git a/src/js/scriptlets/cosmetic-off.js b/src/js/scriptlets/cosmetic-off.js index 8fdac361929e1..cd92eb78042eb 100644 --- a/src/js/scriptlets/cosmetic-off.js +++ b/src/js/scriptlets/cosmetic-off.js @@ -28,24 +28,12 @@ return; } - var styles = vAPI.domFilterer.styleTags; - - // Disable all cosmetic filtering-related styles from the DOM - var i = styles.length, style; - while ( i-- ) { - style = styles[i]; - if ( style.sheet !== null ) { - style.sheet.disabled = true; - style[vAPI.sessionId] = true; - } - } - var elems = []; try { elems = document.querySelectorAll('[' + vAPI.domFilterer.hiddenId + ']'); } catch (e) { } - i = elems.length; + var i = elems.length; while ( i-- ) { vAPI.domFilterer.showNode(elems[i]); } diff --git a/src/js/scriptlets/cosmetic-on.js b/src/js/scriptlets/cosmetic-on.js index 1a450e074f5d1..d3a6de8309547 100644 --- a/src/js/scriptlets/cosmetic-on.js +++ b/src/js/scriptlets/cosmetic-on.js @@ -28,24 +28,12 @@ return; } - // Insert all cosmetic filtering-related style tags in the DOM - - var styles = vAPI.domFilterer.styleTags; - var i = styles.length, style; - while ( i-- ) { - style = styles[i]; - if ( style.sheet !== null ) { - style.sheet.disabled = false; - style[vAPI.sessionId] = undefined; - } - } - var elems = []; try { elems = document.querySelectorAll('[' + vAPI.domFilterer.hiddenId + ']'); } catch (e) { } - i = elems.length; + var i = elems.length; while ( i-- ) { vAPI.domFilterer.unshowNode(elems[i]); } diff --git a/src/js/scriptlets/dom-inspector.js b/src/js/scriptlets/dom-inspector.js index 50c8391c79708..a31f6988a925b 100644 --- a/src/js/scriptlets/dom-inspector.js +++ b/src/js/scriptlets/dom-inspector.js @@ -686,10 +686,6 @@ var cosmeticFilterMapper = (function() { matchesFnName = 'webkitMatchesSelector'; } - // Why the call to hideNode()? - // Not all target nodes have necessarily been force-hidden, - // do it now so that the inspector does not unhide these - // nodes when disabling style tags. var nodesFromStyleTag = function(rootNode) { var filterMap = nodeToCosmeticFilterMap, selectors, selector, @@ -741,16 +737,7 @@ var cosmeticFilterMapper = (function() { }; var incremental = function(rootNode) { - var styleTags = vAPI.domFilterer.styleTags || []; - var styleTag; - var i = styleTags.length; - while ( i-- ) { - styleTag = styleTags[i]; - if ( styleTag.sheet !== null ) { - styleTag.sheet.disabled = true; - styleTag[vAPI.sessionId] = true; - } - } + vAPI.domFilterer.userCSS.toggle(false); nodesFromStyleTag(rootNode); }; @@ -760,16 +747,7 @@ var cosmeticFilterMapper = (function() { }; var shutdown = function() { - var styleTags = vAPI.domFilterer.styleTags || []; - var styleTag; - var i = styleTags.length; - while ( i-- ) { - styleTag = styleTags[i]; - if ( styleTag.sheet !== null ) { - styleTag.sheet.disabled = false; - styleTag[vAPI.sessionId] = undefined; - } - } + vAPI.domFilterer.userCSS.toggle(true); }; return { From cec17097a4aad8387e03b6016228f0b10b36ca0d Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 19 Dec 2016 00:30:14 -0500 Subject: [PATCH 0025/4093] fix #2249 --- src/js/static-net-filtering.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 4d6550b4b9fef..85434ce9ddbcf 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1735,7 +1735,7 @@ FilterContainer.prototype.getFilterClass = function(details) { if ( details.isRegex ) { return FilterRegexHostname; } - if ( this.reIsGeneric.test(s) ) { + if ( this.reIsGeneric.test(s) || details.token === '*' ) { if ( details.hostnameAnchored ) { return FilterGenericHnAnchoredHostname; } @@ -1766,7 +1766,7 @@ FilterContainer.prototype.getFilterClass = function(details) { if ( details.isRegex ) { return FilterRegex; } - if ( this.reIsGeneric.test(s) ) { + if ( this.reIsGeneric.test(s) || details.token === '*' ) { if ( details.hostnameAnchored ) { return FilterGenericHnAnchored; } From 0b8f27801c5e7229d7a40e72730ca4c60def1d0f Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 19 Dec 2016 13:57:17 -0500 Subject: [PATCH 0026/4093] 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 eafc0b33441f5..5eb101a757544 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.3.0", + "version": "1.10.5.0", "default_locale": "en", "description": "__MSG_extShortDesc__", From 2f01fcda54246693f5378fab03a08adeb18a8fa2 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 21 Dec 2016 11:44:03 -0500 Subject: [PATCH 0027/4093] fix #2256 --- platform/firefox/vapi-client.js | 14 ++++++++++++++ src/js/contentscript.js | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index b34f2183a718f..90c46334d0866 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -510,6 +510,20 @@ if ( self.injectCSS ) { /******************************************************************************/ +// https://bugzilla.mozilla.org/show_bug.cgi?id=444165 +// https://github.com/gorhill/uBlock/issues/2256 +// Not the prettiest solution, but that's the safest/simplest I can think +// of at this point. If/when bugzilla issue above is solved, we will need +// version detection to decide whether the patch needs to be applied. + +vAPI.iframeLoadEventPatch = function(target) { + if ( target.localName === 'iframe' ) { + target.dispatchEvent(new Event('load')); + } +}; + +/******************************************************************************/ + // No need to have vAPI client linger around after shutdown if // we are not a top window (because element picker can still // be injected in top window). diff --git a/src/js/contentscript.js b/src/js/contentscript.js index d297caaa22908..91ef518a98f84 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -947,6 +947,8 @@ vAPI.domCollapser = (function() { netSelectorCacheCountMax = response.netSelectorCacheCountMax, aa = [ null ], request, key, entry, target, value; + // https://github.com/gorhill/uBlock/issues/2256 + var iframeLoadEventPatch = vAPI.iframeLoadEventPatch; // Important: process in chronological order -- this ensures the // cached selectors are the most useful ones. for ( var i = 0, ni = requests.length; i < ni; i++ ) { @@ -980,6 +982,7 @@ vAPI.domCollapser = (function() { selectors.push(request.tag + '[' + request.attr + '="' + value + '"]'); netSelectorCacheCount += 1; } + if ( iframeLoadEventPatch ) { iframeLoadEventPatch(target); } } } if ( selectors.length !== 0 ) { @@ -1030,9 +1033,6 @@ vAPI.domCollapser = (function() { } }; - // If needed eventually, we could listen to `src` attribute changes - // for iframes. - var add = function(target) { var tag = target.localName; var prop = src1stProps[tag]; From 73a69711f2c415e2f437455aead329c73a6d7bd8 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 25 Dec 2016 16:56:39 -0500 Subject: [PATCH 0028/4093] add chainable and recursive cosmetic procedural filters --- src/epicker.html | 13 +- src/js/contentscript.js | 414 ++++++++++++++++----------- src/js/cosmetic-filtering.js | 297 +++++++++++++------ src/js/messaging.js | 4 + src/js/reverselookup-worker.js | 17 +- src/js/scriptlets/cosmetic-logger.js | 56 ++-- src/js/scriptlets/dom-inspector.js | 14 +- src/js/scriptlets/element-picker.js | 298 ++++++++----------- 8 files changed, 635 insertions(+), 478 deletions(-) diff --git a/src/epicker.html b/src/epicker.html index f3e038d33f3c9..fc1f34e520a3a 100644 --- a/src/epicker.html +++ b/src/epicker.html @@ -63,7 +63,10 @@ margin: 0; position: relative; } -section > div > textarea { +section.invalidFilter > div:first-child { + border-color: red; +} +section > div:first-child > textarea { background-color: #fff; border: none; box-sizing: border-box; @@ -75,10 +78,7 @@ resize: none; width: 100%; } -section > div > textarea.invalidFilter { - background-color: #fee; -} -section > div > textarea + div { +section > div:first-child > textarea + div { background-color: #aaa; bottom: 0; color: white; @@ -86,6 +86,9 @@ position: absolute; right: 0; } +section.invalidFilter > div:first-child > textarea + div { + background-color: red; +} section > div:first-child + div { direction: ltr; margin: 2px 0; diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 91ef518a98f84..33f241acad538 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -137,19 +137,9 @@ vAPI.domFilterer = (function() { /******************************************************************************/ -var jobQueue = [ - { t: 'css-hide', _0: [] }, // to inject in style tag - { t: 'css-style', _0: [] }, // to inject in style tag - { t: 'css-ssel', _0: [] }, // to manually hide (incremental) - { t: 'css-csel', _0: [] } // to manually hide (not incremental) -]; - -var reParserEx = /:(?:has|matches-css|matches-css-before|matches-css-after|style|xpath)\(.+?\)$/; - var allExceptions = createSet(), allSelectors = createSet(), - stagedNodes = [], - matchesProp = vAPI.matchesProp; + stagedNodes = []; // Complex selectors, due to their nature may need to be "de-committed". A // Set() is used to implement this functionality. @@ -308,100 +298,179 @@ var platformHideNode = vAPI.hideNode, /******************************************************************************/ -var runSimpleSelectorJob = function(job, root, fn) { - if ( job._1 === undefined ) { - job._1 = job._0.join(cssNotHiddenId + ','); - } - if ( root[matchesProp](job._1) ) { - fn(root); - } - var nodes = root.querySelectorAll(job._1), - i = nodes.length; - while ( i-- ) { - fn(nodes[i], job); +// 'P' stands for 'Procedural' + +var PSelectorHasTask = function(task) { + this.selector = task[1]; +}; +PSelectorHasTask.prototype.exec = function(input) { + var output = []; + for ( var i = 0, n = input.length; i < n; i++ ) { + if ( input[i].querySelector(this.selector) !== null ) { + output.push(input[i]); + } } + return output; }; -var runComplexSelectorJob = function(job, fn) { - if ( job._1 === undefined ) { - job._1 = job._0.join(','); - } - var nodes = document.querySelectorAll(job._1), - i = nodes.length; - while ( i-- ) { - fn(nodes[i], job); +var PSelectorHasTextTask = function(task) { + this.needle = new RegExp(task[1]); +}; +PSelectorHasTextTask.prototype.exec = function(input) { + var output = []; + for ( var i = 0, n = input.length; i < n; i++ ) { + if ( this.needle.test(input[i].textContent) ) { + output.push(input[i]); + } } + return output; }; -var runHasJob = function(job, fn) { - var nodes = document.querySelectorAll(job._0), - i = nodes.length, node; - while ( i-- ) { - node = nodes[i]; - if ( node.querySelector(job._1) !== null ) { - fn(node, job); +var PSelectorIfTask = function(task) { + this.pselector = new PSelector(task[1]); +}; +PSelectorIfTask.prototype.target = true; +PSelectorIfTask.prototype.exec = function(input) { + var output = []; + for ( var i = 0, n = input.length; i < n; i++ ) { + if ( this.pselector.test(input[i]) === this.target ) { + output.push(input[i]); } } + return output; }; -// '/' = ascii 0x2F */ - -var parseMatchesCSSJob = function(raw) { - var prop = raw.trim(); - if ( prop === '' ) { return null; } - var pos = prop.indexOf(':'), - v = pos !== -1 ? prop.slice(pos + 1).trim() : '', - vlen = v.length; - if ( - vlen > 1 && - v.charCodeAt(0) === 0x2F && - v.charCodeAt(vlen-1) === 0x2F - ) { - try { v = new RegExp(v.slice(1, -1)); } catch(ex) { return null; } - } - return { k: prop.slice(0, pos).trim(), v: v }; +var PSelectorIfNotTask = function(task) { + PSelectorIfTask.call(this, task); + this.target = false; }; +PSelectorIfNotTask.prototype = Object.create(PSelectorIfTask.prototype); +PSelectorIfNotTask.prototype.constructor = PSelectorIfNotTask; -var runMatchesCSSJob = function(job, fn) { - var nodes = document.querySelectorAll(job._0), - i = nodes.length; - if ( i === 0 ) { return; } - if ( typeof job._1 === 'string' ) { - job._1 = parseMatchesCSSJob(job._1); - } - if ( job._1 === null ) { return; } - var k = job._1.k, - v = job._1.v, - node, style, match; - while ( i-- ) { - node = nodes[i]; - style = window.getComputedStyle(node, job._2); - if ( style === null ) { continue; } /* FF */ - if ( v instanceof RegExp ) { - match = v.test(style[k]); - } else { - match = style[k] === v; +var PSelectorMatchesCSSTask = function(task) { + this.name = task[1].name; + this.value = new RegExp(task[1].value); +}; +PSelectorMatchesCSSTask.prototype.pseudo = null; +PSelectorMatchesCSSTask.prototype.exec = function(input) { + var output = [], style; + for ( var i = 0, n = input.length; i < n; i++ ) { + style = window.getComputedStyle(input[i], this.pseudo); + if ( style === null ) { return null; } /* FF */ + if ( this.value.test(style[this.name]) ) { + output.push(input[i]); } - if ( match ) { - fn(node, job); + } + return output; +}; + +var PSelectorMatchesCSSAfterTask = function(task) { + PSelectorMatchesCSSTask.call(this, task); + this.pseudo = ':after'; +}; +PSelectorMatchesCSSAfterTask.prototype = Object.create(PSelectorMatchesCSSTask.prototype); +PSelectorMatchesCSSAfterTask.prototype.constructor = PSelectorMatchesCSSAfterTask; + +var PSelectorMatchesCSSBeforeTask = function(task) { + PSelectorMatchesCSSTask.call(this, task); + this.pseudo = ':before'; +}; +PSelectorMatchesCSSBeforeTask.prototype = Object.create(PSelectorMatchesCSSTask.prototype); +PSelectorMatchesCSSBeforeTask.prototype.constructor = PSelectorMatchesCSSBeforeTask; + +var PSelectorXpathTask = function(task) { + this.xpe = document.createExpression(task[1], null); + this.xpr = null; +}; +PSelectorXpathTask.prototype.exec = function(input) { + var output = [], j, node; + for ( var i = 0, n = input.length; i < n; i++ ) { + this.xpr = this.xpe.evaluate( + input[i], + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + this.xpr + ); + j = this.xpr.snapshotLength; + while ( j-- ) { + node = this.xpr.snapshotItem(j); + if ( node.nodeType === 1 ) { + output.push(node); + } } } + return output; }; -var runXpathJob = function(job, fn) { - if ( job._1 === undefined ) { - job._1 = document.createExpression(job._0, null); +var PSelector = function(o) { + if ( PSelector.prototype.operatorToTaskMap === undefined ) { + PSelector.prototype.operatorToTaskMap = new Map([ + [ ':has', PSelectorHasTask ], + [ ':has-text', PSelectorHasTextTask ], + [ ':if', PSelectorIfTask ], + [ ':if-not', PSelectorIfNotTask ], + [ ':matches-css', PSelectorMatchesCSSTask ], + [ ':matches-css-after', PSelectorMatchesCSSAfterTask ], + [ ':matches-css-before', PSelectorMatchesCSSBeforeTask ], + [ ':xpath', PSelectorXpathTask ] + ]); } - var xpr = job._2 = job._1.evaluate( - document, - XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, - job._2 || null - ); - var i = xpr.snapshotLength, node; + this.raw = o.raw; + this.selector = o.selector; + this.tasks = []; + var tasks = o.tasks, task, ctor; + for ( var i = 0; i < tasks.length; i++ ) { + task = tasks[i]; + ctor = this.operatorToTaskMap.get(task[0]); + this.tasks.push(new ctor(task)); + } +}; +PSelector.prototype.operatorToTaskMap = undefined; +PSelector.prototype.prime = function(input) { + var root = input || document; + if ( this.selector !== '' ) { + return root.querySelectorAll(this.selector); + } + return [ root ]; +}; +PSelector.prototype.exec = function(input) { + //var t0 = window.performance.now(); + var tasks = this.tasks, nodes = this.prime(input); + for ( var i = 0, n = tasks.length; i < n && nodes.length !== 0; i++ ) { + nodes = tasks[i].exec(nodes); + } + //console.log('%s: %s ms', this.raw, (window.performance.now() - t0).toFixed(2)); + return nodes; +}; +PSelector.prototype.test = function(input) { + //var t0 = window.performance.now(); + var tasks = this.tasks, nodes = this.prime(input), aa0 = [ null ], aa; + for ( var i = 0, ni = nodes.length; i < ni; i++ ) { + aa0[0] = nodes[i]; aa = aa0; + for ( var j = 0, nj = tasks.length; j < nj && aa.length !== 0; j++ ) { + aa = tasks[i].exec(aa); + } + if ( aa.length !== 0 ) { return true; } + } + //console.log('%s: %s ms', this.raw, (window.performance.now() - t0).toFixed(2)); + return false; +}; + +var PSelectors = function() { + this.entries = []; +}; +PSelectors.prototype.add = function(o) { + this.entries.push(new PSelector(o)); +}; +PSelectors.prototype.forEachNode = function(callback) { + var pfilters = this.entries, + i = pfilters.length, + pfilter, nodes, j; while ( i-- ) { - node = xpr.snapshotItem(i); - if ( node.nodeType === 1 ) { - fn(node, job); + pfilter = pfilters[i]; + nodes = pfilter.exec(); + j = nodes.length; + while ( j-- ) { + callback(nodes[j], pfilter); } } }; @@ -418,14 +487,52 @@ var domFilterer = { hiddenNodeCount: 0, hiddenNodeEnforcer: false, loggerEnabled: undefined, - styleTags: [], - jobQueue: jobQueue, - // Stock jobs. - job0: jobQueue[0], - job1: jobQueue[1], - job2: jobQueue[2], - job3: jobQueue[3], + newHideSelectorBuffer: [], // Hide style filter buffer + newStyleRuleBuffer: [], // Non-hide style filter buffer + simpleHideSelectors: { // Hiding filters: simple selectors + entries: [], + matchesProp: vAPI.matchesProp, + selector: undefined, + add: function(selector) { + this.entries.push(selector); + this.selector = undefined; + }, + forEachNodeOfSelector: function(/*callback, root, extra*/) { + }, + forEachNode: function(callback, root, extra) { + if ( this.selector === undefined ) { + this.selector = this.entries.join(extra + ',') + extra; + } + if ( root[this.matchesProp](this.selector) ) { + callback(root); + } + var nodes = root.querySelectorAll(this.selector), + i = nodes.length; + while ( i-- ) { + callback(nodes[i]); + } + } + }, + complexHideSelectors: { // Hiding filters: complex selectors + entries: [], + selector: undefined, + add: function(selector) { + this.entries.push(selector); + this.selector = undefined; + }, + forEachNode: function(callback) { + if ( this.selector === undefined ) { + this.selector = this.entries.join(','); + } + var nodes = document.querySelectorAll(this.selector), + i = nodes.length; + while ( i-- ) { + callback(nodes[i]); + } + } + }, + proceduralSelectors: new PSelectors(), // Hiding filters: procedural addExceptions: function(aa) { for ( var i = 0, n = aa.length; i < n; i++ ) { @@ -433,58 +540,28 @@ var domFilterer = { } }, - // Job: - // Stock jobs in job queue: - // 0 = css rules/css declaration to remove visibility - // 1 = css rules/any css declaration - // 2 = simple css selectors/hide - // 3 = complex css selectors/hide - // Custom jobs: - // matches-css/hide - // has/hide - // xpath/hide - - addSelector: function(s) { - if ( allSelectors.has(s) || allExceptions.has(s) ) { + addSelector: function(selector) { + if ( allSelectors.has(selector) || allExceptions.has(selector) ) { return; } - allSelectors.add(s); - var sel0 = s, sel1 = ''; - if ( s.charCodeAt(s.length - 1) === 0x29 ) { - var parts = reParserEx.exec(s); - if ( parts !== null ) { - sel1 = parts[0]; - } - } - if ( sel1 === '' ) { - this.job0._0.push(sel0); - if ( sel0.indexOf(' ') === -1 ) { - this.job2._0.push(sel0); - this.job2._1 = undefined; + allSelectors.add(selector); + if ( selector.charCodeAt(0) !== 0x7B /* '{' */ ) { + this.newHideSelectorBuffer.push(selector); + if ( selector.indexOf(' ') === -1 ) { + this.simpleHideSelectors.add(selector); } else { - this.job3._0.push(sel0); - this.job3._1 = undefined; + this.complexHideSelectors.add(selector); } return; } - sel0 = sel0.slice(0, sel0.length - sel1.length); - if ( sel1.lastIndexOf(':has', 0) === 0 ) { - this.jobQueue.push({ t: 'has-hide', raw: s, _0: sel0, _1: sel1.slice(5, -1) }); - } else if ( sel1.lastIndexOf(':matches-css', 0) === 0 ) { - if ( sel1.lastIndexOf(':matches-css-before', 0) === 0 ) { - this.jobQueue.push({ t: 'matches-css-hide', raw: s, _0: sel0, _1: sel1.slice(20, -1), _2: ':before' }); - } else if ( sel1.lastIndexOf(':matches-css-after', 0) === 0 ) { - this.jobQueue.push({ t: 'matches-css-hide', raw: s, _0: sel0, _1: sel1.slice(19, -1), _2: ':after' }); - } else { - this.jobQueue.push({ t: 'matches-css-hide', raw: s, _0: sel0, _1: sel1.slice(13, -1), _2: null }); - } - } else if ( sel1.lastIndexOf(':style', 0) === 0 ) { - this.job1._0.push(sel0 + ' { ' + sel1.slice(7, -1) + ' }'); - this.job1._1 = undefined; - } else if ( sel1.lastIndexOf(':xpath', 0) === 0 ) { - this.jobQueue.push({ t: 'xpath-hide', raw: s, _0: sel1.slice(7, -1) }); + var o = JSON.parse(selector); + if ( o.style ) { + this.newStyleRuleBuffer.push(o.parts.join(' ')); + return; + } + if ( o.procedural ) { + this.proceduralSelectors.add(o); } - return; }, addSelectors: function(aa) { @@ -497,27 +574,27 @@ var domFilterer = { this.commitTimer.clear(); var beforeHiddenNodeCount = this.hiddenNodeCount, - styleText = '', i, n; + styleText = '', i; - // Stock job 0 = css rules/hide - if ( this.job0._0.length ) { - styleText = '\n:root ' + this.job0._0.join(',\n:root ') + '\n{ display: none !important; }'; - this.job0._0.length = 0; + // CSS rules/hide + if ( this.newHideSelectorBuffer.length ) { + styleText = '\n:root ' + this.newHideSelectorBuffer.join(',\n:root ') + '\n{ display: none !important; }'; + this.newHideSelectorBuffer.length = 0; } - // Stock job 1 = css rules/any css declaration - if ( this.job1._0.length ) { - styleText += '\n' + this.job1._0.join('\n'); - this.job1._0.length = 0; + // CSS rules/any css declaration + if ( this.newStyleRuleBuffer.length ) { + styleText += '\n' + this.newStyleRuleBuffer.join('\n'); + this.newStyleRuleBuffer.length = 0; } // Simple selectors: incremental. - // Stock job 2 = simple css selectors/hide - if ( this.job2._0.length ) { + // Simple css selectors/hide + if ( this.simpleHideSelectors.entries.length ) { i = stagedNodes.length; while ( i-- ) { - runSimpleSelectorJob(this.job2, stagedNodes[i], hideNode); + this.simpleHideSelectors.forEachNode(hideNode, stagedNodes[i], cssNotHiddenId); } } stagedNodes = []; @@ -526,17 +603,16 @@ var domFilterer = { complexSelectorsOldResultSet = complexSelectorsCurrentResultSet; complexSelectorsCurrentResultSet = createSet('object'); - // Stock job 3 = complex css selectors/hide + // Complex css selectors/hide // The handling of these can be considered optional, since they are // also applied declaratively using a style tag. - if ( this.job3._0.length ) { - runComplexSelectorJob(this.job3, complexHideNode); + if ( this.complexHideSelectors.entries.length ) { + this.complexHideSelectors.forEachNode(complexHideNode); } - // Custom jobs. No optional since they can't be applied in a - // declarative way. - for ( i = 4, n = this.jobQueue.length; i < n; i++ ) { - this.runJob(this.jobQueue[i], complexHideNode); + // Procedural cosmetic filters + if ( this.proceduralSelectors.entries.length ) { + this.proceduralSelectors.forEachNode(complexHideNode); } // https://github.com/gorhill/uBlock/issues/1912 @@ -595,6 +671,10 @@ var domFilterer = { this.commitTimer.start(); }, + createProceduralFilter: function(o) { + return new PSelector(o); + }, + getExcludeId: function() { if ( this.excludeId === undefined ) { this.excludeId = vAPI.randomToken(); @@ -616,20 +696,6 @@ var domFilterer = { this.commitTimer = new vAPI.SafeAnimationFrame(this.commit_.bind(this)); }, - runJob: function(job, fn) { - switch ( job.t ) { - case 'has-hide': - runHasJob(job, fn); - break; - case 'matches-css-hide': - runMatchesCSSJob(job, fn); - break; - case 'xpath-hide': - runXpathJob(job, fn); - break; - } - }, - showNode: function(node) { node.hidden = false; platformUnhideNode(node); @@ -1248,14 +1314,14 @@ vAPI.domSurveyor = (function() { // Need to do this before committing DOM filterer, as needed info // will no longer be there after commit. - if ( firstSurvey || domFilterer.job0._0.length ) { + if ( firstSurvey || domFilterer.newHideSelectorBuffer.length ) { messaging.send( 'contentscript', { what: 'cosmeticFiltersInjected', type: 'cosmetic', hostname: window.location.hostname, - selectors: domFilterer.job0._0, + selectors: domFilterer.newHideSelectorBuffer, first: firstSurvey, cost: surveyCost } @@ -1263,7 +1329,7 @@ vAPI.domSurveyor = (function() { } // Shutdown surveyor if too many consecutive empty resultsets. - if ( domFilterer.job0._0.length === 0 ) { + if ( domFilterer.newHideSelectorBuffer.length === 0 ) { cosmeticSurveyingMissCount += 1; } else { cosmeticSurveyingMissCount = 0; diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index ebfdc7392bd5b..6d693adb5fb4a 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -39,6 +39,35 @@ var µb = µBlock; var encode = JSON.stringify; var decode = JSON.parse; +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); + } + 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); @@ -218,7 +247,7 @@ var FilterParser = function() { this.hostnames = []; this.invalid = false; this.cosmetic = true; - this.reNeedHostname = /^(?:script:contains|script:inject|.+?:has|.+?:matches-css(?:-before|-after)?|:xpath)\(.+?\)$/; + this.reNeedHostname = /^(?:script:contains|script:inject|.+?:has|.+?:has-text|.+?:if|.+?:if-not|.+?:matches-css(?:-before|-after)?|.*?:xpath)\(.+\)$/; }; /******************************************************************************/ @@ -331,7 +360,12 @@ FilterParser.prototype.parse = function(raw) { // ##script:contains(...) // ##script:inject(...) // ##.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 && @@ -698,91 +732,178 @@ FilterContainer.prototype.freeze = function() { // implemented (if ever). Unlikely, see: // https://github.com/gorhill/uBlock/issues/1752 -FilterContainer.prototype.isValidSelector = (function() { - var div = document.createElement('div'); - var matchesProp = (function() { - if ( typeof div.matches === 'function' ) { - return 'matches'; +FilterContainer.prototype.compileSelector = (function() { + var reStyleSelector = /^(.+?):style\((.+?)\)$/, + reStyleBad = /url\([^)]+\)/, + reScriptSelector = /^script:(contains|inject)\((.+)\)$/; + + return function(raw) { + if ( isValidCSSSelector(raw) && raw.indexOf('[-abp-properties=') === -1 ) { + return raw; } - if ( typeof div.mozMatchesSelector === 'function' ) { - return 'mozMatchesSelector'; + + // We rarely reach this point. + var matches; + + // `:style` selector? + if ( (matches = reStyleSelector.exec(raw)) !== null ) { + if ( isValidCSSSelector(matches[1]) && reStyleBad.test(matches[2]) === false ) { + return JSON.stringify({ + style: true, + raw: raw, + parts: [ matches[1], '{' + matches[2] + '}' ] + }); + } + return; } - if ( typeof div.webkitMatchesSelector === 'function' ) { - return 'webkitMatchesSelector'; + + // `script:` filter? + if ( (matches = 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; + } + return; } - return ''; - })(); - // Not all browsers support `Element.matches`: - // http://caniuse.com/#feat=matchesselector - if ( matchesProp === '' ) { - return function() { - return true; - }; - } - var reHasSelector = /^(.+?):has\((.+?)\)$/, - reMatchesCSSSelector = /^(.+?):matches-css(?:-before|-after)?\((.+?)\)$/, - reXpathSelector = /^:xpath\((.+?)\)$/, - reStyleSelector = /^(.+?):style\((.+?)\)$/, - reStyleBad = /url\([^)]+\)/, - reScriptSelector = /^script:(contains|inject)\((.+)\)$/; + // Procedural selector? + var compiled; + if ( (compiled = this.compileProceduralSelector(raw)) ) { + return compiled; + } - // Keep in mind: - // https://github.com/gorhill/uBlock/issues/693 - // https://github.com/gorhill/uBlock/issues/1955 - var isValidCSSSelector = function(s) { - try { - div[matchesProp](s + ', ' + s + ':not(#foo)'); - } catch (ex) { - return false; + µb.logger.writeOne('', 'error', 'Cosmetic filtering – invalid filter: ' + raw); + return; + }; +})(); + +/******************************************************************************/ + +FilterContainer.prototype.compileProceduralSelector = (function() { + var reParserEx = /(:(?:has|has-text|if|if-not|matches-css|matches-css-after|matches-css-before|xpath))\(.+\)$/, + reFirstParentheses = /^\(*/, + reLastParentheses = /\)*$/, + reEscapeRegex = /[.*+?^${}()|[\]\\]/g; + + var lastProceduralSelector = '', + lastProceduralSelectorCompiled; + + var compileCSSSelector = function(s) { + if ( isValidCSSSelector(s) ) { + return s; } - return true; }; - return function(s) { - if ( isValidCSSSelector(s) && s.indexOf('[-abp-properties=') === -1 ) { - return true; + var compileText = function(s) { + if ( reIsRegexLiteral.test(s) ) { + s = s.slice(1, -1); + if ( isBadRegex(s) ) { return; } + } else { + s = s.replace(reEscapeRegex, '\\$&'); } - // We reach this point very rarely. - var matches; + return s; + }; - // Future `:has`-based filter? If so, validate both parts of the whole - // selector. - matches = reHasSelector.exec(s); - if ( matches !== null ) { - return isValidCSSSelector(matches[1]) && isValidCSSSelector(matches[2]); - } - // Custom `:matches-css`-based filter? - matches = reMatchesCSSSelector.exec(s); - if ( matches !== null ) { - return isValidCSSSelector(matches[1]); - } - // Custom `:xpath`-based filter? - matches = reXpathSelector.exec(s); - if ( matches !== null ) { - try { - return document.createExpression(matches[1], null) instanceof XPathExpression; - } catch (e) { - } - return false; + var compileCSSDeclaration = function(s) { + var name, value, + pos = s.indexOf(':'); + if ( pos === -1 ) { return; } + name = s.slice(0, pos).trim(); + value = s.slice(pos + 1).trim(); + if ( reIsRegexLiteral.test(value) ) { + value = value.slice(1, -1); + if ( isBadRegex(value) ) { return; } + } else { + value = value.replace(reEscapeRegex, '\\$&'); } - // `:style` selector? - matches = reStyleSelector.exec(s); - if ( matches !== null ) { - return isValidCSSSelector(matches[1]) && reStyleBad.test(matches[2]) === false; + return { name: name, value: value }; + }; + + var compileConditionalSelector = function(s) { + return compile(s); + }; + + var compileXpathExpression = function(s) { + var dummy; + try { + dummy = document.createExpression(s, null) instanceof XPathExpression; + } catch (e) { + return; } - // Special `script:` filter? - matches = reScriptSelector.exec(s); - if ( matches !== null ) { - if ( matches[1] === 'inject' ) { - return true; + return s; + }; + + 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 ] + ]); + + var compile = function(raw) { + var matches = reParserEx.exec(raw); + if ( matches === null ) { 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 = reParserEx.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; } } - return matches[2].startsWith('/') === false || - matches[2].endsWith('/') === false || - isBadRegex(matches[2].slice(1, -1)) === false; + if ( depth !== 0 ) { + currentArgument += nextOperand + nextOperator; + } else { + 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); } - µb.logger.writeOne('', 'error', 'Cosmetic filtering – invalid filter: ' + s); - return false; + if ( tasks.length === 0 || depth !== 0 ) { return; } + return { selector: firstOperand, tasks: tasks }; + }; + + return function(raw) { + if ( raw === lastProceduralSelector ) { + return lastProceduralSelectorCompiled; + } + lastProceduralSelector = raw; + var compiled = compile(raw); + if ( compiled !== undefined ) { + compiled.procedural = true; + compiled.raw = raw; + compiled = JSON.stringify(compiled); + } + lastProceduralSelectorCompiled = compiled; + return compiled; }; })(); @@ -843,7 +964,7 @@ FilterContainer.prototype.compile = function(s, out) { // still the most common, and can easily be tested using a plain regex. if ( this.reClassOrIdSelector.test(parsed.suffix) === false && - this.isValidSelector(parsed.suffix) === false + this.compileSelector(parsed.suffix) === undefined ) { return true; } @@ -895,15 +1016,15 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, out) { return; } // Composite CSS rule. - if ( this.isValidSelector(selector) ) { + if ( this.compileSelector(selector) ) { out.push('c\vlg+\v' + key + '\v' + selector); } return; } - if ( this.isValidSelector(selector) !== true ) { - return; - } + var compiled = this.compileSelector(selector); + if ( compiled === undefined ) { return; } + // TODO: Detect and error on procedural cosmetic filters. // ["title"] and ["alt"] will go in high-low generic bin. if ( this.reHighLow.test(selector) ) { @@ -948,10 +1069,6 @@ FilterContainer.prototype.compileGenericHideSelector = function(parsed, out) { FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, out) { var selector = parsed.suffix; - if ( this.isValidSelector(selector) !== true ) { - return; - } - // script:contains(...) // script:inject(...) if ( this.reScriptSelector.test(selector) ) { @@ -959,10 +1076,14 @@ FilterContainer.prototype.compileGenericUnhideSelector = function(parsed, out) { return; } + // Procedural cosmetic filters are acceptable as generic exception filters. + var compiled = this.compileSelector(selector); + if ( compiled === undefined ) { return; } + // https://github.com/chrisaljoudi/uBlock/issues/497 // All generic exception filters are put in the same bucket: they are // expected to be very rare. - out.push('c\vg1\v' + selector); + out.push('c\vg1\v' + compiled); }; /******************************************************************************/ @@ -980,20 +1101,24 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, o hostname = this.punycode.toASCII(hostname); } - var domain = this.µburi.domainFromHostname(hostname), + var selector = parsed.suffix, + domain = this.µburi.domainFromHostname(hostname), hash; // script:contains(...) // script:inject(...) - if ( this.reScriptSelector.test(parsed.suffix) ) { + if ( this.reScriptSelector.test(selector) ) { hash = domain !== '' ? domain : this.noDomainHash; if ( unhide ) { hash = '!' + hash; } - out.push('c\vjs\v' + hash + '\v' + hostname + '\v' + parsed.suffix); + out.push('c\vjs\v' + hash + '\v' + hostname + '\v' + 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 ) { @@ -1005,7 +1130,7 @@ FilterContainer.prototype.compileHostnameSelector = function(hostname, parsed, o hash = '!' + hash; } - out.push('c\vh\v' + hash + '\v' + hostname + '\v' + parsed.suffix); + out.push('c\vh\v' + hash + '\v' + hostname + '\v' + compiled); }; /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index b422d1abc8159..c6ade1f4f4acd 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -100,6 +100,10 @@ var onMessage = function(request, sender, callback) { µb.mouseURL = request.url; break; + case 'compileCosmeticFilterSelector': + response = µb.cosmeticFilteringEngine.compileSelector(request.selector); + break; + case 'cosmeticFiltersInjected': µb.cosmeticFilteringEngine.addToSelectorCache(request); /* falls through */ diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index 176a2896c1dc4..ff827fdcd7f0c 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -134,14 +134,23 @@ var fromCosmeticFilter = function(details) { } candidates[details.rawFilter] = new RegExp(reStr.join('\\v') + '(?:\\n|$)'); + // Procedural filters, which are pre-compiled, make thing sort of + // complicated. We are going to also search for one portion of the + // compiled form of a filter. + var filterEx = '(' + + reEscape(filter) + + '|[^\\v]+' + + reEscape(JSON.stringify({ raw: filter }).slice(1,-1)) + + '[^\\v]+)'; + // Second step: find hostname-based versions. // Reference: FilterContainer.compileHostnameSelector(). - var pos; - var hostname = details.hostname; + var pos, + hostname = details.hostname; if ( hostname !== '' ) { for ( ;; ) { candidates[hostname + '##' + filter] = new RegExp( - ['c', 'h', '[^\\v]+', reEscape(hostname), reEscape(filter)].join('\\v') + + ['c', 'h', '[^\\v]+', reEscape(hostname), filterEx].join('\\v') + '(?:\\n|$)' ); pos = hostname.indexOf('.'); @@ -159,7 +168,7 @@ var fromCosmeticFilter = function(details) { if ( pos !== -1 ) { var entity = domain.slice(0, pos) + '.*'; candidates[entity + '##' + filter] = new RegExp( - ['c', 'h', '[^\\v]+', reEscape(entity), reEscape(filter)].join('\\v') + + ['c', 'h', '[^\\v]+', reEscape(entity), filterEx].join('\\v') + '(?:\\n|$)' ); } diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index 29ac23477b1c1..51c67d6fe5dea 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.js @@ -31,38 +31,36 @@ if ( typeof vAPI !== 'object' || !vAPI.domFilterer ) { return; } -var df = vAPI.domFilterer, - loggedSelectors = vAPI.loggedSelectors || {}, - matchedSelectors = [], - selectors, i, selector; - - -// CSS selectors. -selectors = df.jobQueue[2]._0.concat(df.jobQueue[3]._0); -i = selectors.length; -while ( i-- ) { - selector = selectors[i]; - if ( loggedSelectors.hasOwnProperty(selector) ) { - continue; - } - if ( document.querySelector(selector) === null ) { - continue; - } - loggedSelectors[selector] = true; - matchedSelectors.push(selector); -} +var loggedSelectors = vAPI.loggedSelectors || {}, + matchedSelectors = []; -// Non-CSS selectors. -var logHit = function(node, job) { - if ( !job.raw || loggedSelectors.hasOwnProperty(job.raw) ) { - return; + +var evaluateSelector = function(selector) { + if ( + loggedSelectors.hasOwnProperty(selector) === false && + document.querySelector(selector) !== null + ) { + loggedSelectors[selector] = true; + matchedSelectors.push(selector); } - loggedSelectors[job.raw] = true; - matchedSelectors.push(job.raw); }; -for ( i = 4; i < df.jobQueue.length; i++ ) { - df.runJob(df.jobQueue[i], logHit); -} + +// Simple CSS selector-based cosmetic filters. +vAPI.domFilterer.simpleHideSelectors.entries.forEach(evaluateSelector); + +// Complex CSS selector-based cosmetic filters. +vAPI.domFilterer.complexHideSelectors.entries.forEach(evaluateSelector); + +// Procedural cosmetic filters. +vAPI.domFilterer.proceduralSelectors.entries.forEach(function(pfilter) { + if ( + loggedSelectors.hasOwnProperty(pfilter.raw) === false && + pfilter.exec().length !== 0 + ) { + loggedSelectors[pfilter.raw] = true; + matchedSelectors.push(pfilter.raw); + } +}); vAPI.loggedSelectors = loggedSelectors; diff --git a/src/js/scriptlets/dom-inspector.js b/src/js/scriptlets/dom-inspector.js index a31f6988a925b..5ef7658b6b7a6 100644 --- a/src/js/scriptlets/dom-inspector.js +++ b/src/js/scriptlets/dom-inspector.js @@ -693,7 +693,7 @@ var cosmeticFilterMapper = (function() { i, j; // CSS-based selectors: simple one. - selectors = vAPI.domFilterer.job2._0; + selectors = vAPI.domFilterer.simpleHideSelectors.entries; i = selectors.length; while ( i-- ) { selector = selectors[i]; @@ -709,9 +709,9 @@ var cosmeticFilterMapper = (function() { } } } - + // CSS-based selectors: complex one (must query from doc root). - selectors = vAPI.domFilterer.job3._0; + selectors = vAPI.domFilterer.complexHideSelectors.entries; i = selectors.length; while ( i-- ) { selector = selectors[i]; @@ -726,14 +726,12 @@ var cosmeticFilterMapper = (function() { } // Non-CSS selectors. - var runJobCallback = function(node, job) { + var runJobCallback = function(node, pfilter) { if ( filterMap.has(node) === false ) { - filterMap.set(node, job.raw); + filterMap.set(node, pfilter.raw); } }; - for ( i = 4; i < vAPI.domFilterer.jobQueue.length; i++ ) { - vAPI.domFilterer.runJob(vAPI.domFilterer.jobQueue[i], runJobCallback); - } + vAPI.domFilterer.proceduralSelectors.forEachNode(runJobCallback); }; var incremental = function(rootNode) { diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index fc77cbdd0dcd1..1181f77f54bc5 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -177,6 +177,14 @@ var safeQuerySelectorAll = function(node, selector) { /******************************************************************************/ +var rawFilterFromTextarea = function() { + var s = taCandidate.value, + pos = s.indexOf('\n'); + return pos === -1 ? s.trim() : s.slice(0, pos).trim(); +}; + +/******************************************************************************/ + var getElementBoundingClientRect = function(elem) { var rect = typeof elem.getBoundingClientRect === 'function' ? elem.getBoundingClientRect() : @@ -635,7 +643,9 @@ var filtersFrom = function(x, y) { filterToDOMInterface.set @desc Look-up all the HTML elements matching the filter passed in argument. - @param string, a cosmetic of network filter. + @param string, a cosmetic or network filter. + @param function, called once all items matching the filter have been + collected. @return array, or undefined if the filter is invalid. filterToDOMInterface.preview @@ -733,16 +743,15 @@ var filterToDOMInterface = (function() { // ways to compose a valid href to the same effective URL. One idea is to // normalize all a[href] on the page, but for now I will wait and see, as I // prefer to refrain from tampering with the page content if I can avoid it. - var fromCosmeticFilter = function(filter) { + var fromPlainCosmeticFilter = function(filter) { var elems; try { elems = document.querySelectorAll(filter); } catch (e) { - return fromProceduralCosmeticFilter(filter); + return; } - var out = [], - iElem = elems.length; + var out = [], iElem = elems.length; while ( iElem-- ) { out.push({ type: 'cosmetic', elem: elems[iElem]}); } @@ -751,108 +760,27 @@ var filterToDOMInterface = (function() { // https://github.com/gorhill/uBlock/issues/1772 // Handle procedural cosmetic filters. - var fromProceduralCosmeticFilter = function(filter) { - if ( filter.charCodeAt(filter.length - 1) === 0x29 /* ')' */ ) { - var parts = reProceduralCosmeticFilter.exec(filter); - if ( - parts !== null && - proceduralCosmeticFilterFunctions.hasOwnProperty(parts[2]) - ) { - return proceduralCosmeticFilterFunctions[parts[2]]( - parts[1].trim(), - parts[3].trim() - ); - } + var fromCompiledCosmeticFilter = function(raw) { + if ( typeof raw !== 'string' ) { return; } + var o; + try { + o = JSON.parse(raw); + } catch(ex) { + return; } - }; - - var reProceduralCosmeticFilter = /^(.*?):(matches-css|has|style|xpath)\((.+?)\)$/; - - // Collection of handlers for procedural cosmetic filters. - var proceduralCosmeticFilterFunctions = { - 'has': function(selector, arg) { - if ( selector === '' ) { return; } - var elems; - try { - elems = document.querySelectorAll(selector); - document.querySelector(arg); - } catch(ex) { - return; - } - var out = [], elem; - for ( var i = 0, n = elems.length; i < n; i++ ) { - elem = elems[i]; - if ( elem.querySelector(arg) ) { - out.push({ type: 'cosmetic', elem: elem }); - } - } - return out; - }, - 'matches-css': function(selector, arg) { - if ( selector === '' ) { return; } - var elems; - try { - elems = document.querySelectorAll(selector); - } catch(ex) { - return; - } - var out = [], elem, style, - pos = arg.indexOf(':'); - if ( pos === -1 ) { return; } - var prop = arg.slice(0, pos).trim(), - reText = arg.slice(pos + 1).trim(); - if ( reText === '' ) { return; } - var re = reText !== '*' ? - new RegExp('^' + reText.replace(/[.+?${}()|[\]\\^]/g, '\\$&').replace(/\*+/g, '.*?') + '$') : - /./; - for ( var i = 0, n = elems.length; i < n; i++ ) { - elem = elems[i]; - style = window.getComputedStyle(elem, null); - if ( re.test(style[prop]) ) { - out.push({ type: 'cosmetic', elem: elem }); - } - } - return out; - }, - 'style': function(selector, arg) { - if ( selector === '' || arg === '' ) { return; } - var elems; - try { - elems = document.querySelectorAll(selector); - } catch(ex) { - return; - } - var out = []; - for ( var i = 0, n = elems.length; i < n; i++ ) { - out.push({ type: 'cosmetic', elem: elems[i] }); - } - lastAction = selector + ' { ' + arg + ' }'; - return out; - }, - 'xpath': function(selector, arg) { - if ( selector !== '' ) { return []; } - var result; - try { - result = document.evaluate( - arg, - document, - null, - XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, - null - ); - } catch(ex) { - return; - } - if ( result === undefined ) { return []; } - var out = [], elem, i = result.snapshotLength; - while ( i-- ) { - elem = result.snapshotItem(i); - if ( elem.nodeType === 1 ) { - out.push({ type: 'cosmetic', elem: elem }); - } - } - return out; + var elems; + if ( o.style ) { + elems = document.querySelectorAll(o.parts[0]); + lastAction = o.parts.join(' '); + } else if ( o.procedural ) { + elems = vAPI.domFilterer.createProceduralFilter(o).exec(); + } + if ( !elems ) { return; } + var out = []; + for ( var i = 0, n = elems.length; i < n; i++ ) { + out.push({ type: 'cosmetic', elem: elems[i] }); } + return out; }; var lastFilter, @@ -862,26 +790,44 @@ var filterToDOMInterface = (function() { applied = false, previewing = false; - var queryAll = function(filter) { + var queryAll = function(filter, callback) { filter = filter.trim(); if ( filter === lastFilter ) { - return lastResultset; + callback(lastResultset); + return; } unapply(); if ( filter === '' ) { lastFilter = ''; lastResultset = []; - } else { - lastFilter = filter; - lastAction = undefined; - lastResultset = filter.lastIndexOf('##', 0) === 0 ? - fromCosmeticFilter(filter.slice(2)) : - fromNetworkFilter(filter); - if ( previewing ) { - apply(filter); - } + callback(lastResultset); + return; + } + lastFilter = filter; + lastAction = undefined; + if ( filter.lastIndexOf('##', 0) === -1 ) { + lastResultset = fromNetworkFilter(filter); + if ( previewing ) { apply(); } + callback(lastResultset); + return; + } + var selector = filter.slice(2); + lastResultset = fromPlainCosmeticFilter(selector); + if ( lastResultset ) { + if ( previewing ) { apply(); } + callback(lastResultset); + return; } - return lastResultset; + // Procedural cosmetic filter + vAPI.messaging.send( + 'elementPicker', + { what: 'compileCosmeticFilterSelector', selector: selector }, + function(response) { + lastResultset = fromCompiledCosmeticFilter(response); + if ( previewing ) { apply(); } + callback(lastResultset); + } + ); }; var applyHide = function() { @@ -983,9 +929,11 @@ var filterToDOMInterface = (function() { var preview = function(filter) { previewing = filter !== false; if ( previewing ) { - if ( queryAll(filter) !== undefined ) { - apply(); - } + queryAll(filter, function(items) { + if ( items !== undefined ) { + apply(); + } + }); } else { unapply(); } @@ -999,67 +947,72 @@ var filterToDOMInterface = (function() { }; })(); -// https://www.youtube.com/watch?v=nuUXJ6RfIik - /******************************************************************************/ -var userFilterFromCandidate = function() { - var v = taCandidate.value; - var items = filterToDOMInterface.set(v); - if ( !items || items.length === 0 ) { - return false; - } +var userFilterFromCandidate = function(callback) { + var v = rawFilterFromTextarea(); + filterToDOMInterface.set(v, function(items) { + if ( !items || items.length === 0 ) { + callback(); + return; + } - // https://github.com/gorhill/uBlock/issues/738 - // Trim dots. - var hostname = window.location.hostname; - if ( hostname.slice(-1) === '.' ) { - hostname = hostname.slice(0, -1); - } + // https://github.com/gorhill/uBlock/issues/738 + // Trim dots. + var hostname = window.location.hostname; + if ( hostname.slice(-1) === '.' ) { + hostname = hostname.slice(0, -1); + } - // Cosmetic filter? - if ( v.lastIndexOf('##', 0) === 0 ) { - return hostname + v; - } + // Cosmetic filter? + if ( v.lastIndexOf('##', 0) === 0 ) { + callback(hostname + v); + return; + } - // Assume net filter - var opts = []; + // Assume net filter + var opts = []; - // If no domain included in filter, we need domain option - if ( v.lastIndexOf('||', 0) === -1 ) { - opts.push('domain=' + hostname); - } + // If no domain included in filter, we need domain option + if ( v.lastIndexOf('||', 0) === -1 ) { + opts.push('domain=' + hostname); + } - var item = items[0]; - if ( item.opts ) { - opts.push(item.opts); - } + var item = items[0]; + if ( item.opts ) { + opts.push(item.opts); + } - if ( opts.length ) { - v += '$' + opts.join(','); - } + if ( opts.length ) { + v += '$' + opts.join(','); + } - return v; + callback(v); + }); }; /******************************************************************************/ -var onCandidateChanged = function() { - var elems = [], - items = filterToDOMInterface.set(taCandidate.value), - valid = items !== undefined; - if ( valid ) { - for ( var i = 0; i < items.length; i++ ) { - elems.push(items[i].elem); +var onCandidateChanged = (function() { + var process = function(items) { + var elems = [], valid = items !== undefined; + if ( valid ) { + for ( var i = 0; i < items.length; i++ ) { + elems.push(items[i].elem); + } } - } - pickerBody.querySelector('body section textarea + div').textContent = valid ? - items.length.toLocaleString() : - '0'; - taCandidate.classList.toggle('invalidFilter', !valid); - dialog.querySelector('#create').disabled = elems.length === 0; - highlightElements(elems, true); -}; + pickerBody.querySelector('body section textarea + div').textContent = valid ? + items.length.toLocaleString() : + 'ε'; + dialog.querySelector('section').classList.toggle('invalidFilter', !valid); + dialog.querySelector('#create').disabled = elems.length === 0; + highlightElements(elems, true); + }; + + return function() { + filterToDOMInterface.set(rawFilterFromTextarea(), process); + }; +})(); /******************************************************************************/ @@ -1132,8 +1085,8 @@ var onDialogClicked = function(ev) { // We have to exit from preview mode: this guarantees matching elements // will be found for the candidate filter. filterToDOMInterface.preview(false); - var filter = userFilterFromCandidate(); - if ( filter ) { + userFilterFromCandidate(function(filter) { + if ( !filter ) { return; } var d = new Date(); vAPI.messaging.send( 'elementPicker', @@ -1143,9 +1096,9 @@ var onDialogClicked = function(ev) { pageDomain: window.location.hostname } ); - filterToDOMInterface.preview(taCandidate.value); + filterToDOMInterface.preview(rawFilterFromTextarea()); stopPicker(); - } + }); } else if ( ev.target.id === 'pick' ) { @@ -1161,7 +1114,7 @@ var onDialogClicked = function(ev) { if ( filterToDOMInterface.previewing() ) { filterToDOMInterface.preview(false); } else { - filterToDOMInterface.preview(taCandidate.value); + filterToDOMInterface.preview(rawFilterFromTextarea()); } highlightElements(targetElements, true); } @@ -1300,6 +1253,7 @@ var onKeyPressed = function(ev) { if ( ev.which === 27 ) { ev.stopPropagation(); ev.preventDefault(); + filterToDOMInterface.preview(false); stopPicker(); } }; From d4155bf9e0cb1504c5b8be8ee48b6465b8c5bb64 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 25 Dec 2016 17:00:24 -0500 Subject: [PATCH 0029/4093] new revision for dev build + force lists to be recompiled --- platform/chromium/manifest.json | 2 +- src/js/background.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 5eb101a757544..ae2931dd2fba1 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.0", + "version": "1.10.5.1", "default_locale": "en", "description": "__MSG_extShortDesc__", diff --git a/src/js/background.js b/src/js/background.js index 496a1b5dbafc1..a728f43070875 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -108,8 +108,8 @@ return { // read-only systemSettings: { - compiledMagic: 'lbmqiweqbvha', - selfieMagic: 'mhirtyetynnf' + compiledMagic: 'xhjvmgkamffc', + selfieMagic: 'xhjvmgkamffc' }, restoreBackupSettings: { From 251bbe0f439e4fe65ee3970f0b7c68132a5e1101 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 25 Dec 2016 17:05:30 -0500 Subject: [PATCH 0030/4093] use plain E instead of Greek epsilon (for some reason does not render fine in FFox) --- src/js/scriptlets/element-picker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index 1181f77f54bc5..0eb699d6c2c05 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -1003,7 +1003,7 @@ var onCandidateChanged = (function() { } pickerBody.querySelector('body section textarea + div').textContent = valid ? items.length.toLocaleString() : - 'ε'; + 'E'; dialog.querySelector('section').classList.toggle('invalidFilter', !valid); dialog.querySelector('#create').disabled = elems.length === 0; highlightElements(elems, true); From 6e458dca5cb7f6a3c5f7aee2619058608d90af22 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 26 Dec 2016 11:35:37 -0500 Subject: [PATCH 0031/4093] fix #2264 --- src/css/whitelist.css | 34 ++++++++++++++--- src/js/messaging.js | 4 ++ src/js/ublock.js | 33 ++++++++++++++--- src/js/whitelist.js | 86 ++++++++++++++++++++++++++++++------------- src/whitelist.html | 5 ++- 5 files changed, 125 insertions(+), 37 deletions(-) diff --git a/src/css/whitelist.css b/src/css/whitelist.css index 5a0e834e8d7ae..b017cf0cd7bad 100644 --- a/src/css/whitelist.css +++ b/src/css/whitelist.css @@ -5,12 +5,36 @@ div > p:last-child { margin-bottom: 0; } #whitelist { - box-sizing: border-box; + border: 1px solid gray; height: 60vh; - text-align: left; + margin: 0; + padding: 1px; + position: relative; + resize: vertical; + } +#whitelist.invalid { + border-color: red; + } +#whitelist textarea { + border: none; + box-sizing: border-box; + height: 100%; + padding: 0.4em; + resize: none; + text-align: left; white-space: pre; width: 100%; } -#whitelist.bad { - background-color: #fee; - } +#whitelist textarea + div { + background-color: red; + bottom: 0; + color: white; + display: none; + padding: 2px 4px; + pointer-events: none; + position: absolute; + right: 0; +} +#whitelist.invalid textarea + div { + display: block; +} diff --git a/src/js/messaging.js b/src/js/messaging.js index c6ade1f4f4acd..360fc7e0f39fd 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1011,6 +1011,10 @@ var onMessage = function(request, sender, callback) { response = getRules(); break; + case 'validateWhitelistString': + response = µb.validateWhitelistString(request.raw); + break; + case 'writeHiddenSettings': µb.hiddenSettingsFromString(request.content); break; diff --git a/src/js/ublock.js b/src/js/ublock.js index d68a12929df8f..528edb534897f 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -194,9 +194,7 @@ var matchBucket = function(url, hostname, bucket, start) { µBlock.whitelistFromString = function(s) { var whitelist = Object.create(null), - reInvalidHostname = /[^a-z0-9.\-\[\]:]/, - reHostnameExtractor = /([a-z0-9\[][a-z0-9.\-]*[a-z0-9\]])(?::[\d*]+)?\/(?:[^\x00-\x20\/]|$)[^\x00-\x20]*$/, - lines = s.split(/[\n\r]+/), + lineIter = new this.LineIterator(s), line, matches, key, directive, re; // Comment bucket must always be ready to be used. @@ -205,8 +203,9 @@ var matchBucket = function(url, hostname, bucket, start) { // New set of directives, scrap cached data. directiveToRegexpMap.clear(); - for ( var i = 0; i < lines.length; i++ ) { - line = lines[i].trim(); + while ( !lineIter.eot() ) { + line = lineIter.next().trim(); + // https://github.com/gorhill/uBlock/issues/171 // Skip empty lines if ( line === '' ) { @@ -228,7 +227,7 @@ var matchBucket = function(url, hostname, bucket, start) { } } // Regex-based (ensure it is valid) - else if ( line.startsWith('/') && line.endsWith('/') ) { + else if ( line.length > 2 && line.startsWith('/') && line.endsWith('/') ) { key = '//'; directive = line; try { @@ -267,6 +266,28 @@ var matchBucket = function(url, hostname, bucket, start) { return whitelist; }; +µBlock.validateWhitelistString = function(s) { + var lineIter = new this.LineIterator(s), line; + while ( !lineIter.eot() ) { + line = lineIter.next().trim(); + if ( line === '' ) { continue; } + if ( line.startsWith('#') ) { continue; } // Comment + if ( line.indexOf('/') === -1 ) { // Plain hostname + if ( reInvalidHostname.test(line) ) { return false; } + continue; + } + if ( line.length > 2 && line.startsWith('/') && line.endsWith('/') ) { // Regex-based + try { new RegExp(line.slice(1, -1)); } catch(ex) { return false; } + continue; + } + if ( reHostnameExtractor.test(line) === false ) { return false; } // URL + } + return true; +}; + +var reInvalidHostname = /[^a-z0-9.\-\[\]:]/, + reHostnameExtractor = /([a-z0-9\[][a-z0-9.\-]*[a-z0-9\]])(?::[\d*]+)?\/(?:[^\x00-\x20\/]|$)[^\x00-\x20]*$/; + /******************************************************************************/ })(); diff --git a/src/js/whitelist.js b/src/js/whitelist.js index 866ffca0f27e6..52a3a4d7c5021 100644 --- a/src/js/whitelist.js +++ b/src/js/whitelist.js @@ -29,30 +29,68 @@ /******************************************************************************/ -var messaging = vAPI.messaging; -var cachedWhitelist = ''; - -// Could make it more fancy if needed. But speed... It's a compromise. -var reUnwantedChars = /[\x00-\x09\x0b\x0c\x0e-\x1f!"'()<>{}|`~]/; +var messaging = vAPI.messaging, + cachedWhitelist = ''; /******************************************************************************/ -var whitelistChanged = function() { - var textarea = uDom.nodeFromId('whitelist'); - var s = textarea.value.trim(); - var changed = s === cachedWhitelist; - var bad = reUnwantedChars.test(s); - uDom.nodeFromId('whitelistApply').disabled = changed || bad; - uDom.nodeFromId('whitelistRevert').disabled = changed; - textarea.classList.toggle('bad', bad); +var getTextareaNode = function() { + var me = getTextareaNode, + node = me.theNode; + if ( node === undefined ) { + node = me.theNode = uDom.nodeFromSelector('#whitelist textarea'); + } + return node; +}; + +var setErrorNodeHorizontalOffset = function(px) { + var me = setErrorNodeHorizontalOffset, + offset = me.theOffset || 0; + if ( px === offset ) { return; } + var node = me.theNode; + if ( node === undefined ) { + node = me.theNode = uDom.nodeFromSelector('#whitelist textarea + div'); + } + node.style.right = px + 'px'; + me.theOffset = px; }; /******************************************************************************/ +var whitelistChanged = (function() { + var changedWhitelist, changed, timer; + + var updateUI = function(good) { + uDom.nodeFromId('whitelistApply').disabled = changed || !good; + uDom.nodeFromId('whitelistRevert').disabled = changed; + uDom.nodeFromId('whitelist').classList.toggle('invalid', !good); + }; + + var validate = function() { + timer = undefined; + messaging.send( + 'dashboard', + { what: 'validateWhitelistString', raw: changedWhitelist }, + updateUI + ); + }; + + return function() { + changedWhitelist = getTextareaNode().value.trim(); + changed = changedWhitelist === cachedWhitelist; + if ( timer !== undefined ) { clearTimeout(timer); } + timer = vAPI.setTimeout(validate, 251); + var textarea = getTextareaNode(); + setErrorNodeHorizontalOffset(textarea.offsetWidth - textarea.clientWidth); + }; +})(); + +/******************************************************************************/ + var renderWhitelist = function() { var onRead = function(whitelist) { cachedWhitelist = whitelist.trim(); - uDom.nodeFromId('whitelist').value = cachedWhitelist + '\n'; + getTextareaNode().value = cachedWhitelist + '\n'; whitelistChanged(); }; messaging.send('dashboard', { what: 'getWhitelist' }, onRead); @@ -62,8 +100,8 @@ var renderWhitelist = function() { var handleImportFilePicker = function() { var fileReaderOnLoadHandler = function() { - var textarea = uDom('#whitelist'); - textarea.val([textarea.val(), this.result].join('\n').trim()); + var textarea = getTextareaNode(); + textarea.value = [textarea.value.trim(), this.result.trim()].join('\n').trim(); whitelistChanged(); }; var file = this.files[0]; @@ -92,10 +130,8 @@ var startImportFilePicker = function() { /******************************************************************************/ var exportWhitelistToFile = function() { - var val = uDom('#whitelist').val().trim(); - if ( val === '' ) { - return; - } + var val = getTextareaNode().value.trim(); + if ( val === '' ) { return; } var filename = vAPI.i18n('whitelistExportFilename') .replace('{{datetime}}', uBlockDashboard.dateNowToSensibleString()) .replace(/ +/g, '_'); @@ -108,7 +144,7 @@ var exportWhitelistToFile = function() { /******************************************************************************/ var applyChanges = function() { - cachedWhitelist = uDom.nodeFromId('whitelist').value.trim(); + cachedWhitelist = getTextareaNode().value.trim(); var request = { what: 'setWhitelist', whitelist: cachedWhitelist @@ -117,21 +153,21 @@ var applyChanges = function() { }; var revertChanges = function() { - uDom.nodeFromId('whitelist').value = cachedWhitelist + '\n'; + getTextareaNode().value = cachedWhitelist + '\n'; whitelistChanged(); }; /******************************************************************************/ var getCloudData = function() { - return uDom.nodeFromId('whitelist').value; + return getTextareaNode().value; }; var setCloudData = function(data, append) { if ( typeof data !== 'string' ) { return; } - var textarea = uDom.nodeFromId('whitelist'); + var textarea = getTextareaNode(); if ( append ) { data = uBlockDashboard.mergeNewLines(textarea.value.trim(), data); } @@ -147,7 +183,7 @@ self.cloud.onPull = setCloudData; uDom('#importWhitelistFromFile').on('click', startImportFilePicker); uDom('#importFilePicker').on('change', handleImportFilePicker); uDom('#exportWhitelistToFile').on('click', exportWhitelistToFile); -uDom('#whitelist').on('input', whitelistChanged); +uDom('#whitelist textarea').on('input', whitelistChanged); uDom('#whitelistApply').on('click', applyChanges); uDom('#whitelistRevert').on('click', revertChanges); diff --git a/src/whitelist.html b/src/whitelist.html index 0059a39641a6c..67a70ab92dabd 100644 --- a/src/whitelist.html +++ b/src/whitelist.html @@ -17,7 +17,10 @@

-

+

+ +
E
+

From 914599431b810a367065ee4a810f0a064190c2af Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 26 Dec 2016 11:56:51 -0500 Subject: [PATCH 0032/4093] 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 ae2931dd2fba1..83ae9bf1719cd 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.1", + "version": "1.10.5.2", "default_locale": "en", "description": "__MSG_extShortDesc__", From 7558fedc38b6e93521854a90665728a8b9df9871 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 26 Dec 2016 12:37:43 -0500 Subject: [PATCH 0033/4093] fix typo in new :if/:if-not procedural operators --- src/js/contentscript.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 33f241acad538..12d249e54fd45 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -443,11 +443,11 @@ PSelector.prototype.exec = function(input) { }; PSelector.prototype.test = function(input) { //var t0 = window.performance.now(); - var tasks = this.tasks, nodes = this.prime(input), aa0 = [ null ], aa; + var tasks = this.tasks, nodes = this.prime(input), AA = [ null ], aa; for ( var i = 0, ni = nodes.length; i < ni; i++ ) { - aa0[0] = nodes[i]; aa = aa0; + AA[0] = nodes[i]; aa = AA; for ( var j = 0, nj = tasks.length; j < nj && aa.length !== 0; j++ ) { - aa = tasks[i].exec(aa); + aa = tasks[j].exec(aa); } if ( aa.length !== 0 ) { return true; } } From 5aa122e856b80b28dd1d2354e825f9759a173058 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 27 Dec 2016 12:32:52 -0500 Subject: [PATCH 0034/4093] allow lone css selector in :if/:if-not operators --- src/js/cosmetic-filtering.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 6d693adb5fb4a..666f9dccf0eb2 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -850,7 +850,12 @@ FilterContainer.prototype.compileProceduralSelector = (function() { var compile = function(raw) { var matches = reParserEx.exec(raw); - if ( matches === null ) { return; } + if ( matches === null ) { + if ( isValidCSSSelector(raw) ) { + return { selector: raw, tasks: [] }; + } + return; + } var tasks = [], firstOperand = raw.slice(0, matches.index), currentOperator = matches[1], From e09b70247088e0583ec7c1cf64456ba9f0b54330 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 28 Dec 2016 23:39:15 -0500 Subject: [PATCH 0035/4093] fix #2274 (hopefully) --- platform/firefox/vapi-background.js | 2 +- src/css/popup.css | 3 + src/js/popup.js | 89 +++++++++++++++++------------ 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 36964825e0652..d960c8f0db503 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -2563,7 +2563,7 @@ vAPI.toolbarButton = { var win = winWatcher.getCurrentWindow(); var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab); vAPI.tabs.open({ - url: 'popup.html?tabId=' + curTabId, + url: 'popup.html?tabId=' + curTabId + '&mobile=1', index: -1, select: true }); diff --git a/src/css/popup.css b/src/css/popup.css index 2e8dfefc96749..2d47a0a95d6d3 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -11,6 +11,9 @@ body { body.fullsize { overflow: auto; } +body.mobile { + overflow-y: auto; + } /** https://github.com/gorhill/uBlock/issues/83 .portrait = portrait mode = width is constrained = optimize layout accordingly. diff --git a/src/js/popup.js b/src/js/popup.js index 50eddd7816c05..e3453ed503398 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -40,26 +40,15 @@ if ( typeof popupFontSize === 'string' && popupFontSize !== 'unset' ) { var dfPaneVisibleStored = vAPI.localStorage.getItem('popupFirewallPane') === 'true'; -// Hacky? I couldn't figure a CSS recipe for this problem. -// I do not want the left pane -- optional and hidden by defaut -- to -// dictate the height of the popup. The right pane dictates the height -// of the popup, and the left pane will have a scrollbar if ever its -// height is more than what is available. -(function() { - // No restriction on vertical size? - if ( /[\?&]fullsize=1/.test(window.location.search) ) { - document.body.classList.add('fullsize'); - return; - } +// No restriction on vertical size? +if ( /[\?&]fullsize=1/.test(window.location.search) ) { + document.body.classList.add('fullsize'); +} - var rpane = document.querySelector('#panes > div:nth-of-type(1)'); - if ( typeof rpane.offsetHeight === 'number' ) { - document.querySelector('#panes > div:nth-of-type(2)').style.setProperty( - 'height', - rpane.offsetHeight + 'px' - ); - } -})(); +// Mobile device? +if ( /[\?&]mobile=1/.test(window.location.search) ) { + document.body.classList.add('mobile'); +} // The padlock/eraser must be manually positioned: // - Its vertical position depends on the height of the popup title bar @@ -390,23 +379,10 @@ var renderPrivacyExposure = function() { // Assume everything has to be done incrementally. var renderPopup = function() { - if ( popupData.fontSize !== popupFontSize ) { - popupFontSize = popupData.fontSize; - if ( popupFontSize !== 'unset' ) { - document.body.style.setProperty('font-size', popupFontSize); - vAPI.localStorage.setItem('popupFontSize', popupFontSize); - } else { - document.body.style.removeProperty('font-size'); - vAPI.localStorage.removeItem('popupFontSize'); - } - } - if ( popupData.tabTitle ) { document.title = popupData.appName + ' - ' + popupData.tabTitle; } - uDom.nodeFromId('appname').textContent = popupData.appName; - uDom.nodeFromId('version').textContent = popupData.appVersion; uDom('body') .toggleClass('advancedUser', popupData.advancedUserEnabled) .toggleClass( @@ -419,9 +395,9 @@ var renderPopup = function() { // If you think the `=== true` is pointless, you are mistaken uDom.nodeFromId('gotoPick').classList.toggle('enabled', popupData.canElementPicker === true); - var text; - var blocked = popupData.pageBlockedRequestCount; - var total = popupData.pageAllowedRequestCount + blocked; + var text, + blocked = popupData.pageBlockedRequestCount, + total = popupData.pageAllowedRequestCount + blocked; if ( total === 0 ) { text = formatNumber(0); } else { @@ -499,6 +475,48 @@ var renderPopup = function() { /******************************************************************************/ +// All rendering code which need to be executed only once. + +var renderOnce = function() { + if ( popupData.fontSize !== popupFontSize ) { + popupFontSize = popupData.fontSize; + if ( popupFontSize !== 'unset' ) { + document.body.style.setProperty('font-size', popupFontSize); + vAPI.localStorage.setItem('popupFontSize', popupFontSize); + } else { + document.body.style.removeProperty('font-size'); + vAPI.localStorage.removeItem('popupFontSize'); + } + } + + uDom.nodeFromId('appname').textContent = popupData.appName; + uDom.nodeFromId('version').textContent = popupData.appVersion; + + var rpane = uDom.nodeFromSelector('#panes > div:first-of-type'), + lpane = uDom.nodeFromSelector('#panes > div:last-of-type'); + + // I do not want the left pane -- optional and hidden by defaut -- to + // dictate the height of the popup. The right pane dictates the height + // of the popup, and the left pane will have a scrollbar if ever its + // height is more than what is available. + var lpaneHeight = rpane.offsetHeight; + + // https://github.com/gorhill/uBlock/issues/2274 + // Make use of the whole viewport on mobile devices. + if ( document.body.classList.contains('mobile') ) { + lpaneHeight = Math.max( + window.innerHeight - uDom.nodeFromSelector('#gotoPrefs').offsetHeight, + lpaneHeight + ); + lpane.style.setProperty('width', (window.innerWidth - rpane.offsetWidth) + 'px'); + } + lpane.style.setProperty('height', lpaneHeight + 'px'); + + renderOnce = function(){}; +}; + +/******************************************************************************/ + var renderPopupLazy = function() { messaging.send('popupPanel', { what: 'getPopupLazyData', tabId: popupData.tabId }); }; @@ -864,6 +882,7 @@ var pollForContentChange = (function() { var getPopupData = function(tabId) { var onDataReceived = function(response) { cachePopupData(response); + renderOnce(); renderPopup(); renderPopupLazy(); // low priority rendering hashFromPopupData(true); From 35a63c784f20fb93ff9ce6b09f59484d84c5760a Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 28 Dec 2016 23:45:22 -0500 Subject: [PATCH 0036/4093] 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 83ae9bf1719cd..fbf40a8585dbe 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.2", + "version": "1.10.5.3", "default_locale": "en", "description": "__MSG_extShortDesc__", From e5f435c3b1a60e15a6d84fbd8622fed910c76389 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 29 Dec 2016 14:43:20 -0500 Subject: [PATCH 0037/4093] complete fix to #2274: detect device rotation --- src/js/popup.js | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/js/popup.js b/src/js/popup.js index e3453ed503398..7d5acfd7d4b40 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -478,6 +478,8 @@ var renderPopup = function() { // All rendering code which need to be executed only once. var renderOnce = function() { + renderOnce = function(){}; + if ( popupData.fontSize !== popupFontSize ) { popupFontSize = popupData.fontSize; if ( popupFontSize !== 'unset' ) { @@ -492,27 +494,35 @@ var renderOnce = function() { uDom.nodeFromId('appname').textContent = popupData.appName; uDom.nodeFromId('version').textContent = popupData.appVersion; + // For large displays: we do not want the left pane -- optional and + // hidden by defaut -- to dictate the height of the popup. The right pane + // dictates the height of the popup, and the left pane will have a + // scrollbar if ever its height is more than what is available. + // For small displays: we use the whole viewport. + var rpane = uDom.nodeFromSelector('#panes > div:first-of-type'), lpane = uDom.nodeFromSelector('#panes > div:last-of-type'); - // I do not want the left pane -- optional and hidden by defaut -- to - // dictate the height of the popup. The right pane dictates the height - // of the popup, and the left pane will have a scrollbar if ever its - // height is more than what is available. - var lpaneHeight = rpane.offsetHeight; + var fillViewport = function() { + lpane.style.setProperty( + 'height', + Math.max( + window.innerHeight - uDom.nodeFromSelector('#gotoPrefs').offsetHeight, + rpane.offsetHeight + ) + 'px' + ); + lpane.style.setProperty('width', (window.innerWidth - rpane.offsetWidth) + 'px'); + }; // https://github.com/gorhill/uBlock/issues/2274 // Make use of the whole viewport on mobile devices. if ( document.body.classList.contains('mobile') ) { - lpaneHeight = Math.max( - window.innerHeight - uDom.nodeFromSelector('#gotoPrefs').offsetHeight, - lpaneHeight - ); - lpane.style.setProperty('width', (window.innerWidth - rpane.offsetWidth) + 'px'); + fillViewport(); + window.addEventListener('resize', fillViewport); + return; } - lpane.style.setProperty('height', lpaneHeight + 'px'); - renderOnce = function(){}; + lpane.style.setProperty('height', rpane.offsetHeight + 'px'); }; /******************************************************************************/ From 7d08b9da390449c1fe08ce07bd03d030b5994891 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 29 Dec 2016 14:44:06 -0500 Subject: [PATCH 0038/4093] 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 fbf40a8585dbe..5ef677b40a181 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.3", + "version": "1.10.5.4", "default_locale": "en", "description": "__MSG_extShortDesc__", From c6dbdbd23b436dafba94281c15e3b8fbe79d341b Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 30 Dec 2016 10:32:17 -0500 Subject: [PATCH 0039/4093] code review of procedural cosmetic filters + better validate :style option (#2278) --- src/js/background.js | 4 +- src/js/contentscript.js | 57 +++++++++++++++------------- src/js/cosmetic-filtering.js | 41 +++++++++----------- src/js/reverselookup-worker.js | 4 +- src/js/scriptlets/cosmetic-logger.js | 11 ++++++ src/js/scriptlets/element-picker.js | 6 +-- 6 files changed, 66 insertions(+), 57 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index a728f43070875..00b82a1c64997 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -108,8 +108,8 @@ return { // read-only systemSettings: { - compiledMagic: 'xhjvmgkamffc', - selfieMagic: 'xhjvmgkamffc' + compiledMagic: 'zelhzxrhkfjr', + selfieMagic: 'zelhzxrhkfjr' }, restoreBackupSettings: { diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 12d249e54fd45..bf3729b708ce5 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -417,8 +417,9 @@ var PSelector = function(o) { this.raw = o.raw; this.selector = o.selector; this.tasks = []; - var tasks = o.tasks, task, ctor; - for ( var i = 0; i < tasks.length; i++ ) { + var tasks = o.tasks; + if ( !tasks ) { return; } + for ( var i = 0, task, ctor; i < tasks.length; i++ ) { task = tasks[i]; ctor = this.operatorToTaskMap.get(task[0]); this.tasks.push(new ctor(task)); @@ -455,26 +456,6 @@ PSelector.prototype.test = function(input) { return false; }; -var PSelectors = function() { - this.entries = []; -}; -PSelectors.prototype.add = function(o) { - this.entries.push(new PSelector(o)); -}; -PSelectors.prototype.forEachNode = function(callback) { - var pfilters = this.entries, - i = pfilters.length, - pfilter, nodes, j; - while ( i-- ) { - pfilter = pfilters[i]; - nodes = pfilter.exec(); - j = nodes.length; - while ( j-- ) { - callback(nodes[j], pfilter); - } - } -}; - /******************************************************************************/ var domFilterer = { @@ -498,8 +479,6 @@ var domFilterer = { this.entries.push(selector); this.selector = undefined; }, - forEachNodeOfSelector: function(/*callback, root, extra*/) { - }, forEachNode: function(callback, root, extra) { if ( this.selector === undefined ) { this.selector = this.entries.join(extra + ',') + extra; @@ -532,7 +511,29 @@ var domFilterer = { } } }, - proceduralSelectors: new PSelectors(), // Hiding filters: procedural + styleSelectors: { // Style filters + entries: [], + add: function(o) { + this.entries.push(o); + } + }, + proceduralSelectors: { // Hiding filters: procedural + entries: [], + add: function(o) { + this.entries.push(new PSelector(o)); + }, + forEachNode: function(callback) { + var pfilters = this.entries, i = pfilters.length, pfilter, nodes, j; + while ( i-- ) { + pfilter = pfilters[i]; + nodes = pfilter.exec(); + j = nodes.length; + while ( j-- ) { + callback(nodes[j], pfilter); + } + } + } + }, addExceptions: function(aa) { for ( var i = 0, n = aa.length; i < n; i++ ) { @@ -556,11 +557,13 @@ var domFilterer = { } var o = JSON.parse(selector); if ( o.style ) { - this.newStyleRuleBuffer.push(o.parts.join(' ')); + this.newStyleRuleBuffer.push(o.style.join(' ')); + this.styleSelectors.add(o); return; } - if ( o.procedural ) { + if ( o.tasks ) { this.proceduralSelectors.add(o); + return; } }, diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 666f9dccf0eb2..4d35539f07b2e 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -242,7 +242,7 @@ FilterBucket.fromSelfie = function() { /******************************************************************************/ var FilterParser = function() { - this.prefix = this.suffix = this.style = ''; + this.prefix = this.suffix = ''; this.unhide = 0; this.hostnames = []; this.invalid = false; @@ -254,7 +254,7 @@ var FilterParser = function() { FilterParser.prototype.reset = function() { this.raw = ''; - this.prefix = this.suffix = this.style = ''; + this.prefix = this.suffix = ''; this.unhide = 0; this.hostnames.length = 0; this.invalid = false; @@ -628,7 +628,6 @@ var FilterContainer = function() { this.netSelectorCacheCountMax = netSelectorCacheHighWaterMark; this.selectorCacheTimer = null; this.reHasUnicode = /[^\x00-\x7F]/; - this.reClassOrIdSelector = /^[#.][\w-]+$/; this.rePlainSelector = /^[#.][\w\\-]+/; this.rePlainSelectorEscaped = /^[#.](?:\\[0-9A-Fa-f]+ |\\.|\w|-)+/; this.rePlainSelectorEx = /^[^#.\[(]+([#.][\w-]+)/; @@ -735,7 +734,16 @@ FilterContainer.prototype.freeze = function() { FilterContainer.prototype.compileSelector = (function() { var reStyleSelector = /^(.+?):style\((.+?)\)$/, reStyleBad = /url\([^)]+\)/, - reScriptSelector = /^script:(contains|inject)\((.+)\)$/; + reScriptSelector = /^script:(contains|inject)\((.+)\)$/, + div = document.createElement('div'); + + 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; + }; return function(raw) { if ( isValidCSSSelector(raw) && raw.indexOf('[-abp-properties=') === -1 ) { @@ -747,11 +755,10 @@ FilterContainer.prototype.compileSelector = (function() { // `:style` selector? if ( (matches = reStyleSelector.exec(raw)) !== null ) { - if ( isValidCSSSelector(matches[1]) && reStyleBad.test(matches[2]) === false ) { + if ( isValidCSSSelector(matches[1]) && isValidStyleProperty(matches[2]) ) { return JSON.stringify({ - style: true, raw: raw, - parts: [ matches[1], '{' + matches[2] + '}' ] + style: [ matches[1], '{' + matches[2] + '}' ] }); } return; @@ -784,7 +791,7 @@ FilterContainer.prototype.compileSelector = (function() { /******************************************************************************/ FilterContainer.prototype.compileProceduralSelector = (function() { - var reParserEx = /(:(?:has|has-text|if|if-not|matches-css|matches-css-after|matches-css-before|xpath))\(.+\)$/, + var reOperatorParser = /(:(?:has|has-text|if|if-not|matches-css|matches-css-after|matches-css-before|xpath))\(.+\)$/, reFirstParentheses = /^\(*/, reLastParentheses = /\)*$/, reEscapeRegex = /[.*+?^${}()|[\]\\]/g; @@ -849,11 +856,9 @@ FilterContainer.prototype.compileProceduralSelector = (function() { ]); var compile = function(raw) { - var matches = reParserEx.exec(raw); + var matches = reOperatorParser.exec(raw); if ( matches === null ) { - if ( isValidCSSSelector(raw) ) { - return { selector: raw, tasks: [] }; - } + if ( isValidCSSSelector(raw) ) { return { selector: raw }; } return; } var tasks = [], @@ -864,7 +869,7 @@ FilterContainer.prototype.compileProceduralSelector = (function() { depth = 0, opening, closing; if ( firstOperand !== '' && isValidCSSSelector(firstOperand) === false ) { return; } for (;;) { - matches = reParserEx.exec(selector); + matches = reOperatorParser.exec(selector); if ( matches !== null ) { nextOperand = selector.slice(0, matches.index); nextOperator = matches[1]; @@ -903,7 +908,6 @@ FilterContainer.prototype.compileProceduralSelector = (function() { lastProceduralSelector = raw; var compiled = compile(raw); if ( compiled !== undefined ) { - compiled.procedural = true; compiled.raw = raw; compiled = JSON.stringify(compiled); } @@ -965,15 +969,6 @@ FilterContainer.prototype.compile = function(s, out) { return true; } - // For hostname- or entity-based filters, class- or id-based selectors are - // still the most common, and can easily be tested using a plain regex. - if ( - this.reClassOrIdSelector.test(parsed.suffix) === false && - this.compileSelector(parsed.suffix) === undefined - ) { - return true; - } - // https://github.com/chrisaljoudi/uBlock/issues/151 // Negated hostname means the filter applies to all non-negated hostnames // of same filter OR globally if there is no non-negated hostnames. diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index ff827fdcd7f0c..f52002e676750 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -139,9 +139,9 @@ var fromCosmeticFilter = function(details) { // compiled form of a filter. var filterEx = '(' + reEscape(filter) + - '|[^\\v]+' + + '|\{[^\\v]*' + reEscape(JSON.stringify({ raw: filter }).slice(1,-1)) + - '[^\\v]+)'; + '[^\\v]*\})'; // Second step: find hostname-based versions. // Reference: FilterContainer.compileHostnameSelector(). diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index 51c67d6fe5dea..efcf6110512c7 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.js @@ -51,6 +51,17 @@ vAPI.domFilterer.simpleHideSelectors.entries.forEach(evaluateSelector); // Complex CSS selector-based cosmetic filters. vAPI.domFilterer.complexHideSelectors.entries.forEach(evaluateSelector); +// Style cosmetic filters. +vAPI.domFilterer.styleSelectors.entries.forEach(function(filter) { + if ( + loggedSelectors.hasOwnProperty(filter.raw) === false && + document.querySelector(filter.style[0]) !== null + ) { + loggedSelectors[filter.raw] = true; + matchedSelectors.push(filter.raw); + } +}); + // Procedural cosmetic filters. vAPI.domFilterer.proceduralSelectors.entries.forEach(function(pfilter) { if ( diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index 0eb699d6c2c05..80d0b4aa49fda 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -770,9 +770,9 @@ var filterToDOMInterface = (function() { } var elems; if ( o.style ) { - elems = document.querySelectorAll(o.parts[0]); - lastAction = o.parts.join(' '); - } else if ( o.procedural ) { + elems = document.querySelectorAll(o.style[0]); + lastAction = o.style.join(' '); + } else if ( o.tasks ) { elems = vAPI.domFilterer.createProceduralFilter(o).exec(); } if ( !elems ) { return; } From 38a5f5751b575a9e6ba1bb24616788354653b462 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 30 Dec 2016 10:41:16 -0500 Subject: [PATCH 0040/4093] code review: be sure all invalid cosmetic filters are reported in logger --- src/js/cosmetic-filtering.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 4d35539f07b2e..8782fb9ff8377 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -754,14 +754,15 @@ FilterContainer.prototype.compileSelector = (function() { var matches; // `:style` selector? - if ( (matches = reStyleSelector.exec(raw)) !== null ) { - if ( isValidCSSSelector(matches[1]) && isValidStyleProperty(matches[2]) ) { - return JSON.stringify({ - raw: raw, - style: [ matches[1], '{' + matches[2] + '}' ] - }); - } - return; + if ( + (matches = reStyleSelector.exec(raw)) !== null && + isValidCSSSelector(matches[1]) && + isValidStyleProperty(matches[2]) + ) { + return JSON.stringify({ + raw: raw, + style: [ matches[1], '{' + matches[2] + '}' ] + }); } // `script:` filter? @@ -774,7 +775,6 @@ FilterContainer.prototype.compileSelector = (function() { if ( reIsRegexLiteral.test(matches[2]) === false || isBadRegex(matches[2].slice(1, -1)) === false ) { return raw; } - return; } // Procedural selector? @@ -784,7 +784,6 @@ FilterContainer.prototype.compileSelector = (function() { } µb.logger.writeOne('', 'error', 'Cosmetic filtering – invalid filter: ' + raw); - return; }; })(); From c196bf5f535c9f6a81bca4c14c61b0a88612c842 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 30 Dec 2016 10:43:07 -0500 Subject: [PATCH 0041/4093] 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 5ef677b40a181..580efaa1c00fd 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.4", + "version": "1.10.5.5", "default_locale": "en", "description": "__MSG_extShortDesc__", From 1a92fff6418884edad2991ecd49a305a18b8612f Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 2 Jan 2017 10:50:03 -0500 Subject: [PATCH 0042/4093] fix https://github.com/uBlockOrigin/uAssets/issues/255 --- platform/firefox/vapi-background.js | 44 ++++++++++++++++++++--------- src/js/tab.js | 19 +++++++------ 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index d960c8f0db503..a421749245340 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -2361,9 +2361,27 @@ vAPI.net.registerListeners = function() { null; } - var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup'; - var shouldLoadPopupListener = function(openerURL, popupTabId) { - var uri, openerTabId; + var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup', + shouldLoadPopupListenerMap = new Map(), + shouldLoadPopupListenerMapToD = 0; + var shouldLoadPopupListener = function(openerURL, target) { + var popupTabId = tabWatcher.tabIdFromTarget(target), + popupURL = target.currentURI && target.currentURI.asciiSpec || '', + openerTabId, + uri; + if ( shouldLoadPopupListenerMapToD > Date.now() ) { + openerTabId = shouldLoadPopupListenerMap.get(popupURL); + } + + // https://github.com/uBlockOrigin/uAssets/issues/255 + // Handle chained popups. + if ( openerTabId !== undefined ) { + shouldLoadPopupListenerMap.set(target.currentURI.asciiSpec, openerTabId); + shouldLoadPopupListenerMapToD = Date.now() + 10000; + vAPI.tabs.onPopupCreated(popupTabId, openerTabId); + return; + } + for ( var browser of tabWatcher.browsers() ) { uri = browser.currentURI; @@ -2375,15 +2393,16 @@ vAPI.net.registerListeners = function() { // believe this may have to do with those very temporary // browser objects created when opening a new tab, i.e. related // to https://github.com/gorhill/uBlock/issues/212 - if ( !uri || uri.spec !== openerURL ) { - continue; - } + if ( !uri || uri.spec !== openerURL ) { continue; } openerTabId = tabWatcher.tabIdFromTarget(browser); - if ( openerTabId !== popupTabId ) { - vAPI.tabs.onPopupCreated(popupTabId, openerTabId); - break; - } + if ( openerTabId === popupTabId ) { continue; } + + shouldLoadPopupListenerMap = new Map(); + shouldLoadPopupListenerMapToD = Date.now() + 10000; + shouldLoadPopupListenerMap.set(popupURL, openerTabId); + vAPI.tabs.onPopupCreated(popupTabId, openerTabId); + break; } }; var shouldLoadPopupListenerAsync = function(e) { @@ -2391,10 +2410,7 @@ vAPI.net.registerListeners = function() { return; } // We are handling a synchronous message: do not block. - vAPI.setTimeout( - shouldLoadPopupListener.bind(null, e.data, tabWatcher.tabIdFromTarget(e.target)), - 1 - ); + vAPI.setTimeout(shouldLoadPopupListener.bind(null, e.data, e.target), 1); }; vAPI.messaging.globalMessageManager.addMessageListener( diff --git a/src/js/tab.js b/src/js/tab.js index ffad7a4dff3bc..af8c68a6371c7 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -568,22 +568,25 @@ vAPI.tabs.onPopupUpdated = (function() { // URL. // https://github.com/gorhill/uBlock/issues/1735 // Do not bail out on `data:` URI, they are commonly used for popups. + // https://github.com/uBlockOrigin/uAssets/issues/255 + // Do not bail out on `about:blank`: an `about:blank` popup can be + // opened, with the sole purpose to serve as an intermediary in + // a sequence of chained popups. if ( context.requestHostname === '' && - targetURL.startsWith('data:') === false + targetURL.startsWith('data:') === false && + targetURL !== 'about:blank' ) { return ''; } // Dynamic filtering makes sense only when we have a valid hostname. if ( openerHostname !== '' ) { - // Check user switch first - if ( - typeof clickedURL === 'string' && - areDifferentURLs(targetURL, clickedURL) && - µb.hnSwitches.evaluateZ('no-popups', openerHostname) - ) { - return 'ub:no-popups: ' + µb.hnSwitches.z + ' true'; + // Check per-site switch first + if ( µb.hnSwitches.evaluateZ('no-popups', openerHostname) ) { + if ( typeof clickedURL !== 'string' || areDifferentURLs(targetURL, clickedURL) ) { + return 'ub:no-popups: ' + µb.hnSwitches.z + ' true'; + } } // https://github.com/gorhill/uBlock/issues/581 From bacf5021e0ee943421964d0438eeed21df6c583b Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 6 Jan 2017 12:39:37 -0500 Subject: [PATCH 0043/4093] performance work: - refactor "domain=" option matcher in light of https://gorhill.github.io/obj-vs-set-vs-map/set-vs-regexp.html - reuse existing instance of "domain=" matchers and filters wherever possible --- src/js/static-net-filtering.js | 328 +++++++++++++++++---------------- 1 file changed, 168 insertions(+), 160 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 85434ce9ddbcf..f389281bf0117 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -259,126 +259,139 @@ var reURLPostHostnameAnchors = /[\/?#]/; /******************************************************************************/ -// Hostname test helpers: the optimal test function is picked -// according to the content of the `domain` filter option, +// Hostname test helpers: the optimal test function is picked according to the +// content of the `domain=` filter option. -var hostnameTestPicker = function(owner) { - var domainOpt = owner.domainOpt; +// Re-factored in light of: +// - https://gorhill.github.io/obj-vs-set-vs-map/set-vs-regexp.html +// The re-factoring made possible to reuse instances of a matcher. As of +// writing, I observed that just with EasyList, there were ~1,200 reused +// instances out of ~2,800. - // Only one hostname - if ( domainOpt.indexOf('|') === -1 ) { - if ( domainOpt.startsWith('~') ) { - owner._notHostname = domainOpt.slice(1); - return hostnameMissTest; - } - return hostnameHitTest; +var hnMatcherFactory = function(domainOpt) { + var me = hnMatcherFactory; + + // Reuse last instance if possible. + if ( domainOpt === me.domainOpt ) { + return me.hnMatcher; } - // Multiple hostnames: use a dictionary. - var hostnames = domainOpt.split('|'); - var i, hostname, dict; + me.domainOpt = domainOpt; - // First find out whether we have a homogeneous dictionary - var hit = false, miss = false; - i = hostnames.length; - while ( i-- ) { - if ( hostnames[i].startsWith('~') ) { - miss = true; - if ( hit ) { - break; - } - } else { - hit = true; - if ( miss ) { - break; - } + // Only one hostname + if ( domainOpt.indexOf('|') === -1 ) { + if ( domainOpt.charCodeAt(0) === 0x7E /* '~' */ ) { + return (me.hnMatcher = new me.Miss(domainOpt)); } + return (me.hnMatcher = new me.Hit(domainOpt)); } - // Heterogenous dictionary: this can happen, though VERY rarely. - // Spotted one occurrence in EasyList Lite (cjxlist.txt): - // domain=photobucket.com|~secure.photobucket.com - if ( hit && miss ) { - dict = owner._hostnameDict = new Map(); - i = hostnames.length; - while ( i-- ) { - hostname = hostnames[i]; - if ( hostname.startsWith('~') ) { - dict.set(hostname.slice(1), false); - } else { - dict.set(hostname, true); - } - } - return hostnameMixedSetTest; + // Many hostnames. + + // Must be in set (none negated). + if ( domainOpt.indexOf('~') === -1 ) { + return (me.hnMatcher = new me.HitSet(domainOpt)); } - // Homogeneous dictionary. - dict = owner._hostnameDict = new Set(); - i = hostnames.length; - while ( i-- ) { - hostname = hostnames[i]; - dict.add(hostname.startsWith('~') ? hostname.slice(1) : hostname); + // Must not be in set (all negated). + if ( me.reAllNegated.test(domainOpt) ) { + return (me.hnMatcher = new me.MissSet(domainOpt)); } - return hit ? hostnameHitSetTest : hostnameMissSetTest; + // Must be in one set, but not in the other. + return (me.hnMatcher = new me.MixedSet(domainOpt)); }; -var hostnameHitTest = function(owner) { - var current = pageHostnameRegister; - var target = owner.domainOpt; - return current.endsWith(target) && - (current.length === target.length || - current.charAt(current.length - target.length - 1) === '.'); +hnMatcherFactory.reAllNegated = /^~(?:[^|~]+\|~)+[^|~]+$/; +hnMatcherFactory.domainOpt = undefined; +hnMatcherFactory.hnMatcher = undefined; + +hnMatcherFactory.Hit = function(domainOpt) { + this.hostname = domainOpt; +}; +hnMatcherFactory.Hit.prototype.toDomainOpt = function() { + return this.hostname; +}; +hnMatcherFactory.Hit.prototype.test = function() { + var needle = this.hostname, + haystack = pageHostnameRegister; + return haystack.endsWith(needle) && + (haystack.length === needle.length || + haystack.charCodeAt(haystack.length - needle.length - 1) === 0x2E /* '.' */); }; -var hostnameMissTest = function(owner) { - var current = pageHostnameRegister; - var target = owner._notHostname; - return current.endsWith(target) === false || - (current.length !== target.length && - current.charAt(current.length - target.length - 1) !== '.'); +hnMatcherFactory.Miss = function(domainOpt) { + this.hostname = domainOpt.slice(1); +}; +hnMatcherFactory.Miss.prototype.toDomainOpt = function() { + return '~' + this.hostname; +}; +hnMatcherFactory.Miss.prototype.test = function() { + var needle = this.hostname, + haystack = pageHostnameRegister; + return haystack.endsWith(needle) === false || + (haystack.length !== needle.length && + haystack.charCodeAt(haystack.length - needle.length - 1) !== 0x2E /* '.' */); }; -var hostnameHitSetTest = function(owner) { - var dict = owner._hostnameDict, - needle = pageHostnameRegister, - pos; - for (;;) { - if ( dict.has(needle) ) { return true; } - pos = needle.indexOf('.'); - if ( pos === -1 ) { break; } - needle = needle.slice(pos + 1); - } - return false; +hnMatcherFactory.HitSet = function(domainOpt) { + this.domainOpt = domainOpt; +}; +hnMatcherFactory.HitSet.prototype.oneOf = null; +hnMatcherFactory.HitSet.prototype.toDomainOpt = function() { + return this.domainOpt; +}; +hnMatcherFactory.HitSet.prototype.init = function() { + this.oneOf = new RegExp('(?:^|\\.)(?:' + this.domainOpt.replace(/\./g, '\\.') + ')$'); +}; +hnMatcherFactory.HitSet.prototype.test = function() { + if ( this.oneOf === null ) { this.init(); } + return this.oneOf.test(pageHostnameRegister); }; -var hostnameMissSetTest = function(owner) { - var dict = owner._hostnameDict, - needle = pageHostnameRegister, - pos; - for (;;) { - if ( dict.has(needle) ) { return false; } - pos = needle.indexOf('.'); - if ( pos === -1 ) { break; } - needle = needle.slice(pos + 1); - } - return true; +hnMatcherFactory.MissSet = function(domainOpt) { + this.domainOpt = domainOpt; +}; +hnMatcherFactory.MissSet.prototype.noneOf = null; +hnMatcherFactory.MissSet.prototype.toDomainOpt = function() { + return this.domainOpt; +}; +hnMatcherFactory.MissSet.prototype.init = function() { + this.noneOf = new RegExp('(?:^|\\.)(?:' + this.domainOpt.replace(/~/g, '').replace(/\./g, '\\.') + ')$'); +}; +hnMatcherFactory.MissSet.prototype.test = function() { + if ( this.noneOf === null ) { this.init(); } + return this.noneOf.test(pageHostnameRegister) === false; }; -var hostnameMixedSetTest = function(owner) { - var dict = owner._hostnameDict, - needle = pageHostnameRegister, - hit = false, - v, pos; - for (;;) { - v = dict.get(needle); - if ( v === false ) { return false; } - if ( v === true ) { hit = true; } - pos = needle.indexOf('.'); - if ( pos === -1 ) { break; } - needle = needle.slice(pos + 1); +hnMatcherFactory.MixedSet = function(domainOpt) { + this.domainOpt = domainOpt; +}; +hnMatcherFactory.MixedSet.prototype.oneOf = null; +hnMatcherFactory.MixedSet.prototype.noneOf = null; +hnMatcherFactory.MixedSet.prototype.toDomainOpt = function() { + return this.domainOpt; +}; +hnMatcherFactory.MixedSet.prototype.init = function() { + var oneOf = [], noneOf = [], + hostnames = this.domainOpt.split('|'), + i = hostnames.length, + hostname; + while ( i-- ) { + hostname = hostnames[i].replace(/\./g, '\\.'); + if ( hostname.charCodeAt(0) === 0x7E /* '~' */ ) { + noneOf.push(hostname.slice(1)); + } else { + oneOf.push(hostname); + } } - return hit; + this.oneOf = new RegExp('(?:^|\\.)(?:' + oneOf.join('|') + ')$'); + this.noneOf = new RegExp('(?:^|\\.)(?:' + noneOf.join('|') + ')$'); +}; +hnMatcherFactory.MixedSet.prototype.test = function() { + if ( this.oneOf === null ) { this.init(); } + var needle = pageHostnameRegister; + return this.oneOf.test(needle) && this.noneOf.test(needle) === false; }; /******************************************************************************* @@ -443,13 +456,11 @@ FilterPlain.fromSelfie = function(s) { var FilterPlainHostname = function(s, tokenBeg, domainOpt) { this.s = s; this.tokenBeg = tokenBeg; - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterPlainHostname.prototype.match = function(url, tokenBeg) { - return url.startsWith(this.s, tokenBeg - this.tokenBeg) && - this.hostnameTest(this); + return url.startsWith(this.s, tokenBeg - this.tokenBeg) && this.hnMatcher.test(); }; FilterPlainHostname.fid = @@ -458,7 +469,7 @@ FilterPlainHostname.prototype.rtfid = 'ah'; FilterPlainHostname.prototype.toSelfie = FilterPlainHostname.prototype.rtCompile = function() { - return this.s + '\t' + this.tokenBeg + '\t' + this.domainOpt; + return this.s + '\t' + this.tokenBeg + '\t' + this.hnMatcher.toDomainOpt(); }; FilterPlainHostname.compile = function(details) { @@ -501,13 +512,11 @@ FilterPlainPrefix0.fromSelfie = function(s) { var FilterPlainPrefix0Hostname = function(s, domainOpt) { this.s = s; - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterPlainPrefix0Hostname.prototype.match = function(url, tokenBeg) { - return url.startsWith(this.s, tokenBeg) && - this.hostnameTest(this); + return url.startsWith(this.s, tokenBeg) && this.hnMatcher.test(); }; FilterPlainPrefix0Hostname.fid = @@ -516,7 +525,7 @@ FilterPlainPrefix0Hostname.prototype.rtfid = '0ah'; FilterPlainPrefix0Hostname.prototype.toSelfie = FilterPlainPrefix0Hostname.prototype.rtCompile = function() { - return this.s + '\t' + this.domainOpt; + return this.s + '\t' + this.hnMatcher.toDomainOpt(); }; FilterPlainPrefix0Hostname.compile = function(details) { @@ -559,13 +568,11 @@ FilterPlainPrefix1.fromSelfie = function(s) { var FilterPlainPrefix1Hostname = function(s, domainOpt) { this.s = s; - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterPlainPrefix1Hostname.prototype.match = function(url, tokenBeg) { - return url.startsWith(this.s, tokenBeg - 1) && - this.hostnameTest(this); + return url.startsWith(this.s, tokenBeg - 1) && this.hnMatcher.test(); }; FilterPlainPrefix1Hostname.fid = @@ -574,7 +581,7 @@ FilterPlainPrefix1Hostname.prototype.rtfid = '1ah'; FilterPlainPrefix1Hostname.prototype.toSelfie = FilterPlainPrefix1Hostname.prototype.rtCompile = function() { - return this.s + '\t' + this.domainOpt; + return this.s + '\t' + this.hnMatcher.toDomainOpt(); }; FilterPlainPrefix1Hostname.compile = function(details) { @@ -617,13 +624,11 @@ FilterPlainLeftAnchored.fromSelfie = function(s) { var FilterPlainLeftAnchoredHostname = function(s, domainOpt) { this.s = s; - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterPlainLeftAnchoredHostname.prototype.match = function(url) { - return url.startsWith(this.s) && - this.hostnameTest(this); + return url.startsWith(this.s) && this.hnMatcher.test(); }; FilterPlainLeftAnchoredHostname.fid = @@ -632,7 +637,7 @@ FilterPlainLeftAnchoredHostname.prototype.rtfid = '|ah'; FilterPlainLeftAnchoredHostname.prototype.toSelfie = FilterPlainLeftAnchoredHostname.prototype.rtCompile = function() { - return this.s + '\t' + this.domainOpt; + return this.s + '\t' + this.hnMatcher.toDomainOpt(); }; FilterPlainLeftAnchoredHostname.compile = function(details) { @@ -675,13 +680,11 @@ FilterPlainRightAnchored.fromSelfie = function(s) { var FilterPlainRightAnchoredHostname = function(s, domainOpt) { this.s = s; - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterPlainRightAnchoredHostname.prototype.match = function(url) { - return url.endsWith(this.s) && - this.hostnameTest(this); + return url.endsWith(this.s) && this.hnMatcher.test(); }; FilterPlainRightAnchoredHostname.fid = @@ -690,7 +693,7 @@ FilterPlainRightAnchoredHostname.prototype.rtfid = 'a|h'; FilterPlainRightAnchoredHostname.prototype.toSelfie = FilterPlainRightAnchoredHostname.prototype.rtCompile = function() { - return this.s + '\t' + this.domainOpt; + return this.s + '\t' + this.hnMatcher.toDomainOpt(); }; FilterPlainRightAnchoredHostname.compile = function(details) { @@ -742,13 +745,12 @@ FilterPlainHnAnchored.fromSelfie = function(s) { var FilterPlainHnAnchoredHostname = function(s, domainOpt) { this.s = s; - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterPlainHnAnchoredHostname.prototype.match = function(url, tokenBeg) { return url.startsWith(this.s, tokenBeg) && - this.hostnameTest(this) && + this.hnMatcher.test() && isHnAnchored(url, tokenBeg); }; @@ -758,7 +760,7 @@ FilterPlainHnAnchoredHostname.prototype.rtfid = '||ah'; FilterPlainHnAnchoredHostname.prototype.toSelfie = FilterPlainHnAnchoredHostname.prototype.rtCompile = function() { - return this.s + '\t' + this.domainOpt; + return this.s + '\t' + this.hnMatcher.toDomainOpt(); }; FilterPlainHnAnchoredHostname.compile = function(details) { @@ -811,15 +813,13 @@ FilterGeneric.fromSelfie = function(s) { var FilterGenericHostname = function(s, anchor, domainOpt) { FilterGeneric.call(this, s, anchor); - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterGenericHostname.prototype = Object.create(FilterGeneric.prototype); FilterGenericHostname.prototype.constructor = FilterGenericHostname; FilterGenericHostname.prototype.match = function(url) { - return this.hostnameTest(this) && - FilterGeneric.prototype.match.call(this, url); + return this.hnMatcher.test() && FilterGeneric.prototype.match.call(this, url); }; FilterGenericHostname.fid = @@ -828,7 +828,7 @@ FilterGenericHostname.prototype.rtfid = '_h'; FilterGenericHostname.prototype.toSelfie = FilterGenericHostname.prototype.rtCompile = function() { - return FilterGeneric.prototype.toSelfie.call(this) + '\t' + this.domainOpt; + return FilterGeneric.prototype.toSelfie.call(this) + '\t' + this.hnMatcher.toDomainOpt(); }; FilterGenericHostname.compile = function(details) { @@ -882,15 +882,13 @@ FilterGenericHnAnchored.fromSelfie = function(s) { var FilterGenericHnAnchoredHostname = function(s, anchor, domainOpt) { FilterGenericHnAnchored.call(this, s, anchor); - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterGenericHnAnchoredHostname.prototype = Object.create(FilterGenericHnAnchored.prototype); FilterGenericHnAnchoredHostname.prototype.constructor = FilterGenericHnAnchoredHostname; FilterGenericHnAnchoredHostname.prototype.match = function(url) { - return this.hostnameTest(this) && - FilterGenericHnAnchored.prototype.match.call(this, url); + return this.hnMatcher.test() && FilterGenericHnAnchored.prototype.match.call(this, url); }; FilterGenericHnAnchoredHostname.fid = @@ -899,7 +897,7 @@ FilterGenericHnAnchoredHostname.prototype.rtfid = '||_h'; FilterGenericHnAnchoredHostname.prototype.toSelfie = FilterGenericHnAnchoredHostname.prototype.rtCompile = function() { - return this.s + '\t' + this.anchor + '\t' + this.domainOpt; + return this.s + '\t' + this.anchor + '\t' + this.hnMatcher.toDomainOpt(); }; FilterGenericHnAnchoredHostname.compile = function(details) { @@ -944,14 +942,12 @@ FilterRegex.fromSelfie = function(s) { var FilterRegexHostname = function(s, domainOpt) { this.re = new RegExp(s, 'i'); - this.domainOpt = domainOpt; - this.hostnameTest = hostnameTestPicker(this); + this.hnMatcher = hnMatcherFactory(domainOpt); }; FilterRegexHostname.prototype.match = function(url) { // test hostname first, it's cheaper than evaluating a regex - return this.hostnameTest(this) && - this.re.test(url); + return this.hnMatcher.test() && this.re.test(url); }; FilterRegexHostname.fid = @@ -960,7 +956,7 @@ FilterRegexHostname.prototype.rtfid = '//h'; FilterRegexHostname.prototype.toSelfie = FilterRegexHostname.prototype.rtCompile = function() { - return this.re.source + '\t' + this.domainOpt; + return this.re.source + '\t' + this.hnMatcher.toDomainOpt(); }; FilterRegexHostname.compile = function(details) { @@ -1582,6 +1578,11 @@ FilterContainer.prototype.reset = function() { this.filterParser.reset(); this.filterCounts = {}; + // Reuse filter instances whenever possible at load time. + this.fclassLast = null; + this.fdataLast = null; + this.filterLast = null; + // Runtime registers this.keyRegister = undefined; this.tokenRegister = undefined; @@ -1594,6 +1595,9 @@ FilterContainer.prototype.freeze = function() { histogram('allFilters', this.categories); this.duplicateBuster = new Set(); this.filterParser.reset(); + this.fclassLast = null; + this.fdataLast = null; + this.filterLast = null; this.frozen = true; }; @@ -1624,6 +1628,17 @@ FilterContainer.prototype.factories = { /******************************************************************************/ +FilterContainer.prototype.filterFromSelfie = function(fclass, fdata) { + if ( fdata !== this.fdataLast || fclass !== this.fclassLast ) { + this.fclassLast = fclass; + this.fdataLast = fdata; + this.filterLast = this.factories[fclass].fromSelfie(fdata); + } + return this.filterLast; +}; + +/******************************************************************************/ + FilterContainer.prototype.toSelfie = function() { var categoryToSelfie = function(map) { var selfie = [], @@ -1631,15 +1646,11 @@ FilterContainer.prototype.toSelfie = function() { entry, bucket, ff, f; for (;;) { entry = iterator.next(); - if ( entry.done ) { - break; - } + if ( entry.done ) { break; } selfie.push('k2\t' + entry.value[0]); bucket = entry.value[1]; selfie.push(bucket.fid + '\t' + bucket.toSelfie()); - if ( bucket.fid !== '[]' ) { - continue; - } + if ( bucket.fid !== '[]' ) { continue; } ff = bucket.filters; for ( var i = 0, ni = ff.length; i < ni; i++ ) { f = ff[i]; @@ -1655,9 +1666,7 @@ FilterContainer.prototype.toSelfie = function() { entry; for (;;) { entry = iterator.next(); - if ( entry.done ) { - break; - } + if ( entry.done ) { break; } selfie.push('k1\t' + entry.value[0]); selfie.push(categoryToSelfie(entry.value[1])); } @@ -1692,7 +1701,7 @@ FilterContainer.prototype.fromSelfie = function(selfie) { var rawText = selfie.categories; var rawEnd = rawText.length; var lineBeg = 0, lineEnd; - var line, pos, what, factory; + var line, pos, what, data, filter; while ( lineBeg < rawEnd ) { lineEnd = rawText.indexOf('\n', lineBeg); if ( lineEnd < 0 ) { @@ -1702,27 +1711,28 @@ FilterContainer.prototype.fromSelfie = function(selfie) { lineBeg = lineEnd + 1; pos = line.indexOf('\t'); what = line.slice(0, pos); + data = line.slice(pos + 1); if ( what === 'k1' ) { - catKey = line.slice(pos + 1); + catKey = data; submap = new Map(); map.set(catKey, submap); bucket = null; continue; } if ( what === 'k2' ) { - tokenKey = line.slice(pos + 1); + tokenKey = data; bucket = null; continue; } - factory = this.factories[what]; + filter = this.filterFromSelfie(what, data); if ( bucket === null ) { - bucket = factory.fromSelfie(line.slice(pos + 1)); + bucket = filter; submap.set(tokenKey, bucket); continue; } // When token key is reused, it can't be anything // else than FilterBucket - bucket.add(factory.fromSelfie(line.slice(pos + 1))); + bucket.add(filter); } }; @@ -1946,7 +1956,7 @@ FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed, FilterContainer.prototype.fromCompiledContent = function(lineIter) { var line, hash, token, fclass, fdata, - bucket, entry, factory, filter, + bucket, entry, filter, fieldIter = new µb.FieldIterator('\v'); while ( lineIter.eot() === false ) { @@ -1996,9 +2006,7 @@ FilterContainer.prototype.fromCompiledContent = function(lineIter) { } this.duplicateBuster.add(line); - factory = this.factories[fclass]; - - filter = factory.fromSelfie(fdata); + filter = this.filterFromSelfie(fclass, fdata); if ( entry === undefined ) { bucket.set(token, filter); continue; From 30e02a72508d752d16a0398c99ff5c06aa6ff0e4 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 6 Jan 2017 13:26:01 -0500 Subject: [PATCH 0044/4093] 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 580efaa1c00fd..0dbf79df188e1 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.5", + "version": "1.10.5.6", "default_locale": "en", "description": "__MSG_extShortDesc__", From 6175a216b7974699244a5a7b1942ff3bb22dea6d Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 7 Jan 2017 10:50:53 -0500 Subject: [PATCH 0045/4093] fix #2291 --- src/js/tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/tab.js b/src/js/tab.js index af8c68a6371c7..de97ff97b815a 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -584,7 +584,7 @@ vAPI.tabs.onPopupUpdated = (function() { if ( openerHostname !== '' ) { // Check per-site switch first if ( µb.hnSwitches.evaluateZ('no-popups', openerHostname) ) { - if ( typeof clickedURL !== 'string' || areDifferentURLs(targetURL, clickedURL) ) { + if ( typeof clickedURL === 'string' && areDifferentURLs(targetURL, clickedURL) ) { return 'ub:no-popups: ' + µb.hnSwitches.z + ' true'; } } From 8d8905aab304cbfad257b2d8535345dcbea0db43 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 7 Jan 2017 10:57:16 -0500 Subject: [PATCH 0046/4093] dont focus newly opened logger window: FF webext complains about it and not needed anyway --- platform/chromium/vapi-background.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 3aac3c4a1fcab..db6b0305ec29c 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -443,11 +443,7 @@ vAPI.tabs.open = function(details) { // Open in a standalone window if ( details.popup === true ) { - chrome.windows.create({ - url: details.url, - focused: details.active, - type: 'popup' - }); + chrome.windows.create({ url: details.url, type: 'popup' }); return; } From 4e747fd39e3f10bd753172fcfe6b8fc79981c877 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 7 Jan 2017 10:58:25 -0500 Subject: [PATCH 0047/4093] minor code review --- platform/firefox/vapi-background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index a421749245340..594c83c9d6e37 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -2376,7 +2376,7 @@ vAPI.net.registerListeners = function() { // https://github.com/uBlockOrigin/uAssets/issues/255 // Handle chained popups. if ( openerTabId !== undefined ) { - shouldLoadPopupListenerMap.set(target.currentURI.asciiSpec, openerTabId); + shouldLoadPopupListenerMap.set(popupURL, openerTabId); shouldLoadPopupListenerMapToD = Date.now() + 10000; vAPI.tabs.onPopupCreated(popupTabId, openerTabId); return; From 2691ac95b446198b8ab5acf0e2fcf82b4f45da69 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 7 Jan 2017 13:02:33 -0500 Subject: [PATCH 0048/4093] fix fullsize popup regression (https://github.com/gorhill/uBlock/issues/2153#issuecomment-271095067) --- src/js/popup.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/popup.js b/src/js/popup.js index 7d5acfd7d4b40..f99747c7b4a64 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -522,7 +522,9 @@ var renderOnce = function() { return; } - lpane.style.setProperty('height', rpane.offsetHeight + 'px'); + if ( document.body.classList.contains('fullsize') === false ) { + lpane.style.setProperty('height', rpane.offsetHeight + 'px'); + } }; /******************************************************************************/ From a927725bd9389389f1a4f0f71b6c47941878d48e Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 7 Jan 2017 17:18:22 -0500 Subject: [PATCH 0049/4093] code review: one getter per instance is wasteful --- src/js/static-net-filtering.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f389281bf0117..9f092072b4bac 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1066,14 +1066,14 @@ var FilterBucket = function(a, b) { this.filters[1] = b; } } - - Object.defineProperty(this, 'rtfid', { - get: function() { - return this.f.rtfid; - } - }); }; +Object.defineProperty(FilterBucket.prototype, 'rtfid', { + get: function() { + return this.f.rtfid; + } +}); + FilterBucket.prototype.add = function(a) { this.filters.push(a); }; From a303c7800ed441c1741e203e04bdee2a945f02c0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 8 Jan 2017 14:36:08 -0500 Subject: [PATCH 0050/4093] fix #2290 --- platform/firefox/frameModule.js | 158 ++++++++++++---------------- platform/firefox/vapi-background.js | 40 ++----- src/js/tab.js | 5 +- 3 files changed, 78 insertions(+), 125 deletions(-) diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 66c75fa90ad3d..19997ffa9e335 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors + Copyright (C) 2014-2017 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 @@ -161,7 +161,7 @@ var contentObserver = { popupMessageName: hostName + ':shouldLoadPopup', ignoredPopups: new WeakMap(), uniqueSandboxId: 1, - canE10S: Services.vc.compare(Services.appinfo.platformVersion, '44') > 0, + modernFirefox: Services.vc.compare(Services.appinfo.platformVersion, '44') > 0, get componentRegistrar() { return Components.manager.QueryInterface(Ci.nsIComponentRegistrar); @@ -189,31 +189,40 @@ var contentObserver = { register: function() { Services.obs.addObserver(this, 'document-element-inserted', true); + Services.obs.addObserver(this, 'content-document-global-created', true); - this.componentRegistrar.registerFactory( - this.classID, - this.classDescription, - this.contractID, - this - ); - this.categoryManager.addCategoryEntry( - 'content-policy', - this.contractID, - this.contractID, - false, - true - ); + // https://bugzilla.mozilla.org/show_bug.cgi?id=1232354 + // For modern versions of Firefox, the frameId/parentFrameId + // information can be found in channel.loadInfo of the HTTP observer. + if ( this.modernFirefox !== true ) { + this.componentRegistrar.registerFactory( + this.classID, + this.classDescription, + this.contractID, + this + ); + this.categoryManager.addCategoryEntry( + 'content-policy', + this.contractID, + this.contractID, + false, + true + ); + } }, unregister: function() { Services.obs.removeObserver(this, 'document-element-inserted'); - - this.componentRegistrar.unregisterFactory(this.classID, this); - this.categoryManager.deleteCategoryEntry( - 'content-policy', - this.contractID, - false - ); + Services.obs.removeObserver(this, 'content-document-global-created'); + + if ( this.modernFirefox !== true ) { + this.componentRegistrar.unregisterFactory(this.classID, this); + this.categoryManager.deleteCategoryEntry( + 'content-policy', + this.contractID, + false + ); + } }, getFrameId: function(win) { @@ -223,48 +232,6 @@ var contentObserver = { .outerWindowID; }, - handlePopup: function(location, origin, context) { - let openeeContext = context.contentWindow || context; - if ( - typeof openeeContext.opener !== 'object' || - openeeContext.opener === null || - openeeContext.opener === context || - this.ignoredPopups.has(openeeContext) - ) { - return; - } - // https://github.com/gorhill/uBlock/issues/452 - // Use location of top window, not that of a frame, as this - // would cause tab id lookup (necessary for popup blocking) to - // always fail. - // https://github.com/gorhill/uBlock/issues/1305 - // Opener could be a dead object, using it would cause a throw. - // Repro case: - // - Open http://delishows.to/show/chicago-med/season/1/episode/6 - // - Click anywhere in the background - let openerURL = null; - try { - let opener = openeeContext.opener.top || openeeContext.opener; - openerURL = opener.location && opener.location.href; - } catch(ex) { - } - // If no valid opener URL found, use the origin URL. - if ( openerURL === null ) { - openerURL = origin.asciiSpec; - } - let messageManager = getMessageManager(openeeContext); - if ( messageManager === null ) { - return; - } - if ( typeof messageManager.sendRpcMessage === 'function' ) { - // https://bugzil.la/1092216 - messageManager.sendRpcMessage(this.popupMessageName, openerURL); - } else { - // Compatibility for older versions - messageManager.sendSyncMessage(this.popupMessageName, openerURL); - } - }, - // https://bugzil.la/612921 shouldLoad: function(type, location, origin, context) { // For whatever reason, sometimes the global scope is completely @@ -278,17 +245,6 @@ var contentObserver = { return this.ACCEPT; } - if ( type === this.MAIN_FRAME ) { - this.handlePopup(location, origin, context); - } - - // https://bugzilla.mozilla.org/show_bug.cgi?id=1232354 - // For modern versions of Firefox, the frameId/parentFrameId - // information can be found in channel.loadInfo of the HTTP observer. - if ( this.canE10S ) { - return this.ACCEPT; - } - if ( !location.schemeIs('http') && !location.schemeIs('https') ) { return this.ACCEPT; } @@ -504,17 +460,44 @@ var contentObserver = { }, ignorePopup: function(e) { - if ( e.isTrusted === false ) { - return; - } - + if ( e.isTrusted === false ) { return; } let contObs = contentObserver; contObs.ignoredPopups.set(this, true); this.removeEventListener('keydown', contObs.ignorePopup, true); this.removeEventListener('mousedown', contObs.ignorePopup, true); }, + lookupPopupOpenerURL: function(popup) { + var opener, openerURL; + for (;;) { + opener = popup.opener; + if ( !opener ) { break; } + if ( opener.top ) { opener = opener.top; } + if ( opener === popup ) { break; } + if ( !opener.location ) { break; } + if ( !this.reGoodPopupURLs.test(opener.location.href) ) { break; } + openerURL = opener.location.href; + // https://github.com/uBlockOrigin/uAssets/issues/255 + // - Mind chained about:blank popups. + if ( openerURL !== 'about:blank' ) { break; } + popup = opener; + } + return openerURL; + }, + reGoodPopupURLs: /^(?:about:blank|blob:|data:|https?:|javascript:)/, + + observe: function(subject, topic) { + // https://github.com/gorhill/uBlock/issues/2290 + if ( topic === 'content-document-global-created' ) { + if ( subject !== subject.top ) { return; } + if ( this.ignoredPopups.has(subject) ) { return; } + let openerURL = this.lookupPopupOpenerURL(subject); + if ( !openerURL ) { return; } + let messager = getMessageManager(subject); + if ( !messager ) { return; } + messager.sendAsyncMessage(this.popupMessageName, openerURL); + return; + } - observe: function(doc) { // For whatever reason, sometimes the global scope is completely // uninitialized at this point. Repro steps: // - Launch FF with uBlock enabled @@ -522,14 +505,11 @@ var contentObserver = { // - Enable uBlock // - Services and all other global variables are undefined // Hopefully will eventually understand why this happens. - if ( Services === undefined ) { - return; - } + if ( Services === undefined ) { return; } + let doc = subject; let win = doc.defaultView || null; - if ( win === null ) { - return; - } + if ( win === null ) { return; } if ( win.opener && this.ignoredPopups.has(win) === false ) { win.addEventListener('keydown', this.ignorePopup, true); @@ -543,17 +523,13 @@ var contentObserver = { // TODO: We may have to exclude more types, for now let's be // conservative and focus only on the one issue reported, i.e. let's // not test against 'text/html'. - if ( doc.contentType.startsWith('image/') ) { - return; - } + if ( doc.contentType.startsWith('image/') ) { return; } let loc = win.location; - if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) { if ( loc.protocol === 'chrome:' && loc.host === hostName ) { this.initContentScripts(win); } - // What about data: and about:blank? return; } diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 594c83c9d6e37..9024583dec869 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2106 The uBlock Origin authors + Copyright (C) 2014-2107 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 @@ -2361,26 +2361,14 @@ vAPI.net.registerListeners = function() { null; } - var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup', - shouldLoadPopupListenerMap = new Map(), - shouldLoadPopupListenerMapToD = 0; - var shouldLoadPopupListener = function(openerURL, target) { - var popupTabId = tabWatcher.tabIdFromTarget(target), - popupURL = target.currentURI && target.currentURI.asciiSpec || '', + var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup'; + var shouldLoadPopupListener = function(e) { + if ( typeof vAPI.tabs.onPopupCreated !== 'function' ) { return; } + var target = e.target, + openerURL = e.data, + popupTabId = tabWatcher.tabIdFromTarget(target), openerTabId, uri; - if ( shouldLoadPopupListenerMapToD > Date.now() ) { - openerTabId = shouldLoadPopupListenerMap.get(popupURL); - } - - // https://github.com/uBlockOrigin/uAssets/issues/255 - // Handle chained popups. - if ( openerTabId !== undefined ) { - shouldLoadPopupListenerMap.set(popupURL, openerTabId); - shouldLoadPopupListenerMapToD = Date.now() + 10000; - vAPI.tabs.onPopupCreated(popupTabId, openerTabId); - return; - } for ( var browser of tabWatcher.browsers() ) { uri = browser.currentURI; @@ -2398,24 +2386,14 @@ vAPI.net.registerListeners = function() { openerTabId = tabWatcher.tabIdFromTarget(browser); if ( openerTabId === popupTabId ) { continue; } - shouldLoadPopupListenerMap = new Map(); - shouldLoadPopupListenerMapToD = Date.now() + 10000; - shouldLoadPopupListenerMap.set(popupURL, openerTabId); vAPI.tabs.onPopupCreated(popupTabId, openerTabId); break; } }; - var shouldLoadPopupListenerAsync = function(e) { - if ( typeof vAPI.tabs.onPopupCreated !== 'function' ) { - return; - } - // We are handling a synchronous message: do not block. - vAPI.setTimeout(shouldLoadPopupListener.bind(null, e.data, e.target), 1); - }; vAPI.messaging.globalMessageManager.addMessageListener( shouldLoadPopupListenerMessageName, - shouldLoadPopupListenerAsync + shouldLoadPopupListener ); var shouldLoadListenerMessageName = location.host + ':shouldLoad'; @@ -2501,7 +2479,7 @@ vAPI.net.registerListeners = function() { cleanupTasks.push(function() { vAPI.messaging.globalMessageManager.removeMessageListener( shouldLoadPopupListenerMessageName, - shouldLoadPopupListenerAsync + shouldLoadPopupListener ); vAPI.messaging.globalMessageManager.removeMessageListener( diff --git a/src/js/tab.js b/src/js/tab.js index de97ff97b815a..4ddbc6dda25e4 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -174,10 +174,9 @@ housekeep itself. vAPI.tabs.onPopupCreated = function(targetTabId, openerTabId) { var popup = popupCandidates[targetTabId]; - if ( popup !== undefined ) { - return; + if ( popup === undefined ) { + popupCandidates[targetTabId] = new PopupCandidate(targetTabId, openerTabId); } - popupCandidates[targetTabId] = new PopupCandidate(targetTabId, openerTabId); popupCandidateTest(targetTabId); }; From eca33ea65968f7f69a8c234b4708c8387908a149 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 8 Jan 2017 14:37:44 -0500 Subject: [PATCH 0051/4093] 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 0dbf79df188e1..5622b90a45a87 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.6", + "version": "1.10.5.7", "default_locale": "en", "description": "__MSG_extShortDesc__", From 3b0d3e3330b9455c11b89e3f499bab8bf25d6510 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 8 Jan 2017 17:52:38 -0500 Subject: [PATCH 0052/4093] code review: saner way to find a popup's opener tab id --- platform/firefox/frameModule.js | 42 +++++++++++++-------- platform/firefox/vapi-background.js | 57 ++++++++++++++++++----------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 19997ffa9e335..5777c4703df68 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -160,6 +160,7 @@ var contentObserver = { cpMessageName: hostName + ':shouldLoad', popupMessageName: hostName + ':shouldLoadPopup', ignoredPopups: new WeakMap(), + uniquePopupEventId: 1, uniqueSandboxId: 1, modernFirefox: Services.vc.compare(Services.appinfo.platformVersion, '44') > 0, @@ -466,35 +467,44 @@ var contentObserver = { this.removeEventListener('keydown', contObs.ignorePopup, true); this.removeEventListener('mousedown', contObs.ignorePopup, true); }, - lookupPopupOpenerURL: function(popup) { - var opener, openerURL; + lookupPopupOpener: function(popup) { for (;;) { - opener = popup.opener; - if ( !opener ) { break; } + let opener = popup.opener; + if ( !opener ) { return; } if ( opener.top ) { opener = opener.top; } - if ( opener === popup ) { break; } - if ( !opener.location ) { break; } - if ( !this.reGoodPopupURLs.test(opener.location.href) ) { break; } - openerURL = opener.location.href; + if ( opener === popup ) { return; } + if ( !opener.location ) { return; } + if ( this.reValidPopups.test(opener.location.protocol) ) { + return opener; + } // https://github.com/uBlockOrigin/uAssets/issues/255 // - Mind chained about:blank popups. - if ( openerURL !== 'about:blank' ) { break; } + if ( opener.location.href !== 'about:blank' ) { return; } popup = opener; } - return openerURL; }, - reGoodPopupURLs: /^(?:about:blank|blob:|data:|https?:|javascript:)/, + reValidPopups: /^(?:blob|data|https?|javascript):/, observe: function(subject, topic) { // https://github.com/gorhill/uBlock/issues/2290 if ( topic === 'content-document-global-created' ) { if ( subject !== subject.top ) { return; } if ( this.ignoredPopups.has(subject) ) { return; } - let openerURL = this.lookupPopupOpenerURL(subject); - if ( !openerURL ) { return; } - let messager = getMessageManager(subject); - if ( !messager ) { return; } - messager.sendAsyncMessage(this.popupMessageName, openerURL); + let opener = this.lookupPopupOpener(subject); + if ( !opener ) { return; } + let popupMessager = getMessageManager(subject); + if ( !popupMessager ) { return; } + let openerMessager = getMessageManager(opener); + if ( !openerMessager ) { return; } + popupMessager.sendAsyncMessage(this.popupMessageName, { + id: this.uniquePopupEventId, + popup: true + }); + openerMessager.sendAsyncMessage(this.popupMessageName, { + id: this.uniquePopupEventId, + opener: true + }); + this.uniquePopupEventId += 1; return; } diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 9024583dec869..742a99258f2ff 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -2362,32 +2362,45 @@ vAPI.net.registerListeners = function() { } var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup'; + var shouldLoadPopupListenerEntries = []; var shouldLoadPopupListener = function(e) { if ( typeof vAPI.tabs.onPopupCreated !== 'function' ) { return; } - var target = e.target, - openerURL = e.data, - popupTabId = tabWatcher.tabIdFromTarget(target), - openerTabId, - uri; - - for ( var browser of tabWatcher.browsers() ) { - uri = browser.currentURI; - - // Probably isn't the best method to identify the source tab. - - // https://github.com/gorhill/uBlock/issues/450 - // Skip entry if no valid URI available. - // Apparently URI can be undefined under some circumstances: I - // believe this may have to do with those very temporary - // browser objects created when opening a new tab, i.e. related - // to https://github.com/gorhill/uBlock/issues/212 - if ( !uri || uri.spec !== openerURL ) { continue; } - openerTabId = tabWatcher.tabIdFromTarget(browser); - if ( openerTabId === popupTabId ) { continue; } + var target = e.target, + data = e.data, + now = Date.now(), + entries = shouldLoadPopupListenerEntries, + entry; - vAPI.tabs.onPopupCreated(popupTabId, openerTabId); - break; + var i = entries.length; + while ( i-- ) { + entry = entries[i]; + if ( entry.id === data.id ) { + entries.splice(i, 1); + break; + } + if ( entry.expire <= now ) { + entries.splice(i, 1); + } + entry = undefined; + } + if ( !entry ) { + entry = { + id: data.id, + popupTabId: undefined, + openerTabId: undefined, + expire: now + 10000 + }; + entries.push(entry); + } + var tabId = tabWatcher.tabIdFromTarget(target); + if ( data.popup ) { + entry.popupTabId = tabId; + } else /* if ( data.opener ) */ { + entry.openerTabId = tabId; + } + if ( entry.popupTabId && entry.openerTabId ) { + vAPI.tabs.onPopupCreated(entry.popupTabId, entry.openerTabId); } }; From 693758aacbb4904507d6d3d569b47839682b9a62 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 8 Jan 2017 18:03:20 -0500 Subject: [PATCH 0053/4093] 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 5622b90a45a87..1ddf9032f4a96 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.7", + "version": "1.10.5.8", "default_locale": "en", "description": "__MSG_extShortDesc__", From 9c4fbeb1fc1ee0258807c0f1e584461326c8045e Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 9 Jan 2017 08:56:42 -0500 Subject: [PATCH 0054/4093] fix #2294 --- src/js/static-net-filtering.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 9f092072b4bac..404e2b4bd252c 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1134,6 +1134,7 @@ FilterBucket.fromSelfie = function() { var FilterParser = function() { this.cantWebsocket = vAPI.cantWebsocket; + this.reBadDomainOptChars = /[*+?^${}()[\]\\]/; this.reHostnameRule1 = /^[0-9a-z][0-9a-z.-]*[0-9a-z]$/i; this.reHostnameRule2 = /^\**[0-9a-z][0-9a-z.-]*[0-9a-z]\^?$/i; this.reCleanupHostnameRule2 = /^\**|\^$/g; @@ -1285,8 +1286,15 @@ FilterParser.prototype.parseOptions = function(s) { } continue; } + // https://github.com/gorhill/uBlock/issues/2294 + // Detect and discard filter if domain option contains nonsensical + // characters. if ( opt.startsWith('domain=') ) { this.domainOpt = opt.slice(7); + if ( this.reBadDomainOptChars.test(this.domainOpt) ) { + this.unsupported = true; + break; + } continue; } if ( opt === 'important' ) { @@ -1824,7 +1832,7 @@ FilterContainer.prototype.compile = function(raw, out) { // Ignore filters with unsupported options if ( parsed.unsupported ) { - //console.log('static-net-filtering.js > FilterContainer.add(): unsupported filter "%s"', raw); + µb.logger.writeOne('', 'error', 'Network filtering – invalid filter: ' + raw); return false; } From b21e765f5c2a5188324e340d6df64cfda07f14ef Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 9 Jan 2017 09:16:37 -0500 Subject: [PATCH 0055/4093] minor code review --- platform/firefox/frameModule.js | 67 ++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 5777c4703df68..03ef0bebdf6fe 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -460,6 +460,21 @@ var contentObserver = { return sandbox; }, + injectOtherContentScripts: function(doc, sandbox) { + let docReady = (e) => { + let doc = e.target; + doc.removeEventListener(e.type, docReady, true); + if ( doc.querySelector('a[href^="abp:"],a[href^="https://subscribe.adblockplus.org/?"]') ) { + Services.scriptloader.loadSubScript(this.contentBaseURI + 'scriptlets/subscriber.js', sandbox); + } + }; + if ( doc.readyState === 'loading') { + doc.addEventListener('DOMContentLoaded', docReady, true); + } else { + docReady({ target: doc, type: 'DOMContentLoaded' }); + } + }, + ignorePopup: function(e) { if ( e.isTrusted === false ) { return; } let contObs = contentObserver; @@ -467,6 +482,7 @@ var contentObserver = { this.removeEventListener('keydown', contObs.ignorePopup, true); this.removeEventListener('mousedown', contObs.ignorePopup, true); }, + lookupPopupOpener: function(popup) { for (;;) { let opener = popup.opener; @@ -483,15 +499,28 @@ var contentObserver = { popup = opener; } }, + reValidPopups: /^(?:blob|data|https?|javascript):/, + reMustInjectScript: /^(?:file|https?):/, observe: function(subject, topic) { + // For whatever reason, sometimes the global scope is completely + // uninitialized at this point. Repro steps: + // - Launch FF with uBlock enabled + // - Disable uBlock + // - Enable uBlock + // - Services and all other global variables are undefined + // Hopefully will eventually understand why this happens. + if ( Services === undefined ) { return; } + // https://github.com/gorhill/uBlock/issues/2290 if ( topic === 'content-document-global-created' ) { - if ( subject !== subject.top ) { return; } + if ( subject !== subject.top || !subject.opener ) { return; } if ( this.ignoredPopups.has(subject) ) { return; } let opener = this.lookupPopupOpener(subject); if ( !opener ) { return; } + subject.addEventListener('keydown', this.ignorePopup, true); + subject.addEventListener('mousedown', this.ignorePopup, true); let popupMessager = getMessageManager(subject); if ( !popupMessager ) { return; } let openerMessager = getMessageManager(opener); @@ -508,24 +537,12 @@ var contentObserver = { return; } - // For whatever reason, sometimes the global scope is completely - // uninitialized at this point. Repro steps: - // - Launch FF with uBlock enabled - // - Disable uBlock - // - Enable uBlock - // - Services and all other global variables are undefined - // Hopefully will eventually understand why this happens. - if ( Services === undefined ) { return; } + // topic === 'document-element-inserted' let doc = subject; let win = doc.defaultView || null; if ( win === null ) { return; } - if ( win.opener && this.ignoredPopups.has(win) === false ) { - win.addEventListener('keydown', this.ignorePopup, true); - win.addEventListener('mousedown', this.ignorePopup, true); - } - // https://github.com/gorhill/uBlock/issues/260 // https://developer.mozilla.org/en-US/docs/Web/API/Document/contentType // "Non-standard, only supported by Gecko. To be used in @@ -536,7 +553,7 @@ var contentObserver = { if ( doc.contentType.startsWith('image/') ) { return; } let loc = win.location; - if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) { + if ( this.reMustInjectScript.test(loc.protocol) === false ) { if ( loc.protocol === 'chrome:' && loc.host === hostName ) { this.initContentScripts(win); } @@ -544,9 +561,9 @@ var contentObserver = { return; } + // Content scripts injection. let lss = Services.scriptloader.loadSubScript; let sandbox = this.initContentScripts(win, true); - try { lss(this.contentBaseURI + 'vapi-client.js', sandbox); lss(this.contentBaseURI + 'contentscript.js', sandbox); @@ -554,20 +571,10 @@ var contentObserver = { //console.exception(ex.msg, ex.stack); return; } - - let docReady = (e) => { - let doc = e.target; - doc.removeEventListener(e.type, docReady, true); - - if ( doc.querySelector('a[href^="abp:"],a[href^="https://subscribe.adblockplus.org/?"]') ) { - lss(this.contentBaseURI + 'scriptlets/subscriber.js', sandbox); - } - }; - - if ( doc.readyState === 'loading') { - doc.addEventListener('DOMContentLoaded', docReady, true); - } else { - docReady({ target: doc, type: 'DOMContentLoaded' }); + // The remaining scripts are worth injecting only on a top-level window + // and at document_idle time. + if ( win === win.top ) { + this.injectOtherContentScripts(doc, sandbox); } } }; From 7f4863fbcc35705107ac8e2144fa5b5636f1cc04 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 9 Jan 2017 09:16:48 -0500 Subject: [PATCH 0056/4093] 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 1ddf9032f4a96..e63d49aad9fb8 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.8", + "version": "1.10.5.9", "default_locale": "en", "description": "__MSG_extShortDesc__", From 2b1ab2234ff8c85803f55ec84941e6cf4c293db1 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 9 Jan 2017 09:53:57 -0500 Subject: [PATCH 0057/4093] re #2294: mind unicode in "domain=" option + update URL of Adguard lists --- assets/ublock/filter-lists.json | 12 +++++++++++ src/js/static-net-filtering.js | 37 ++++++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/assets/ublock/filter-lists.json b/assets/ublock/filter-lists.json index be69f11dfd704..27cd938eb045e 100644 --- a/assets/ublock/filter-lists.json +++ b/assets/ublock/filter-lists.json @@ -20,6 +20,12 @@ "supportURL": "http://noads.it/" }, "https://adguard.com/en/filter-rules.html?id=1": { + "off": true, + "title": "RUS: Adguard Russian Filter (obsolete, will be removed)", + "group": "regions", + "supportURL": "http://forum.adguard.com/forumdisplay.php?69-%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B-Adguard" + }, + "https://filters.adtidy.org/extension/chromium/filters/1.txt": { "off": true, "title": "RUS: Adguard Russian Filter", "group": "regions", @@ -328,6 +334,12 @@ "supportURL": "https://forums.lanik.us/" }, "https://adguard.com/filter-rules.html?id=13": { + "off": true, + "title": "TUR: Adguard Turkish Filter (obsolete, will be removed)", + "group": "regions", + "supportURL": "http://forum.adguard.com/forumdisplay.php?51-Filter-Rules" + }, + "http://filters.adtidy.org/extension/chromium/filters/13.txt": { "off": true, "title": "TUR: Adguard Turkish Filter", "group": "regions", diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 404e2b4bd252c..dfa6f3a7a62ad 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1211,7 +1211,7 @@ FilterParser.prototype.bitFromType = function(type) { // https://github.com/chrisaljoudi/uBlock/issues/589 // Be ready to handle multiple negated types -FilterParser.prototype.parseOptType = function(raw, not) { +FilterParser.prototype.parseTypeOption = function(raw, not) { var typeBit = this.bitFromType(this.toNormalizedType[raw]); if ( !not ) { @@ -1231,7 +1231,7 @@ FilterParser.prototype.parseOptType = function(raw, not) { /******************************************************************************/ -FilterParser.prototype.parseOptParty = function(firstParty, not) { +FilterParser.prototype.parsePartyOption = function(firstParty, not) { if ( firstParty ) { not = !not; } @@ -1244,6 +1244,23 @@ FilterParser.prototype.parseOptParty = function(firstParty, not) { /******************************************************************************/ +FilterParser.prototype.parseDomainOption = function(s) { + if ( this.reHasUnicode.test(s) ) { + var hostnames = s.split('|'), + i = hostnames.length; + while ( i-- ) { + hostnames[i] = punycode.toASCII(hostnames[i]); + } + s = hostnames.join('|'); + } + if ( this.reBadDomainOptChars.test(s) ) { + return ''; + } + return s; +}; + +/******************************************************************************/ + FilterParser.prototype.parseOptions = function(s) { this.fopts = s; var opts = s.split(','); @@ -1255,7 +1272,7 @@ FilterParser.prototype.parseOptions = function(s) { opt = opt.slice(1); } if ( opt === 'third-party' ) { - this.parseOptParty(false, not); + this.parsePartyOption(false, not); continue; } // https://issues.adblockplus.org/ticket/616 @@ -1263,7 +1280,7 @@ FilterParser.prototype.parseOptions = function(s) { // adding support for the new keyword. if ( opt === 'elemhide' || opt === 'generichide' ) { if ( not === false ) { - this.parseOptType('generichide', false); + this.parseTypeOption('generichide', false); continue; } this.unsupported = true; @@ -1271,18 +1288,18 @@ FilterParser.prototype.parseOptions = function(s) { } if ( opt === 'document' ) { if ( this.action === BlockAction ) { - this.parseOptType('document', not); + this.parseTypeOption('document', not); continue; } this.unsupported = true; break; } if ( this.toNormalizedType.hasOwnProperty(opt) ) { - this.parseOptType(opt, not); + this.parseTypeOption(opt, not); // Due to ABP categorizing `websocket` requests as `other`, we need // to add `websocket` for when `other` is used. if ( opt === 'other' ) { - this.parseOptType('websocket', not); + this.parseTypeOption('websocket', not); } continue; } @@ -1290,8 +1307,8 @@ FilterParser.prototype.parseOptions = function(s) { // Detect and discard filter if domain option contains nonsensical // characters. if ( opt.startsWith('domain=') ) { - this.domainOpt = opt.slice(7); - if ( this.reBadDomainOptChars.test(this.domainOpt) ) { + this.domainOpt = this.parseDomainOption(opt.slice(7)); + if ( this.domainOpt === '' ) { this.unsupported = true; break; } @@ -1302,7 +1319,7 @@ FilterParser.prototype.parseOptions = function(s) { continue; } if ( opt === 'first-party' ) { - this.parseOptParty(true, not); + this.parsePartyOption(true, not); continue; } if ( opt.startsWith('redirect=') ) { From 257dd27e5dfe79b57a532f448f4668055dc339dd Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 9 Jan 2017 10:00:13 -0500 Subject: [PATCH 0058/4093] oops, always use https when available --- assets/ublock/filter-lists.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/ublock/filter-lists.json b/assets/ublock/filter-lists.json index 27cd938eb045e..cd3877f8e2abb 100644 --- a/assets/ublock/filter-lists.json +++ b/assets/ublock/filter-lists.json @@ -23,13 +23,13 @@ "off": true, "title": "RUS: Adguard Russian Filter (obsolete, will be removed)", "group": "regions", - "supportURL": "http://forum.adguard.com/forumdisplay.php?69-%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B-Adguard" + "supportURL": "https://forum.adguard.com/forumdisplay.php?69-%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B-Adguard" }, "https://filters.adtidy.org/extension/chromium/filters/1.txt": { "off": true, "title": "RUS: Adguard Russian Filter", "group": "regions", - "supportURL": "http://forum.adguard.com/forumdisplay.php?69-%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B-Adguard" + "supportURL": "https://forum.adguard.com/forumdisplay.php?69-%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B-Adguard" }, "https://easylist-downloads.adblockplus.org/advblock.txt": { "off": true, @@ -339,12 +339,12 @@ "group": "regions", "supportURL": "http://forum.adguard.com/forumdisplay.php?51-Filter-Rules" }, - "http://filters.adtidy.org/extension/chromium/filters/13.txt": { + "https://filters.adtidy.org/extension/chromium/filters/13.txt": { "off": true, "title": "TUR: Adguard Turkish Filter", "group": "regions", "lang": "tr", - "supportURL": "http://forum.adguard.com/forumdisplay.php?51-Filter-Rules" + "supportURL": "https://forum.adguard.com/forumdisplay.php?51-Filter-Rules" }, "https://www.fanboy.co.nz/fanboy-vietnam.txt": { "off": true, From 3b41237e4b4f08903ea435d6df4762c9dda573ec Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 12 Jan 2017 08:45:46 -0500 Subject: [PATCH 0059/4093] fix #2301 --- src/js/logger-ui.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index ba05a6bd19b30..ad398f17352e0 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -543,7 +543,7 @@ var renderLogEntry = function(entry) { var time = logDate; time.setTime(entry.tstamp - logDateTimezoneOffset); tr.cells[0].textContent = padTo2(time.getUTCHours()) + ':' + - padTo2(time.getMinutes()) + ':' + + padTo2(time.getUTCMinutes()) + ':' + padTo2(time.getSeconds()); if ( entry.tab ) { From 54032e520b4259f010517e55381cdaa286d2d1c0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 17 Jan 2017 18:18:28 -0500 Subject: [PATCH 0060/4093] fix https://github.com/gorhill/uBO-Extra/issues/19 --- src/js/traffic.js | 143 ++++++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 87 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 1c67e0de52c28..6e4587d0fca45 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-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 @@ -386,95 +386,69 @@ var onBeforeBehindTheSceneRequest = function(details) { // To handle: // - inline script tags +// - websockets // - media elements larger than n kB var onHeadersReceived = function(details) { // Do not interfere with behind-the-scene requests. var tabId = details.tabId; - if ( vAPI.isBehindTheSceneTabId(tabId) ) { - return; - } + if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; } - var requestType = details.type; + var µb = µBlock, + requestType = details.type; if ( requestType === 'main_frame' ) { - return onRootFrameHeadersReceived(details); - } - - if ( requestType === 'sub_frame' ) { - return onFrameHeadersReceived(details); + µb.tabContextManager.push(tabId, details.url); } - if ( requestType === 'image' || requestType === 'media' ) { - return foilLargeMediaElement(details); - } -}; - -/******************************************************************************/ - -var onRootFrameHeadersReceived = function(details) { - var µb = µBlock, - tabId = details.tabId; - - µb.tabContextManager.push(tabId, details.url); - - // Lookup the page store associated with this tab id. var pageStore = µb.pageStoreFromTabId(tabId); - if ( !pageStore ) { + if ( pageStore === null ) { + if ( requestType !== 'main_frame' ) { return; } pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest'); } - // I can't think of how pageStore could be null at this point. - - return processCSP(details, pageStore, pageStore.createContextFromPage()); -}; + if ( pageStore.getNetFilteringSwitch() === false ) { return; } -/******************************************************************************/ - -var onFrameHeadersReceived = function(details) { - // Lookup the page store associated with this tab id. - var pageStore = µBlock.pageStoreFromTabId(details.tabId); - if ( !pageStore ) { - return; + if ( requestType === 'image' || requestType === 'media' ) { + return foilLargeMediaElement(pageStore, details); } - // Frame id of frame request is their own id, while the request is made - // in the context of the parent. - return processCSP( - details, - pageStore, - pageStore.createContextFromFrameId(details.frameId) - ); + // https://github.com/gorhill/uBO-Extra/issues/19 + // Turns out scripts must also be considered as potential embedded + // contexts (as workers) and as such we may need to inject content + // security policy directives. + if ( requestType === 'script' || requestType === 'main_frame' || requestType === 'sub_frame' ) { + return processCSP(pageStore, details); + } }; /******************************************************************************/ -var processCSP = function(details, pageStore, context) { +var processCSP = function(pageStore, details) { var µb = µBlock, tabId = details.tabId, requestURL = details.url, loggerEnabled = µb.logger.isEnabled(); + var context = pageStore.createContextFromPage(); context.requestURL = requestURL; context.requestHostname = µb.URI.hostnameFromURI(requestURL); + if ( details.type !== 'main_frame' ) { + context.pageHostname = context.pageDomain = context.requestHostname; + } - context.requestType = 'inline-script'; - var inlineScriptResult = pageStore.filterRequestNoCache(context), + var inlineScriptResult, blockInlineScript; + if ( details.type !== 'script' ) { + context.requestType = 'inline-script'; + inlineScriptResult = pageStore.filterRequestNoCache(context); blockInlineScript = µb.isBlockResult(inlineScriptResult); + } context.requestType = 'websocket'; µb.staticNetFilteringEngine.matchStringExactType(context, requestURL, 'websocket'); var websocketResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled), blockWebsocket = µb.isBlockResult(websocketResult); - // https://github.com/gorhill/uBlock/issues/2050 - // Blanket-blocking websockets is exceptional, so we test whether the - // page is whitelisted if and only if there is a hit against a websocket - // filter. - if ( blockWebsocket && pageStore.getNetFilteringSwitch() === false ) { - websocketResult = ''; - blockWebsocket = false; - } - var headersChanged = false; + var headersChanged; if ( blockInlineScript || blockWebsocket ) { headersChanged = foilWithCSP( details.responseHeaders, @@ -484,34 +458,33 @@ var processCSP = function(details, pageStore, context) { } if ( loggerEnabled ) { - µb.logger.writeOne( - tabId, - 'net', - inlineScriptResult, - 'inline-script', - requestURL, - context.rootHostname, - context.pageHostname - ); - } - - if ( loggerEnabled && blockWebsocket ) { - µb.logger.writeOne( - tabId, - 'net', - websocketResult, - 'websocket', - requestURL, - context.rootHostname, - context.pageHostname - ); + if ( blockInlineScript !== undefined ) { + µb.logger.writeOne( + tabId, + 'net', + inlineScriptResult, + 'inline-script', + requestURL, + context.rootHostname, + context.pageHostname + ); + } + if ( websocketResult !== '' ) { + µb.logger.writeOne( + tabId, + 'net', + websocketResult, + 'websocket', + requestURL, + context.rootHostname, + context.pageHostname + ); + } } context.dispose(); - if ( headersChanged !== true ) { - return; - } + if ( headersChanged !== true ) { return; } µb.updateBadgeAsync(tabId); @@ -523,19 +496,14 @@ var processCSP = function(details, pageStore, context) { // https://github.com/gorhill/uBlock/issues/1163 // "Block elements by size" -var foilLargeMediaElement = function(details) { +var foilLargeMediaElement = function(pageStore, details) { var µb = µBlock; var i = headerIndexFromName('content-length', details.responseHeaders); if ( i === -1 ) { return; } var tabId = details.tabId, - pageStore = µb.pageStoreFromTabId(tabId); - if ( pageStore === null || pageStore.getNetFilteringSwitch() === false ) { - return; - } - - var size = parseInt(details.responseHeaders[i].value, 10) || 0, + size = parseInt(details.responseHeaders[i].value, 10) || 0, result = pageStore.filterLargeMediaElement(size); if ( result === undefined ) { return; } @@ -691,7 +659,8 @@ vAPI.net.onHeadersReceived = { 'main_frame', 'sub_frame', 'image', - 'media' + 'media', + 'script' ], extra: [ 'blocking', 'responseHeaders' ], callback: onHeadersReceived From 69fc59305ec60607593474b238e476f808ec7294 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 18 Jan 2017 13:01:13 -0500 Subject: [PATCH 0061/4093] fix https://github.com/uBlockOrigin/uAssets/issues/263#issuecomment-272615772 --- src/js/tab.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/js/tab.js b/src/js/tab.js index 4ddbc6dda25e4..9b9976d6263ce 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -561,26 +561,23 @@ vAPI.tabs.onPopupUpdated = (function() { context.requestHostname = µb.URI.hostnameFromURI(targetURL); context.requestType = 'popup'; - // https://github.com/gorhill/uBlock/commit/1d448b85b2931412508aa01bf899e0b6f0033626#commitcomment-14944764 - // Ignore bad target URL. On Firefox, an `about:blank` tab may be - // opened for a new tab before it is filled in with the real target - // URL. // https://github.com/gorhill/uBlock/issues/1735 // Do not bail out on `data:` URI, they are commonly used for popups. // https://github.com/uBlockOrigin/uAssets/issues/255 // Do not bail out on `about:blank`: an `about:blank` popup can be // opened, with the sole purpose to serve as an intermediary in // a sequence of chained popups. - if ( - context.requestHostname === '' && - targetURL.startsWith('data:') === false && - targetURL !== 'about:blank' - ) { - return ''; - } + // https://github.com/uBlockOrigin/uAssets/issues/263#issuecomment-272615772 + // Do not bail out, period: the static filtering engine must be + // able to examine all sorts of URLs for popup filtering purpose. - // Dynamic filtering makes sense only when we have a valid hostname. - if ( openerHostname !== '' ) { + // Dynamic filtering makes sense only when we have a valid opener + // hostname. + // https://github.com/gorhill/uBlock/commit/1d448b85b2931412508aa01bf899e0b6f0033626#commitcomment-14944764 + // Ignore bad target URL. On Firefox, an `about:blank` tab may be + // opened for a new tab before it is filled in with the real target + // URL. + if ( openerHostname !== '' && targetURL !== 'about:blank' ) { // Check per-site switch first if ( µb.hnSwitches.evaluateZ('no-popups', openerHostname) ) { if ( typeof clickedURL === 'string' && areDifferentURLs(targetURL, clickedURL) ) { From 3b9fd49c508812dfe2545836c63bd5d8cfd2ead4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Jan 2017 13:17:47 -0500 Subject: [PATCH 0062/4093] Assets management refactored (#2314) * refactoring assets management code * finalizing refactoring of assets management * various code review of new assets management code * fix #2281 * fix #1961 * fix #1293 * fix #1275 * fix update scheduler timing logic * forward compatibility (to be removed once 1.11+ is widespread) * more codereview; give admins ability to specify own assets.json * "assetKey" is more accurate than "path" * fix group count update when building dom incrementally * reorganize content (order, added URLs, etc.) * ability to customize updater through advanced settings * better spinner icon --- assets/assets.json | 585 +++++++++ src/3p-filters.html | 16 +- src/_locales/en/messages.json | 4 +- src/background.html | 1 - src/css/3p-filters.css | 91 +- src/js/3p-filters.js | 433 +++---- src/js/assets.js | 2094 +++++++++++-------------------- src/js/background.js | 105 +- src/js/logger.js | 4 +- src/js/messaging.js | 89 +- src/js/redirect-engine.js | 24 +- src/js/reverselookup-worker.js | 18 +- src/js/reverselookup.js | 38 +- src/js/scriptlets/subscriber.js | 9 +- src/js/settings.js | 5 +- src/js/start.js | 27 +- src/js/storage.js | 957 +++++++------- src/js/ublock.js | 8 +- tools/make-assets.sh | 4 +- tools/make-chromium.sh | 6 +- 20 files changed, 2082 insertions(+), 2436 deletions(-) create mode 100644 assets/assets.json diff --git a/assets/assets.json b/assets/assets.json new file mode 100644 index 0000000000000..883b87ba01ee4 --- /dev/null +++ b/assets/assets.json @@ -0,0 +1,585 @@ +{ + "assets.json": { + "content": "internal", + "updateAfter": 13, + "contentURL": [ + "https://raw.githubusercontent.com/gorhill/uBlock/master/assets/assets.json", + "assets/assets.json" + ] + }, + "public_suffix_list.dat": { + "content": "internal", + "updateAfter": 19, + "contentURL": [ + "https://publicsuffix.org/list/public_suffix_list.dat", + "assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat" + ] + }, + "ublock-resources": { + "content": "internal", + "updateAfter": 7, + "contentURL": [ + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/resources.txt", + "assets/ublock/resources.txt" + ] + }, + "ublock-filters": { + "content": "filters", + "group": "default", + "title": "uBlock filters", + "contentURL": [ + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt", + "assets/ublock/filters.txt" + ] + }, + "ublock-badware": { + "content": "filters", + "group": "default", + "title": "uBlock filters – Badware risks", + "contentURL": [ + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/badware.txt", + "assets/ublock/badware.txt" + ], + "supportURL": "https://github.com/gorhill/uBlock/wiki/Badware-risks", + "instructionURL": "https://github.com/gorhill/uBlock/wiki/Badware-risks" + }, + "ublock-experimental": { + "content": "filters", + "group": "default", + "title": "uBlock filters – Experimental", + "off": true, + "contentURL": [ + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/experimental.txt", + "assets/ublock/experimental.txt" + ], + "supportURL": "https://github.com/gorhill/uBlock/wiki/Experimental-filters", + "instructionURL": "https://github.com/gorhill/uBlock/wiki/Experimental-filters" + }, + "ublock-privacy": { + "content": "filters", + "group": "default", + "title": "uBlock filters – Privacy", + "contentURL": [ + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/privacy.txt", + "assets/ublock/privacy.txt" + ] + }, + "ublock-unbreak": { + "content": "filters", + "group": "default", + "title": "uBlock filters – Unbreak", + "contentURL": [ + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/unbreak.txt", + "assets/ublock/unbreak.txt" + ] + }, + "awrl-0": { + "content": "filters", + "group": "ads", + "off": true, + "title": "Adblock Warning Removal List", + "contentURL": "https://easylist-downloads.adblockplus.org/antiadblockfilters.txt", + "supportURL": "https://forums.lanik.us/" + }, + "reek-0": { + "content": "filters", + "group": "ads", + "off": true, + "title": "Anti-Adblock Killer | Reek", + "contentURL": "https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt", + "supportURL": "https://github.com/reek/anti-adblock-killer", + "instructionURL": "https://github.com/reek/anti-adblock-killer#instruction" + }, + "easylist": { + "content": "filters", + "group": "ads", + "title": "EasyList", + "contentURL": [ + "https://easylist.to/easylist/easylist.txt", + "https://easylist-downloads.adblockplus.org/easylist.txt", + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/thirdparties/easylist-downloads.adblockplus.org/easylist.txt", + "assets/thirdparties/easylist-downloads.adblockplus.org/easylist.txt" + ], + "supportURL": "https://forums.lanik.us/" + }, + "easylist-nocosmetic": { + "content": "filters", + "group": "ads", + "off": true, + "title": "EasyList without element hiding rules", + "contentURL": "https://easylist-downloads.adblockplus.org/easylist_noelemhide.txt", + "supportURL": "https://forums.lanik.us/" + }, + "disconnect-tracking": { + "content": "filters", + "group": "privacy", + "off": true, + "title": "Basic tracking list by Disconnect", + "contentURL": "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt" + }, + "easyprivacy": { + "content": "filters", + "group": "privacy", + "title": "EasyPrivacy", + "contentURL": [ + "https://easylist.to/easylist/easyprivacy.txt", + "https://easylist-downloads.adblockplus.org/easyprivacy.txt", + "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt", + "assets/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt" + ], + "supportURL": "https://forums.lanik.us/" + }, + "fanboy-enhanced": { + "content": "filters", + "group": "privacy", + "off": true, + "title": "Fanboy’s Enhanced Tracking List", + "contentURL": "https://www.fanboy.co.nz/enhancedstats.txt", + "supportURL": "https://forums.lanik.us/" + }, + "disconnect-malvertising": { + "content": "filters", + "group": "malware", + "off": true, + "title": "Malvertising filter list by Disconnect", + "contentURL": "https://s3.amazonaws.com/lists.disconnect.me/simple_malvertising.txt" + }, + "malware-0": { + "content": "filters", + "group": "malware", + "title": "Malware Domain List", + "contentURL": [ + "https://www.malwaredomainlist.com/hostslist/hosts.txt", + "assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt" + ] + }, + "malware-1": { + "content": "filters", + "group": "malware", + "title": "Malware domains", + "contentURL": [ + "https://mirror.cedia.org.ec/malwaredomains/justdomains", + "https://mirror1.malwaredomains.com/files/justdomains", + "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains", + "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains.txt" + ], + "supportURL": "http://www.malwaredomains.com/" + }, + "malware-2": { + "content": "filters", + "group": "malware", + "off": true, + "title": "Malware domains (long-lived)", + "contentURL": [ + "https://mirror1.malwaredomains.com/files/immortal_domains.txt", + "https://mirror.cedia.org.ec/malwaredomains/immortal_domains.txt" + ], + "supportURL": "http://www.malwaredomains.com/" + }, + "disconnect-malware": { + "content": "filters", + "group": "malware", + "off": true, + "title": "Malware filter list by Disconnect", + "contentURL": "https://s3.amazonaws.com/lists.disconnect.me/simple_malware.txt" + }, + "spam404-0": { + "content": "filters", + "group": "malware", + "off": true, + "title": "Spam404", + "contentURL": "https://raw.githubusercontent.com/Dawsey21/Lists/master/adblock-list.txt", + "supportURL": "http://www.spam404.com/" + }, + "fanboy-thirdparty_social": { + "content": "filters", + "group": "social", + "off": true, + "title": "Anti-ThirdpartySocial (see warning inside list)", + "contentURL": "https://www.fanboy.co.nz/fanboy-antifacebook.txt", + "supportURL": "https://forums.lanik.us/" + }, + "fanboy-annoyance": { + "content": "filters", + "group": "social", + "off": true, + "title": "Fanboy’s Annoyance List", + "contentURL": [ + "https://easylist.to/easylist/fanboy-annoyance.txt", + "https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt" + ], + "supportURL": "https://forums.lanik.us/" + }, + "fanboy-social": { + "content": "filters", + "group": "social", + "off": true, + "title": "Fanboy’s Social Blocking List", + "contentURL": [ + "https://easylist.to/easylist/fanboy-social.txt", + "https://easylist-downloads.adblockplus.org/fanboy-social.txt" + ], + "supportURL": "https://forums.lanik.us/" + }, + "dpollock-0": { + "content": "filters", + "group": "multipurpose", + "updateAfter": 11, + "off": true, + "title": "Dan Pollock’s hosts file", + "contentURL": "http://someonewhocares.org/hosts/hosts", + "supportURL": "http://someonewhocares.org/hosts/" + }, + "fanboy-ultimate": { + "content": "filters", + "group": "multipurpose", + "off": true, + "title": "Fanboy+Easylist-Merged Ultimate List", + "contentURL": "https://www.fanboy.co.nz/r/fanboy-ultimate.txt", + "supportURL": "https://forums.lanik.us/" + }, + "hphosts": { + "content": "filters", + "group": "multipurpose", + "updateAfter": 11, + "off": true, + "title": "hpHosts’ Ad and tracking servers", + "contentURL": "https://hosts-file.net/.%5Cad_servers.txt", + "supportURL": "https://hosts-file.net/" + }, + "mvps-0": { + "content": "filters", + "group": "multipurpose", + "updateAfter": 11, + "off": true, + "title": "MVPS HOSTS", + "contentURL": "http://winhelp2002.mvps.org/hosts.txt", + "supportURL": "http://winhelp2002.mvps.org/" + }, + "plowe-0": { + "content": "filters", + "group": "multipurpose", + "updateAfter": 13, + "title": "Peter Lowe’s Ad and tracking server list", + "contentURL": [ + "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext", + "assets/thirdparties/pgl.yoyo.org/as/serverlist", + "assets/thirdparties/pgl.yoyo.org/as/serverlist.txt" + ], + "supportURL": "https://pgl.yoyo.org/adservers/" + }, + "ara-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "ara: Liste AR", + "lang": "ar", + "contentURL": "https://easylist-downloads.adblockplus.org/Liste_AR.txt", + "supportURL": "https://forums.lanik.us/viewforum.php?f=98" + }, + "BGR-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "BGR: Bulgarian Adblock list", + "lang": "bg", + "contentURL": "https://stanev.org/abp/adblock_bg.txt", + "supportURL": "https://stanev.org/abp/" + }, + "CHN-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "CHN: EasyList China (中文)", + "lang": "zh", + "contentURL": "https://easylist-downloads.adblockplus.org/easylistchina.txt", + "supportURL": "http://abpchina.org/forum/forum.php" + }, + "CHN-1": { + "content": "filters", + "group": "regions", + "off": true, + "title": "CHN: CJX's EasyList Lite (main focus on Chinese sites)", + "contentURL": "https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt", + "supportURL": "https://github.com/cjx82630/cjxlist" + }, + "CHN-2": { + "content": "filters", + "group": "regions", + "off": true, + "title": "CHN: CJX's Annoyance List", + "contentURL": "https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjx-annoyance.txt", + "supportURL": "https://github.com/cjx82630/cjxlist" + }, + "CZE-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "CZE, SVK: EasyList Czech and Slovak", + "lang": "cs", + "contentURL": "https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt", + "supportURL": "https://github.com/tomasko126/easylistczechandslovak" + }, + "DEU-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "DEU: EasyList Germany", + "lang": "de", + "contentURL": [ + "https://easylist.to/easylistgermany/easylistgermany.txt", + "https://easylist-downloads.adblockplus.org/easylistgermany.txt" + ], + "supportURL": "https://forums.lanik.us/viewforum.php?f=90" + }, + "DNK-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "DNK: Schacks Adblock Plus liste", + "lang": "da", + "contentURL": "https://adblock.dk/block.csv", + "supportURL": "https://henrik.schack.dk/adblock/" + }, + "EST-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "EST: Eesti saitidele kohandatud filter", + "lang": "et", + "contentURL": "http://adblock.ee/list.php", + "supportURL": "http://adblock.ee/" + }, + "EU-prebake": { + "content": "filters", + "group": "regions", + "off": true, + "title": "EU: Prebake - Filter Obtrusive Cookie Notices", + "contentURL": "https://raw.githubusercontent.com/liamja/Prebake/master/obtrusive.txt", + "supportURL": "https://github.com/liamja/Prebake" + }, + "FIN-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "FIN: Finnish Addition to Easylist", + "lang": "fi", + "contentURL": "http://adb.juvander.net/Finland_adb.txt", + "supportURL": "http://www.juvander.fi/AdblockFinland" + }, + "FRA-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "FRA: EasyList Liste FR", + "lang": "fr", + "contentURL": "https://easylist-downloads.adblockplus.org/liste_fr.txt", + "supportURL": "https://forums.lanik.us/viewforum.php?f=91" + }, + "GRC-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "GRC: Greek AdBlock Filter", + "lang": "el", + "contentURL": "https://www.void.gr/kargig/void-gr-filters.txt", + "supportURL": "https://github.com/kargig/greek-adblockplus-filter" + }, + "HUN-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "HUN: hufilter", + "lang": "hu", + "contentURL": "https://raw.githubusercontent.com/szpeter80/hufilter/master/hufilter.txt", + "supportURL": "https://github.com/szpeter80/hufilter" + }, + "IDN-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "IDN: ABPindo", + "lang": "id", + "contentURL": [ + "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt", + "https://raw.githubusercontent.com/heradhis/indonesianadblockrules/master/subscriptions/abpindo.txt" + ], + "supportURL": "https://github.com/ABPindo/indonesianadblockrules" + }, + "ISL-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "ISL: Icelandic ABP List", + "lang": "is", + "contentURL": "http://adblock.gardar.net/is.abp.txt", + "supportURL": "http://adblock.gardar.net/" + }, + "ISR-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "ISR: EasyList Hebrew", + "lang": "he", + "contentURL": "https://raw.githubusercontent.com/easylist/EasyListHebrew/master/EasyListHebrew.txt", + "supportURL": "https://github.com/easylist/EasyListHebrew" + }, + "ITA-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "ITA: EasyList Italy", + "lang": "it", + "contentURL": "https://easylist-downloads.adblockplus.org/easylistitaly.txt", + "supportURL": "https://forums.lanik.us/viewforum.php?f=96" + }, + "ITA-1": { + "content": "filters", + "group": "regions", + "off": true, + "title": "ITA: ABP X Files", + "contentURL": "https://raw.githubusercontent.com/gioxx/xfiles/master/filtri.txt", + "supportURL": "http://noads.it/" + }, + "JPN-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "JPN: ABP Japanese filters (日本用フィルタ)", + "lang": "ja", + "contentURL": "https://raw.githubusercontent.com/k2jp/abp-japanese-filters/master/abpjf.txt", + "supportURL": "https://github.com/k2jp/abp-japanese-filters/wiki/Support_Policy" + }, + "KOR-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "KOR: Korean Adblock List", + "lang": "ko", + "contentURL": "https://raw.githubusercontent.com/gfmaster/adblock-korea-contrib/master/filter.txt", + "supportURL": "https://github.com/gfmaster/adblock-korea-contrib" + }, + "KOR-1": { + "content": "filters", + "group": "regions", + "off": true, + "title": "KOR: YousList", + "lang": "ko", + "contentURL": "https://raw.githubusercontent.com/yous/YousList/master/youslist.txt", + "supportURL": "https://github.com/yous/YousList" + }, + "KOR-2": { + "content": "filters", + "group": "regions", + "off": true, + "title": "KOR: Fanboy's Korean", + "contentURL": "https://www.fanboy.co.nz/fanboy-korean.txt", + "supportURL": "https://forums.lanik.us/" + }, + "LTU-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "LTU: Adblock Plus Lithuania", + "lang": "lt", + "contentURL": "http://margevicius.lt/easylistlithuania.txt", + "supportURL": "http://margevicius.lt/easylist_lithuania/" + }, + "LVA-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "LVA: Latvian List", + "lang": "lv", + "contentURL": "https://notabug.org/latvian-list/adblock-latvian/raw/master/lists/latvian-list.txt", + "supportURL": "https://notabug.org/latvian-list/adblock-latvian" + }, + "NLD-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "NLD: EasyList Dutch", + "lang": "nl", + "contentURL": "https://easylist-downloads.adblockplus.org/easylistdutch.txt", + "supportURL": "https://forums.lanik.us/viewforum.php?f=100" + }, + "POL-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "POL: polskie filtry do Adblocka i uBlocka", + "lang": "pl", + "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", + "supportURL": "https://www.certyficate.it/adblock-ublock-polish-filters/" + }, + "RUS-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "RUS: RU AdList (Дополнительная региональная подписка)", + "lang": "ru", + "contentURL": "https://easylist-downloads.adblockplus.org/advblock.txt", + "supportURL": "https://forums.lanik.us/viewforum.php?f=102" + }, + "RUS-1": { + "content": "filters", + "group": "regions", + "off": true, + "title": "RUS: BitBlock List (Дополнительная подписка фильтров)", + "contentURL": "https://easylist-downloads.adblockplus.org/bitblock.txt", + "supportURL": "https://forums.lanik.us/viewforum.php?f=102" + }, + "RUS-2": { + "content": "filters", + "group": "regions", + "off": true, + "title": "RUS: Adguard Russian Filter", + "contentURL": "https://filters.adtidy.org/extension/chromium/filters/1.txt", + "supportURL": "https://forum.adguard.com/forumdisplay.php?69-%D0%A4%D0%B8%D0%BB%D1%8C%D1%82%D1%80%D1%8B-Adguard" + }, + "spa-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "spa: EasyList Spanish", + "lang": "es", + "contentURL": "https://easylist-downloads.adblockplus.org/easylistspanish.txt", + "supportURL": "https://forums.lanik.us/viewforum.php?f=103" + }, + "SVN-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "SVN: Slovenian List", + "lang": "sl", + "contentURL": "https://raw.githubusercontent.com/betterwebleon/slovenian-list/master/filters.txt", + "supportURL": "https://github.com/betterwebleon/slovenian-list" + }, + "SWE-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "SWE: Fanboy's Swedish", + "lang": "sv", + "contentURL": "https://www.fanboy.co.nz/fanboy-swedish.txt", + "supportURL": "https://forums.lanik.us/" + }, + "TUR-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "TUR: Adguard Turkish Filter", + "lang": "tr", + "contentURL": "https://filters.adtidy.org/extension/chromium/filters/13.txt", + "supportURL": "https://forum.adguard.com/forumdisplay.php?51-Filter-Rules" + }, + "VIE-0": { + "content": "filters", + "group": "regions", + "off": true, + "title": "VIE: Fanboy's Vietnamese", + "lang": "vi", + "contentURL": "https://www.fanboy.co.nz/fanboy-vietnam.txt", + "supportURL": "https://forums.lanik.us/" + } +} diff --git a/src/3p-filters.html b/src/3p-filters.html index 8bce9d01aaab4..48086f71fe271 100644 --- a/src/3p-filters.html +++ b/src/3p-filters.html @@ -40,16 +40,10 @@

-
-
- -
-
- diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index b31059c90154c..653cf75c40c94 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -580,8 +580,8 @@ "description": "Message asking user to confirm reset" }, "errorCantConnectTo":{ - "message":"Unable to connect to {{url}}", - "description":"English: Network error: unable to connect to {{url}}" + "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}}", diff --git a/src/background.html b/src/background.html index a8463068e76c3..0d56942fc2b0e 100644 --- a/src/background.html +++ b/src/background.html @@ -8,7 +8,6 @@ - diff --git a/src/css/3p-filters.css b/src/css/3p-filters.css index 99f87cfd48235..8aa6760f9cf51 100644 --- a/src/css/3p-filters.css +++ b/src/css/3p-filters.css @@ -1,3 +1,7 @@ +@keyframes spin { + 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg); } + } + ul { padding: 0; list-style-type: none; @@ -88,7 +92,7 @@ body[dir=rtl] #buttonApply { span.status { border: 1px solid transparent; color: #444; - display: inline-block; + display: none; font-size: smaller; line-height: 1; margin: 0 0 0 0.5em; @@ -99,6 +103,16 @@ span.unsecure { background-color: hsl(0, 100%, 88%); border-color: hsl(0, 100%, 83%); } +li.listEntry.unsecure span.unsecure { + display: inline; + } +span.obsolete { + background-color: hsl(36, 100%, 80%); + border-color: hsl(36, 100%, 75%); + } +li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.obsolete { + display: inline; + } span.purge { border-color: #ddd; background-color: #eee; @@ -107,10 +121,16 @@ span.purge { span.purge:hover { opacity: 1; } -span.obsolete, -span.new { - background-color: hsl(36, 100%, 80%); - border-color: hsl(36, 100%, 75%); +li.listEntry.cached span.purge { + display: inline; + } +span.updating { + border: none; + padding: 0; + } +li.listEntry.updating span.updating { + animation: spin 2s linear infinite; + display: inline-block; } #externalListsDiv { margin: 2em auto 0 2em; @@ -125,64 +145,3 @@ body[dir=rtl] #externalListsDiv { width: 100%; word-wrap: normal; } -body #busyOverlay { - background-color: transparent; - bottom: 0; - cursor: wait; - display: none; - left: 0; - position: fixed; - right: 0; - top: 0; - z-index: 1000; - } -body.busy #busyOverlay { - display: block; - } -#busyOverlay > div:nth-of-type(1) { - background-color: white; - bottom: 0; - left: 0; - opacity: 0.75; - position: absolute; - right: 0; - top: 0; - } -#busyOverlay > div:nth-of-type(2) { - background-color: #eee; - border: 1px solid transparent; - border-color: #80b3ff #80b3ff hsl(216, 100%, 75%); - border-radius: 3px; - box-sizing: border-box; - height: 3em; - left: 10%; - position: absolute; - bottom: 75%; - width: 80%; - } -#busyOverlay > div:nth-of-type(2) > div:nth-of-type(1) { - background-color: hsl(216, 100%, 75%); - background-image: linear-gradient(#a8cbff, #80b3ff); - background-repeat: repeat-x; - border: 0; - box-sizing: border-box; - color: #222; - height: 100%; - left: 0; - padding: 0; - position: absolute; - width: 25%; - } -#busyOverlay > div:nth-of-type(2) > div:nth-of-type(2) { - background-color: transparent; - border: 0; - box-sizing: border-box; - height: 100%; - left: 0; - line-height: 3em; - overflow: hidden; - position: absolute; - text-align: center; - top: 0; - width: 100%; - } diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 0b960e81c0911..4b7dfa1b9dfea 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.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 @@ -21,38 +21,30 @@ /* global uDom */ +'use strict'; + /******************************************************************************/ (function() { -'use strict'; - /******************************************************************************/ -var userListName = vAPI.i18n('1pPageName'); var listDetails = {}; var parseCosmeticFilters = true; var ignoreGenericCosmeticFilters = false; +var selectedListsHashBefore = ''; var externalLists = ''; -var cacheWasPurged = false; -var needUpdate = false; -var hasCachedContent = false; /******************************************************************************/ var onMessage = function(msg) { switch ( msg.what ) { + case 'assetUpdated': + updateAssetStatus(msg); + break; case 'staticFilteringDataChanged': renderFilterLists(); break; - - case 'forceUpdateAssetsProgress': - renderBusyOverlay(true, msg.progress); - if ( msg.done ) { - messaging.send('dashboard', { what: 'reloadAllFilters' }); - } - break; - default: break; } @@ -69,20 +61,15 @@ var renderNumber = function(value) { /******************************************************************************/ -// TODO: get rid of background page dependencies - var renderFilterLists = function() { - var listGroupTemplate = uDom('#templates .groupEntry'); - var listEntryTemplate = uDom('#templates .listEntry'); - var listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'); - var renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString; - var lastUpdateString = vAPI.i18n('3pLastUpdate'); + var listGroupTemplate = uDom('#templates .groupEntry'), + listEntryTemplate = uDom('#templates .listEntry'), + listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'), + renderElapsedTimeToString = vAPI.i18n.renderElapsedTimeToString, + lastUpdateString = vAPI.i18n('3pLastUpdate'); - // Assemble a pretty blacklist name if possible + // Assemble a pretty list name if possible var listNameFromListKey = function(listKey) { - if ( listKey === listDetails.userFiltersPath ) { - return userListName; - } var list = listDetails.current[listKey] || listDetails.available[listKey]; var listTitle = list ? list.title : ''; if ( listTitle === '' ) { @@ -91,73 +78,68 @@ var renderFilterLists = function() { return listTitle; }; - var liFromListEntry = function(listKey) { + var liFromListEntry = function(listKey, li) { var entry = listDetails.available[listKey]; - var li = listEntryTemplate.clone(); - + li = li ? li : listEntryTemplate.clone().nodeAt(0); + li.setAttribute('data-listkey', listKey); + var elem = li.querySelector('input[type="checkbox"]'); if ( entry.off !== true ) { - li.descendants('input').attr('checked', ''); + elem.setAttribute('checked', ''); + } else { + elem.removeAttribute('checked'); } - - var elem = li.descendants('a:nth-of-type(1)'); - elem.attr('href', 'asset-viewer.html?url=' + encodeURI(listKey)); - elem.attr('type', 'text/html'); - elem.attr('data-listkey', listKey); - elem.text(listNameFromListKey(listKey) + '\u200E'); - + elem = li.querySelector('a:nth-of-type(1)'); + elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey)); + elem.setAttribute('type', 'text/html'); + elem.textContent = listNameFromListKey(listKey) + '\u200E'; + elem = li.querySelector('a:nth-of-type(2)'); if ( entry.instructionURL ) { - elem = li.descendants('a:nth-of-type(2)'); - elem.attr('href', entry.instructionURL); - elem.css('display', ''); + elem.setAttribute('href', entry.instructionURL); + elem.style.setProperty('display', ''); + } else { + elem.style.setProperty('display', 'none'); } - + elem = li.querySelector('a:nth-of-type(3)'); if ( entry.supportName ) { - elem = li.descendants('a:nth-of-type(3)'); - elem.attr('href', entry.supportURL); - elem.text('(' + entry.supportName + ')'); - elem.css('display', ''); + elem.setAttribute('href', entry.supportURL); + elem.textContent = '(' + entry.supportName + ')'; + elem.style.setProperty('display', ''); + } else { + elem.style.setProperty('display', 'none'); } - - elem = li.descendants('span.counts'); + elem = li.querySelector('span.counts'); var text = listStatsTemplate .replace('{{used}}', renderNumber(!entry.off && !isNaN(+entry.entryUsedCount) ? entry.entryUsedCount : 0)) .replace('{{total}}', !isNaN(+entry.entryCount) ? renderNumber(entry.entryCount) : '?'); - elem.text(text); - - // https://github.com/gorhill/uBlock/issues/78 - // Badge for non-secure connection - var remoteURL = listKey; - if ( remoteURL.lastIndexOf('http:', 0) !== 0 ) { - remoteURL = entry.homeURL || ''; - } - if ( remoteURL.lastIndexOf('http:', 0) === 0 ) { - li.descendants('span.status.unsecure').css('display', ''); - } + elem.textContent = text; // https://github.com/chrisaljoudi/uBlock/issues/104 var asset = listDetails.cache[listKey] || {}; + // https://github.com/gorhill/uBlock/issues/78 + // Badge for non-secure connection + var remoteURL = asset.remoteURL; + li.classList.toggle( + 'unsecure', + typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0 + ); // Badge for update status - if ( entry.off !== true ) { - if ( asset.repoObsolete ) { - li.descendants('span.status.new').css('display', ''); - needUpdate = true; - } else if ( asset.cacheObsolete ) { - li.descendants('span.status.obsolete').css('display', ''); - needUpdate = true; - } else if ( entry.external && !asset.cached ) { - li.descendants('span.status.obsolete').css('display', ''); - needUpdate = true; - } - } - - // In cache + li.classList.toggle( + 'obsolete', + entry.off !== true && asset.obsolete === true + ); + // Badge for cache status + li.classList.toggle( + 'cached', + asset.cached === true && asset.writeTime > 0 + ); if ( asset.cached ) { - elem = li.descendants('span.status.purge'); - elem.css('display', ''); - elem.attr('title', lastUpdateString.replace('{{ago}}', renderElapsedTimeToString(asset.lastModified))); - hasCachedContent = true; + li.querySelector('.status.purge').setAttribute( + 'title', + lastUpdateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime)) + ); } + li.classList.remove('discard'); return li; }; @@ -176,27 +158,31 @@ var renderFilterLists = function() { }; var liFromListGroup = function(groupKey, listKeys) { - var liGroup = listGroupTemplate.clone(); - var groupName = vAPI.i18n('3pGroup' + groupKey.charAt(0).toUpperCase() + groupKey.slice(1)); - if ( groupName !== '' ) { - liGroup.descendants('span.geName').text(groupName); - liGroup.descendants('span.geCount').text(listEntryCountFromGroup(listKeys)); + var liGroup = document.querySelector('#lists > .groupEntry[data-groupkey="' + groupKey + '"]'); + if ( liGroup === null ) { + liGroup = listGroupTemplate.clone().nodeAt(0); + var groupName = vAPI.i18n('3pGroup' + groupKey.charAt(0).toUpperCase() + groupKey.slice(1)); + if ( groupName !== '' ) { + liGroup.querySelector('.geName').textContent = groupName; + } } - var ulGroup = liGroup.descendants('ul'); - if ( !listKeys ) { - return liGroup; + if ( liGroup.querySelector('.geName:empty') === null ) { + liGroup.querySelector('.geCount').textContent = listEntryCountFromGroup(listKeys); } + var ulGroup = liGroup.querySelector('.listEntries'); + if ( !listKeys ) { return liGroup; } listKeys.sort(function(a, b) { return (listDetails.available[a].title || '').localeCompare(listDetails.available[b].title || ''); }); for ( var i = 0; i < listKeys.length; i++ ) { - ulGroup.append(liFromListEntry(listKeys[i])); + var liEntry = liFromListEntry(listKeys[i], ulGroup.children[i]); + if ( liEntry.parentElement === null ) { + ulGroup.appendChild(liEntry); + } } return liGroup; }; - // https://www.youtube.com/watch?v=unCVi4hYRlY#t=30m18s - var groupsFromLists = function(lists) { var groups = {}; var listKeys = Object.keys(lists); @@ -219,14 +205,16 @@ var renderFilterLists = function() { listDetails = details; parseCosmeticFilters = details.parseCosmeticFilters; ignoreGenericCosmeticFilters = details.ignoreGenericCosmeticFilters; - needUpdate = false; - hasCachedContent = false; + + // Incremental rendering: this will allow us to easily discard unused + // DOM list entries. + uDom('#lists .listEntries .listEntry').addClass('discard'); // Visually split the filter lists in purpose-based groups - var ulLists = uDom('#lists').empty(), liGroup; - var groups = groupsFromLists(details.available); - var groupKey, i; - var groupKeys = [ + var ulLists = document.querySelector('#lists'), + groups = groupsFromLists(details.available), + liGroup, i, groupKey, + groupKeys = [ 'default', 'ads', 'privacy', @@ -239,31 +227,44 @@ var renderFilterLists = function() { for ( i = 0; i < groupKeys.length; i++ ) { groupKey = groupKeys[i]; liGroup = liFromListGroup(groupKey, groups[groupKey]); - liGroup.toggleClass( + liGroup.setAttribute('data-groupkey', groupKey); + liGroup.classList.toggle( 'collapsed', vAPI.localStorage.getItem('collapseGroup' + (i + 1)) === 'y' ); - ulLists.append(liGroup); + if ( liGroup.parentElement === null ) { + ulLists.appendChild(liGroup); + } delete groups[groupKey]; } // For all groups not covered above (if any left) groupKeys = Object.keys(groups); for ( i = 0; i < groupKeys.length; i++ ) { groupKey = groupKeys[i]; - ulLists.append(liFromListGroup(groupKey, groups[groupKey])); + ulLists.appendChild(liFromListGroup(groupKey, groups[groupKey])); } + uDom('#lists .listEntries .listEntry.discard').remove(); + uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete') === null); + uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true); + uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true); + uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true); uDom('#listsOfBlockedHostsPrompt').text( vAPI.i18n('3pListsOfBlockedHostsPrompt') .replace('{{netFilterCount}}', renderNumber(details.netFilterCount)) .replace('{{cosmeticFilterCount}}', renderNumber(details.cosmeticFilterCount)) ); - uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true); - uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true); - uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true); + + // Compute a hash of the lists currently enabled in memory. + var selectedListsBefore = []; + for ( var key in listDetails.current ) { + if ( listDetails.current[key].off !== true ) { + selectedListsBefore.push(key); + } + } + selectedListsHashBefore = selectedListsBefore.sort().join(); renderWidgets(); - renderBusyOverlay(details.manualUpdate, details.manualUpdateProgress); }; messaging.send('dashboard', { what: 'getLists' }, onListsReceived); @@ -271,33 +272,22 @@ var renderFilterLists = function() { /******************************************************************************/ -// Progress must be normalized to [0, 1], or can be undefined. +// This is to give a visual hint that the selection of blacklists has changed. -var renderBusyOverlay = function(state, progress) { - progress = progress || {}; - var showProgress = typeof progress.value === 'number'; - if ( showProgress ) { - uDom('#busyOverlay > div:nth-of-type(2) > div:first-child').css( - 'width', - (progress.value * 100).toFixed(1) + '%' - ); - var text = progress.text || ''; - if ( text !== '' ) { - uDom('#busyOverlay > div:nth-of-type(2) > div:last-child').text(text); - } - } - uDom('#busyOverlay > div:nth-of-type(2)').css('display', showProgress ? '' : 'none'); - uDom('body').toggleClass('busy', !!state); +var renderWidgets = function() { + uDom('#buttonApply').toggleClass('disabled', !listsSelectionChanged()); + uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null); + uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete') === null); }; /******************************************************************************/ -// This is to give a visual hint that the selection of blacklists has changed. - -var renderWidgets = function() { - uDom('#buttonApply').toggleClass('disabled', !listsSelectionChanged()); - uDom('#buttonUpdate').toggleClass('disabled', !listsContentChanged()); - uDom('#buttonPurgeAll').toggleClass('disabled', !hasCachedContent); +var updateAssetStatus = function(details) { + var li = uDom('#lists .listEntry[data-listkey="' + details.key + '"]'); + li.toggleClass('obsolete', !details.cached); + li.toggleClass('cached', details.cached); + li.removeClass('updating'); + renderWidgets(); }; /******************************************************************************/ @@ -307,98 +297,49 @@ var renderWidgets = function() { var listsSelectionChanged = function() { if ( listDetails.parseCosmeticFilters !== parseCosmeticFilters || - listDetails.parseCosmeticFilters && listDetails.ignoreGenericCosmeticFilters !== ignoreGenericCosmeticFilters + listDetails.parseCosmeticFilters && + listDetails.ignoreGenericCosmeticFilters !== ignoreGenericCosmeticFilters ) { return true; } - - if ( cacheWasPurged ) { - return true; - } - - var availableLists = listDetails.available; - var currentLists = listDetails.current; - var location, availableOff, currentOff; - - // This check existing entries - for ( location in availableLists ) { - if ( availableLists.hasOwnProperty(location) === false ) { - continue; - } - availableOff = availableLists[location].off === true; - currentOff = currentLists[location] === undefined || currentLists[location].off === true; - if ( availableOff !== currentOff ) { - return true; - } + var selectedListsAfter = [], + listEntries = uDom('#lists .listEntry[data-listkey] > input[type="checkbox"]:checked'); + for ( var i = 0, n = listEntries.length; i < n; i++ ) { + selectedListsAfter.push(listEntries.at(i).ancestors('.listEntry[data-listkey]').attr('data-listkey')); } - // This check removed entries - for ( location in currentLists ) { - if ( currentLists.hasOwnProperty(location) === false ) { - continue; - } - currentOff = currentLists[location].off === true; - availableOff = availableLists[location] === undefined || availableLists[location].off === true; - if ( availableOff !== currentOff ) { - return true; - } - } - - return false; -}; - -/******************************************************************************/ - -// Return whether content need update. - -var listsContentChanged = function() { - return needUpdate; + return selectedListsHashBefore !== selectedListsAfter.sort().join(); }; /******************************************************************************/ var onListCheckboxChanged = function() { - var href = uDom(this).parent().descendants('a').first().attr('data-listkey'); - if ( typeof href !== 'string' ) { - return; - } - if ( listDetails.available[href] === undefined ) { - return; - } - listDetails.available[href].off = !this.checked; renderWidgets(); }; /******************************************************************************/ var onPurgeClicked = function() { - var button = uDom(this); - var li = button.parent(); - var href = li.descendants('a').first().attr('data-listkey'); - if ( !href ) { - return; - } + var button = uDom(this), + liEntry = button.ancestors('[data-listkey]'), + listKey = liEntry.attr('data-listkey'); + if ( !listKey ) { return; } - messaging.send('dashboard', { what: 'purgeCache', path: href }); - button.remove(); + messaging.send('dashboard', { what: 'purgeCache', assetKey: listKey }); // If the cached version is purged, the installed version must be assumed // to be obsolete. // https://github.com/gorhill/uBlock/issues/1733 // An external filter list must not be marked as obsolete, they will always // be fetched anyways if there is no cached copy. - var entry = listDetails.current && listDetails.current[href]; - if ( entry && entry.off !== true && /^[a-z]+:\/\//.test(href) === false ) { - if ( typeof entry.homeURL !== 'string' || entry.homeURL === '' ) { - li.descendants('span.status.new').css('display', ''); - } else { - li.descendants('span.status.obsolete').css('display', ''); - } - needUpdate = true; + var entry = listDetails.current && listDetails.current[listKey]; + if ( entry && entry.off !== true ) { + liEntry.addClass('obsolete'); + uDom('#buttonUpdate').removeClass('disabled'); } + liEntry.removeClass('cached'); - if ( li.descendants('input').first().prop('checked') ) { - cacheWasPurged = true; + if ( liEntry.descendants('input').first().prop('checked') ) { renderWidgets(); } }; @@ -419,22 +360,21 @@ var selectFilterLists = function(callback) { }); // Filter lists - var switches = []; - var lis = uDom('#lists .listEntry'), li; - var i = lis.length; + var listKeys = [], + liEntries = uDom('#lists .listEntry'), liEntry, + i = liEntries.length; while ( i-- ) { - li = lis.at(i); - switches.push({ - location: li.descendants('a').attr('data-listkey'), - off: li.descendants('input').prop('checked') === false - }); + liEntry = liEntries.at(i); + if ( liEntry.descendants('input').first().prop('checked') ) { + listKeys.push(liEntry.attr('data-listkey')); + } } messaging.send( 'dashboard', { what: 'selectFilterLists', - switches: switches + keys: listKeys }, callback ); @@ -444,49 +384,34 @@ var selectFilterLists = function(callback) { var buttonApplyHandler = function() { uDom('#buttonApply').removeClass('enabled'); - - renderBusyOverlay(true); - var onSelectionDone = function() { messaging.send('dashboard', { what: 'reloadAllFilters' }); }; - selectFilterLists(onSelectionDone); - - cacheWasPurged = false; }; /******************************************************************************/ var buttonUpdateHandler = function() { - uDom('#buttonUpdate').removeClass('enabled'); - - if ( needUpdate ) { - renderBusyOverlay(true); - - var onSelectionDone = function() { - messaging.send('dashboard', { what: 'forceUpdateAssets' }); - }; - - selectFilterLists(onSelectionDone); - - cacheWasPurged = false; - } + var onSelectionDone = function() { + uDom('#lists .listEntry.obsolete').addClass('updating'); + messaging.send('dashboard', { what: 'forceUpdateAssets' }); + }; + selectFilterLists(onSelectionDone); }; /******************************************************************************/ -var buttonPurgeAllHandler = function() { +var buttonPurgeAllHandler = function(ev) { uDom('#buttonPurgeAll').removeClass('enabled'); - - renderBusyOverlay(true); - - var onCompleted = function() { - cacheWasPurged = true; - renderFilterLists(); - }; - - messaging.send('dashboard', { what: 'purgeAllCaches' }, onCompleted); + messaging.send( + 'dashboard', + { + what: 'purgeAllCaches', + hard: ev.ctrlKey && ev.shiftKey + }, + renderFilterLists + ); }; /******************************************************************************/ @@ -562,7 +487,7 @@ var groupEntryClickHandler = function() { /******************************************************************************/ -var getCloudData = function() { +var toCloudData = function() { var bin = { parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked, ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked, @@ -570,24 +495,22 @@ var getCloudData = function() { externalLists: externalLists }; - var lis = uDom('#lists .listEntry'), li; - var i = lis.length; + var liEntries = uDom('#lists .listEntry'), liEntry; + var i = liEntries.length; while ( i-- ) { - li = lis.at(i); - if ( li.descendants('input').prop('checked') ) { - bin.selectedLists.push(li.descendants('a').attr('data-listkey')); + liEntry = liEntries.at(i); + if ( liEntry.descendants('input').prop('checked') ) { + bin.selectedLists.push(liEntry.attr('data-listkey')); } } return bin; }; -var setCloudData = function(data, append) { - if ( typeof data !== 'object' || data === null ) { - return; - } +var fromCloudData = function(data, append) { + if ( typeof data !== 'object' || data === null ) { return; } - var elem, checked; + var elem, checked, i, n; elem = uDom.nodeFromId('parseCosmeticFilters'); checked = data.parseCosmeticFilters === true || append && elem.checked; @@ -597,30 +520,34 @@ var setCloudData = function(data, append) { checked = data.ignoreGenericCosmeticFilters === true || append && elem.checked; elem.checked = listDetails.ignoreGenericCosmeticFilters = checked; - var lis = uDom('#lists .listEntry'), li, listKey; - var i = lis.length; - while ( i-- ) { - li = lis.at(i); - elem = li.descendants('input'); - listKey = li.descendants('a').attr('data-listkey'); - checked = data.selectedLists.indexOf(listKey) !== -1 || - append && elem.prop('checked'); - elem.prop('checked', checked); - listDetails.available[listKey].off = !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; + for ( i = 0, n = listEntries.length; i < n; i++ ) { + listEntry = listEntries.at(i); + listKey = listEntry.attr('data-listkey'); + input = listEntry.descendants('input').first(); + if ( append && input.prop('checked') ) { continue; } + input.prop('checked', selectedSet.has(listKey) ); } elem = uDom.nodeFromId('externalLists'); - if ( !append ) { - elem.value = ''; - } + if ( !append ) { elem.value = ''; } elem.value += data.externalLists || ''; renderWidgets(); externalListsChangeHandler(); }; -self.cloud.onPush = getCloudData; -self.cloud.onPull = setCloudData; +self.cloud.onPush = toCloudData; +self.cloud.onPull = fromCloudData; /******************************************************************************/ diff --git a/src/js/assets.js b/src/js/assets.js index 88909948b5b81..f4daf8f28f7dc 100644 --- a/src/js/assets.js +++ b/src/js/assets.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 @@ -19,311 +19,54 @@ Home: https://github.com/gorhill/uBlock */ -/* global YaMD5 */ - 'use strict'; -/******************************************************************************* - -File system structure: - assets - ublock - ... - thirdparties - ... - user - filters.txt - ... - -*/ - /******************************************************************************/ -// Low-level asset files manager - µBlock.assets = (function() { /******************************************************************************/ -var oneSecond = 1000; -var oneMinute = 60 * oneSecond; -var oneHour = 60 * oneMinute; -var oneDay = 24 * oneHour; - -/******************************************************************************/ +var reIsExternalPath = /^(?:[a-z-]+):\/\//, + reIsUserAsset = /^user-/, + errorCantConnectTo = vAPI.i18n('errorCantConnectTo'); -var projectRepositoryRoot = µBlock.projectServerRoot; -var assetsRepositoryRoot = 'https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/'; -var nullFunc = function() {}; -var reIsExternalPath = /^(file|ftps?|https?|resource):\/\//; -var reIsUserPath = /^assets\/user\//; -var reIsCachePath = /^cache:\/\//; -var lastRepoMetaTimestamp = 0; -var lastRepoMetaIsRemote = false; -var refreshRepoMetaPeriod = 5 * oneHour; -var errorCantConnectTo = vAPI.i18n('errorCantConnectTo'); -var xhrTimeout = vAPI.localStorage.getItem('xhrTimeout') || 30000; -var onAssetRemovedListener = null; - -var exports = { - autoUpdate: true, - autoUpdateDelay: 4 * oneDay, - - // https://github.com/chrisaljoudi/uBlock/issues/426 - remoteFetchBarrier: 0 +var api = { }; /******************************************************************************/ -var AssetEntry = function() { - this.localChecksum = ''; - this.repoChecksum = ''; - this.expireTimestamp = 0; -}; - -var RepoMetadata = function() { - this.entries = {}; - this.waiting = []; -}; - -var repoMetadata = null; - -// We need these to persist beyond repoMetaData -var homeURLs = {}; - -/******************************************************************************/ +var observers = []; -var stringIsNotEmpty = function(s) { - return typeof s === 'string' && s !== ''; +api.addObserver = function(observer) { + if ( observers.indexOf(observer) === -1 ) { + observers.push(observer); + } }; -/******************************************************************************/ - -var cacheIsObsolete = function(t) { - return typeof t !== 'number' || (Date.now() - t) >= exports.autoUpdateDelay; +api.removeObserver = function(observer) { + var pos; + while ( (pos = observers.indexOf(observer)) !== -1 ) { + observers.splice(pos, 1); + } }; -/******************************************************************************/ - -var cachedAssetsManager = (function() { - var exports = {}; - var entries = null; - var cachedAssetPathPrefix = 'cached_asset_content://'; - - var getEntries = function(callback) { - if ( entries !== null ) { - callback(entries); - return; - } - // Flush cached non-user assets if these are from a prior version. - // https://github.com/gorhill/httpswitchboard/issues/212 - var onLastVersionRead = function(store) { - var currentVersion = vAPI.app.version; - var lastVersion = store.extensionLastVersion || '0.0.0.0'; - if ( currentVersion !== lastVersion ) { - vAPI.cacheStorage.set({ 'extensionLastVersion': currentVersion }); - } - callback(entries); - }; - var onLoaded = function(bin) { - // https://github.com/gorhill/httpswitchboard/issues/381 - // Maybe the index was requested multiple times and already - // fetched by one of the occurrences. - if ( entries === null ) { - var lastError = vAPI.lastError(); - if ( lastError ) { - console.error( - 'µBlock> cachedAssetsManager> getEntries():', - lastError.message - ); - } - entries = bin.cached_asset_entries || {}; - } - vAPI.cacheStorage.get('extensionLastVersion', onLastVersionRead); - }; - vAPI.cacheStorage.get('cached_asset_entries', onLoaded); - }; - exports.entries = getEntries; - - exports.load = function(path, cbSuccess, cbError) { - cbSuccess = cbSuccess || nullFunc; - cbError = cbError || cbSuccess; - var details = { - 'path': path, - 'content': '' - }; - var cachedContentPath = cachedAssetPathPrefix + path; - var onLoaded = function(bin) { - var lastError = vAPI.lastError(); - if ( lastError ) { - details.error = 'Error: ' + lastError.message; - console.error('µBlock> cachedAssetsManager.load():', details.error); - cbError(details); - return; - } - // Not sure how this can happen, but I've seen it happen. It could - // be because the save occurred while I was stepping in the code - // though, which means it would not occur during normal operation. - // Still, just to be safe. - if ( stringIsNotEmpty(bin[cachedContentPath]) === false ) { - exports.remove(path); - details.error = 'Error: not found'; - cbError(details); - return; - } - details.content = bin[cachedContentPath]; - cbSuccess(details); - }; - var onEntries = function(entries) { - if ( entries[path] === undefined ) { - details.error = 'Error: not found'; - cbError(details); - return; - } - vAPI.cacheStorage.get(cachedContentPath, onLoaded); - }; - getEntries(onEntries); - }; - - exports.save = function(path, content, cbSuccess, cbError) { - cbSuccess = cbSuccess || nullFunc; - cbError = cbError || cbSuccess; - var details = { - path: path, - content: content - }; - if ( content === '' ) { - exports.remove(path); - cbSuccess(details); - return; +var fireNotification = function(topic, details) { + var result; + for ( var i = 0; i < observers.length; i++ ) { + if ( observers[i](topic, details) === false ) { + result = false; } - var cachedContentPath = cachedAssetPathPrefix + path; - var bin = {}; - bin[cachedContentPath] = content; - var removedItems = []; - var onSaved = function() { - var lastError = vAPI.lastError(); - if ( lastError ) { - details.error = 'Error: ' + lastError.message; - console.error('µBlock> cachedAssetsManager.save():', details.error); - cbError(details); - return; - } - // Saving over an existing item must be seen as removing an - // existing item and adding a new one. - if ( onAssetRemovedListener instanceof Function ) { - onAssetRemovedListener(removedItems); - } - cbSuccess(details); - }; - var onEntries = function(entries) { - if ( entries.hasOwnProperty(path) ) { - removedItems.push(path); - } - entries[path] = Date.now(); - bin.cached_asset_entries = entries; - vAPI.cacheStorage.set(bin, onSaved); - }; - getEntries(onEntries); - }; - - exports.remove = function(pattern, before) { - var onEntries = function(entries) { - var keystoRemove = []; - var removedItems = []; - var paths = Object.keys(entries); - var i = paths.length; - var path; - while ( i-- ) { - path = paths[i]; - if ( typeof pattern === 'string' && path !== pattern ) { - continue; - } - if ( pattern instanceof RegExp && !pattern.test(path) ) { - continue; - } - if ( typeof before === 'number' && entries[path] >= before ) { - continue; - } - removedItems.push(path); - keystoRemove.push(cachedAssetPathPrefix + path); - delete entries[path]; - } - if ( keystoRemove.length ) { - vAPI.cacheStorage.remove(keystoRemove); - vAPI.cacheStorage.set({ 'cached_asset_entries': entries }); - if ( onAssetRemovedListener instanceof Function ) { - onAssetRemovedListener(removedItems); - } - } - }; - getEntries(onEntries); - }; - - exports.removeAll = function(callback) { - var onEntries = function() { - // Careful! do not remove 'assets/user/' - exports.remove(/^https?:\/\/[a-z0-9]+/); - exports.remove(/^assets\/(ublock|thirdparties)\//); - exports.remove(/^cache:\/\//); - exports.remove('assets/checksums.txt'); - if ( typeof callback === 'function' ) { - callback(null); - } - }; - getEntries(onEntries); - }; - - exports.rmrf = function() { - exports.remove(/./); - }; - - exports.exists = function(path) { - return entries !== null && entries.hasOwnProperty(path); - }; - - getEntries(function(){}); - - return exports; -})(); - -/******************************************************************************/ - -var toRepoURL = function(path) { - if ( path.startsWith('assets/ublock/filter-lists.json') ) { - return projectRepositoryRoot + path; - } - - if ( path.startsWith('assets/checksums.txt') ) { - return path.replace( - /^assets\/checksums.txt/, - assetsRepositoryRoot + 'checksums/ublock0.txt' - ); - } - - if ( path.startsWith('assets/thirdparties/') ) { - return path.replace( - /^assets\/thirdparties\//, - assetsRepositoryRoot + 'thirdparties/' - ); - } - - if ( path.startsWith('assets/ublock/') ) { - return path.replace( - /^assets\/ublock\//, - assetsRepositoryRoot + 'filters/' - ); } - - // At this point, `path` is assumed to point to a resource specific to - // this project. - return projectRepositoryRoot + path; + return result; }; /******************************************************************************/ var getTextFileFromURL = function(url, onLoad, onError) { - // console.log('µBlock.assets/getTextFileFromURL("%s"):', url); + if ( reIsExternalPath.test(url) === false ) { + url = vAPI.getURL(url); + } if ( typeof onError !== 'function' ) { onError = onLoad; @@ -353,6 +96,7 @@ var getTextFileFromURL = function(url, onLoad, onError) { var onErrorReceived = function() { this.onload = this.onerror = this.ontimeout = null; + µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', '')); onError.call(this); }; @@ -362,7 +106,7 @@ var getTextFileFromURL = function(url, onLoad, onError) { var xhr = new XMLHttpRequest(); try { xhr.open('get', url, true); - xhr.timeout = xhrTimeout; + xhr.timeout = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000; xhr.onload = onResponseReceived; xhr.onerror = onErrorReceived; xhr.ontimeout = onErrorReceived; @@ -373,1289 +117,897 @@ var getTextFileFromURL = function(url, onLoad, onError) { } }; -/******************************************************************************/ +/******************************************************************************* -var updateLocalChecksums = function() { - var localChecksums = []; - var entries = repoMetadata.entries; - var entry; - for ( var path in entries ) { - if ( entries.hasOwnProperty(path) === false ) { - continue; - } - entry = entries[path]; - if ( entry.localChecksum !== '' ) { - localChecksums.push(entry.localChecksum + ' ' + path); + 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://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/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", + "http://malwaredomains.lehigh.edu/files/immortal_domains.txt": "malware-0", + "assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt": "malware-1", + "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains": "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); } - } - cachedAssetsManager.save('assets/checksums.txt', localChecksums.join('\n')); -}; + 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(); + }; -// Gather meta data of all assets. + vAPI.cacheStorage.get( + [ 'cached_asset_entries', 'assetCacheRegistry' ], + onEntries + ); +}; -var getRepoMetadata = function(callback) { - callback = callback || nullFunc; +/******************************************************************************* - // https://github.com/chrisaljoudi/uBlock/issues/515 - // Handle re-entrancy here, i.e. we MUST NOT tamper with the waiting list - // of callers, if any, except to add one at the end of the list. - if ( repoMetadata !== null && repoMetadata.waiting.length !== 0 ) { - repoMetadata.waiting.push(callback); - return; + The purpose of the asset source registry is to keep key detail information + about an asset: + - Where to load it from: this may consist of one or more URLs, either local + or remote. + - After how many days an asset should be deemed obsolete -- i.e. in need of + an update. + - The origin and type of an asset. + - The last time an asset was registered. + +**/ + +var assetSourceRegistryStatus, + assetSourceRegistry = Object.create(null); + +var registerAssetSource = function(assetKey, dict) { + var entry = assetSourceRegistry[assetKey] || {}; + for ( var prop in dict ) { + if ( dict.hasOwnProperty(prop) === false ) { continue; } + if ( dict[prop] === undefined ) { + delete entry[prop]; + } else { + entry[prop] = dict[prop]; + } } - - if ( exports.remoteFetchBarrier === 0 && lastRepoMetaIsRemote === false ) { - lastRepoMetaTimestamp = 0; + var contentURL = dict.contentURL; + if ( contentURL !== undefined ) { + if ( typeof contentURL === 'string' ) { + contentURL = entry.contentURL = [ contentURL ]; + } else if ( Array.isArray(contentURL) === false ) { + contentURL = entry.contentURL = []; + } + var remoteURLCount = 0; + for ( var i = 0; i < contentURL.length; i++ ) { + if ( reIsExternalPath.test(contentURL[i]) ) { + remoteURLCount += 1; + } + } + entry.hasLocalURL = remoteURLCount !== contentURL.length; + entry.hasRemoteURL = remoteURLCount !== 0; + } else if ( entry.contentURL === undefined ) { + entry.contentURL = []; } - if ( (Date.now() - lastRepoMetaTimestamp) >= refreshRepoMetaPeriod ) { - repoMetadata = null; + if ( typeof entry.updateAfter !== 'number' ) { + entry.updateAfter = 5; } - if ( repoMetadata !== null ) { - callback(repoMetadata); - return; + if ( entry.submitter ) { + entry.submitTime = Date.now(); // To detect stale entries } + assetSourceRegistry[assetKey] = entry; +}; - lastRepoMetaTimestamp = Date.now(); - lastRepoMetaIsRemote = exports.remoteFetchBarrier === 0; - - var defaultChecksums; - var localChecksums; - var repoChecksums; +var unregisterAssetSource = function(assetKey) { + assetCacheRemove(assetKey); + delete assetSourceRegistry[assetKey]; +}; - var checksumsReceived = function() { - if ( - defaultChecksums === undefined || - localChecksums === undefined || - repoChecksums === undefined - ) { - return; - } - // Remove from cache assets which no longer exist in the repo - var entries = repoMetadata.entries; - var checksumsChanged = false; - var entry; - for ( var path in entries ) { - if ( entries.hasOwnProperty(path) === false ) { - continue; - } - entry = entries[path]; - // https://github.com/gorhill/uBlock/issues/760 - // If the resource does not have a cached instance, we must reset - // the checksum to its value at install time. - if ( - stringIsNotEmpty(defaultChecksums[path]) && - entry.localChecksum !== defaultChecksums[path] && - cachedAssetsManager.exists(path) === false - ) { - entry.localChecksum = defaultChecksums[path]; - checksumsChanged = true; - } - // If repo checksums could not be fetched, assume no change. - // https://github.com/gorhill/uBlock/issues/602 - // Added: if repo checksum is that of the empty string, - // assume no change - if ( - repoChecksums === '' || - entry.repoChecksum === 'd41d8cd98f00b204e9800998ecf8427e' - ) { - entry.repoChecksum = entry.localChecksum; - } - if ( entry.repoChecksum !== '' || entry.localChecksum === '' ) { - continue; - } - checksumsChanged = true; - cachedAssetsManager.remove(path); - entry.localChecksum = ''; - } - if ( checksumsChanged ) { - updateLocalChecksums(); - } - // Notify all waiting callers - // https://github.com/chrisaljoudi/uBlock/issues/515 - // VERY IMPORTANT: because of re-entrancy, we MUST: - // - process the waiting callers in a FIFO manner - // - not cache repoMetadata.waiting.length, we MUST use the live - // value, because it can change while looping - // - not change the waiting list until they are all processed - for ( var i = 0; i < repoMetadata.waiting.length; i++ ) { - repoMetadata.waiting[i](repoMetadata); - } - repoMetadata.waiting.length = 0; +var saveAssetSourceRegistry = (function() { + var timer; + var save = function() { + timer = undefined; + vAPI.cacheStorage.set({ assetSourceRegistry: assetSourceRegistry }); }; - - var validateChecksums = function(details) { - if ( details.error || details.content === '' ) { - return ''; - } - if ( /^(?:[0-9a-f]{32}\s+\S+(?:\s+|$))+/.test(details.content) === false ) { - return ''; + return function(lazily) { + if ( timer !== undefined ) { + clearTimeout(timer); } - // https://github.com/gorhill/uBlock/issues/602 - // External filter lists are not meant to appear in checksums.txt. - // TODO: remove this code once v1.1.0.0 is everywhere. - var out = []; - var listMap = µBlock.oldListToNewListMap; - var lines = details.content.split(/\s*\n\s*/); - var line, matches; - for ( var i = 0; i < lines.length; i++ ) { - line = lines[i]; - matches = line.match(/^[0-9a-f]+ (.+)$/); - if ( matches === null || listMap.hasOwnProperty(matches[1]) ) { - continue; - } - out.push(line); + if ( lazily ) { + timer = vAPI.setTimeout(save, 500); + } else { + save(); } - return out.join('\n'); }; +})(); - var parseChecksums = function(text, eachFn) { - var lines = text.split(/\n+/); - var i = lines.length; - var fields; - while ( i-- ) { - fields = lines[i].trim().split(/\s+/); - if ( fields.length !== 2 ) { - continue; - } - eachFn(fields[1], fields[0]); - } - }; +var updateAssetSourceRegistry = function(json) { + var newDict; + try { + newDict = JSON.parse(json); + } catch (ex) { + } + if ( newDict instanceof Object === false ) { return; } - var onLocalChecksumsLoaded = function(details) { - var entries = repoMetadata.entries; - var processChecksum = function(path, checksum) { - if ( entries.hasOwnProperty(path) === false ) { - entries[path] = new AssetEntry(); + getAssetSourceRegistry(function(oldDict) { + var assetKey; + // Remove obsolete entries + for ( assetKey in oldDict ) { + if ( newDict[assetKey] === undefined ) { + unregisterAssetSource(assetKey); } - entries[path].localChecksum = checksum; - }; - if ( (localChecksums = validateChecksums(details)) ) { - parseChecksums(localChecksums, processChecksum); } - checksumsReceived(); - }; - - var onRepoChecksumsLoaded = function(details) { - var entries = repoMetadata.entries; - var processChecksum = function(path, checksum) { - if ( entries.hasOwnProperty(path) === false ) { - entries[path] = new AssetEntry(); - } - entries[path].repoChecksum = checksum; - }; - if ( (repoChecksums = validateChecksums(details)) ) { - parseChecksums(repoChecksums, processChecksum); + // Add/update existing entries + for ( assetKey in newDict ) { + registerAssetSource(assetKey, newDict[assetKey]); } - checksumsReceived(); - }; - - // https://github.com/gorhill/uBlock/issues/760 - // We need the checksum values at install time, because some resources - // may have been purged, in which case the checksum must be reset to the - // value at install time. - var onDefaultChecksumsLoaded = function() { - defaultChecksums = Object.create(null); - var processChecksum = function(path, checksum) { - defaultChecksums[path] = checksum; - }; - parseChecksums(this.responseText || '', processChecksum); - checksumsReceived(); - }; - - repoMetadata = new RepoMetadata(); - repoMetadata.waiting.push(callback); - readRepoFile('assets/checksums.txt', onRepoChecksumsLoaded); - getTextFileFromURL(vAPI.getURL('assets/checksums.txt'), onDefaultChecksumsLoaded); - readLocalFile('assets/checksums.txt', onLocalChecksumsLoaded); + saveAssetSourceRegistry(); + }); }; -// https://www.youtube.com/watch?v=-t3WYfgM4x8 - -/******************************************************************************/ - -exports.setHomeURL = function(path, homeURL) { - if ( typeof homeURL !== 'string' || homeURL === '' ) { +var getAssetSourceRegistry = function(callback) { + // Already loaded. + if ( assetSourceRegistryStatus === 'ready' ) { + callback(assetSourceRegistry); return; } - homeURLs[path] = homeURL; -}; -/******************************************************************************/ + // Being loaded. + if ( Array.isArray(assetSourceRegistryStatus) ) { + assetSourceRegistryStatus.push(callback); + return; + } -// Get a local asset, do not look-up repo or remote location if local asset -// is not found. + // Not loaded: load it. + assetSourceRegistryStatus = [ callback ]; -var readLocalFile = function(path, callback) { - var reportBack = function(content, err) { - var details = { - 'path': path, - 'content': content - }; - if ( err ) { - details.error = err; + var registryReady = function() { + var callers = assetSourceRegistryStatus; + assetSourceRegistryStatus = 'ready'; + var fn; + while ( (fn = callers.shift()) ) { + fn(assetSourceRegistry); } - callback(details); - }; - - var onInstallFileLoaded = function() { - //console.log('µBlock> readLocalFile("%s") / onInstallFileLoaded()', path); - reportBack(this.responseText); - }; - - var onInstallFileError = function() { - console.error('µBlock> readLocalFile("%s") / onInstallFileError()', path); - reportBack('', 'Error'); }; - var onCachedContentLoaded = function(details) { - //console.log('µBlock> readLocalFile("%s") / onCachedContentLoaded()', path); - reportBack(details.content); + // First-install case. + var createRegistry = function() { + getTextFileFromURL( + µBlock.assetsBootstrapLocation || 'assets/assets.json', + function() { + updateAssetSourceRegistry(this.responseText); + registryReady(); + } + ); }; - var onCachedContentError = function(details) { - //console.error('µBlock> readLocalFile("%s") / onCachedContentError()', path); - if ( reIsExternalPath.test(path) ) { - reportBack('', 'Error: asset not found'); - return; - } - // It's ok for user data to not be found - if ( reIsUserPath.test(path) ) { - reportBack(''); + vAPI.cacheStorage.get('assetSourceRegistry', function(bin) { + if ( !bin || !bin.assetSourceRegistry ) { + createRegistry(); return; } - getTextFileFromURL(vAPI.getURL(details.path), onInstallFileLoaded, onInstallFileError); - }; + assetSourceRegistry = bin.assetSourceRegistry; + registryReady(); + }); +}; - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); +api.registerAssetSource = function(assetKey, details) { + getAssetSourceRegistry(function() { + registerAssetSource(assetKey, details); + saveAssetSourceRegistry(true); + }); }; -// https://www.youtube.com/watch?v=r9KVpuFPtHc +api.unregisterAssetSource = function(assetKey) { + getAssetSourceRegistry(function() { + unregisterAssetSource(assetKey); + saveAssetSourceRegistry(true); + }); +}; -/******************************************************************************/ +/******************************************************************************* + + The purpose of the asset cache registry is to keep track of all assets + which have been persisted into the local cache. -// Get the repository copy of a built-in asset. +**/ -var readRepoFile = function(path, callback) { - // https://github.com/chrisaljoudi/uBlock/issues/426 - if ( exports.remoteFetchBarrier !== 0 ) { - readLocalFile(path, callback); +var assetCacheRegistryStatus, + assetCacheRegistryStartTime = Date.now(), + assetCacheRegistry = {}; + +var getAssetCacheRegistry = function(callback) { + // Already loaded. + if ( assetCacheRegistryStatus === 'ready' ) { + callback(assetCacheRegistry); return; } - var reportBack = function(content, err) { - var details = { - 'path': path, - 'content': content, - 'error': err - }; - callback(details); - }; + // Being loaded. + if ( Array.isArray(assetCacheRegistryStatus) ) { + assetCacheRegistryStatus.push(callback); + return; + } - var repositoryURL = toRepoURL(path); + // Not loaded: load it. + assetCacheRegistryStatus = [ callback ]; - var onRepoFileLoaded = function() { - //console.log('µBlock> readRepoFile("%s") / onRepoFileLoaded()', path); - // https://github.com/gorhill/httpswitchboard/issues/263 - if ( this.status === 200 ) { - reportBack(this.responseText); - } else { - reportBack('', 'Error: ' + this.statusText); + var registryReady = function() { + var callers = assetCacheRegistryStatus; + assetCacheRegistryStatus = 'ready'; + var fn; + while ( (fn = callers.shift()) ) { + fn(assetCacheRegistry); } }; - var onRepoFileError = function() { - console.error(errorCantConnectTo.replace('{{url}}', repositoryURL)); - reportBack('', 'Error'); + var migrationDone = function() { + vAPI.cacheStorage.get('assetCacheRegistry', function(bin) { + if ( bin && bin.assetCacheRegistry ) { + assetCacheRegistry = bin.assetCacheRegistry; + } + registryReady(); + }); }; - // '_=...' is to skip browser cache - getTextFileFromURL( - repositoryURL + '?_=' + Date.now(), - onRepoFileLoaded, - onRepoFileError - ); + migrate(migrationDone); }; -/******************************************************************************/ - -// An asset from an external source with a copy shipped with the extension: -// Path --> starts with 'assets/(thirdparties|ublock)/', with a home URL -// External --> -// Repository --> has checksum (to detect need for update only) -// Cache --> has expiration timestamp (in cache) -// Local --> install time version +var saveAssetCacheRegistry = (function() { + var timer; + var save = function() { + timer = undefined; + vAPI.cacheStorage.set({ assetCacheRegistry: assetCacheRegistry }); + }; + return function(lazily) { + if ( timer !== undefined ) { clearTimeout(timer); } + if ( lazily ) { + timer = vAPI.setTimeout(save, 500); + } else { + save(); + } + }; +})(); -var readRepoCopyAsset = function(path, callback) { - var assetEntry; - var homeURL = homeURLs[path]; +var assetCacheRead = function(assetKey, callback) { + var internalKey = 'cache/' + assetKey; var reportBack = function(content, err) { - var details = { - 'path': path, - 'content': content - }; - if ( err ) { - details.error = err; - } + var details = { assetKey: assetKey, content: content }; + if ( err ) { details.error = err; } callback(details); }; - var updateChecksum = function() { - if ( assetEntry !== undefined && assetEntry.repoChecksum !== assetEntry.localChecksum ) { - assetEntry.localChecksum = assetEntry.repoChecksum; - updateLocalChecksums(); + var onAssetRead = function(bin) { + if ( !bin || !bin[internalKey] ) { + return reportBack('', 'E_NOTFOUND'); } + var entry = assetCacheRegistry[assetKey]; + if ( entry === undefined ) { + return reportBack('', 'E_NOTFOUND'); + } + entry.readTime = Date.now(); + saveAssetCacheRegistry(true); + reportBack(bin[internalKey]); }; - var onInstallFileLoaded = function() { - //console.log('µBlock> readRepoCopyAsset("%s") / onInstallFileLoaded()', path); - reportBack(this.responseText); - }; - - var onInstallFileError = function() { - console.error('µBlock> readRepoCopyAsset("%s") / onInstallFileError():', path, this.statusText); - reportBack('', 'Error'); + var onReady = function() { + vAPI.cacheStorage.get(internalKey, onAssetRead); }; - var onCachedContentLoaded = function(details) { - //console.log('µBlock> readRepoCopyAsset("%s") / onCacheFileLoaded()', path); - reportBack(details.content); - }; + getAssetCacheRegistry(onReady); +}; - var onCachedContentError = function(details) { - //console.log('µBlock> readRepoCopyAsset("%s") / onCacheFileError()', path); - getTextFileFromURL(vAPI.getURL(details.path), onInstallFileLoaded, onInstallFileError); - }; +var assetCacheWrite = function(assetKey, details, callback) { + var internalKey = 'cache/' + assetKey; + var content = ''; + if ( typeof details === 'string' ) { + content = details; + } else if ( details instanceof Object ) { + content = details.content || ''; + } - var repositoryURL = toRepoURL(path); - var repositoryURLSkipCache = repositoryURL + '?_=' + Date.now(); + if ( content === '' ) { + return assetCacheRemove(assetKey, callback); + } - var onRepoFileLoaded = function() { - if ( stringIsNotEmpty(this.responseText) === false ) { - console.error('µBlock> readRepoCopyAsset("%s") / onRepoFileLoaded("%s"): error', path, repositoryURL); - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); - return; + var reportBack = function(content) { + var details = { assetKey: assetKey, content: content }; + if ( typeof callback === 'function' ) { + callback(details); } - //console.log('µBlock> readRepoCopyAsset("%s") / onRepoFileLoaded("%s")', path, repositoryURL); - updateChecksum(); - cachedAssetsManager.save(path, this.responseText, callback); + fireNotification('after-asset-updated', details); }; - var onRepoFileError = function() { - console.error(errorCantConnectTo.replace('{{url}}', repositoryURL)); - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); + var onReady = function() { + var entry = assetCacheRegistry[assetKey]; + if ( entry === undefined ) { + entry = assetCacheRegistry[assetKey] = {}; + } + entry.writeTime = entry.readTime = Date.now(); + if ( details instanceof Object && typeof details.url === 'string' ) { + entry.remoteURL = details.url; + } + var bin = { assetCacheRegistry: assetCacheRegistry }; + bin[internalKey] = content; + vAPI.cacheStorage.set(bin); + reportBack(content); }; + getAssetCacheRegistry(onReady); +}; - var onHomeFileLoaded = function() { - if ( stringIsNotEmpty(this.responseText) === false ) { - console.error('µBlock> readRepoCopyAsset("%s") / onHomeFileLoaded("%s"): no response', path, homeURL); - // Fetch from repo only if obsolescence was due to repo checksum - if ( assetEntry.localChecksum !== assetEntry.repoChecksum ) { - getTextFileFromURL(repositoryURLSkipCache, onRepoFileLoaded, onRepoFileError); - } else { - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); +var assetCacheRemove = function(pattern, callback) { + var onReady = function() { + var cacheDict = assetCacheRegistry, + removedEntries = [], + removedContent = []; + for ( var assetKey in cacheDict ) { + if ( pattern instanceof RegExp && !pattern.test(assetKey) ) { + continue; } - return; + if ( typeof pattern === 'string' && assetKey !== pattern ) { + continue; + } + removedEntries.push(assetKey); + removedContent.push('cache/' + assetKey); + delete cacheDict[assetKey]; } - //console.log('µBlock> readRepoCopyAsset("%s") / onHomeFileLoaded("%s")', path, homeURL); - updateChecksum(); - cachedAssetsManager.save(path, this.responseText, callback); - }; - - var onHomeFileError = function() { - console.error(errorCantConnectTo.replace('{{url}}', homeURL)); - // Fetch from repo only if obsolescence was due to repo checksum - if ( assetEntry.localChecksum !== assetEntry.repoChecksum ) { - getTextFileFromURL(repositoryURLSkipCache, onRepoFileLoaded, onRepoFileError); - } else { - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); + if ( removedContent.length !== 0 ) { + vAPI.cacheStorage.remove(removedContent); + var bin = { assetCacheRegistry: assetCacheRegistry }; + vAPI.cacheStorage.set(bin); + } + if ( typeof callback === 'function' ) { + callback(); + } + for ( var i = 0; i < removedEntries.length; i++ ) { + fireNotification('after-asset-updated', { assetKey: removedEntries[i] }); } }; - var onCacheMetaReady = function(entries) { - // Fetch from remote if: - // - Auto-update enabled AND (not in cache OR in cache but obsolete) - var timestamp = entries[path]; - var inCache = typeof timestamp === 'number'; - if ( - exports.remoteFetchBarrier === 0 && - exports.autoUpdate && stringIsNotEmpty(homeURL) - ) { - if ( inCache === false || cacheIsObsolete(timestamp) ) { - //console.log('µBlock> readRepoCopyAsset("%s") / onCacheMetaReady(): not cached or obsolete', path); - getTextFileFromURL(homeURL, onHomeFileLoaded, onHomeFileError); - return; + getAssetCacheRegistry(onReady); +}; + +var assetCacheMarkAsDirty = function(pattern, callback) { + var onReady = function() { + var cacheDict = assetCacheRegistry, + cacheEntry, + mustSave = false; + for ( var assetKey in cacheDict ) { + if ( pattern instanceof RegExp && !pattern.test(assetKey) ) { + continue; } + if ( typeof pattern === 'string' && assetKey !== pattern ) { + continue; + } + cacheEntry = cacheDict[assetKey]; + if ( !cacheEntry.writeTime ) { continue; } + cacheDict[assetKey].writeTime = 0; + mustSave = true; } - - // In cache - if ( inCache ) { - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); - return; + if ( mustSave ) { + var bin = { assetCacheRegistry: assetCacheRegistry }; + vAPI.cacheStorage.set(bin); + } + if ( typeof callback === 'function' ) { + callback(); } - - // Not in cache - getTextFileFromURL(vAPI.getURL(path), onInstallFileLoaded, onInstallFileError); }; - var onRepoMetaReady = function(meta) { - assetEntry = meta.entries[path]; + getAssetCacheRegistry(onReady); +}; - // Asset doesn't exist - if ( assetEntry === undefined ) { - reportBack('', 'Error: asset not found'); - return; - } +/******************************************************************************/ - // Repo copy changed: fetch from home URL - if ( - exports.remoteFetchBarrier === 0 && - exports.autoUpdate && - assetEntry.localChecksum !== assetEntry.repoChecksum - ) { - //console.log('µBlock> readRepoCopyAsset("%s") / onRepoMetaReady(): repo has newer version', path); - if ( stringIsNotEmpty(homeURL) ) { - getTextFileFromURL(homeURL, onHomeFileLoaded, onHomeFileError); - } else { - getTextFileFromURL(repositoryURLSkipCache, onRepoFileLoaded, onRepoFileError); - } - return; - } +var stringIsNotEmpty = function(s) { + return typeof s === 'string' && s !== ''; +}; + +/******************************************************************************* + + User assets are NOT persisted in the cache storage. User assets are + recognized by the asset key which always starts with 'user-'. + + TODO(seamless migration): + Can remove instances of old user asset keys when I am confident all users + are using uBO v1.11 and beyond. + +**/ - // Load from cache - cachedAssetsManager.entries(onCacheMetaReady); +var readUserAsset = function(assetKey, callback) { + var reportBack = function(content) { + callback({ assetKey: assetKey, content: content }); }; - getRepoMetadata(onRepoMetaReady); + var onLoaded = function(bin) { + if ( !bin ) { return reportBack(''); } + var content = ''; + if ( typeof bin['cached_asset_content://assets/user/filters.txt'] === 'string' ) { + content = bin['cached_asset_content://assets/user/filters.txt']; + vAPI.cacheStorage.remove('cached_asset_content://assets/user/filters.txt'); + } + if ( typeof bin['assets/user/filters.txt'] === 'string' ) { + content = bin['assets/user/filters.txt']; + // TODO(seamless migration): + // Uncomment once all moved to v1.11+. + //vAPI.storage.remove('assets/user/filters.txt'); + } + if ( typeof bin[assetKey] === 'string' ) { + // TODO(seamless migration): + // Replace conditional with assignment once all moved to v1.11+ + if ( content !== bin[assetKey] ) { + saveUserAsset(assetKey, content); + } + } else if ( content !== '' ) { + saveUserAsset(assetKey, content); + } + return reportBack(content); + }; + var toRead = assetKey; + if ( assetKey === µBlock.userFiltersPath ) { + toRead = [ + assetKey, + 'assets/user/filters.txt', + 'cached_asset_content://assets/user/filters.txt' + ]; + } + vAPI.storage.get(toRead, onLoaded); }; -// https://www.youtube.com/watch?v=uvUW4ozs7pY +var saveUserAsset = function(assetKey, content, callback) { + var bin = {}; + bin[assetKey] = content; + // TODO(seamless migration): + // This is for forward compatibility. Only for a limited time. Remove when + // everybody moved to 1.11.0 and beyond. + // >>>>>>>> + if ( assetKey === µBlock.userFiltersPath ) { + bin['assets/user/filters.txt'] = content; + } + // <<<<<<<< + var onSaved = function() { + if ( callback instanceof Function ) { + callback({ assetKey: assetKey, content: content }); + } + }; + vAPI.storage.set(bin, onSaved); +}; /******************************************************************************/ -// An important asset shipped with the extension -- typically small, or -// doesn't change often: -// Path --> starts with 'assets/(thirdparties|ublock)/', without a home URL -// Repository --> has checksum (to detect need for update and corruption) -// Cache --> whatever from above -// Local --> install time version - -var readRepoOnlyAsset = function(path, callback) { +api.get = function(assetKey, callback) { + if ( assetKey === µBlock.userFiltersPath ) { + readUserAsset(assetKey, callback); + return; + } - var assetEntry; + var assetDetails = {}, + contentURLs, + contentURL; var reportBack = function(content, err) { - var details = { - 'path': path, - 'content': content - }; + var details = { assetKey: assetKey, content: content }; if ( err ) { - details.error = err; + details.error = assetDetails.lastError = err; + } else { + assetDetails.lastError = undefined; } callback(details); }; - var onInstallFileLoaded = function() { - //console.log('µBlock> readRepoOnlyAsset("%s") / onInstallFileLoaded()', path); - reportBack(this.responseText); + var onContentNotLoaded = function() { + var isExternal; + while ( (contentURL = contentURLs.shift()) ) { + isExternal = reIsExternalPath.test(contentURL); + if ( isExternal === false || assetDetails.hasLocalURL !== true ) { + break; + } + } + if ( !contentURL ) { + return reportBack('', 'E_NOTFOUND'); + } + getTextFileFromURL(contentURL, onContentLoaded, onContentNotLoaded); }; - var onInstallFileError = function() { - console.error('µBlock> readRepoOnlyAsset("%s") / onInstallFileError()', path); - reportBack('', 'Error'); + var onContentLoaded = function() { + if ( stringIsNotEmpty(this.responseText) === false ) { + onContentNotLoaded(); + return; + } + if ( reIsExternalPath.test(contentURL) ) { + assetCacheWrite(assetKey, { + content: this.responseText, + url: contentURL + }); + } + reportBack(this.responseText); }; var onCachedContentLoaded = function(details) { - //console.log('µBlock> readRepoOnlyAsset("%s") / onCachedContentLoaded()', path); - reportBack(details.content); - }; - - var onCachedContentError = function() { - //console.log('µBlock> readRepoOnlyAsset("%s") / onCachedContentError()', path); - getTextFileFromURL(vAPI.getURL(path), onInstallFileLoaded, onInstallFileError); - }; - - var repositoryURL = toRepoURL(path + '?_=' + Date.now()); - - var onRepoFileLoaded = function() { - if ( typeof this.responseText !== 'string' ) { - console.error('µBlock> readRepoOnlyAsset("%s") / onRepoFileLoaded("%s"): no response', path, repositoryURL); - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); - return; - } - if ( YaMD5.hashStr(this.responseText) !== assetEntry.repoChecksum ) { - console.error('µBlock> readRepoOnlyAsset("%s") / onRepoFileLoaded("%s"): bad md5 checksum', path, repositoryURL); - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); - return; - } - //console.log('µBlock> readRepoOnlyAsset("%s") / onRepoFileLoaded("%s")', path, repositoryURL); - assetEntry.localChecksum = assetEntry.repoChecksum; - updateLocalChecksums(); - cachedAssetsManager.save(path, this.responseText, callback); - }; - - var onRepoFileError = function() { - console.error(errorCantConnectTo.replace('{{url}}', repositoryURL)); - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); - }; - - var onRepoMetaReady = function(meta) { - assetEntry = meta.entries[path]; - - // Asset doesn't exist - if ( assetEntry === undefined ) { - reportBack('', 'Error: asset not found'); - return; - } - - // Asset added or changed: load from repo URL and then cache result - if ( - exports.remoteFetchBarrier === 0 && - exports.autoUpdate && - assetEntry.localChecksum !== assetEntry.repoChecksum - ) { - //console.log('µBlock> readRepoOnlyAsset("%s") / onRepoMetaReady(): repo has newer version', path); - getTextFileFromURL(repositoryURL, onRepoFileLoaded, onRepoFileError); - return; - } - - // Load from cache - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); + if ( details.content !== '' ) { + return reportBack(details.content); + } + getAssetSourceRegistry(function(registry) { + assetDetails = registry[assetKey] || {}; + if ( typeof assetDetails.contentURL === 'string' ) { + contentURLs = [ assetDetails.contentURL ]; + } else if ( Array.isArray(assetDetails.contentURL) ) { + contentURLs = assetDetails.contentURL.slice(0); + } else { + contentURLs = []; + } + onContentNotLoaded(); + }); }; - getRepoMetadata(onRepoMetaReady); -}; - -/******************************************************************************/ - -// Asset doesn't exist. Just for symmetry purpose. - -var readNilAsset = function(path, callback) { - callback({ - 'path': path, - 'content': '', - 'error': 'Error: asset not found' - }); + assetCacheRead(assetKey, onCachedContentLoaded); }; /******************************************************************************/ -// An external asset: -// Path --> starts with 'http' -// External --> https://..., http://... -// Cache --> has expiration timestamp (in cache) +var getRemote = function(assetKey, callback) { + var assetDetails = {}, + contentURLs, + contentURL; -var readExternalAsset = function(path, callback) { var reportBack = function(content, err) { - var details = { - 'path': path, - 'content': content - }; + var details = { assetKey: assetKey, content: content }; if ( err ) { - details.error = err; + details.error = assetDetails.lastError = err; + } else { + assetDetails.lastError = undefined; } callback(details); }; - var onCachedContentLoaded = function(details) { - //console.log('µBlock> readExternalAsset("%s") / onCachedContentLoaded()', path); - reportBack(details.content); - }; - - var onCachedContentError = function() { - console.error('µBlock> readExternalAsset("%s") / onCachedContentError()', path); - reportBack('', 'Error'); - }; - - var onExternalFileLoaded = function() { - // https://github.com/chrisaljoudi/uBlock/issues/708 - // A successful download should never return an empty file: turn this - // into an error condition. + var onRemoteContentLoaded = function() { if ( stringIsNotEmpty(this.responseText) === false ) { - onExternalFileError(); + registerAssetSource(assetKey, { error: { time: Date.now(), error: 'No content' } }); + tryLoading(); return; } - //console.log('µBlock> readExternalAsset("%s") / onExternalFileLoaded1()', path); - cachedAssetsManager.save(path, this.responseText); + assetCacheWrite(assetKey, { + content: this.responseText, + url: contentURL + }); + registerAssetSource(assetKey, { error: undefined }); reportBack(this.responseText); }; - var onExternalFileError = function() { - console.error(errorCantConnectTo.replace('{{url}}', path)); - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); + var onRemoteContentError = function() { + registerAssetSource(assetKey, { error: { time: Date.now(), error: this.statusText } }); + tryLoading(); }; - var onCacheMetaReady = function(entries) { - // Fetch from remote if: - // - Not in cache OR - // - // - Auto-update enabled AND in cache but obsolete - var timestamp = entries[path]; - var notInCache = typeof timestamp !== 'number'; - var updateCache = exports.remoteFetchBarrier === 0 && - exports.autoUpdate && - cacheIsObsolete(timestamp); - if ( notInCache || updateCache ) { - getTextFileFromURL(path, onExternalFileLoaded, onExternalFileError); - return; + var tryLoading = function() { + while ( (contentURL = contentURLs.shift()) ) { + if ( reIsExternalPath.test(contentURL) ) { break; } } - - // In cache - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); - }; - - cachedAssetsManager.entries(onCacheMetaReady); -}; - -/******************************************************************************/ - -// User data: -// Path --> starts with 'assets/user/' -// Cache --> whatever user saved - -var readUserAsset = function(path, callback) { - // TODO: remove when confident all users no longer have their custom - // filters saved into vAPI.cacheStorage. - var onCachedContentLoaded = function(details) { - saveUserAsset(path, details.content); - //console.log('µBlock.assets/readUserAsset("%s")/onCachedContentLoaded()', path); - callback({ 'path': path, 'content': details.content }); - }; - - var onCachedContentError = function() { - saveUserAsset(path, ''); - //console.log('µBlock.assets/readUserAsset("%s")/onCachedContentError()', path); - callback({ 'path': path, 'content': '' }); - }; - - var onLoaded = function(bin) { - var content = bin && bin[path]; - if ( typeof content === 'string' ) { - callback({ 'path': path, 'content': content }); - return; - } - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); - }; - - vAPI.storage.get(path, onLoaded); -}; - -var saveUserAsset = function(path, content, callback) { - var bin = {}; - bin[path] = content; - var onSaved = function() { - // Saving over an existing asset must be seen as removing an - // existing asset and adding a new one. - if ( onAssetRemovedListener instanceof Function ) { - onAssetRemovedListener([ path ]); - } - if ( callback instanceof Function ) { - callback({ path: path, content: content }); + if ( !contentURL ) { + return reportBack('', 'E_NOTFOUND'); } + getTextFileFromURL(contentURL, onRemoteContentLoaded, onRemoteContentError); }; - vAPI.storage.set(bin, onSaved); -}; -/******************************************************************************/ - -// Asset available only from the cache. -// Cache data: -// Path --> starts with 'cache://' -// Cache --> whatever - -var readCacheAsset = function(path, callback) { - var onCachedContentLoaded = function(details) { - //console.log('µBlock.assets/readCacheAsset("%s")/onCachedContentLoaded()', path); - callback({ 'path': path, 'content': details.content }); - }; - - var onCachedContentError = function() { - //console.log('µBlock.assets/readCacheAsset("%s")/onCachedContentError()', path); - callback({ 'path': path, 'content': '' }); - }; - - cachedAssetsManager.load(path, onCachedContentLoaded, onCachedContentError); -}; - -/******************************************************************************/ - -// Assets -// -// A copy of an asset from an external source shipped with the extension: -// Path --> starts with 'assets/(thirdparties|ublock)/', with a home URL -// External --> -// Repository --> has checksum (to detect obsolescence) -// Cache --> has expiration timestamp (to detect obsolescence) -// Local --> install time version -// -// An important asset shipped with the extension (usually small, or doesn't -// change often): -// Path --> starts with 'assets/(thirdparties|ublock)/', without a home URL -// Repository --> has checksum (to detect obsolescence or data corruption) -// Cache --> whatever from above -// Local --> install time version -// -// An external filter list: -// Path --> starts with 'http' -// External --> -// Cache --> has expiration timestamp (to detect obsolescence) -// -// User data: -// Path --> starts with 'assets/user/' -// Cache --> whatever user saved -// -// When a checksum is present, it is used to determine whether the asset -// needs to be updated. -// When an expiration timestamp is present, it is used to determine whether -// the asset needs to be updated. -// -// If no update required, an asset if first fetched from the cache. If the -// asset is not cached it is fetched from the closest location: local for -// an asset shipped with the extension, external for an asset not shipped -// with the extension. - -exports.get = function(path, callback) { - - if ( reIsUserPath.test(path) ) { - readUserAsset(path, callback); - return; - } - - if ( reIsCachePath.test(path) ) { - readCacheAsset(path, callback); - return; - } - - if ( reIsExternalPath.test(path) ) { - readExternalAsset(path, callback); - return; - } - - var onRepoMetaReady = function(meta) { - var assetEntry = meta.entries[path]; - - // Asset doesn't exist - if ( assetEntry === undefined ) { - readNilAsset(path, callback); - return; - } - - // Asset is repo copy of external content - if ( stringIsNotEmpty(homeURLs[path]) ) { - readRepoCopyAsset(path, callback); - return; + getAssetSourceRegistry(function(registry) { + assetDetails = registry[assetKey] || {}; + if ( typeof assetDetails.contentURL === 'string' ) { + contentURLs = [ assetDetails.contentURL ]; + } else if ( Array.isArray(assetDetails.contentURL) ) { + contentURLs = assetDetails.contentURL.slice(0); + } else { + contentURLs = []; } - - // Asset is repo only - readRepoOnlyAsset(path, callback); - }; - - getRepoMetadata(onRepoMetaReady); + tryLoading(); + }); }; -// https://www.youtube.com/watch?v=98y0Q7nLGWk - -/******************************************************************************/ - -exports.getLocal = readLocalFile; - /******************************************************************************/ -exports.put = function(path, content, callback) { - if ( reIsUserPath.test(path) ) { - saveUserAsset(path, content, callback); - return; +api.put = function(assetKey, content, callback) { + if ( reIsUserAsset.test(assetKey) ) { + return saveUserAsset(assetKey, content, callback); } - - cachedAssetsManager.save(path, content, callback); -}; - -/******************************************************************************/ - -exports.rmrf = function() { - cachedAssetsManager.rmrf(); -}; - -/******************************************************************************/ - -exports.rename = function(from, to, callback) { - var done = function() { - if ( typeof callback === 'function' ) { - callback(); - } - }; - - var fromLoaded = function(details) { - cachedAssetsManager.remove(from); - cachedAssetsManager.save(to, details.content, callback); - done(); - }; - - var toLoaded = function(details) { - // `to` already exists: do nothing - if ( details.content !== '' ) { - return done(); - } - cachedAssetsManager.load(from, fromLoaded); - }; - - // If `to` content already exists, do nothing. - cachedAssetsManager.load(to, toLoaded); + assetCacheWrite(assetKey, content, callback); }; /******************************************************************************/ -exports.metadata = function(callback) { - var out = {}; - - // https://github.com/chrisaljoudi/uBlock/issues/186 - // We need to check cache obsolescence when both cache and repo meta data - // has been gathered. - var checkCacheObsolescence = function() { - var entry, homeURL; - for ( var path in out ) { - if ( out.hasOwnProperty(path) === false ) { - continue; - } - entry = out[path]; - // https://github.com/gorhill/uBlock/issues/528 - // Not having a homeURL property does not mean the filter list - // is not external. - homeURL = reIsExternalPath.test(path) ? path : homeURLs[path]; - entry.cacheObsolete = stringIsNotEmpty(homeURL) && - cacheIsObsolete(entry.lastModified); - } - callback(out); - }; +api.metadata = function(callback) { + var assetRegistryReady = false, + cacheRegistryReady = false; - var onRepoMetaReady = function(meta) { - var entries = meta.entries; - var entryRepo, entryOut; - for ( var path in entries ) { - if ( entries.hasOwnProperty(path) === false ) { - continue; - } - entryRepo = entries[path]; - entryOut = out[path]; - if ( entryOut === undefined ) { - entryOut = out[path] = {}; + var onReady = function() { + var assetDict = JSON.parse(JSON.stringify(assetSourceRegistry)), + cacheDict = assetCacheRegistry, + assetEntry, cacheEntry, + now = Date.now(), obsoleteAfter; + for ( var assetKey in assetDict ) { + assetEntry = assetDict[assetKey]; + cacheEntry = cacheDict[assetKey]; + if ( cacheEntry ) { + assetEntry.cached = true; + assetEntry.writeTime = cacheEntry.writeTime; + obsoleteAfter = cacheEntry.writeTime + assetEntry.updateAfter * 86400000; + assetEntry.obsolete = obsoleteAfter < now; + assetEntry.remoteURL = cacheEntry.remoteURL; + } else { + assetEntry.writeTime = 0; + obsoleteAfter = 0; + assetEntry.obsolete = true; } - entryOut.localChecksum = entryRepo.localChecksum; - entryOut.repoChecksum = entryRepo.repoChecksum; - entryOut.homeURL = homeURLs[path] || ''; - entryOut.supportURL = entryRepo.supportURL || ''; - entryOut.repoObsolete = entryOut.localChecksum !== entryOut.repoChecksum; } - checkCacheObsolescence(); + callback(assetDict); }; - var onCacheMetaReady = function(entries) { - var entryOut; - for ( var path in entries ) { - if ( entries.hasOwnProperty(path) === false ) { - continue; - } - entryOut = out[path]; - if ( entryOut === undefined ) { - entryOut = out[path] = {}; - } - entryOut.lastModified = entries[path]; - // User data is not literally cache data - if ( reIsUserPath.test(path) ) { - continue; - } - entryOut.cached = true; - if ( reIsExternalPath.test(path) ) { - entryOut.homeURL = path; - } - } - getRepoMetadata(onRepoMetaReady); - }; + getAssetSourceRegistry(function() { + assetRegistryReady = true; + if ( cacheRegistryReady ) { onReady(); } + }); - cachedAssetsManager.entries(onCacheMetaReady); + getAssetCacheRegistry(function() { + cacheRegistryReady = assetCacheRegistry; + if ( assetRegistryReady ) { onReady(); } + }); }; /******************************************************************************/ -exports.purge = function(pattern, before) { - cachedAssetsManager.remove(pattern, before); +api.purge = function(pattern, callback) { + assetCacheMarkAsDirty(pattern, callback); }; -exports.purgeCacheableAsset = function(pattern, before) { - cachedAssetsManager.remove(pattern, before); - lastRepoMetaTimestamp = 0; +api.remove = function(pattern, callback) { + assetCacheRemove(pattern, callback); }; -exports.purgeAll = function(callback) { - cachedAssetsManager.removeAll(callback); - lastRepoMetaTimestamp = 0; +api.rmrf = function() { + assetCacheRemove(/./); }; /******************************************************************************/ -exports.onAssetRemoved = { - addListener: function(callback) { - onAssetRemovedListener = callback instanceof Function ? callback : null; - } -}; +// Asset updater area. +var updaterStatus, + updaterTimer, + updaterAssetDelayDefault = 120000, + updaterAssetDelay = updaterAssetDelayDefault, + updaterUpdated = [], + updaterFetched = new Set(); -/******************************************************************************/ - -return exports; - -})(); - -/******************************************************************************/ -/******************************************************************************/ - -µBlock.assetUpdater = (function() { - -/******************************************************************************/ - -var µb = µBlock; - -var updateDaemonTimer = null; -var autoUpdateDaemonTimerPeriod = 11 * 60 * 1000; // 11 minutes -var manualUpdateDaemonTimerPeriod = 5 * 1000; // 5 seconds - -var updateCycleFirstPeriod = 7 * 60 * 1000; // 7 minutes -var updateCycleNextPeriod = 11 * 60 * 60 * 1000; // 11 hours -var updateCycleTime = 0; - -var toUpdate = {}; -var toUpdateCount = 0; -var updated = {}; -var updatedCount = 0; -var metadata = null; - -var onStartListener = null; -var onCompletedListener = null; -var onAssetUpdatedListener = null; - -var exports = { - manualUpdate: false, - manualUpdateProgress: { - value: 0, - text: null - } -}; - -/******************************************************************************/ - -var onOneUpdated = function(details) { - // Resource fetched, we can safely restart the daemon. - scheduleUpdateDaemon(); - - var path = details.path; - if ( details.error ) { - manualUpdateNotify(false, updatedCount / (updatedCount + toUpdateCount)); - //console.debug('µBlock.assetUpdater/onOneUpdated: "%s" failed', path); - return; - } - - //console.debug('µBlock.assetUpdater/onOneUpdated: "%s"', path); - updated[path] = true; - updatedCount += 1; - - if ( typeof onAssetUpdatedListener === 'function' ) { - onAssetUpdatedListener(details); - } - - manualUpdateNotify(false, updatedCount / (updatedCount + toUpdateCount + 1)); +var updateFirst = function() { + updaterStatus = 'updating'; + updaterFetched.clear(); + updaterUpdated = []; + fireNotification('before-assets-updated'); + updateNext(); }; -/******************************************************************************/ - -var updateOne = function() { - // Because this can be called from outside the daemon's main loop - µb.assets.autoUpdate = µb.userSettings.autoUpdate || exports.manualUpdate; - - var metaEntry; - var updatingCount = 0; - var updatingText = null; +var updateNext = function() { + var assetDict, cacheDict; - for ( var path in toUpdate ) { - if ( toUpdate.hasOwnProperty(path) === false ) { - continue; + // This will remove a cached asset when it's no longer in use. + var garbageCollectOne = function(assetKey) { + var cacheEntry = cacheDict[assetKey]; + if ( cacheEntry && cacheEntry.readTime < assetCacheRegistryStartTime ) { + assetCacheRemove(assetKey); } - if ( toUpdate[path] !== true ) { - continue; - } - toUpdate[path] = false; - toUpdateCount -= 1; - if ( metadata.hasOwnProperty(path) === false ) { - continue; - } - metaEntry = metadata[path]; - if ( !metaEntry.cacheObsolete && !metaEntry.repoObsolete ) { - continue; - } - - // Will restart the update daemon once the resource is received: the - // fetching of a resource may take some time, possibly beyond the - // next scheduled daemon cycle, so this ensure the daemon won't do - // anything else before the resource is fetched (or times out). - suspendUpdateDaemon(); - - //console.debug('µBlock.assetUpdater/updateOne: assets.get("%s")', path); - µb.assets.get(path, onOneUpdated); - updatingCount = 1; - updatingText = metaEntry.homeURL || path; - break; - } - - manualUpdateNotify( - false, - (updatedCount + updatingCount/2) / (updatedCount + toUpdateCount + updatingCount + 1), - updatingText - ); -}; - -/******************************************************************************/ - -// Update one asset, fetch metadata if not done yet. - -var safeUpdateOne = function() { - if ( metadata !== null ) { - updateOne(); - return; - } - - // Because this can be called from outside the daemon's main loop - µb.assets.autoUpdate = µb.userSettings.autoUpdate || exports.manualUpdate; - - var onMetadataReady = function(response) { - scheduleUpdateDaemon(); - metadata = response; - updateOne(); }; - suspendUpdateDaemon(); - µb.assets.metadata(onMetadataReady); -}; - -/******************************************************************************/ - -var safeStartListener = function(callback) { - // Because this can be called from outside the daemon's main loop - µb.assets.autoUpdate = µb.userSettings.autoUpdate || exports.manualUpdate; - - var onStartListenerDone = function(assets) { - scheduleUpdateDaemon(); - assets = assets || {}; - for ( var path in assets ) { - if ( assets.hasOwnProperty(path) === false ) { + var findOne = function() { + var now = Date.now(), + assetEntry, cacheEntry; + for ( var assetKey in assetDict ) { + assetEntry = assetDict[assetKey]; + if ( assetEntry.hasRemoteURL !== true ) { continue; } + if ( updaterFetched.has(assetKey) ) { continue; } + cacheEntry = cacheDict[assetKey]; + if ( cacheEntry && (cacheEntry.writeTime + assetEntry.updateAfter * 86400000) > now ) { continue; } - if ( toUpdate.hasOwnProperty(path) ) { - continue; + if ( fireNotification('before-asset-updated', { assetKey: assetKey }) !== false ) { + return assetKey; } - //console.debug('assets.js > µBlock.assetUpdater/safeStartListener: "%s"', path); - toUpdate[path] = true; - toUpdateCount += 1; - } - if ( typeof callback === 'function' ) { - callback(); + garbageCollectOne(assetKey); } }; - if ( typeof onStartListener === 'function' ) { - suspendUpdateDaemon(); - onStartListener(onStartListenerDone); - } else { - onStartListenerDone(null); - } -}; - -/******************************************************************************/ - -var updateDaemon = function() { - updateDaemonTimer = null; - scheduleUpdateDaemon(); - - µb.assets.autoUpdate = µb.userSettings.autoUpdate || exports.manualUpdate; - - if ( µb.assets.autoUpdate !== true ) { - return; - } - - // Start an update cycle? - if ( updateCycleTime !== 0 ) { - if ( Date.now() >= updateCycleTime ) { - //console.debug('µBlock.assetUpdater/updateDaemon: update cycle started'); - reset(); - safeStartListener(); + var updatedOne = function(details) { + if ( details.content !== '' ) { + updaterUpdated.push(details.assetKey); + if ( details.assetKey === 'assets.json' ) { + updateAssetSourceRegistry(details.content); + } } - return; - } - - // Any asset to update? - if ( toUpdateCount !== 0 ) { - safeUpdateOne(); - return; - } - // Nothing left to update - - // In case of manual update, fire progress notifications - manualUpdateNotify(true, 1, ''); - - // If anything was updated, notify listener - if ( updatedCount !== 0 ) { - if ( typeof onCompletedListener === 'function' ) { - //console.debug('µBlock.assetUpdater/updateDaemon: update cycle completed'); - onCompletedListener({ - updated: JSON.parse(JSON.stringify(updated)), // give callee its own safe copy - updatedCount: updatedCount - }); + if ( findOne() !== undefined ) { + vAPI.setTimeout(updateNext, updaterAssetDelay); + } else { + updateDone(); } - } - - // Schedule next update cycle - if ( updateCycleTime === 0 ) { - reset(); - //console.debug('µBlock.assetUpdater/updateDaemon: update cycle re-scheduled'); - updateCycleTime = Date.now() + updateCycleNextPeriod; - } -}; - -/******************************************************************************/ - -var scheduleUpdateDaemon = function() { - if ( updateDaemonTimer !== null ) { - clearTimeout(updateDaemonTimer); - } - updateDaemonTimer = vAPI.setTimeout( - updateDaemon, - exports.manualUpdate ? manualUpdateDaemonTimerPeriod : autoUpdateDaemonTimerPeriod - ); -}; - -var suspendUpdateDaemon = function() { - if ( updateDaemonTimer !== null ) { - clearTimeout(updateDaemonTimer); - updateDaemonTimer = null; - } -}; - -scheduleUpdateDaemon(); - -/******************************************************************************/ - -var reset = function() { - toUpdate = {}; - toUpdateCount = 0; - updated = {}; - updatedCount = 0; - updateCycleTime = 0; - metadata = null; -}; - -/******************************************************************************/ - -var manualUpdateNotify = function(done, value, text) { - if ( exports.manualUpdate === false ) { - return; - } + }; - exports.manualUpdate = !done; - exports.manualUpdateProgress.value = value || 0; - if ( typeof text === 'string' ) { - exports.manualUpdateProgress.text = text; - } + var updateOne = function() { + var assetKey = findOne(); + if ( assetKey === undefined ) { + return updateDone(); + } + updaterFetched.add(assetKey); + getRemote(assetKey, updatedOne); + }; - vAPI.messaging.broadcast({ - what: 'forceUpdateAssetsProgress', - done: !exports.manualUpdate, - progress: exports.manualUpdateProgress, - updatedCount: updatedCount + getAssetSourceRegistry(function(dict) { + assetDict = dict; + if ( !cacheDict ) { return; } + updateOne(); }); - // When manually updating, whatever launched the manual update is - // responsible to launch a reload of the filter lists. - if ( exports.manualUpdate !== true ) { - reset(); - } + getAssetCacheRegistry(function(dict) { + cacheDict = dict; + if ( !assetDict ) { return; } + updateOne(); + }); }; -/******************************************************************************/ - -// Manual update: just a matter of forcing the update daemon to work on a -// tighter schedule. - -exports.force = function() { - if ( exports.manualUpdate ) { - return; - } - - reset(); - - exports.manualUpdate = true; - - var onStartListenerDone = function() { - if ( toUpdateCount === 0 ) { - updateCycleTime = Date.now() + updateCycleNextPeriod; - manualUpdateNotify(true, 1); - } else { - manualUpdateNotify(false, 0); - safeUpdateOne(); - } - }; - - safeStartListener(onStartListenerDone); +var updateDone = function() { + var assetKeys = updaterUpdated.slice(0); + updaterFetched.clear(); + updaterUpdated = []; + updaterStatus = undefined; + updaterAssetDelay = updaterAssetDelayDefault; + fireNotification('after-assets-updated', { assetKeys: assetKeys }); }; -/******************************************************************************/ - -exports.onStart = { - addEventListener: function(callback) { - onStartListener = callback || null; - if ( typeof onStartListener === 'function' ) { - updateCycleTime = Date.now() + updateCycleFirstPeriod; +api.updateStart = function(details) { + var oldUpdateDelay = updaterAssetDelay, + newUpdateDelay = details.delay || updaterAssetDelayDefault; + updaterAssetDelay = Math.min(oldUpdateDelay, newUpdateDelay); + if ( updaterStatus !== undefined ) { + if ( newUpdateDelay < oldUpdateDelay ) { + clearTimeout(updaterTimer); + updaterTimer = vAPI.setTimeout(updateNext, updaterAssetDelay); } + return; } + updateFirst(); }; -/******************************************************************************/ - -exports.onAssetUpdated = { - addEventListener: function(callback) { - onAssetUpdatedListener = callback || null; +api.updateStop = function() { + if ( updaterTimer ) { + clearTimeout(updaterTimer); + updaterTimer = undefined; } -}; - -/******************************************************************************/ - -exports.onCompleted = { - addEventListener: function(callback) { - onCompletedListener = callback || null; + if ( updaterStatus !== undefined ) { + updateDone(); } }; /******************************************************************************/ -// Typically called when an update has been forced. - -exports.restart = function() { - reset(); - updateCycleTime = Date.now() + updateCycleNextPeriod; -}; - -/******************************************************************************/ - -// Call when disabling uBlock, to ensure it doesn't stick around as a detached -// window object in Firefox. - -exports.shutdown = function() { - suspendUpdateDaemon(); - reset(); -}; +return api; /******************************************************************************/ -return exports; - })(); /******************************************************************************/ diff --git a/src/js/background.js b/src/js/background.js index 00b82a1c64997..791e56be94d6f 100644 --- a/src/js/background.js +++ b/src/js/background.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 @@ -19,20 +19,16 @@ Home: https://github.com/gorhill/uBlock */ -/* exported µBlock */ - 'use strict'; /******************************************************************************/ -var µBlock = (function() { +var µBlock = (function() { // jshint ignore:line /******************************************************************************/ var oneSecond = 1000; var oneMinute = 60 * oneSecond; -var oneHour = 60 * oneMinute; -// var oneDay = 24 * oneHour; /******************************************************************************/ @@ -71,8 +67,12 @@ return { }, hiddenSettingsDefault: { + assetFetchTimeout: 30, + autoUpdateAssetFetchPeriod: 120, + autoUpdatePeriod: 7, ignoreRedirectFilters: false, ignoreScriptInjectFilters: false, + manualUpdateAssetFetchPeriod: 2000, popupFontSize: 'unset', suspendTabsUntilReady: false }, @@ -119,92 +119,15 @@ return { lastBackupTime: 0 }, - // EasyList, EasyPrivacy and many others have an 4-day update period, - // as per list headers. - updateAssetsEvery: 97 * oneHour, - projectServerRoot: 'https://raw.githubusercontent.com/gorhill/uBlock/master/', - userFiltersPath: 'assets/user/filters.txt', - pslPath: 'assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat', - - // permanent lists - permanentLists: { - // User - 'assets/user/filters.txt': { - group: 'default' - }, - // uBlock - 'assets/ublock/filters.txt': { - title: 'uBlock filters', - group: 'default' - }, - 'assets/ublock/privacy.txt': { - title: 'uBlock filters – Privacy', - group: 'default' - }, - 'assets/ublock/unbreak.txt': { - title: 'uBlock filters – Unbreak', - group: 'default' - }, - 'assets/ublock/badware.txt': { - title: 'uBlock filters – Badware risks', - group: 'default', - supportURL: 'https://github.com/gorhill/uBlock/wiki/Badware-risks', - instructionURL: 'https://github.com/gorhill/uBlock/wiki/Badware-risks' - }, - 'assets/ublock/experimental.txt': { - title: 'uBlock filters – Experimental', - group: 'default', - off: true, - supportURL: 'https://github.com/gorhill/uBlock/wiki/Experimental-filters', - instructionURL: 'https://github.com/gorhill/uBlock/wiki/Experimental-filters' - } - }, + // Allows to fully customize uBO's assets, typically set through admin + // settings. The content of 'assets.json' will also tell which filter + // lists to enable by default when uBO is first installed. + assetsBootstrapLocation: 'assets/assets.json', - // current lists - remoteBlacklists: {}, - oldListToNewListMap: { - "assets/thirdparties/adblock.gardar.net/is.abp.txt": "http://adblock.gardar.net/is.abp.txt", - "assets/thirdparties/adblock.schack.dk/block.txt": "https://adblock.dk/block.csv", - "https://adblock.schack.dk/block.txt": "https://adblock.dk/block.csv", - "assets/thirdparties/dl.dropboxusercontent.com/u/1289327/abpxfiles/filtri.txt": "https://dl.dropboxusercontent.com/u/1289327/abpxfiles/filtri.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/advblock.txt": "https://easylist-downloads.adblockplus.org/advblock.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/bitblock.txt": "https://easylist-downloads.adblockplus.org/bitblock.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/easylist_noelemhide.txt": "https://easylist-downloads.adblockplus.org/easylist_noelemhide.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/easylistchina.txt": "https://easylist-downloads.adblockplus.org/easylistchina.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/easylistdutch.txt": "https://easylist-downloads.adblockplus.org/easylistdutch.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/easylistgermany.txt": "https://easylist-downloads.adblockplus.org/easylistgermany.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/easylistitaly.txt": "https://easylist-downloads.adblockplus.org/easylistitaly.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/fanboy-annoyance.txt": "https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/fanboy-social.txt": "https://easylist-downloads.adblockplus.org/fanboy-social.txt", - "assets/thirdparties/easylist-downloads.adblockplus.org/liste_fr.txt": "https://easylist-downloads.adblockplus.org/liste_fr.txt", - "assets/thirdparties/gitorious.org/adblock-latvian/adblock-latvian/raw/master_lists/latvian-list.txt": "https://notabug.org/latvian-list/adblock-latvian/raw/master/lists/latvian-list.txt", - "assets/thirdparties/home.fredfiber.no/langsholt/adblock.txt": "http://home.fredfiber.no/langsholt/adblock.txt", - "assets/thirdparties/hosts-file.net/ad-servers": "http://hosts-file.net/.%5Cad_servers.txt", - "assets/thirdparties/http://www.certyficate.it/adblock/adblock.txt": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", - "assets/thirdparties/liste-ar-adblock.googlecode.com/hg/Liste_AR.txt": "https://liste-ar-adblock.googlecode.com/hg/Liste_AR.txt", - "assets/thirdparties/margevicius.lt/easylistlithuania.txt": "http://margevicius.lt/easylistlithuania.txt", - "assets/thirdparties/mirror1.malwaredomains.com/files/immortal_domains.txt": "http://malwaredomains.lehigh.edu/files/immortal_domains.txt", - "assets/thirdparties/raw.githubusercontent.com/AdBlockPlusIsrael/EasyListHebrew/master/EasyListHebrew.txt": "https://raw.githubusercontent.com/AdBlockPlusIsrael/EasyListHebrew/master/EasyListHebrew.txt", - "assets/thirdparties/raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt": "https://raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt", - "assets/thirdparties/raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt": "https://raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt", - "assets/thirdparties/raw.githubusercontent.com/szpeter80/hufilter/master/hufilter.txt": "https://raw.githubusercontent.com/szpeter80/hufilter/master/hufilter.txt", - "assets/thirdparties/raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt": "https://raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt", - "assets/thirdparties/someonewhocares.org/hosts/hosts": "http://someonewhocares.org/hosts/hosts", - "assets/thirdparties/spam404bl.com/spam404scamlist.txt": "https://spam404bl.com/spam404scamlist.txt", - "assets/thirdparties/stanev.org/abp/adblock_bg.txt": "http://stanev.org/abp/adblock_bg.txt", - "assets/thirdparties/winhelp2002.mvps.org/hosts.txt": "http://winhelp2002.mvps.org/hosts.txt", - "assets/thirdparties/www.fanboy.co.nz/enhancedstats.txt": "https://www.fanboy.co.nz/enhancedstats.txt", - "assets/thirdparties/www.fanboy.co.nz/fanboy-antifacebook.txt": "https://www.fanboy.co.nz/fanboy-antifacebook.txt", - "assets/thirdparties/www.fanboy.co.nz/fanboy-korean.txt": "https://www.fanboy.co.nz/fanboy-korean.txt", - "assets/thirdparties/www.fanboy.co.nz/fanboy-swedish.txt": "https://www.fanboy.co.nz/fanboy-swedish.txt", - "assets/thirdparties/www.fanboy.co.nz/fanboy-ultimate.txt": "https://www.fanboy.co.nz/r/fanboy-ultimate.txt", - "assets/thirdparties/www.fanboy.co.nz/fanboy-vietnam.txt": "https://www.fanboy.co.nz/fanboy-vietnam.txt", - "assets/thirdparties/www.void.gr/kargig/void-gr-filters.txt": "https://www.void.gr/kargig/void-gr-filters.txt", - "assets/thirdparties/www.zoso.ro/pages/rolist.txt": "", - "https://iadb.azurewebsites.net/Finland_adb.txt": "http://adb.juvander.net/Finland_adb.txt", - "https://www.certyficate.it/adblock/adblock.txt": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", - "https://raw.githubusercontent.com/heradhis/indonesianadblockrules/master/subscriptions/abpindo.txt": "https://raw.githubusercontent.com/ABPindo/indonesianadblockrules/master/subscriptions/abpindo.txt" - }, + userFiltersPath: 'user-filters', + pslAssetKey: 'public_suffix_list.dat', + + availableFilterLists: {}, selfieAfter: 23 * oneMinute, diff --git a/src/js/logger.js b/src/js/logger.js index b9a7bee328748..700521ab5eada 100644 --- a/src/js/logger.js +++ b/src/js/logger.js @@ -19,15 +19,13 @@ Home: https://github.com/gorhill/uBlock */ -/* global µBlock */ +'use strict'; /******************************************************************************/ /******************************************************************************/ µBlock.logger = (function() { -'use strict'; - /******************************************************************************/ /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 360fc7e0f39fd..4cc466f7c4314 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-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 @@ -77,7 +77,7 @@ var onMessage = function(request, sender, callback) { return; case 'reloadAllFilters': - µb.reloadAllFilters(callback); + µb.loadFilterLists(); return; case 'scriptlet': @@ -121,7 +121,8 @@ var onMessage = function(request, sender, callback) { break; case 'forceUpdateAssets': - µb.assetUpdater.force(); + µb.scheduleAssetUpdater(0); + µb.assets.updateStart({ delay: µb.hiddenSettings.manualUpdateAssetFetchPeriod || 2000 }); break; case 'getAppData': @@ -160,7 +161,7 @@ var onMessage = function(request, sender, callback) { break; case 'selectFilterLists': - µb.selectFilterLists(request.switches); + µb.saveSelectedFilterLists(request.keys, request.append); break; case 'setWhitelist': @@ -753,7 +754,7 @@ var backupUserData = function(callback) { timeStamp: Date.now(), version: vAPI.app.version, userSettings: µb.userSettings, - filterLists: {}, + selectedFilterLists: [], hiddenSettingsString: µb.stringFromHiddenSettings(), netWhitelist: µb.stringFromWhitelist(µb.netWhitelist), dynamicFilteringString: µb.permanentFirewall.toString(), @@ -762,8 +763,17 @@ var backupUserData = function(callback) { userFilters: '' }; - var onSelectedListsReady = function(filterLists) { - userData.filterLists = filterLists; + var onSelectedListsReady = function(selectedFilterLists) { + userData.selectedFilterLists = selectedFilterLists; + + // 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. + // >>>>>>>> + userData.filterLists = µb.oldDataFromNewListKeys(selectedFilterLists); + // <<<<<<<< var filename = vAPI.i18n('aboutBackupFilename') .replace('{{datetime}}', µb.dateNowToSensibleString()) @@ -773,17 +783,15 @@ var backupUserData = function(callback) { 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(userData, null, ' ')), 'filename': filename }); - µb.restoreBackupSettings.lastBackupFile = filename; µb.restoreBackupSettings.lastBackupTime = Date.now(); vAPI.storage.set(µb.restoreBackupSettings); - getLocalData(callback); }; var onUserFiltersReady = function(details) { userData.userFilters = details.content; - µb.extractSelectedFilterLists(onSelectedListsReady); + µb.loadSelectedFilterLists(onSelectedListsReady); }; µb.assets.get(µb.userFiltersPath, onUserFiltersReady); @@ -791,32 +799,32 @@ var backupUserData = function(callback) { var restoreUserData = function(request) { var userData = request.userData; - var countdown = 8; - var onCountdown = function() { - countdown -= 1; - if ( countdown === 0 ) { - vAPI.app.restart(); - } - }; var onAllRemoved = function() { - // Be sure to adjust `countdown` if adding/removing anything below - µb.keyvalSetOne('version', userData.version); µBlock.saveLocalSettings(); - vAPI.storage.set(userData.userSettings, onCountdown); - µb.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown); + vAPI.storage.set(userData.userSettings); µb.hiddenSettingsFromString(userData.hiddenSettingsString || ''); - µb.keyvalSetOne('netWhitelist', userData.netWhitelist || '', onCountdown); - µb.keyvalSetOne('dynamicFilteringString', userData.dynamicFilteringString || '', onCountdown); - µb.keyvalSetOne('urlFilteringString', userData.urlFilteringString || '', onCountdown); - µb.keyvalSetOne('hostnameSwitchesString', userData.hostnameSwitchesString || '', onCountdown); - µb.assets.put(µb.userFiltersPath, userData.userFilters, onCountdown); vAPI.storage.set({ + netWhitelist: userData.netWhitelist || '', + dynamicFilteringString: userData.dynamicFilteringString || '', + urlFilteringString: userData.urlFilteringString || '', + hostnameSwitchesString: userData.hostnameSwitchesString || '', lastRestoreFile: request.file || '', lastRestoreTime: Date.now(), lastBackupFile: '', lastBackupTime: 0 - }, onCountdown); + }); + µ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. + if ( Array.isArray(userData.selectedFilterLists) ) { + µb.saveSelectedFilterLists(userData.selectedFilterLists); + } else if ( userData.filterLists instanceof Object ) { + µb.saveSelectedFilterLists(µb.newListKeysFromOldData(userData.filterLists)); + } + + vAPI.app.restart(); }; // https://github.com/chrisaljoudi/uBlock/issues/1102 @@ -848,9 +856,7 @@ var prepListEntries = function(entries) { var µburi = µb.URI; var entry, hn; for ( var k in entries ) { - if ( entries.hasOwnProperty(k) === false ) { - continue; - } + if ( entries.hasOwnProperty(k) === false ) { continue; } entry = entries[k]; if ( typeof entry.supportURL === 'string' && entry.supportURL !== '' ) { entry.supportName = µburi.hostnameFromURI(entry.supportURL); @@ -869,16 +875,14 @@ var getLists = function(callback) { cache: null, parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters, cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(), - current: µb.remoteBlacklists, + current: µb.availableFilterLists, ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters, - manualUpdate: false, netFilterCount: µb.staticNetFilteringEngine.getFilterCount(), - userFiltersPath: µb.userFiltersPath + userFiltersPath: µb.userFiltersPath, + aliases: µb.assets.listKeyAliases }; var onMetadataReady = function(entries) { r.cache = entries; - r.manualUpdate = µb.assetUpdater.manualUpdate; - r.manualUpdateProgress = µb.assetUpdater.manualUpdateProgress; prepListEntries(r.cache); callback(r); }; @@ -952,9 +956,6 @@ var onMessage = function(request, sender, callback) { case 'getLocalData': return getLocalData(callback); - case 'purgeAllCaches': - return µb.assets.purgeAll(callback); - case 'readUserFilters': return µb.loadUserFilters(callback); @@ -973,8 +974,18 @@ var onMessage = function(request, sender, callback) { response = getRules(); break; + case 'purgeAllCaches': + if ( request.hard ) { + µb.assets.remove(/./); + } else { + µb.assets.remove(/compiled\//); + µb.assets.purge(/./); + } + break; + case 'purgeCache': - µb.assets.purgeCacheableAsset(request.path); + µb.assets.purge(request.assetKey); + µb.assets.remove('compiled/' + request.assetKey); break; case 'readHiddenSettings': diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 473bffa79a372..dfae64ffd158d 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -402,27 +402,15 @@ RedirectEngine.prototype.resourceContentFromName = function(name, mime) { // TODO: combine same key-redirect pairs into a single regex. RedirectEngine.prototype.resourcesFromString = function(text) { - var textEnd = text.length; - var lineBeg = 0, lineEnd; - var line, fields, encoded; - var reNonEmptyLine = /\S/; + var line, fields, encoded, + reNonEmptyLine = /\S/, + lineIter = new µBlock.LineIterator(text); this.resources = new Map(); - while ( lineBeg < textEnd ) { - lineEnd = text.indexOf('\n', lineBeg); - if ( lineEnd < 0 ) { - lineEnd = text.indexOf('\r', lineBeg); - if ( lineEnd < 0 ) { - lineEnd = textEnd; - } - } - line = text.slice(lineBeg, lineEnd); - lineBeg = lineEnd + 1; - - if ( line.startsWith('#') ) { - continue; - } + while ( lineIter.eot() === false ) { + line = lineIter.next(); + if ( line.startsWith('#') ) { continue; } if ( fields === undefined ) { fields = line.trim().split(/\s+/); diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index f52002e676750..17e8f99151af5 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -1,7 +1,7 @@ /******************************************************************************* - uBlock - a browser extension to block requests. - Copyright (C) 2015 Raymond Hill + uBlock Origin - a browser extension to block requests. + 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 @@ -43,8 +43,8 @@ var fromNetFilter = function(details) { var lists = []; var compiledFilter = details.compiledFilter; var entry, content, pos, c; - for ( var path in listEntries ) { - entry = listEntries[path]; + for ( var assetKey in listEntries ) { + entry = listEntries[assetKey]; if ( entry === undefined ) { continue; } @@ -173,11 +173,11 @@ var fromCosmeticFilter = function(details) { ); } - var re, path, entry; + var re, assetKey, entry; for ( var candidate in candidates ) { re = candidates[candidate]; - for ( path in listEntries ) { - entry = listEntries[path]; + for ( assetKey in listEntries ) { + entry = listEntries[assetKey]; if ( entry === undefined ) { continue; } @@ -206,7 +206,7 @@ var reHighMedium = /^\[href\^="https?:\/\/([^"]{8})[^"]*"\]$/; /******************************************************************************/ -onmessage = function(e) { +onmessage = function(e) { // jshint ignore:line var msg = e.data; switch ( msg.what ) { @@ -215,7 +215,7 @@ onmessage = function(e) { break; case 'setList': - listEntries[msg.details.path] = msg.details; + listEntries[msg.details.assetKey] = msg.details; break; case 'fromNetFilter': diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index 79af14e7c571e..c18bfba6662b7 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -1,7 +1,7 @@ /******************************************************************************* - uBlock - a browser extension to block requests. - Copyright (C) 2015 Raymond Hill + uBlock Origin - a browser extension to block requests. + 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 @@ -19,14 +19,12 @@ Home: https://github.com/gorhill/uBlock */ -/* global µBlock */ +'use strict'; /******************************************************************************/ µBlock.staticFilteringReverseLookup = (function() { -'use strict'; - /******************************************************************************/ var worker = null; @@ -77,16 +75,16 @@ var initWorker = function(callback) { var countdown = 0; var onListLoaded = function(details) { - var entry = entries[details.path]; + var entry = entries[details.assetKey]; // https://github.com/gorhill/uBlock/issues/536 - // Use path string when there is no filter list title. + // Use assetKey when there is no filter list title. worker.postMessage({ what: 'setList', details: { - path: details.path, - title: entry.title || details.path, + assetKey: details.assetKey, + title: entry.title || details.assetKey, supportURL: entry.supportURL, content: details.content } @@ -99,18 +97,18 @@ var initWorker = function(callback) { }; var µb = µBlock; - var path, entry; + var listKey, entry; - for ( path in µb.remoteBlacklists ) { - if ( µb.remoteBlacklists.hasOwnProperty(path) === false ) { - continue; - } - entry = µb.remoteBlacklists[path]; - if ( entry.off === true ) { + for ( listKey in µb.availableFilterLists ) { + if ( µb.availableFilterLists.hasOwnProperty(listKey) === false ) { continue; } - entries[path] = { - title: path !== µb.userFiltersPath ? entry.title : vAPI.i18n('1pPageName'), + entry = µb.availableFilterLists[listKey]; + if ( entry.off === true ) { continue; } + entries[listKey] = { + title: listKey !== µb.userFiltersPath ? + entry.title : + vAPI.i18n('1pPageName'), supportURL: entry.supportURL || '' }; countdown += 1; @@ -121,8 +119,8 @@ var initWorker = function(callback) { return; } - for ( path in entries ) { - µb.getCompiledFilterList(path, onListLoaded); + for ( listKey in entries ) { + µb.getCompiledFilterList(listKey, onListLoaded); } }; diff --git a/src/js/scriptlets/subscriber.js b/src/js/scriptlets/subscriber.js index 9c525de0d9b36..887e95db831f3 100644 --- a/src/js/scriptlets/subscriber.js +++ b/src/js/scriptlets/subscriber.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 @@ -21,6 +21,8 @@ /* global vAPI, HTMLDocument */ +'use strict'; + /******************************************************************************/ // Injected into specific web pages, those which have been pre-selected @@ -30,8 +32,6 @@ (function() { -'use strict'; - /******************************************************************************/ // https://github.com/chrisaljoudi/uBlock/issues/464 @@ -100,7 +100,8 @@ var onAbpLinkClicked = function(ev) { 'scriptlets', { what: 'selectFilterLists', - switches: [ { location: location, off: false } ] + keys: [ location ], + append: true }, onListsSelectionDone ); diff --git a/src/js/settings.js b/src/js/settings.js index 6e75aa1584e30..16582955916ed 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -56,7 +56,10 @@ var handleImportFilePicker = function() { if ( typeof userData.netWhitelist !== 'string' ) { throw 'Invalid'; } - if ( typeof userData.filterLists !== 'object' ) { + if ( + typeof userData.filterLists !== 'object' && + Array.isArray(userData.selectedFilterLists) === false + ) { throw 'Invalid'; } } diff --git a/src/js/start.js b/src/js/start.js index 12a583db48137..2ccd4fc8dc061 100644 --- a/src/js/start.js +++ b/src/js/start.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 @@ -39,7 +39,7 @@ var µb = µBlock; vAPI.app.onShutdown = function() { µb.staticFilteringReverseLookup.shutdown(); - µb.assetUpdater.shutdown(); + µb.assets.updateStop(); µb.staticNetFilteringEngine.reset(); µb.cosmeticFilteringEngine.reset(); µb.sessionFirewall.reset(); @@ -58,14 +58,8 @@ vAPI.app.onShutdown = function() { var onAllReady = function() { // https://github.com/chrisaljoudi/uBlock/issues/184 // Check for updates not too far in the future. - µb.assetUpdater.onStart.addEventListener(µb.updateStartHandler.bind(µb)); - µb.assetUpdater.onCompleted.addEventListener(µb.updateCompleteHandler.bind(µb)); - µb.assetUpdater.onAssetUpdated.addEventListener(µb.assetUpdatedHandler.bind(µb)); - µb.assets.onAssetRemoved.addListener(µb.assetCacheRemovedHandler.bind(µb)); - - // Important: remove barrier to remote fetching, this was useful only - // for launch time. - µb.assets.remoteFetchBarrier -= 1; + µb.assets.addObserver(µb.assetObserver.bind(µb)); + µb.scheduleAssetUpdater(µb.userSettings.autoUpdate ? 7 * 60 * 1000 : 0); // vAPI.cloud is optional. if ( µb.cloudStorageSupported ) { @@ -129,7 +123,7 @@ var onSelfieReady = function(selfie) { return false; } - µb.remoteBlacklists = selfie.filterLists; + µb.availableFilterLists = selfie.availableFilterLists; µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine); µb.redirectEngine.fromSelfie(selfie.redirectEngine); µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmeticFilteringEngine); @@ -157,12 +151,6 @@ var onUserSettingsReady = function(fetched) { fromFetch(userSettings, fetched); - // https://github.com/chrisaljoudi/uBlock/issues/426 - // Important: block remote fetching for when loading assets at launch - // time. - µb.assets.autoUpdate = userSettings.autoUpdate; - µb.assets.autoUpdateDelay = µb.updateAssetsEvery; - if ( µb.privacySettingsSupported ) { vAPI.browserSettings.set({ 'hyperlinkAuditing': !userSettings.hyperlinkAuditingDisabled, @@ -192,7 +180,7 @@ var onUserSettingsReady = function(fetched) { var onSystemSettingsReady = function(fetched) { var mustSaveSystemSettings = false; if ( fetched.compiledMagic !== µb.systemSettings.compiledMagic ) { - µb.assets.purge(/^cache:\/\/compiled-/); + µb.assets.remove(/^compiled\//); mustSaveSystemSettings = true; } if ( fetched.selfieMagic !== µb.systemSettings.selfieMagic ) { @@ -254,9 +242,6 @@ var fromFetch = function(to, fetched) { /******************************************************************************/ var onAdminSettingsRestored = function() { - // Forbid remote fetching of assets - µb.assets.remoteFetchBarrier += 1; - var fetchableProps = { 'compiledMagic': '', 'dynamicFilteringString': 'behind-the-scene * 3p noop\nbehind-the-scene * 3p-frame noop', diff --git a/src/js/storage.js b/src/js/storage.js index db079f5ac1b8f..c587919487353 100644 --- a/src/js/storage.js +++ b/src/js/storage.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 @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* global YaMD5, objectAssign, punycode, publicSuffixList */ +/* global objectAssign, punycode, publicSuffixList */ 'use strict'; @@ -104,6 +104,12 @@ case 'string': out[name] = value; break; + case 'number': + out[name] = parseInt(value, 10); + if ( isNaN(out[name]) ) { + out[name] = this.hiddenSettingsDefault[name]; + } + break; default: break; } @@ -151,55 +157,107 @@ this.netWhitelistModifyTime = Date.now(); }; -/******************************************************************************/ +/******************************************************************************* -// This will remove all unused filter list entries from -// µBlock.remoteBlacklists`. This helps reduce the size of backup files. + TODO(seamless migration): + The code related to 'remoteBlacklist' can be removed when I am confident + all users have moved to a version of uBO which no longer depends on + the property 'remoteBlacklists, i.e. v1.11 and beyond. -µBlock.extractSelectedFilterLists = function(callback) { - var µb = this; +**/ - var onBuiltinListsLoaded = function(details) { - var builtin; - try { - builtin = JSON.parse(details.content); - } catch (e) { - builtin = {}; +µBlock.loadSelectedFilterLists = function(callback) { + var µb = this; + vAPI.storage.get([ 'selectedFilterLists', 'remoteBlacklists' ], function(bin) { + if ( !bin || !bin.selectedFilterLists && !bin.remoteBlacklists ) { + return callback(); } - - var result = JSON.parse(JSON.stringify(µb.remoteBlacklists)); - var entry, builtinPath, defaultState; - - for ( var path in result ) { - if ( result.hasOwnProperty(path) === false ) { - continue; - } - entry = result[path]; - // https://github.com/gorhill/uBlock/issues/277 - // uBlock's filter lists are always enabled by default, so we - // have to include in backup only those which are turned off. - if ( path.startsWith('assets/ublock/') ) { - if ( entry.off !== true ) { - delete result[path]; - } - continue; - } - builtinPath = path.replace(/^assets\/thirdparties\//, ''); - defaultState = builtin.hasOwnProperty(builtinPath) === false || - builtin[builtinPath].off === true; - if ( entry.off === true && entry.off === defaultState ) { - delete result[path]; + var listKeys = []; + if ( bin.selectedFilterLists ) { + listKeys = bin.selectedFilterLists; + } + 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'); } + callback(listKeys); + }); +}; - callback(result); +µBlock.saveSelectedFilterLists = function(listKeys, append) { + var µb = this; + var save = function(keys) { + var bin = { + selectedFilterLists: keys, + remoteBlacklists: µb.oldDataFromNewListKeys(keys) + }; + vAPI.storage.set(bin); }; + if ( append ) { + this.loadSelectedFilterLists(function(keys) { + listKeys = listKeys.concat(keys || []); + save(listKeys); + }); + } else { + save(listKeys); + } +}; + +// 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; +}; - // https://github.com/gorhill/uBlock/issues/63 - // Get built-in block lists: this will help us determine whether a - // specific list must be included in the result. - this.loadAndPatchStockFilterLists(onBuiltinListsLoaded); +µ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; }; +// <<<<<<<< /******************************************************************************/ @@ -207,14 +265,11 @@ // https://github.com/gorhill/uBlock/issues/1022 // Be sure to end with an empty line. content = content.trim(); - if ( content !== '' ) { - content += '\n'; - } + if ( content !== '' ) { content += '\n'; } this.assets.put(this.userFiltersPath, content, callback); + this.removeCompiledFilterList(this.userFiltersPath); }; -/******************************************************************************/ - µBlock.loadUserFilters = function(callback) { return this.assets.get(this.userFiltersPath, callback); }; @@ -222,25 +277,23 @@ /******************************************************************************/ µBlock.appendUserFilters = function(filters) { - if ( filters.length === 0 ) { - return; - } + if ( filters.length === 0 ) { return; } var µb = this; var onSaved = function() { - var compiledFilters = µb.compileFilters(filters); - var snfe = µb.staticNetFilteringEngine; - var cfe = µb.cosmeticFilteringEngine; - var acceptedCount = snfe.acceptedCount + cfe.acceptedCount; - var discardedCount = snfe.discardedCount + cfe.discardedCount; + var compiledFilters = µb.compileFilters(filters), + snfe = µb.staticNetFilteringEngine, + cfe = µb.cosmeticFilteringEngine, + acceptedCount = snfe.acceptedCount + cfe.acceptedCount, + discardedCount = snfe.discardedCount + cfe.discardedCount; µb.applyCompiledFilters(compiledFilters, true); - var entry = µb.remoteBlacklists[µb.userFiltersPath]; - var deltaEntryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount; - var deltaEntryUsedCount = deltaEntryCount - (snfe.discardedCount + cfe.discardedCount - discardedCount); + var entry = µb.availableFilterLists[µb.userFiltersPath], + deltaEntryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount, + deltaEntryUsedCount = deltaEntryCount - (snfe.discardedCount + cfe.discardedCount - discardedCount); entry.entryCount += deltaEntryCount; entry.entryUsedCount += deltaEntryUsedCount; - vAPI.storage.set({ 'remoteBlacklists': µb.remoteBlacklists }); + vAPI.storage.set({ 'availableFilterLists': µb.availableFilterLists }); µb.staticNetFilteringEngine.freeze(); µb.redirectEngine.freeze(); µb.cosmeticFilteringEngine.freeze(); @@ -248,9 +301,7 @@ }; var onLoaded = function(details) { - if ( details.error ) { - return; - } + if ( details.error ) { return; } // https://github.com/chrisaljoudi/uBlock/issues/976 // If we reached this point, the filter quite probably needs to be // added for sure: do not try to be too smart, trying to avoid @@ -263,166 +314,194 @@ /******************************************************************************/ -µBlock.getAvailableLists = function(callback) { - var availableLists = {}; - var relocationMap = {}; - - var fixLocation = function(location) { - // https://github.com/chrisaljoudi/uBlock/issues/418 - // We now support built-in external filter lists - if ( /^https?:/.test(location) === false ) { - location = 'assets/thirdparties/' + location; +µBlock.listKeysFromCustomFilterLists = function(raw) { + var out = {}; + var reIgnore = /^[!#]|[^0-9A-Za-z!*'();:@&=+$,\/?%#\[\]_.~-]/, + lineIter = new this.LineIterator(raw), + location; + while ( lineIter.eot() === false ) { + location = lineIter.next().trim(); + if ( location === '' || reIgnore.test(location) ) { continue; } + out[location] = true; + } + return Object.keys(out); +}; + +/******************************************************************************/ + +µBlock.autoSelectRegionalFilterLists = function(lists) { + var lang = self.navigator.language.slice(0, 2), + selectedListKeys = [], + list; + for ( var key in lists ) { + if ( lists.hasOwnProperty(key) === false ) { continue; } + list = lists[key]; + if ( list.off !== true ) { + selectedListKeys.push(key); + continue; } - return location; - }; + if ( list.lang === lang ) { + selectedListKeys.push(key); + list.off = false; + } + } + return selectedListKeys; +}; - // selected lists - var onSelectedListsLoaded = function(store) { - var µb = µBlock; - var lists = store.remoteBlacklists; - var locations = Object.keys(lists); - var location, availableEntry, storedEntry; - var off; - - while ( (location = locations.pop()) ) { - storedEntry = lists[location]; - off = storedEntry.off === true; - // New location? - if ( relocationMap.hasOwnProperty(location) ) { - µb.purgeFilterList(location); - location = relocationMap[location]; - if ( off && lists.hasOwnProperty(location) ) { - off = lists[location].off === true; - } +/******************************************************************************/ + +µBlock.changeExternalFilterLists = function(before, after) { + var µb = µBlock; + var onLoaded = function(keys) { + var fullDict = new Set(keys || []), + mustSave = false, + oldKeys = µb.listKeysFromCustomFilterLists(before), + oldDict = new Set(oldKeys), + newKeys = µb.listKeysFromCustomFilterLists(after), + newDict = new Set(newKeys), + i, key; + i = oldKeys.length; + while ( i-- ) { + key = oldKeys[i]; + if ( fullDict.has(key) && !newDict.has(key) ) { + fullDict.delete(key); + mustSave = true; } - availableEntry = availableLists[location]; - if ( availableEntry === undefined ) { - µb.purgeFilterList(location); - continue; + } + i = newKeys.length; + while ( i-- ) { + key = newKeys[i]; + if ( !fullDict.has(key) && !oldDict.has(key) ) { + fullDict.add(key); + mustSave = true; } - availableEntry.off = off; - if ( typeof availableEntry.homeURL === 'string' ) { - µb.assets.setHomeURL(location, availableEntry.homeURL); + } + if ( mustSave ) { + µb.saveSelectedFilterLists(µb.setToArray(fullDict)); + } + }; + this.loadSelectedFilterLists(onLoaded); +}; + +/******************************************************************************/ + +µBlock.getAvailableLists = function(callback) { + var µb = this, + oldAvailableLists = {}, + newAvailableLists = {}; + + // User filter list. + newAvailableLists[this.userFiltersPath] = { + group: 'default', + title: vAPI.i18n('1pPageName') + }; + + // Custom filter lists. + var importedListKeys = this.listKeysFromCustomFilterLists(µb.userSettings.externalLists), + i = importedListKeys.length, listKey, entry; + while ( i-- ) { + listKey = importedListKeys[i]; + entry = { + content: 'filters', + contentURL: importedListKeys[i], + external: true, + group: 'custom', + submitter: 'user', + title: '' + }; + newAvailableLists[listKey] = entry; + this.assets.registerAssetSource(listKey, entry); + } + + // Final steps: + // - reuse existing list metadata if any; + // - unregister unreferenced imported filter lists if any. + var finalize = function() { + var assetKey, newEntry, oldEntry; + + // Reuse existing metadata. + for ( assetKey in oldAvailableLists ) { + oldEntry = oldAvailableLists[assetKey]; + newEntry = newAvailableLists[assetKey]; + if ( newEntry === undefined ) { + µb.removeFilterList(assetKey); + continue; } - if ( storedEntry.entryCount !== undefined ) { - availableEntry.entryCount = storedEntry.entryCount; + if ( oldEntry.entryCount !== undefined ) { + newEntry.entryCount = oldEntry.entryCount; } - if ( storedEntry.entryUsedCount !== undefined ) { - availableEntry.entryUsedCount = storedEntry.entryUsedCount; + if ( oldEntry.entryUsedCount !== undefined ) { + newEntry.entryUsedCount = oldEntry.entryUsedCount; } // This may happen if the list name was pulled from the list // content. // https://github.com/chrisaljoudi/uBlock/issues/982 // There is no guarantee the title was successfully extracted from // the list content. - if ( availableEntry.title === '' && - typeof storedEntry.title === 'string' && - storedEntry.title !== '' + if ( + newEntry.title === '' && + typeof oldEntry.title === 'string' && + oldEntry.title !== '' ) { - availableEntry.title = storedEntry.title; + newEntry.title = oldEntry.title; } } - // https://github.com/gorhill/uBlock/issues/747 - if ( µb.firstInstall ) { - µb.autoSelectFilterLists(availableLists); + // Remove unreferenced imported filter lists. + var dict = new Set(importedListKeys); + for ( assetKey in newAvailableLists ) { + newEntry = newAvailableLists[assetKey]; + if ( newEntry.submitter !== 'user' ) { continue; } + if ( dict.has(assetKey) ) { continue; } + delete newAvailableLists[assetKey]; + µb.assets.unregisterAssetSource(assetKey); + µb.removeFilterList(assetKey); } - - callback(availableLists); }; - // built-in lists - var onBuiltinListsLoaded = function(details) { - var location, locations; - try { - locations = JSON.parse(details.content); - } catch (e) { - locations = {}; - } - var entry; - for ( location in locations ) { - if ( locations.hasOwnProperty(location) === false ) { - continue; - } - entry = locations[location]; - location = fixLocation(location); - // Migrate obsolete location to new location, if any - if ( typeof entry.oldLocation === 'string' ) { - entry.oldLocation = fixLocation(entry.oldLocation); - relocationMap[entry.oldLocation] = location; + // Selected lists. + var onSelectedListsLoaded = function(keys) { + var listKey; + // No user lists data means use default settings. + if ( Array.isArray(keys) ) { + var listKeySet = new Set(keys); + for ( listKey in newAvailableLists ) { + if ( newAvailableLists.hasOwnProperty(listKey) ) { + newAvailableLists[listKey].off = !listKeySet.has(listKey); + } } - availableLists[location] = entry; + } else if ( µb.firstInstall ) { + µb.saveSelectedFilterLists(µb.autoSelectRegionalFilterLists(newAvailableLists)); } - // Now get user's selection of lists - vAPI.storage.get( - { 'remoteBlacklists': availableLists }, - onSelectedListsLoaded - ); + finalize(); + callback(newAvailableLists); }; - // permanent lists - var location; - var lists = this.permanentLists; - for ( location in lists ) { - if ( lists.hasOwnProperty(location) === false ) { - continue; - } - availableLists[location] = lists[location]; - } - - // custom lists - var c; - var locations = this.userSettings.externalLists.split('\n'); - for ( var i = 0; i < locations.length; i++ ) { - location = locations[i].trim(); - c = location.charAt(0); - if ( location === '' || c === '!' || c === '#' ) { - continue; - } - // Coarse validation - if ( /[^0-9A-Za-z!*'();:@&=+$,\/?%#\[\]_.~-]/.test(location) ) { - continue; + // Built-in filter lists. + var onBuiltinListsLoaded = function(entries) { + for ( var assetKey in entries ) { + if ( entries.hasOwnProperty(assetKey) === false ) { continue; } + entry = entries[assetKey]; + if ( entry.content !== 'filters' ) { continue; } + newAvailableLists[assetKey] = objectAssign({}, entry); } - availableLists[location] = { - title: '', - group: 'custom', - external: true - }; - } - // get built-in block lists. - this.loadAndPatchStockFilterLists(onBuiltinListsLoaded); -}; - -/******************************************************************************/ - -µBlock.autoSelectFilterLists = function(lists) { - var lang = self.navigator.language.slice(0, 2), - list; - for ( var path in lists ) { - if ( lists.hasOwnProperty(path) === false ) { - continue; - } - list = lists[path]; - if ( list.off !== true ) { - continue; - } - if ( list.lang === lang ) { - list.off = false; - } - } -}; + // Load set of currently selected filter lists. + µb.loadSelectedFilterLists(onSelectedListsLoaded); + }; -/******************************************************************************/ + // Available lists previously computed. + var onOldAvailableListsLoaded = function(bin) { + oldAvailableLists = bin && bin.availableFilterLists || {}; + µb.assets.metadata(onBuiltinListsLoaded); + }; -µBlock.createShortUniqueId = function(path) { - var md5 = YaMD5.hashStr(path); - return md5.slice(0, 4) + md5.slice(-4); + // Load previously saved available lists -- these contains data + // computed at run-time, we will reuse this data if possible. + vAPI.storage.get('availableFilterLists', onOldAvailableListsLoaded); }; -µBlock.createShortUniqueId.idLength = 8; - /******************************************************************************/ // This is used to be re-entrancy resistant. @@ -444,18 +523,11 @@ callback = this.noopFunc; } - // Never fetch from remote servers when we load filter lists: this has to - // be as fast as possible. - µb.assets.remoteFetchBarrier += 1; - var onDone = function() { - // Remove barrier to remote fetching - µb.assets.remoteFetchBarrier -= 1; - µb.staticNetFilteringEngine.freeze(); µb.cosmeticFilteringEngine.freeze(); µb.redirectEngine.freeze(); - vAPI.storage.set({ 'remoteBlacklists': µb.remoteBlacklists }); + vAPI.storage.set({ 'availableFilterLists': µb.availableFilterLists }); //quickProfiler.stop(0); @@ -473,15 +545,15 @@ var acceptedCount = snfe.acceptedCount + cfe.acceptedCount; var discardedCount = snfe.discardedCount + cfe.discardedCount; µb.applyCompiledFilters(compiled, path === µb.userFiltersPath); - if ( µb.remoteBlacklists.hasOwnProperty(path) ) { - var entry = µb.remoteBlacklists[path]; + if ( µb.availableFilterLists.hasOwnProperty(path) ) { + var entry = µb.availableFilterLists[path]; entry.entryCount = snfe.acceptedCount + cfe.acceptedCount - acceptedCount; entry.entryUsedCount = entry.entryCount - (snfe.discardedCount + cfe.discardedCount - discardedCount); } }; var onCompiledListLoaded = function(details) { - applyCompiledFilters(details.path, details.content); + applyCompiledFilters(details.assetKey, details.content); filterlistsCount -= 1; if ( filterlistsCount === 0 ) { onDone(); @@ -489,7 +561,7 @@ }; var onFilterListsReady = function(lists) { - µb.remoteBlacklists = lists; + µb.availableFilterLists = lists; µb.redirectEngine.reset(); µb.cosmeticFilteringEngine.reset(); @@ -502,14 +574,10 @@ // This happens for assets which do not exist, ot assets with no // content. var toLoad = []; - for ( var path in lists ) { - if ( lists.hasOwnProperty(path) === false ) { - continue; - } - if ( lists[path].off ) { - continue; - } - toLoad.push(path); + for ( var assetKey in lists ) { + if ( lists.hasOwnProperty(assetKey) === false ) { continue; } + if ( lists[assetKey].off ) { continue; } + toLoad.push(assetKey); } filterlistsCount = toLoad.length; if ( filterlistsCount === 0 ) { @@ -528,32 +596,17 @@ /******************************************************************************/ -µBlock.getCompiledFilterListPath = function(path) { - return 'cache://compiled-filter-list:' + this.createShortUniqueId(path); -}; - -/******************************************************************************/ - -µBlock.getCompiledFilterList = function(path, callback) { - var compiledPath = this.getCompiledFilterListPath(path); - var µb = this; +µBlock.getCompiledFilterList = function(assetKey, callback) { + var µb = this, + compiledPath = 'compiled/' + assetKey; var onRawListLoaded = function(details) { + details.assetKey = assetKey; if ( details.content === '' ) { callback(details); return; } - var listMeta = µb.remoteBlacklists[path]; - // https://github.com/gorhill/uBlock/issues/313 - // Always try to fetch the name if this is an external filter list. - if ( listMeta && (listMeta.title === '' || listMeta.group === 'custom') ) { - var matches = details.content.slice(0, 1024).match(/(?:^|\n)!\s*Title:([^\n]+)/i); - if ( matches !== null ) { - listMeta.title = matches[1].trim(); - } - } - - //console.debug('µBlock.getCompiledFilterList/onRawListLoaded: compiling "%s"', path); + µb.extractFilterListMetadata(assetKey, details.content); details.content = µb.compileFilters(details.content); µb.assets.put(compiledPath, details.content); callback(details); @@ -561,12 +614,10 @@ var onCompiledListLoaded = function(details) { if ( details.content === '' ) { - //console.debug('µBlock.getCompiledFilterList/onCompiledListLoaded: no compiled version for "%s"', path); - µb.assets.get(path, onRawListLoaded); + µb.assets.get(assetKey, onRawListLoaded); return; } - //console.debug('µBlock.getCompiledFilterList/onCompiledListLoaded: using compiled version for "%s"', path); - details.path = path; + details.assetKey = assetKey; callback(details); }; @@ -575,61 +626,70 @@ /******************************************************************************/ -µBlock.purgeCompiledFilterList = function(path) { - this.assets.purge(this.getCompiledFilterListPath(path)); +µBlock.extractFilterListMetadata = function(assetKey, raw) { + var listEntry = this.availableFilterLists[assetKey]; + if ( listEntry === undefined ) { return; } + // Metadata expected to be found at the top of content. + var head = raw.slice(0, 1024), + matches, v; + // https://github.com/gorhill/uBlock/issues/313 + // Always try to fetch the name if this is an external filter list. + if ( listEntry.title === '' || listEntry.group === 'custom' ) { + matches = head.match(/(?:^|\n)!\s*Title:([^\n]+)/i); + if ( matches !== null ) { + listEntry.title = matches[1].trim(); + } + } + // Extract update frequency information + matches = head.match(/(?:^|\n)![\t ]*Expires:[\t ]*([\d]+)[\t ]*days?/i); + if ( matches !== null ) { + v = Math.max(parseInt(matches[1], 10), 2); + if ( v !== listEntry.updateAfter ) { + this.assets.registerAssetSource(assetKey, { updateAfter: v }); + } + } }; /******************************************************************************/ -µBlock.purgeFilterList = function(path) { - this.purgeCompiledFilterList(path); - this.assets.purge(path); +µBlock.removeCompiledFilterList = function(assetKey) { + this.assets.remove('compiled/' + assetKey); +}; + +µBlock.removeFilterList = function(assetKey) { + this.removeCompiledFilterList(assetKey); + this.assets.remove(assetKey); }; /******************************************************************************/ µBlock.compileFilters = function(rawText) { - var rawEnd = rawText.length; var compiledFilters = []; // Useful references: // https://adblockplus.org/en/filter-cheatsheet // https://adblockplus.org/en/filters - var staticNetFilteringEngine = this.staticNetFilteringEngine; - var cosmeticFilteringEngine = this.cosmeticFilteringEngine; - var reIsWhitespaceChar = /\s/; - var reMaybeLocalIp = /^[\d:f]/; - var reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)(?=\s|$)/; - var reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/; - - var lineBeg = 0, lineEnd, currentLineBeg; - var line, lineRaw, c, pos; - - while ( lineBeg < rawEnd ) { - lineEnd = rawText.indexOf('\n', lineBeg); - if ( lineEnd === -1 ) { - lineEnd = rawText.indexOf('\r', lineBeg); - if ( lineEnd === -1 ) { - lineEnd = rawEnd; - } - } + var staticNetFilteringEngine = this.staticNetFilteringEngine, + cosmeticFilteringEngine = this.cosmeticFilteringEngine, + reIsWhitespaceChar = /\s/, + reMaybeLocalIp = /^[\d:f]/, + reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)(?=\s|$)/, + reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/, + line, lineRaw, c, pos, + lineIter = new this.LineIterator(rawText); + + while ( lineIter.eot() === false ) { + line = lineRaw = 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 // following parsing code. - line = lineRaw = rawText.slice(lineBeg, lineEnd).trim(); - currentLineBeg = lineBeg; - lineBeg = lineEnd + 1; - if ( line.length === 0 ) { - continue; - } + if ( line.length === 0 ) { continue; } // Strip comments c = line.charAt(0); - if ( c === '!' || c === '[' ) { - continue; - } + if ( c === '!' || c === '[' ) { continue; } // Parse or skip cosmetic filters // All cosmetic filters are caught here @@ -640,9 +700,7 @@ // Whatever else is next can be assumed to not be a cosmetic filter // Most comments start in first column - if ( c === '#' ) { - continue; - } + if ( c === '#' ) { continue; } // Catch comments somewhere on the line // Remove: @@ -663,15 +721,11 @@ // Ignore hosts file redirect configuration // 127.0.0.1 localhost // 255.255.255.255 broadcasthost - if ( reIsLocalhostRedirect.test(line) ) { - continue; - } + if ( reIsLocalhostRedirect.test(line) ) { continue; } line = line.replace(reLocalIp, '').trim(); } - if ( line.length === 0 ) { - continue; - } + if ( line.length === 0 ) { continue; } staticNetFilteringEngine.compile(line, compiledFilters); } @@ -699,55 +753,6 @@ /******************************************************************************/ -// `switches` contains the filter lists for which the switch must be revisited. - -µBlock.selectFilterLists = function(switches) { - switches = switches || {}; - - // Only the lists referenced by the switches are touched. - var filterLists = this.remoteBlacklists; - var entry, state, location; - var i = switches.length; - while ( i-- ) { - entry = switches[i]; - state = entry.off === true; - location = entry.location; - if ( filterLists.hasOwnProperty(location) === false ) { - if ( state !== true ) { - filterLists[location] = { off: state }; - } - continue; - } - if ( filterLists[location].off === state ) { - continue; - } - filterLists[location].off = state; - } - - vAPI.storage.set({ 'remoteBlacklists': filterLists }); -}; - -/******************************************************************************/ - -// Plain reload of all filters. - -µBlock.reloadAllFilters = function() { - var µb = this; - - // We are just reloading the filter lists: we do not want assets to update. - // TODO: probably not needed anymore, since filter lists are now always - // loaded without update => see `µb.assets.remoteFetchBarrier`. - this.assets.autoUpdate = false; - - var onFiltersReady = function() { - µb.assets.autoUpdate = µb.userSettings.autoUpdate; - }; - - this.loadFilterLists(onFiltersReady); -}; - -/******************************************************************************/ - µBlock.loadRedirectResources = function(callback) { var µb = this; @@ -762,40 +767,46 @@ callback(); }; - this.assets.get('assets/ublock/resources.txt', onResourcesLoaded); + this.assets.get('ublock-resources', onResourcesLoaded); }; /******************************************************************************/ µBlock.loadPublicSuffixList = function(callback) { - var µb = this; - var path = µb.pslPath; - var compiledPath = 'cache://compiled-publicsuffixlist'; + var µb = this, + assetKey = µb.pslAssetKey, + compiledAssetKey = 'compiled/' + assetKey; if ( typeof callback !== 'function' ) { callback = this.noopFunc; } var onRawListLoaded = function(details) { if ( details.content !== '' ) { - //console.debug('µBlock.loadPublicSuffixList/onRawListLoaded: compiling "%s"', path); - publicSuffixList.parse(details.content, punycode.toASCII); - µb.assets.put(compiledPath, JSON.stringify(publicSuffixList.toSelfie())); + µb.compilePublicSuffixList(details.content); } callback(); }; var onCompiledListLoaded = function(details) { if ( details.content === '' ) { - //console.debug('µBlock.loadPublicSuffixList/onCompiledListLoaded: no compiled version for "%s"', path); - µb.assets.get(path, onRawListLoaded); + µb.assets.get(assetKey, onRawListLoaded); return; } - //console.debug('µBlock.loadPublicSuffixList/onCompiledListLoaded: using compiled version for "%s"', path); publicSuffixList.fromSelfie(JSON.parse(details.content)); callback(); }; - this.assets.get(compiledPath, onCompiledListLoaded); + this.assets.get(compiledAssetKey, onCompiledListLoaded); +}; + +/******************************************************************************/ + +µBlock.compilePublicSuffixList = function(content) { + publicSuffixList.parse(content, punycode.toASCII); + this.assets.put( + 'compiled/' + this.pslAssetKey, + JSON.stringify(publicSuffixList.toSelfie()) + ); }; /******************************************************************************/ @@ -814,7 +825,7 @@ var selfie = { magic: µb.systemSettings.selfieMagic, publicSuffixList: publicSuffixList.toSelfie(), - filterLists: µb.remoteBlacklists, + availableFilterLists: µb.availableFilterLists, staticNetFilteringEngine: µb.staticNetFilteringEngine.toSelfie(), redirectEngine: µb.redirectEngine.toSelfie(), cosmeticFilteringEngine: µb.cosmeticFilteringEngine.toSelfie() @@ -885,6 +896,13 @@ var bin = {}; var binNotEmpty = false; + // Allows an admin to set their own 'assets.json' file, with their own + // set of stock assets. + if ( typeof data.assetsBootstrapLocation === 'string' ) { + bin.assetsBootstrapLocation = data.assetsBootstrapLocation; + binNotEmpty = true; + } + if ( typeof data.userSettings === 'object' ) { for ( var name in µb.userSettings ) { if ( µb.userSettings.hasOwnProperty(name) === false ) { @@ -898,8 +916,13 @@ } } - if ( typeof data.filterLists === 'object' ) { - bin.remoteBlacklists = data.filterLists; + // 'selectedFilterLists' is an array of filter list tokens. Each token + // is a reference to an asset in 'assets.json'. + 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; } @@ -939,203 +962,95 @@ /******************************************************************************/ -µBlock.updateStartHandler = function(callback) { - var µb = this; - var onListsReady = function(lists) { - var assets = {}; - for ( var location in lists ) { - if ( lists.hasOwnProperty(location) === false ) { - continue; - } - if ( lists[location].off ) { - continue; - } - assets[location] = true; - } - assets[µb.pslPath] = true; - assets['assets/ublock/resources.txt'] = true; - callback(assets); - }; - - this.getAvailableLists(onListsReady); -}; - -/******************************************************************************/ - -µBlock.assetUpdatedHandler = function(details) { - var path = details.path || ''; - if ( this.remoteBlacklists.hasOwnProperty(path) === false ) { - return; - } - var entry = this.remoteBlacklists[path]; - if ( entry.off ) { - return; - } - // Compile the list while we have the raw version in memory - //console.debug('µBlock.getCompiledFilterList/onRawListLoaded: compiling "%s"', path); - this.assets.put( - this.getCompiledFilterListPath(path), - this.compileFilters(details.content) - ); -}; - -/******************************************************************************/ - -µBlock.updateCompleteHandler = function(details) { - var µb = this; - var updatedCount = details.updatedCount; - - // Assets are supposed to have been all updated, prevent fetching from - // remote servers. - µb.assets.remoteFetchBarrier += 1; - - var onFiltersReady = function() { - µb.assets.remoteFetchBarrier -= 1; - }; - - var onPSLReady = function() { - if ( updatedCount !== 0 ) { - //console.debug('storage.js > µBlock.updateCompleteHandler: reloading filter lists'); - µb.loadFilterLists(onFiltersReady); - } else { - onFiltersReady(); +µBlock.scheduleAssetUpdater = (function() { + var timer, next = 0; + return function(updateDelay) { + if ( timer ) { + clearTimeout(timer); + timer = undefined; } - }; - - if ( details.hasOwnProperty(this.pslPath) ) { - //console.debug('storage.js > µBlock.updateCompleteHandler: reloading PSL'); - this.loadPublicSuffixList(onPSLReady); - updatedCount -= 1; - } else { - onPSLReady(); - } -}; - -/******************************************************************************/ - -µBlock.assetCacheRemovedHandler = (function() { - var barrier = false; - - var handler = function(paths) { - if ( barrier ) { + if ( updateDelay === 0 ) { + next = 0; return; } - barrier = true; - var i = paths.length; - var path; - while ( i-- ) { - path = paths[i]; - if ( this.remoteBlacklists.hasOwnProperty(path) ) { - //console.debug('µBlock.assetCacheRemovedHandler: decompiling "%s"', path); - this.purgeCompiledFilterList(path); - continue; - } - if ( path === this.pslPath ) { - //console.debug('µBlock.assetCacheRemovedHandler: decompiling "%s"', path); - this.assets.purge('cache://compiled-publicsuffixlist'); - continue; - } + var now = Date.now(); + // Use the new schedule if and only if it is earlier than the previous + // one. + if ( next !== 0 ) { + updateDelay = Math.min(updateDelay, Math.max(next - now, 0)); } - this.selfieManager.destroy(); - barrier = false; + next = now + updateDelay; + timer = vAPI.setTimeout(function() { + timer = undefined; + next = 0; + var µb = µBlock; + µb.assets.updateStart({ + delay: µb.hiddenSettings.autoUpdateAssetFetchPeriod * 1000 || 120000 + }); + }, updateDelay); }; - - return handler; })(); /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/602 -// - Load and patch `filter-list.json` -// - Load and patch user's `remoteBlacklists` -// - Load and patch cached filter lists -// - Load and patch compiled filter lists -// -// Once enough time has passed to safely assume all uBlock Origin -// installations have been converted to the new stock filter lists, this code -// can be removed. - -µBlock.patchFilterLists = function(filterLists) { - var modified = false; - var oldListKey, newListKey, listEntry; - for ( var listKey in filterLists ) { - if ( filterLists.hasOwnProperty(listKey) === false ) { - continue; - } - oldListKey = listKey; - if ( this.oldListToNewListMap.hasOwnProperty(oldListKey) === false ) { - oldListKey = 'assets/thirdparties/' + listKey; - if ( this.oldListToNewListMap.hasOwnProperty(oldListKey) === false ) { - continue; - } +µBlock.assetObserver = function(topic, details) { + // Do not update filter list if not in use. + if ( topic === 'before-asset-updated' ) { + if ( + this.availableFilterLists.hasOwnProperty(details.assetKey) && + this.availableFilterLists[details.assetKey].off === true + ) { + return false; } - newListKey = this.oldListToNewListMap[oldListKey]; - // https://github.com/gorhill/uBlock/issues/668 - // https://github.com/gorhill/uBlock/issues/669 - // Beware: an entry for the new list key may already exists. If it is - // the case, leave it as is. - if ( newListKey !== '' && filterLists.hasOwnProperty(newListKey) === false ) { - listEntry = filterLists[listKey]; - listEntry.homeURL = undefined; - filterLists[newListKey] = listEntry; - } - delete filterLists[listKey]; - modified = true; + return; } - return modified; -}; - -µBlock.loadAndPatchStockFilterLists = function(callback) { - var onStockListsLoaded = function(details) { - var µb = µBlock; - var stockLists; - try { - stockLists = JSON.parse(details.content); - } catch (e) { - stockLists = {}; - } - // Migrate assets affected by the change to their new name. - var reExternalURL = /^https?:\/\//; - var newListKey; - for ( var oldListKey in stockLists ) { - if ( stockLists.hasOwnProperty(oldListKey) === false ) { - continue; - } - // https://github.com/gorhill/uBlock/issues/708 - // Support migrating external stock filter lists as well. - if ( reExternalURL.test(oldListKey) === false ) { - oldListKey = 'assets/thirdparties/' + oldListKey; + // Compile the list while we have the raw version in memory + if ( topic === 'after-asset-updated' ) { + var cached = typeof details.content === 'string' && details.content !== ''; + if ( this.availableFilterLists.hasOwnProperty(details.assetKey) ) { + if ( cached ) { + if ( this.availableFilterLists[details.assetKey].off !== true ) { + this.extractFilterListMetadata( + details.assetKey, + details.content + ); + this.assets.put( + 'compiled/' + details.assetKey, + this.compileFilters(details.content) + ); + } + } else { + this.removeCompiledFilterList(details.assetKey); } - if ( µb.oldListToNewListMap.hasOwnProperty(oldListKey) === false ) { - continue; + } else if ( details.assetKey === this.pslAssetKey ) { + if ( cached ) { + this.compilePublicSuffixList(details.content); } - newListKey = µb.oldListToNewListMap[oldListKey]; - if ( newListKey === '' ) { - continue; + } else if ( details.assetKey === 'ublock-resources' ) { + if ( cached ) { + this.redirectEngine.resourcesFromString(details.content); } - // Rename cached asset to preserve content -- so it does not - // need to be fetched from remote server. - µb.assets.rename(oldListKey, newListKey); - µb.assets.purge(µb.getCompiledFilterListPath(oldListKey)); } - µb.patchFilterLists(stockLists); - - // Stock lists information cascades into - // - In-memory user's selected filter lists, so we need to patch this. - µb.patchFilterLists(µb.remoteBlacklists); - - // Stock lists information cascades into - // - In-storage user's selected filter lists, so we need to patch this. - vAPI.storage.get('remoteBlacklists', function(bin) { - var userLists = bin.remoteBlacklists || {}; - if ( µb.patchFilterLists(userLists) ) { - µb.keyvalSetOne('remoteBlacklists', userLists); - } - details.content = JSON.stringify(stockLists); - callback(details); + vAPI.messaging.broadcast({ + what: 'assetUpdated', + key: details.assetKey, + cached: cached + }); - }; + return; + } - this.assets.get('assets/ublock/filter-lists.json', onStockListsLoaded); + // Reload all filter lists if needed. + if ( topic === 'after-assets-updated' ) { + if ( details.assetKeys.length !== 0 ) { + this.loadFilterLists(); + } + if ( this.userSettings.autoUpdate ) { + this.scheduleAssetUpdater(this.hiddenSettings.assetAutoUpdatePeriod * 3600000 || 25200000); + } else { + this.scheduleAssetUpdater(0); + } + return; + } }; diff --git a/src/js/ublock.js b/src/js/ublock.js index 528edb534897f..f765fbd74d4b8 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.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 @@ -317,6 +317,9 @@ var reInvalidHostname = /[^a-z0-9.\-\[\]:]/, // Pre-change switch ( name ) { + case 'externalLists': + this.changeExternalFilterLists(us.externalLists, value); + break; case 'largeMediaSize': if ( typeof value !== 'number' ) { value = parseInt(value, 10) || 0; @@ -340,6 +343,9 @@ var reInvalidHostname = /[^a-z0-9.\-\[\]:]/, us.dynamicFilteringEnabled = true; } break; + case 'autoUpdate': + this.scheduleAssetUpdater(value ? 7 * 60 * 1000 : 0); + break; case 'collapseBlocked': if ( value === false ) { this.cosmeticFilteringEngine.removeFromSelectorCache('*', 'net'); diff --git a/tools/make-assets.sh b/tools/make-assets.sh index fade452dcbafe..cebea3f454e8e 100755 --- a/tools/make-assets.sh +++ b/tools/make-assets.sh @@ -24,8 +24,6 @@ cp -R ../uAssets/thirdparties/www.malwaredomainlist.com $DES/thirdparti mkdir $DES/ublock cp -R ../uAssets/filters/* $DES/ublock/ -cp -R ./assets/ublock/filter-lists.json $DES/ublock/ - -cp ../uAssets/checksums/ublock0.txt $DES/checksums.txt +cp -R ./assets/assets.json $DES/ echo "done." diff --git a/tools/make-chromium.sh b/tools/make-chromium.sh index 8d1f58911131a..eda30f2991edf 100755 --- a/tools/make-chromium.sh +++ b/tools/make-chromium.sh @@ -5,7 +5,11 @@ echo "*** uBlock0.chromium: Creating web store package" echo "*** uBlock0.chromium: Copying files" -DES=dist/build/uBlock0.chromium +if [ "$1" = experimental ]; then + DES=dist/build/experimental/uBlock0.chromium +else + DES=dist/build/uBlock0.chromium +fi rm -rf $DES mkdir -p $DES From ff64a8340cfd2d82ded628b7f904ca539e786025 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 18 Jan 2017 13:35:10 -0500 Subject: [PATCH 0063/4093] code review: only built-in assets are candidates for removal when updating assets.json --- src/js/assets.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index f4daf8f28f7dc..42211c33a9fe0 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -352,9 +352,12 @@ var updateAssetSourceRegistry = function(json) { getAssetSourceRegistry(function(oldDict) { var assetKey; - // Remove obsolete entries + // Remove obsolete entries (only those which were built-in). for ( assetKey in oldDict ) { - if ( newDict[assetKey] === undefined ) { + if ( + newDict[assetKey] === undefined && + oldDict[assetKey].submitter === undefined + ) { unregisterAssetSource(assetKey); } } From 00b19515cf310c17eb68361d6db9b33a37781ac9 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 18 Jan 2017 13:36:24 -0500 Subject: [PATCH 0064/4093] minor change --- 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 cebea3f454e8e..ec3c5b38daca2 100755 --- a/tools/make-assets.sh +++ b/tools/make-assets.sh @@ -14,6 +14,7 @@ fi rm -rf $DES mkdir $DES +cp ./assets/assets.json $DES/ mkdir $DES/thirdparties cp -R ../uAssets/thirdparties/easylist-downloads.adblockplus.org $DES/thirdparties/ @@ -24,6 +25,5 @@ cp -R ../uAssets/thirdparties/www.malwaredomainlist.com $DES/thirdparti mkdir $DES/ublock cp -R ../uAssets/filters/* $DES/ublock/ -cp -R ./assets/assets.json $DES/ echo "done." From cc00dd3adcf0020b93419d5b76216ffa8d9fea09 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 18 Jan 2017 13:47:21 -0500 Subject: [PATCH 0065/4093] 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 e63d49aad9fb8..242e7740320d9 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.9", + "version": "1.10.5.10", "default_locale": "en", "description": "__MSG_extShortDesc__", From f4d2d6c8919f2046333436cc64a6227c6ffa4b4d Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 18 Jan 2017 17:59:49 -0500 Subject: [PATCH 0066/4093] forgot to adjust alises after modifying assts.json --- src/js/assets.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 42211c33a9fe0..b701316bb8734 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -180,9 +180,9 @@ api.listKeyAliases = { "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", - "http://malwaredomains.lehigh.edu/files/immortal_domains.txt": "malware-0", - "assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt": "malware-1", - "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains": "malware-2", + "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", @@ -356,7 +356,7 @@ var updateAssetSourceRegistry = function(json) { for ( assetKey in oldDict ) { if ( newDict[assetKey] === undefined && - oldDict[assetKey].submitter === undefined + newDict[assetKey].submitter === oldDict[assetKey].submitter ) { unregisterAssetSource(assetKey); } From 726f0d6e1f24128d5b11e4687bff4d9ad6f7b62a Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 18 Jan 2017 18:22:33 -0500 Subject: [PATCH 0067/4093] remove stray change mistakenly added to last commit --- 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 b701316bb8734..28acd991d7d40 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -356,7 +356,7 @@ var updateAssetSourceRegistry = function(json) { for ( assetKey in oldDict ) { if ( newDict[assetKey] === undefined && - newDict[assetKey].submitter === oldDict[assetKey].submitter + oldDict[assetKey].submitter === undefined ) { unregisterAssetSource(assetKey); } From 82155c09b63a364c6a214bf338c5e531ce3dabec Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 19 Jan 2017 08:35:08 -0500 Subject: [PATCH 0068/4093] fix #2323 --- src/js/background.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index 791e56be94d6f..08c56ba508f10 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -108,8 +108,8 @@ return { // read-only systemSettings: { - compiledMagic: 'zelhzxrhkfjr', - selfieMagic: 'zelhzxrhkfjr' + compiledMagic: 'fxtcjjhbhyiw', + selfieMagic: 'fxtcjjhbhyiw' }, restoreBackupSettings: { From f2f7375e00fa5ca181cb20377b509abbc2ade17c Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 19 Jan 2017 08:35:37 -0500 Subject: [PATCH 0069/4093] 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 242e7740320d9..ad12c746bb6e8 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.10", + "version": "1.10.5.11", "default_locale": "en", "description": "__MSG_extShortDesc__", From 29c7ba6a1a09a5cabb8d17ed22c9da2504821090 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 19 Jan 2017 14:03:08 -0500 Subject: [PATCH 0070/4093] fix occasional sticky spinner --- src/js/3p-filters.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 4b7dfa1b9dfea..165dedcbfdc12 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -139,6 +139,7 @@ var renderFilterLists = function() { lastUpdateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime)) ); } + li.classList.remove('updating'); li.classList.remove('discard'); return li; }; From 71a1e8c6940e974f2b6785138ef6d46a090ca461 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 20 Jan 2017 08:40:19 -0500 Subject: [PATCH 0071/4093] code review of 3rd-party filters pane code --- src/js/3p-filters.js | 177 +++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 98 deletions(-) diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 165dedcbfdc12..1e3ab20dc323a 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -30,9 +30,7 @@ /******************************************************************************/ var listDetails = {}; -var parseCosmeticFilters = true; -var ignoreGenericCosmeticFilters = false; -var selectedListsHashBefore = ''; +var filteringSettingsHash = ''; var externalLists = ''; /******************************************************************************/ @@ -61,7 +59,7 @@ var renderNumber = function(value) { /******************************************************************************/ -var renderFilterLists = function() { +var renderFilterLists = function(first) { var listGroupTemplate = uDom('#templates .groupEntry'), listEntryTemplate = uDom('#templates .listEntry'), listStatsTemplate = vAPI.i18n('3pListsOfBlockedHostsPerListStats'), @@ -72,40 +70,39 @@ var renderFilterLists = function() { var listNameFromListKey = function(listKey) { var list = listDetails.current[listKey] || listDetails.available[listKey]; var listTitle = list ? list.title : ''; - if ( listTitle === '' ) { - return listKey; - } + if ( listTitle === '' ) { return listKey; } return listTitle; }; var liFromListEntry = function(listKey, li) { - var entry = listDetails.available[listKey]; - li = li ? li : listEntryTemplate.clone().nodeAt(0); - li.setAttribute('data-listkey', listKey); - var elem = li.querySelector('input[type="checkbox"]'); - if ( entry.off !== true ) { - elem.setAttribute('checked', ''); - } else { - elem.removeAttribute('checked'); - } - elem = li.querySelector('a:nth-of-type(1)'); - elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey)); - elem.setAttribute('type', 'text/html'); - elem.textContent = listNameFromListKey(listKey) + '\u200E'; - elem = li.querySelector('a:nth-of-type(2)'); - if ( entry.instructionURL ) { - elem.setAttribute('href', entry.instructionURL); - elem.style.setProperty('display', ''); - } else { - elem.style.setProperty('display', 'none'); + var entry = listDetails.available[listKey], + elem; + if ( !li ) { + li = listEntryTemplate.clone().nodeAt(0); } - elem = li.querySelector('a:nth-of-type(3)'); - if ( entry.supportName ) { - elem.setAttribute('href', entry.supportURL); - elem.textContent = '(' + entry.supportName + ')'; - elem.style.setProperty('display', ''); - } else { - elem.style.setProperty('display', 'none'); + if ( li.getAttribute('data-listkey') !== listKey ) { + li.setAttribute('data-listkey', listKey); + elem = li.querySelector('input[type="checkbox"]'); + elem.checked = entry.off !== true; + elem = li.querySelector('a:nth-of-type(1)'); + elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey)); + elem.setAttribute('type', 'text/html'); + elem.textContent = listNameFromListKey(listKey) + '\u200E'; + elem = li.querySelector('a:nth-of-type(2)'); + if ( entry.instructionURL ) { + elem.setAttribute('href', entry.instructionURL); + elem.style.setProperty('display', ''); + } else { + elem.style.setProperty('display', 'none'); + } + elem = li.querySelector('a:nth-of-type(3)'); + if ( entry.supportName ) { + elem.setAttribute('href', entry.supportURL); + elem.textContent = '(' + entry.supportName + ')'; + elem.style.setProperty('display', ''); + } else { + elem.style.setProperty('display', 'none'); + } } elem = li.querySelector('span.counts'); var text = listStatsTemplate @@ -124,15 +121,9 @@ var renderFilterLists = function() { typeof remoteURL === 'string' && remoteURL.lastIndexOf('http:', 0) === 0 ); // Badge for update status - li.classList.toggle( - 'obsolete', - entry.off !== true && asset.obsolete === true - ); + li.classList.toggle('obsolete', entry.off !== true && asset.obsolete === true); // Badge for cache status - li.classList.toggle( - 'cached', - asset.cached === true && asset.writeTime > 0 - ); + li.classList.toggle('cached', asset.cached === true && asset.writeTime > 0); if ( asset.cached ) { li.querySelector('.status.purge').setAttribute( 'title', @@ -145,9 +136,7 @@ var renderFilterLists = function() { }; var listEntryCountFromGroup = function(listKeys) { - if ( Array.isArray(listKeys) === false ) { - return ''; - } + if ( Array.isArray(listKeys) === false ) { return ''; } var count = 0; var i = listKeys.length; while ( i-- ) { @@ -204,8 +193,6 @@ var renderFilterLists = function() { var onListsReceived = function(details) { // Before all, set context vars listDetails = details; - parseCosmeticFilters = details.parseCosmeticFilters; - ignoreGenericCosmeticFilters = details.ignoreGenericCosmeticFilters; // Incremental rendering: this will allow us to easily discard unused // DOM list entries. @@ -248,23 +235,19 @@ var renderFilterLists = function() { uDom('#lists .listEntries .listEntry.discard').remove(); uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete') === null); uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true); - uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true); - uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true); uDom('#listsOfBlockedHostsPrompt').text( vAPI.i18n('3pListsOfBlockedHostsPrompt') .replace('{{netFilterCount}}', renderNumber(details.netFilterCount)) .replace('{{cosmeticFilterCount}}', renderNumber(details.cosmeticFilterCount)) ); - // Compute a hash of the lists currently enabled in memory. - var selectedListsBefore = []; - for ( var key in listDetails.current ) { - if ( listDetails.current[key].off !== true ) { - selectedListsBefore.push(key); - } + // Compute a hash of the settings so that we can keep track of changes + // affecting the loading of filter lists. + if ( first ) { + uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true); + uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true); + filteringSettingsHash = hashFromCurrentFromSettings(); } - selectedListsHashBefore = selectedListsBefore.sort().join(); - renderWidgets(); }; @@ -276,9 +259,9 @@ var renderFilterLists = function() { // This is to give a visual hint that the selection of blacklists has changed. var renderWidgets = function() { - uDom('#buttonApply').toggleClass('disabled', !listsSelectionChanged()); + uDom('#buttonApply').toggleClass('disabled', filteringSettingsHash === hashFromCurrentFromSettings()); uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null); - uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete') === null); + uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete > input[type="checkbox"]:checked') === null); }; /******************************************************************************/ @@ -291,30 +274,34 @@ var updateAssetStatus = function(details) { renderWidgets(); }; -/******************************************************************************/ +/******************************************************************************* -// Return whether selection of lists changed. + Compute a hash from all the settings affecting how filter lists are loaded + in memory. -var listsSelectionChanged = function() { - if ( - listDetails.parseCosmeticFilters !== parseCosmeticFilters || - listDetails.parseCosmeticFilters && - listDetails.ignoreGenericCosmeticFilters !== ignoreGenericCosmeticFilters - ) { - return true; - } - var selectedListsAfter = [], - listEntries = uDom('#lists .listEntry[data-listkey] > input[type="checkbox"]:checked'); - for ( var i = 0, n = listEntries.length; i < n; i++ ) { - selectedListsAfter.push(listEntries.at(i).ancestors('.listEntry[data-listkey]').attr('data-listkey')); - } +**/ - return selectedListsHashBefore !== selectedListsAfter.sort().join(); +var hashFromCurrentFromSettings = function() { + var hash = [ + document.getElementById('parseCosmeticFilters').checked, + document.getElementById('ignoreGenericCosmeticFilters').checked + ]; + var listHash = [], + listEntries = document.querySelectorAll('#lists .listEntry[data-listkey]'), + liEntry, + i = listEntries.length; + while ( i-- ) { + liEntry = listEntries[i]; + if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) { + listHash.push(liEntry.getAttribute('data-listkey')); + } + } + return hash.concat(listHash.sort()).join(); }; /******************************************************************************/ -var onListCheckboxChanged = function() { +var onFilteringSettingsChanged = function() { renderWidgets(); }; @@ -352,33 +339,33 @@ var selectFilterLists = function(callback) { messaging.send('dashboard', { what: 'userSettings', name: 'parseAllABPHideFilters', - value: listDetails.parseCosmeticFilters + value: document.getElementById('parseCosmeticFilters').checked }); messaging.send('dashboard', { what: 'userSettings', name: 'ignoreGenericCosmeticFilters', - value: listDetails.ignoreGenericCosmeticFilters + value: document.getElementById('ignoreGenericCosmeticFilters').checked }); // Filter lists var listKeys = [], - liEntries = uDom('#lists .listEntry'), liEntry, - i = liEntries.length; + liEntries = document.querySelectorAll('#lists .listEntry[data-listkey]'), + i = liEntries.length, + liEntry; while ( i-- ) { - liEntry = liEntries.at(i); - if ( liEntry.descendants('input').first().prop('checked') ) { - listKeys.push(liEntry.attr('data-listkey')); + liEntry = liEntries[i]; + if ( liEntry.querySelector('input[type="checkbox"]:checked') !== null ) { + listKeys.push(liEntry.getAttribute('data-listkey')); } } messaging.send( 'dashboard', - { - what: 'selectFilterLists', - keys: listKeys - }, + { what: 'selectFilterLists', keys: listKeys }, callback ); + + filteringSettingsHash = hashFromCurrentFromSettings(); }; /******************************************************************************/ @@ -389,6 +376,7 @@ var buttonApplyHandler = function() { messaging.send('dashboard', { what: 'reloadAllFilters' }); }; selectFilterLists(onSelectionDone); + renderWidgets(); }; /******************************************************************************/ @@ -399,6 +387,7 @@ var buttonUpdateHandler = function() { messaging.send('dashboard', { what: 'forceUpdateAssets' }); }; selectFilterLists(onSelectionDone); + renderWidgets(); }; /******************************************************************************/ @@ -430,14 +419,6 @@ var autoUpdateCheckboxChanged = function() { /******************************************************************************/ -var cosmeticSwitchChanged = function() { - listDetails.parseCosmeticFilters = uDom.nodeFromId('parseCosmeticFilters').checked; - listDetails.ignoreGenericCosmeticFilters = uDom.nodeFromId('ignoreGenericCosmeticFilters').checked; - renderWidgets(); -}; - -/******************************************************************************/ - var renderExternalLists = function() { var onReceived = function(details) { uDom('#externalLists').val(details); @@ -553,18 +534,18 @@ self.cloud.onPull = fromCloudData; /******************************************************************************/ uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged); -uDom('#parseCosmeticFilters').on('change', cosmeticSwitchChanged); -uDom('#ignoreGenericCosmeticFilters').on('change', cosmeticSwitchChanged); +uDom('#parseCosmeticFilters').on('change', onFilteringSettingsChanged); +uDom('#ignoreGenericCosmeticFilters').on('change', onFilteringSettingsChanged); uDom('#buttonApply').on('click', buttonApplyHandler); uDom('#buttonUpdate').on('click', buttonUpdateHandler); uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler); -uDom('#lists').on('change', '.listEntry > input', onListCheckboxChanged); +uDom('#lists').on('change', '.listEntry > input', onFilteringSettingsChanged); uDom('#lists').on('click', 'span.purge', onPurgeClicked); uDom('#externalLists').on('input', externalListsChangeHandler); uDom('#externalListsApply').on('click', externalListsApplyHandler); uDom('#lists').on('click', '.groupEntry > span', groupEntryClickHandler); -renderFilterLists(); +renderFilterLists(true); renderExternalLists(); /******************************************************************************/ From 4a8dd58f2d7b5b3d9014f09744b773844c3eaf5f Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 20 Jan 2017 11:24:18 -0500 Subject: [PATCH 0072/4093] 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 ad12c746bb6e8..8712097d8fe03 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.11", + "version": "1.10.5.12", "default_locale": "en", "description": "__MSG_extShortDesc__", From 8f46662a24f2a73725e36368a19715e2b2a173ab Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 20 Jan 2017 12:48:42 -0500 Subject: [PATCH 0073/4093] added POL list compatible with uBO-specific syntax --- assets/assets.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/assets/assets.json b/assets/assets.json index 883b87ba01ee4..7b361c3cf52d3 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -512,6 +512,15 @@ "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock.txt", "supportURL": "https://www.certyficate.it/adblock-ublock-polish-filters/" }, + "POL-1": { + "content": "filters", + "group": "regions", + "off": true, + "title": "POL: polskie filtry do uBlocka uzupelnienie", + "lang": "pl", + "contentURL": "https://raw.githubusercontent.com/MajkiIT/polish-ads-filter/master/polish-adblock-filters/adblock_ublock.txt", + "supportURL": "https://www.certyficate.it/adblock-ublock-polish-filters/" + }, "RUS-0": { "content": "filters", "group": "regions", From 6e48c74e4e06167f3ea806d28dfaaf3c2046418c Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 20 Jan 2017 15:17:11 -0500 Subject: [PATCH 0074/4093] code review: auto-select new built-in asset if it matches locale (https://github.com/uBlockOrigin/uAssets/issues/268#issuecomment-274146120) --- src/js/3p-filters.js | 10 ++++++--- src/js/assets.js | 8 +++++++- src/js/storage.js | 48 ++++++++++++++++++++++++++++++++------------ 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 1e3ab20dc323a..bd035874c7393 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -29,9 +29,9 @@ /******************************************************************************/ -var listDetails = {}; -var filteringSettingsHash = ''; -var externalLists = ''; +var listDetails = {}, + filteringSettingsHash = '', + externalLists = ''; /******************************************************************************/ @@ -41,6 +41,10 @@ var onMessage = function(msg) { updateAssetStatus(msg); break; case 'staticFilteringDataChanged': + filteringSettingsHash = [ + msg.parseCosmeticFilters, + msg.ignoreGenericCosmeticFilters + ].concat(msg.listKeys.sort()).join(); renderFilterLists(); break; default: diff --git a/src/js/assets.js b/src/js/assets.js index 28acd991d7d40..20f38aded8a22 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -361,8 +361,14 @@ var updateAssetSourceRegistry = function(json) { unregisterAssetSource(assetKey); } } - // Add/update existing entries + // Add/update existing entries. Notify of new asset sources. for ( assetKey in newDict ) { + if ( oldDict[assetKey] === undefined ) { + fireNotification( + 'builtin-asset-source-added', + { assetKey: assetKey, entry: newDict[assetKey] } + ); + } registerAssetSource(assetKey, newDict[assetKey]); } saveAssetSourceRegistry(); diff --git a/src/js/storage.js b/src/js/storage.js index c587919487353..6b07118532164 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -193,9 +193,10 @@ µBlock.saveSelectedFilterLists = function(listKeys, append) { var µb = this; var save = function(keys) { + var uniqueKeys = µb.setToArray(new Set(keys)); var bin = { - selectedFilterLists: keys, - remoteBlacklists: µb.oldDataFromNewListKeys(keys) + selectedFilterLists: uniqueKeys, + remoteBlacklists: µb.oldDataFromNewListKeys(uniqueKeys) }; vAPI.storage.set(bin); }; @@ -516,8 +517,9 @@ //quickProfiler.start('µBlock.loadFilterLists()'); - var µb = this; - var filterlistsCount = 0; + var µb = this, + filterlistsCount = 0, + loadedListKeys = []; if ( typeof callback !== 'function' ) { callback = this.noopFunc; @@ -531,7 +533,12 @@ //quickProfiler.stop(0); - vAPI.messaging.broadcast({ what: 'staticFilteringDataChanged' }); + vAPI.messaging.broadcast({ + what: 'staticFilteringDataChanged', + parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters, + ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters, + listKeys: loadedListKeys + }); callback(); @@ -539,17 +546,18 @@ µb.loadingFilterLists = false; }; - var applyCompiledFilters = function(path, compiled) { - var snfe = µb.staticNetFilteringEngine; - var cfe = µb.cosmeticFilteringEngine; - var acceptedCount = snfe.acceptedCount + cfe.acceptedCount; - var discardedCount = snfe.discardedCount + cfe.discardedCount; - µb.applyCompiledFilters(compiled, path === µb.userFiltersPath); - if ( µb.availableFilterLists.hasOwnProperty(path) ) { - var entry = µb.availableFilterLists[path]; + var applyCompiledFilters = function(assetKey, compiled) { + var snfe = µb.staticNetFilteringEngine, + cfe = µb.cosmeticFilteringEngine, + acceptedCount = snfe.acceptedCount + cfe.acceptedCount, + discardedCount = snfe.discardedCount + cfe.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); } + loadedListKeys.push(assetKey); }; var onCompiledListLoaded = function(details) { @@ -1053,4 +1061,18 @@ } return; } + + // New asset source became available, if it's a filter list, should we + // auto-select it? + if ( topic === 'builtin-asset-source-added' ) { + if ( details.entry.content === 'filters' ) { + if ( + details.entry.off !== true || + self.navigator.language.startsWith(details.entry.lang) + ) { + this.saveSelectedFilterLists([ details.assetKey ], true); + } + } + return; + } }; From 20ebe6d18f54ba30bc5c554cb267812889e17a6c Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 20 Jan 2017 15:31:47 -0500 Subject: [PATCH 0075/4093] 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 8712097d8fe03..1293bffe54c9e 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.12", + "version": "1.10.5.13", "default_locale": "en", "description": "__MSG_extShortDesc__", From 9309df4196cbe1a30b7b8f7cc88dd67aac3c151c Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 22 Jan 2017 16:05:16 -0500 Subject: [PATCH 0076/4093] 3rd-party filters pane revisited --- assets/assets.json | 11 +- src/3p-filters.html | 37 +++--- src/css/3p-filters.css | 121 +++++++++++------- src/js/3p-filters.js | 217 ++++++++++++++++++-------------- src/js/assets.js | 10 +- src/js/background.js | 1 + src/js/messaging.js | 22 ++-- src/js/scriptlets/subscriber.js | 40 +----- src/js/storage.js | 204 ++++++++++++++++++++---------- src/js/ublock.js | 3 - 10 files changed, 389 insertions(+), 277 deletions(-) diff --git a/assets/assets.json b/assets/assets.json index 7b361c3cf52d3..24bc1f4738996 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -30,7 +30,8 @@ "contentURL": [ "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt", "assets/ublock/filters.txt" - ] + ], + "supportURL": "https://github.com/uBlockOrigin/uAssets/issues" }, "ublock-badware": { "content": "filters", @@ -196,7 +197,11 @@ "group": "social", "off": true, "title": "Anti-ThirdpartySocial (see warning inside list)", - "contentURL": "https://www.fanboy.co.nz/fanboy-antifacebook.txt", + "contentURL": [ + "https://easylist.to/easylist/fanboy-social.txt", + "https://fanboy.co.nz/fanboy-antifacebook.txt", + "https://easylist-downloads.adblockplus.org/fanboy-social.txt" + ], "supportURL": "https://forums.lanik.us/" }, "fanboy-annoyance": { @@ -206,6 +211,7 @@ "title": "Fanboy’s Annoyance List", "contentURL": [ "https://easylist.to/easylist/fanboy-annoyance.txt", + "https://fanboy.co.nz/fanboy-annoyance.txt", "https://easylist-downloads.adblockplus.org/fanboy-annoyance.txt" ], "supportURL": "https://forums.lanik.us/" @@ -217,6 +223,7 @@ "title": "Fanboy’s Social Blocking List", "contentURL": [ "https://easylist.to/easylist/fanboy-social.txt", + "https://fanboy.co.nz/fanboy-social.txt", "https://easylist-downloads.adblockplus.org/fanboy-social.txt" ], "supportURL": "https://forums.lanik.us/" diff --git a/src/3p-filters.html b/src/3p-filters.html index 48086f71fe271..d35910399759f 100644 --- a/src/3p-filters.html +++ b/src/3p-filters.html @@ -21,24 +21,19 @@
  • -
      -
    • -
      -
    -
  • +
  • +
    +

      -

      - - -

      -

      - -

      -
      +

      + + + +

    • diff --git a/src/css/3p-filters.css b/src/css/3p-filters.css index b3687bae1f4bd..07a549af91dc0 100644 --- a/src/css/3p-filters.css +++ b/src/css/3p-filters.css @@ -133,9 +133,6 @@ li.listEntry span.status { li.listEntry span.status:hover { opacity: 1; } -li.listEntry span.status.fa { - /* font-size: 110%; */ - } li.listEntry span.unsecure { color: darkred; } @@ -151,20 +148,19 @@ li.listEntry.failed span.failed { li.listEntry span.cache { cursor: pointer; } -li.listEntry.cached:not(.updating):not(.obsolete) > input[type="checkbox"]:checked ~ span.cache { +li.listEntry.cached:not(.obsolete) > input[type="checkbox"]:checked ~ span.cache { display: inline-block; } li.listEntry span.obsolete { color: hsl(36, 100%, 40%); } -li.listEntry.obsolete:not(.updating) > input[type="checkbox"]:checked ~ span.obsolete { +body:not(.updating) li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.obsolete { display: inline-block; } li.listEntry span.updating { - border: none; - padding: 0; + transform-origin: 50% 46%; } -li.listEntry.updating span.updating { +body.updating li.listEntry.obsolete span.updating { animation: spin 2s linear infinite; display: inline-block; } diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 1b52d5276893b..f478da3ad9b6a 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -40,6 +40,9 @@ var onMessage = function(msg) { case 'assetUpdated': updateAssetStatus(msg); break; + case 'assetsUpdated': + document.body.classList.remove('updating'); + break; case 'staticFilteringDataChanged': renderFilterLists(); break; @@ -139,7 +142,6 @@ var renderFilterLists = function(soft) { lastUpdateTemplateString.replace('{{ago}}', renderElapsedTimeToString(asset.writeTime)) ); } - li.classList.remove('updating'); li.classList.remove('discard'); return li; }; @@ -264,12 +266,10 @@ var renderFilterLists = function(soft) { /******************************************************************************/ -// This is to give a visual hint that the selection of blacklists has changed. - var renderWidgets = function() { uDom('#buttonApply').toggleClass('disabled', filteringSettingsHash === hashFromCurrentFromSettings()); uDom('#buttonPurgeAll').toggleClass('disabled', document.querySelector('#lists .listEntry.cached') === null); - uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('#lists .listEntry.obsolete:not(.updating) > input[type="checkbox"]:checked') === null); + uDom('#buttonUpdate').toggleClass('disabled', document.querySelector('body:not(.updating) #lists .listEntry.obsolete > input[type="checkbox"]:checked') === null); }; /******************************************************************************/ @@ -280,7 +280,6 @@ var updateAssetStatus = function(details) { li.classList.toggle('failed', !!details.failed); li.classList.toggle('obsolete', !details.cached); li.classList.toggle('cached', !!details.cached); - li.classList.remove('updating'); if ( details.cached ) { li.querySelector('.status.cache').setAttribute( 'title', @@ -429,9 +428,7 @@ var buttonApplyHandler = function() { var buttonUpdateHandler = function() { var onSelectionDone = function() { - uDom('#lists .listEntry.obsolete > input[type="checkbox"]:checked') - .ancestors('.listEntry[data-listkey]') - .addClass('updating'); + document.body.classList.add('updating'); messaging.send('dashboard', { what: 'forceUpdateAssets' }); renderWidgets(); }; diff --git a/src/js/assets.js b/src/js/assets.js index 9a29812d7a0eb..bf15d395c8f81 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -598,17 +598,25 @@ var assetCacheRemove = function(pattern, callback) { getAssetCacheRegistry(onReady); }; -var assetCacheMarkAsDirty = function(pattern, callback) { +var assetCacheMarkAsDirty = function(pattern, exclude, callback) { var onReady = function() { var cacheDict = assetCacheRegistry, cacheEntry, mustSave = false; for ( var assetKey in cacheDict ) { - if ( pattern instanceof RegExp && !pattern.test(assetKey) ) { - continue; + if ( pattern instanceof RegExp ) { + if ( pattern.test(assetKey) === false ) { continue; } + } else if ( typeof pattern === 'string' ) { + if ( assetKey !== pattern ) { continue; } + } else if ( Array.isArray(pattern) ) { + if ( pattern.indexOf(assetKey) === -1 ) { continue; } } - if ( typeof pattern === 'string' && assetKey !== pattern ) { - continue; + if ( exclude instanceof RegExp ) { + if ( exclude.test(assetKey) ) { continue; } + } else if ( typeof exclude === 'string' ) { + if ( assetKey === exclude ) { continue; } + } else if ( Array.isArray(exclude) ) { + if ( exclude.indexOf(assetKey) !== -1 ) { continue; } } cacheEntry = cacheDict[assetKey]; if ( !cacheEntry.writeTime ) { continue; } @@ -623,7 +631,10 @@ var assetCacheMarkAsDirty = function(pattern, callback) { callback(); } }; - + if ( typeof exclude === 'function' ) { + callback = exclude; + exclude = undefined; + } getAssetCacheRegistry(onReady); }; @@ -887,9 +898,7 @@ api.metadata = function(callback) { /******************************************************************************/ -api.purge = function(pattern, callback) { - assetCacheMarkAsDirty(pattern, callback); -}; +api.purge = assetCacheMarkAsDirty; api.remove = function(pattern, callback) { assetCacheRemove(pattern, callback); diff --git a/src/js/messaging.js b/src/js/messaging.js index d5c780b6954ed..cab4802fe6737 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -979,8 +979,7 @@ var onMessage = function(request, sender, callback) { if ( request.hard ) { µb.assets.remove(/./); } else { - µb.assets.remove(/compiled\//); - µb.assets.purge(/./); + µb.assets.purge(/./, 'public_suffix_list.dat'); } break; diff --git a/src/js/storage.js b/src/js/storage.js index 782d2da24f416..7d394f52d7589 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -1131,6 +1131,10 @@ } else { this.scheduleAssetUpdater(0); } + vAPI.messaging.broadcast({ + what: 'assetsUpdated', + assetKeys: details.assetKeys + }); return; } From 96df129ddb2571b06afed288c4715bee02b00a61 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 23 Jan 2017 10:13:07 -0500 Subject: [PATCH 0081/4093] code reivew: do not cache assets fetched for viewing purpose --- src/js/assets.js | 14 +++++++++++--- src/js/messaging.js | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index bf15d395c8f81..ba83f5d141a16 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -29,7 +29,8 @@ var reIsExternalPath = /^(?:[a-z-]+):\/\//, reIsUserAsset = /^user-/, - errorCantConnectTo = vAPI.i18n('errorCantConnectTo'); + errorCantConnectTo = vAPI.i18n('errorCantConnectTo'), + noopfunc = function(){}; var api = { }; @@ -716,7 +717,14 @@ var saveUserAsset = function(assetKey, content, callback) { /******************************************************************************/ -api.get = function(assetKey, callback) { +api.get = function(assetKey, options, callback) { + if ( typeof options === 'function' ) { + callback = options; + options = {}; + } else if ( typeof callback !== 'function' ) { + callback = noopfunc; + } + if ( assetKey === µBlock.userFiltersPath ) { readUserAsset(assetKey, callback); return; @@ -755,7 +763,7 @@ api.get = function(assetKey, callback) { onContentNotLoaded(); return; } - if ( reIsExternalPath.test(contentURL) ) { + if ( reIsExternalPath.test(contentURL) && options.dontCache !== true ) { assetCacheWrite(assetKey, { content: this.responseText, url: contentURL diff --git a/src/js/messaging.js b/src/js/messaging.js index cab4802fe6737..201ddbf2878fc 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -57,7 +57,7 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'getAssetContent': // https://github.com/chrisaljoudi/uBlock/issues/417 - µb.assets.get(request.url, callback); + µb.assets.get(request.url, { dontCache: true }, callback); return; case 'listsFromNetFilter': From 975b0d8280091905ed2a50b15e96198428779bc8 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 23 Jan 2017 11:08:01 -0500 Subject: [PATCH 0082/4093] new version 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 f20c12d0e50b5..1f2e054509c3e 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.14", + "version": "1.10.5.100", "default_locale": "en", "description": "__MSG_extShortDesc__", From 8ab6c13167762b8d4572f457e48f3d0dc944f643 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 23 Jan 2017 14:31:43 -0500 Subject: [PATCH 0083/4093] code review: non-enabled lists must not be shown as updating --- src/css/3p-filters.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/css/3p-filters.css b/src/css/3p-filters.css index 07a549af91dc0..a38b1ae60dff8 100644 --- a/src/css/3p-filters.css +++ b/src/css/3p-filters.css @@ -160,7 +160,7 @@ body:not(.updating) li.listEntry.obsolete > input[type="checkbox"]:checked ~ spa li.listEntry span.updating { transform-origin: 50% 46%; } -body.updating li.listEntry.obsolete span.updating { +body.updating li.listEntry.obsolete > input[type="checkbox"]:checked ~ span.updating { animation: spin 2s linear infinite; display: inline-block; } From 97db7ba13e66fae7d4f2a61df836f20433d1af97 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 23 Jan 2017 17:16:37 -0500 Subject: [PATCH 0084/4093] fix #2332 --- src/js/3p-filters.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index f478da3ad9b6a..855f89eed835b 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -31,7 +31,8 @@ var listDetails = {}, filteringSettingsHash = '', - lastUpdateTemplateString = vAPI.i18n('3pLastUpdate'); + lastUpdateTemplateString = vAPI.i18n('3pLastUpdate'), + reValidExternalList = /[a-z-]+:\/\/\S*\/\S+/; /******************************************************************************/ @@ -111,6 +112,7 @@ var renderFilterLists = function(soft) { } else { li.classList.remove('mustread'); } + li.classList.remove('toRemove'); } // https://github.com/gorhill/uBlock/issues/1429 if ( !soft ) { @@ -314,8 +316,11 @@ var hashFromCurrentFromSettings = function() { listHash.push(liEntry.getAttribute('data-listkey')); } } - hash.push(listHash.sort().join()); - hash.push(document.getElementById('externalLists').value.trim()); + hash.push( + listHash.sort().join(), + reValidExternalList.test(document.getElementById('externalLists').value), + document.querySelector('#lists .listEntry.toRemove') !== null + ); return hash.join(); }; From 82b58664d4b431156feb63007373ab441198c097 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 23 Jan 2017 17:17:12 -0500 Subject: [PATCH 0085/4093] 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 1f2e054509c3e..6b233209d3043 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.100", + "version": "1.10.5.101", "default_locale": "en", "description": "__MSG_extShortDesc__", From 1a075bc6734eae4017762c4d1311e38e9bd14f52 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 24 Jan 2017 08:23:52 -0500 Subject: [PATCH 0086/4093] code review: mobile-friendly changes --- src/1p-filters.html | 1 + src/3p-filters.html | 8 ++++---- src/about.html | 1 + src/css/3p-filters.css | 1 - src/css/dashboard-common.css | 5 +++++ src/css/dashboard.css | 5 +++++ src/dyna-rules.html | 1 + src/js/3p-filters.js | 2 +- src/whitelist.html | 1 + 9 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/1p-filters.html b/src/1p-filters.html index 37e81610eab3e..b1146a09d1d52 100644 --- a/src/1p-filters.html +++ b/src/1p-filters.html @@ -2,6 +2,7 @@ + uBlock — Your filters diff --git a/src/3p-filters.html b/src/3p-filters.html index 29ccb57365015..823dfff7e3754 100644 --- a/src/3p-filters.html +++ b/src/3p-filters.html @@ -3,7 +3,7 @@ -uBlock — Ubiquitous rules +uBlock — Filter lists @@ -15,11 +15,11 @@
      -
        -
      •   - +
      • + +
      • diff --git a/src/about.html b/src/about.html index 855170e966206..842088d956f13 100644 --- a/src/about.html +++ b/src/about.html @@ -2,6 +2,7 @@ + uBlock — About diff --git a/src/css/3p-filters.css b/src/css/3p-filters.css index a38b1ae60dff8..a5052ca45d4fa 100644 --- a/src/css/3p-filters.css +++ b/src/css/3p-filters.css @@ -1,7 +1,6 @@ @keyframes spin { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg); } } - ul { padding: 0; list-style-type: none; diff --git a/src/css/dashboard-common.css b/src/css/dashboard-common.css index dd0a8c19c75cf..bc8f3ddd28e3a 100644 --- a/src/css/dashboard-common.css +++ b/src/css/dashboard-common.css @@ -2,6 +2,11 @@ body { margin: 0; padding: 0 0.5em 0.5em 0.5em; } +@media screen and (max-device-width: 960px) { + body { + zoom: 1.4; + } + } h2, h3 { margin: 1em 0; font-family: sans-serif; diff --git a/src/css/dashboard.css b/src/css/dashboard.css index 1b34a7e7df50a..ea8f0573ecce1 100644 --- a/src/css/dashboard.css +++ b/src/css/dashboard.css @@ -7,6 +7,11 @@ html, body { height: 100%; overflow: hidden; } +@media screen and (max-device-width: 960px) { + #dashboard-nav { + zoom: 1.2; + } + } #dashboard-nav { border: 0; margin: 0; diff --git a/src/dyna-rules.html b/src/dyna-rules.html index cb81f99efc575..fac16dd28b652 100644 --- a/src/dyna-rules.html +++ b/src/dyna-rules.html @@ -2,6 +2,7 @@ + uBlock — Dynamic filtering rules diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 855f89eed835b..f43cb8bd364c4 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -92,6 +92,7 @@ var renderFilterLists = function(soft) { elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey)); elem.setAttribute('type', 'text/html'); elem.textContent = listNameFromListKey(listKey) + '\u200E'; + li.classList.remove('toRemove'); if ( entry.supportName ) { li.classList.add('support'); elem = li.querySelector('a.support'); @@ -112,7 +113,6 @@ var renderFilterLists = function(soft) { } else { li.classList.remove('mustread'); } - li.classList.remove('toRemove'); } // https://github.com/gorhill/uBlock/issues/1429 if ( !soft ) { diff --git a/src/whitelist.html b/src/whitelist.html index 67a70ab92dabd..f8a45905292c5 100644 --- a/src/whitelist.html +++ b/src/whitelist.html @@ -2,6 +2,7 @@ + uBlock — Whitelist From 0e11d6e95ea83d48e23422f62cc8799fb6a61330 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 24 Jan 2017 13:53:04 -0500 Subject: [PATCH 0087/4093] code review: fix rtl rendering of 3rd-party filters pane --- src/3p-filters.html | 18 +++++++++--------- src/css/3p-filters.css | 8 ++++---- src/js/3p-filters.js | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/3p-filters.html b/src/3p-filters.html index 823dfff7e3754..64931d5693134 100644 --- a/src/3p-filters.html +++ b/src/3p-filters.html @@ -43,15 +43,15 @@
      diff --git a/src/css/3p-filters.css b/src/css/3p-filters.css index a5052ca45d4fa..359ab709e5b61 100644 --- a/src/css/3p-filters.css +++ b/src/css/3p-filters.css @@ -23,7 +23,7 @@ body.hideUnused #listsOfBlockedHostsPrompt:before { padding-left: 0.5em; padding-right: 0em; } -body[dir=rtl] #lists { +body[dir="rtl"] #lists { padding-left: 0em; padding-right: 0.5em; } @@ -57,12 +57,12 @@ li.listEntry { line-height: 150%; margin: 0 auto 0 auto; margin-left: 2.5em; - margin-right: 0em; + margin-right: 0; text-indent: -2em; } -body[dir=rtl] li.listEntry { +body[dir="rtl"] li.listEntry { margin-left: 0em; - margin-right: 1em; + margin-right: 2.5em; } li.listEntry > * { margin-right: 0.5em; diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index f43cb8bd364c4..57cccf2f6ae16 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -91,7 +91,7 @@ var renderFilterLists = function(soft) { elem = li.querySelector('a:nth-of-type(1)'); elem.setAttribute('href', 'asset-viewer.html?url=' + encodeURI(listKey)); elem.setAttribute('type', 'text/html'); - elem.textContent = listNameFromListKey(listKey) + '\u200E'; + elem.textContent = listNameFromListKey(listKey); li.classList.remove('toRemove'); if ( entry.supportName ) { li.classList.add('support'); From c2a3ff141be9b5d5e85c55d5f54973c76e411518 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 24 Jan 2017 16:58:27 -0500 Subject: [PATCH 0088/4093] translation work from https://crowdin.com/project/ublock --- dist/description/description-hi.txt | 28 ++++++++++++++-------------- src/_locales/bg/messages.json | 2 +- src/_locales/el/messages.json | 2 +- src/_locales/et/messages.json | 2 +- src/_locales/fi/messages.json | 2 +- src/_locales/hi/messages.json | 2 +- src/_locales/id/messages.json | 2 +- src/_locales/it/messages.json | 2 +- src/_locales/ko/messages.json | 2 +- src/_locales/lv/messages.json | 8 ++++---- src/_locales/pt_BR/messages.json | 2 +- src/_locales/pt_PT/messages.json | 8 ++++---- src/_locales/sr/messages.json | 2 +- src/_locales/vi/messages.json | 2 +- src/_locales/zh_CN/messages.json | 2 +- 15 files changed, 34 insertions(+), 34 deletions(-) diff --git a/dist/description/description-hi.txt b/dist/description/description-hi.txt index 6bb418ca3f6f3..33caf12a383ed 100644 --- a/dist/description/description-hi.txt +++ b/dist/description/description-hi.txt @@ -1,28 +1,28 @@ -An efficient blocker: easy on memory and CPU footprint, and yet can load and enforce thousands more filters than other popular blockers out there. +एक कुशल अवरोधक: स्मृति और सीपीयू पदचिह्न पर आसान है, और अभी तक लोड और हजारों लागू वहाँ से बाहर अन्य लोकप्रिय ब्लॉकर्स और अधिक से अधिक फिल्टर कर सकते हैं। अपनी क्षमता का सचित्र अवलोकन: 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. +उपयोग: पॉपअप में बड़ी शक्ति बटन स्थायी रूप से अक्षम / वर्तमान वेब साइट uBlock लिए सक्षम है। यह केवल मौजूदा वेब साइट पर लागू होता है, यह एक वैश्विक शक्ति बटन नहीं है। *** -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 +- आसान सूची +- Peter Lowe's विज्ञापन सर्वर सूची +- आसान गुप्तता +- मैलवेयर डोमेन -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 +- Fanboy's बढ़ी ट्रैकिंग सूची +- Dan Pollock's मेजबान फ़ाइल +- hpHosts's विज्ञापन और ट्रैकिंग सर्वर +- MVPS मेज़बान +-स्पैम404 +- और बहुत सारे 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. diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 782775f42dbc6..9a607eb4f3682 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -680,7 +680,7 @@ "description":"used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning":{ - "message":"Внимание! Променяте тези настройки на свой собствен риск.", + "message":"Внимание! Променяте настройките на свой собствен риск.", "description":"A warning to users at the top of 'Advanced settings' page" }, "genericSubmit":{ diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index ad9818a10af18..1cea6f934bc98 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -292,7 +292,7 @@ "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":"

      Τα γενικά κοσμητικά φίλτρα είναι εκείνα τα κοσμητικά φίλτρα που εφαρμόζονται σε όλες τις ιστοσελίδες.

      Αν και γίνεται αποτελεσματική διαχείρισή τους από το uBlock₀, τα γενικά κοσμητικά φίλτρα ενδέχεται να καταναλώσουν σημαντική μνήμη και να υπερφορτώσουν τη CPU σε μερικές ιστοσελίδες, ειδικά για μεγάλες μακροχρόνιες.

      Η ενεργοποίηση αυτής της επιλογής θα εξαλείψει την υπερφόρτωση μνήμης και CPU στις ιστοσελίδες ως αποτέλεσμα της διαχείρισης γενικών κοσμητικών φίλτρων, ενώ ενδέχεται να μειώσει την κατανάλωση μνήμης του ίδιου του uBlock₀.

      Προτείνεται η ενεργοποίηση αυτής της επιλογής στις λιγότερο ισχυρές συσκευές.", "description":"Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pListsOfBlockedHostsHeader":{ diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index edd521c2f94f1..de3eade6be014 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Nimekiri domeenidest, millel uBlock₀ keelatakse. Üks domeen rea kohta, vigased domeeninimed eiratakse vaikimisi ning kommenteeritakse välja.", + "message":"Valge nimekirja direktiivid määravad, millistel veebilehtedel peaks uBlock Origin keelatud olema. Üks domeen rea kohta. Sobimatuid direktiive vaikselt ignoreeritakse ja kommenteeritakse välja.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index 6678f3b5e9c2f..efd1f28cc400a 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -616,7 +616,7 @@ "description":"Firefox\/Fennec-specific: Show Dashboard" }, "showNetworkLogButton":{ - "message":"Näytä Verkkopyyntöjen Loki", + "message":"Näytä loki", "description":"Firefox\/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff":{ diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 2777ccbb282bc..d52d1536616d9 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -40,7 +40,7 @@ "description":"appears as tab name in dashboard" }, "advancedSettingsPageName":{ - "message":"Advanced settings", + "message":"उन्नत सेटिंग्स", "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index e17a97b95e0b2..6f9362d2940a0 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Daftar nama host yang mana uBlock₀ akan dinonfungsikan. Satu entri per baris. Nama host yang tidak valid akan diabaikan tanpa peringatan.", + "message":"Daftar putih domain atau halaman, uBlock Origin akan dinonaktifkan. Satu entri per baris. Entri yang tidak valid akan diabaikan dan dijadikan komentar.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 22b31136dd3a6..594851c70ddd8 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -680,7 +680,7 @@ "description":"used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning":{ - "message":"Attenzione! Cambia queste impostazioni a tuo rischio.", + "message":"Attenzione! Cambia queste impostazioni avanzate a tuo rischio e pericolo.", "description":"A warning to users at the top of 'Advanced settings' page" }, "genericSubmit":{ diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index f8466f176dd2f..83ed0caa5514f 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -424,7 +424,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":{ diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index d137cd2decd3a..b7b96f754a7dd 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -40,7 +40,7 @@ "description":"appears as tab name in dashboard" }, "advancedSettingsPageName":{ - "message":"Advanced settings", + "message":"Papildu iestatījumi", "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ @@ -212,7 +212,7 @@ "description":"" }, "settingsAdvancedUserSettings":{ - "message":"advanced settings", + "message":"papildu iestatījumi", "description":"For the tooltip of a link which gives access to advanced settings" }, "settingsPrefetchingDisabledPrompt":{ @@ -288,7 +288,7 @@ "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ - "message":"Ignore generic cosmetic filters", + "message":"Ignorēt vispārīgos kosmētiskos filtrus", "description":"This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo":{ @@ -540,7 +540,7 @@ "description":"English: project' wiki on Github" }, "aboutSupport":{ - "message":"Support", + "message":"Atbalsts", "description":"A link for where to get support" }, "aboutCode":{ diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index a642797772981..ecdf542ae01a0 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Sua lista de exceções de servidores para qual o uBlock₀ será desativado. Uma entrada por linha. Servidores inválidos serão silenciosamente ignorados.", + "message":"Sua lista branca de servidores para qual o uBlock Origin será desativado. Uma regra por linha. Servidores inválidos serão silenciosamente ignorados.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index b0bd15256d2e2..e2d63b466d109 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -84,7 +84,7 @@ "description":"Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia":{ - "message":"Alternar o bloqueio de grandes elementos multimédia para este site", + "message":"Alternar o bloqueio de elementos multimédia grandes para este site", "description":"Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering":{ @@ -144,7 +144,7 @@ "description":"" }, "popupHitDomainCountPrompt":{ - "message":"domínios conectados", + "message":"domínios ligados", "description":"appears in popup" }, "popupHitDomainCount":{ @@ -640,7 +640,7 @@ "description":"English: List of filter list names follows" }, "docblockedBack":{ - "message":"Recuar", + "message":"Retroceder", "description":"English: Go back" }, "docblockedClose":{ @@ -700,7 +700,7 @@ "description":"" }, "contextMenuTemporarilyAllowLargeMediaElements":{ - "message":"Permitir temporariamente grandes elementos multimédia", + "message":"Permitir temporariamente elementos multimédia grandes", "description":"A context menu entry, present when large media elements have been blocked on the current site" }, "dummy":{ diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index 9e060b20bb341..aef2d92d94501 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -152,7 +152,7 @@ "description":"appears in popup" }, "pickerCreate":{ - "message":"Направи", + "message":"Креирај", "description":"English: Create" }, "pickerPick":{ diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index f112d6f706eeb..b4d2248e7bb0e 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Danh sách tên các máy chủ mà uBlock₀ sẽ bị chặn. Một mục nhập trên mỗi dòng. Tên máy chủ không hợp lệ sẽ được tự động bỏ qua.", + "message":"Danh sách tên các máy chủ mà µBlock₀ sẽ bị vô hiệu. Một mục nhập trên mỗi dòng. Tên máy chủ không hợp lệ sẽ được tự động bỏ qua.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 9a753bad9c379..1d5fe1c75f8c7 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"uBlock₀ 在列表里的城名将会停用。一行一条规则。无效的城名将直接被忽略。", + "message":"您的列表中针对 µBlock 的主机名将被禁用。每行一条规则。无效的主机名将直接被忽略。", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ From 18096366808f7b49153e92f544e81468aa8a9482 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 25 Jan 2017 08:05:41 -0500 Subject: [PATCH 0089/4093] fix #2337 --- platform/firefox/vapi-background.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 742a99258f2ff..c9cc54b83c031 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -274,7 +274,11 @@ vAPI.browserSettings = { // has a `media.peerconnection.ice.default_address_only` pref which // purpose is to prevent local IP address leakage. case 'webrtcIPAddress': - if ( this.getValue('media.peerconnection', 'ice.default_address_only') !== undefined ) { + // https://github.com/gorhill/uBlock/issues/2337 + if ( this.getValue('media.peerconnection', 'ice.no_host') !== undefined ) { + prefName = 'ice.no_host'; + prefVal = true; + } else if ( this.getValue('media.peerconnection', 'ice.default_address_only') !== undefined ) { prefName = 'ice.default_address_only'; prefVal = true; } else { From d79a781bea6872c8a12a1b284de518d7762c4a99 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 25 Jan 2017 09:21:16 -0500 Subject: [PATCH 0090/4093] report blanket websocket blocking once only --- src/js/traffic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 6e4587d0fca45..29918bfbe0073 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -457,7 +457,7 @@ var processCSP = function(pageStore, details) { ); } - if ( loggerEnabled ) { + if ( loggerEnabled && details.type !== 'script' ) { if ( blockInlineScript !== undefined ) { µb.logger.writeOne( tabId, From 393d0b2d0887812161fe7ede066a0766e9610bc3 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 25 Jan 2017 09:21:44 -0500 Subject: [PATCH 0091/4093] new revision or 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 6b233209d3043..07989cebbac84 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.5.101", + "version": "1.10.7.100", "default_locale": "en", "description": "__MSG_extShortDesc__", From aadf4a6427fa2960b4fb9e20cfbaab66e2642d81 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 26 Jan 2017 10:17:38 -0500 Subject: [PATCH 0092/4093] fix #2340 --- src/js/assets.js | 182 ++++++++++++++++++++++---------------------- src/js/messaging.js | 19 ++--- src/js/start.js | 14 +++- src/js/storage.js | 81 +++++++++----------- 4 files changed, 147 insertions(+), 149 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index ba83f5d141a16..1e13848cfec66 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -137,72 +137,74 @@ var getTextFileFromURL = function(url, onLoad, onError) { **/ 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://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/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" + "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) { @@ -343,7 +345,7 @@ var saveAssetSourceRegistry = (function() { }; })(); -var updateAssetSourceRegistry = function(json) { +var updateAssetSourceRegistry = function(json, silent) { var newDict; try { newDict = JSON.parse(json); @@ -351,29 +353,29 @@ var updateAssetSourceRegistry = function(json) { } if ( newDict instanceof Object === false ) { return; } - getAssetSourceRegistry(function(oldDict) { - var assetKey; - // Remove obsolete entries (only those which were built-in). - for ( assetKey in oldDict ) { - if ( - newDict[assetKey] === undefined && - oldDict[assetKey].submitter === undefined - ) { - unregisterAssetSource(assetKey); - } + var oldDict = assetSourceRegistry, + assetKey; + + // Remove obsolete entries (only those which were built-in). + for ( assetKey in oldDict ) { + if ( + newDict[assetKey] === undefined && + oldDict[assetKey].submitter === undefined + ) { + unregisterAssetSource(assetKey); } - // Add/update existing entries. Notify of new asset sources. - for ( assetKey in newDict ) { - if ( oldDict[assetKey] === undefined ) { - fireNotification( - 'builtin-asset-source-added', - { assetKey: assetKey, entry: newDict[assetKey] } - ); - } - registerAssetSource(assetKey, newDict[assetKey]); + } + // Add/update existing entries. Notify of new asset sources. + for ( assetKey in newDict ) { + if ( oldDict[assetKey] === undefined && !silent ) { + fireNotification( + 'builtin-asset-source-added', + { assetKey: assetKey, entry: newDict[assetKey] } + ); } - saveAssetSourceRegistry(); - }); + registerAssetSource(assetKey, newDict[assetKey]); + } + saveAssetSourceRegistry(); }; var getAssetSourceRegistry = function(callback) { @@ -406,7 +408,7 @@ var getAssetSourceRegistry = function(callback) { getTextFileFromURL( µBlock.assetsBootstrapLocation || 'assets/assets.json', function() { - updateAssetSourceRegistry(this.responseText); + updateAssetSourceRegistry(this.responseText, true); registryReady(); } ); @@ -899,7 +901,7 @@ api.metadata = function(callback) { }); getAssetCacheRegistry(function() { - cacheRegistryReady = assetCacheRegistry; + cacheRegistryReady = true; if ( assetRegistryReady ) { onReady(); } }); }; diff --git a/src/js/messaging.js b/src/js/messaging.js index 201ddbf2878fc..4549d6c5ba18b 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -754,27 +754,25 @@ var backupUserData = function(callback) { timeStamp: Date.now(), version: vAPI.app.version, userSettings: µb.userSettings, - selectedFilterLists: [], + selectedFilterLists: µb.selectedFilterLists, hiddenSettingsString: µb.stringFromHiddenSettings(), netWhitelist: µb.stringFromWhitelist(µb.netWhitelist), dynamicFilteringString: µb.permanentFirewall.toString(), urlFilteringString: µb.permanentURLFiltering.toString(), hostnameSwitchesString: µb.hnSwitches.toString(), - userFilters: '' - }; - - var onSelectedListsReady = function(selectedFilterLists) { - userData.selectedFilterLists = selectedFilterLists; - + 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. // >>>>>>>> - userData.filterLists = µb.oldDataFromNewListKeys(selectedFilterLists); + filterLists: µb.oldDataFromNewListKeys(µb.selectedFilterLists) // <<<<<<<< + }; + var onUserFiltersReady = function(details) { + userData.userFilters = details.content; var filename = vAPI.i18n('aboutBackupFilename') .replace('{{datetime}}', µb.dateNowToSensibleString()) .replace(/ +/g, '_'); @@ -789,11 +787,6 @@ var backupUserData = function(callback) { getLocalData(callback); }; - var onUserFiltersReady = function(details) { - userData.userFilters = details.content; - µb.loadSelectedFilterLists(onSelectedListsReady); - }; - µb.assets.get(µb.userFiltersPath, onUserFiltersReady); }; diff --git a/src/js/start.js b/src/js/start.js index 2ccd4fc8dc061..8c37217f9b439 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -241,7 +241,7 @@ var fromFetch = function(to, fetched) { /******************************************************************************/ -var onAdminSettingsRestored = function() { +var onSelectedFilterListsLoaded = function() { var fetchableProps = { 'compiledMagic': '', 'dynamicFilteringString': 'behind-the-scene * 3p noop\nbehind-the-scene * 3p-frame noop', @@ -266,6 +266,18 @@ var onAdminSettingsRestored = function() { /******************************************************************************/ +// TODO(seamless migration): +// Eventually selected filter list keys will be loaded as a fetchable +// property. Until then we need to handle backward and forward +// compatibility, this means a special asynchronous call to load selected +// filter lists. + +var onAdminSettingsRestored = function() { + µb.loadSelectedFilterLists(onSelectedFilterListsLoaded); +}; + +/******************************************************************************/ + µb.hiddenSettings = (function() { var out = objectAssign({}, µb.hiddenSettingsDefault), json = vAPI.localStorage.getItem('hiddenSettings'); diff --git a/src/js/storage.js b/src/js/storage.js index 7d394f52d7589..f914c045237ac 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -170,7 +170,12 @@ var µb = this; vAPI.storage.get([ 'selectedFilterLists', 'remoteBlacklists' ], function(bin) { if ( !bin || !bin.selectedFilterLists && !bin.remoteBlacklists ) { - return callback(); + // Select default filter lists if first-time launch. + µb.assets.metadata(function(availableLists) { + µb.saveSelectedFilterLists(µb.autoSelectRegionalFilterLists(availableLists)); + callback(); + }); + return; } var listKeys = []; if ( bin.selectedFilterLists ) { @@ -186,33 +191,30 @@ // Uncomment when all have moved to v1.11 and beyond. //vAPI.storage.remove('remoteBlacklists'); } - µb.selectedFilterLists = listKeys.slice(); - callback(listKeys); + µb.selectedFilterLists = listKeys; + callback(); }); }; µBlock.saveSelectedFilterLists = function(newKeys, append) { - var µb = this; - this.loadSelectedFilterLists(function(oldKeys) { - oldKeys = oldKeys || []; - if ( append ) { - newKeys = newKeys.concat(oldKeys); - } - var newSet = new Set(newKeys); - // Purge unused filter lists from cache. - for ( var i = 0, n = oldKeys.length; i < n; i++ ) { - if ( newSet.has(oldKeys[i]) === false ) { - µb.removeFilterList(oldKeys[i]); - } + var oldKeys = this.selectedFilterLists.slice(); + if ( append ) { + newKeys = newKeys.concat(oldKeys); + } + var newSet = new Set(newKeys); + // Purge unused filter lists from cache. + for ( var i = 0, n = oldKeys.length; i < n; i++ ) { + if ( newSet.has(oldKeys[i]) === false ) { + this.removeFilterList(oldKeys[i]); } - newKeys = µb.setToArray(newSet); - var bin = { - selectedFilterLists: newKeys, - remoteBlacklists: µb.oldDataFromNewListKeys(newKeys) - }; - µb.selectedFilterLists = newKeys; - vAPI.storage.set(bin); - }); + } + newKeys = this.setToArray(newSet); + var bin = { + selectedFilterLists: newKeys, + remoteBlacklists: this.oldDataFromNewListKeys(newKeys) + }; + this.selectedFilterLists = newKeys; + vAPI.storage.set(bin); }; // TODO(seamless migration): @@ -429,7 +431,7 @@ µBlock.autoSelectRegionalFilterLists = function(lists) { var lang = self.navigator.language.slice(0, 2), - selectedListKeys = [], + selectedListKeys = [ this.userFiltersPath ], list; for ( var key in lists ) { if ( lists.hasOwnProperty(key) === false ) { continue; } @@ -522,26 +524,7 @@ } }; - // Selected lists. - var onSelectedListsLoaded = function(keys) { - var listKey; - // No user lists data means use default settings. - if ( Array.isArray(keys) ) { - var listKeySet = new Set(keys); - for ( listKey in newAvailableLists ) { - if ( newAvailableLists.hasOwnProperty(listKey) ) { - newAvailableLists[listKey].off = !listKeySet.has(listKey); - } - } - } else if ( µb.firstInstall ) { - µb.saveSelectedFilterLists(µb.autoSelectRegionalFilterLists(newAvailableLists)); - } - - finalize(); - callback(newAvailableLists); - }; - - // Built-in filter lists. + // Built-in filter lists loaded. var onBuiltinListsLoaded = function(entries) { for ( var assetKey in entries ) { if ( entries.hasOwnProperty(assetKey) === false ) { continue; } @@ -551,7 +534,15 @@ } // Load set of currently selected filter lists. - µb.loadSelectedFilterLists(onSelectedListsLoaded); + var listKeySet = new Set(µb.selectedFilterLists); + for ( listKey in newAvailableLists ) { + if ( newAvailableLists.hasOwnProperty(listKey) ) { + newAvailableLists[listKey].off = !listKeySet.has(listKey); + } + } + + finalize(); + callback(newAvailableLists); }; // Available lists previously computed. From 597583265315763347c47b59fb2ff7e9815a5ec7 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 26 Jan 2017 10:24:28 -0500 Subject: [PATCH 0093/4093] new 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 07989cebbac84..b04c85130c35b 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.7.100", + "version": "1.10.7.101", "default_locale": "en", "description": "__MSG_extShortDesc__", From 0b4f31bd8aba77c5ee84ce46a095480bb821e9bf Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 27 Jan 2017 13:44:52 -0500 Subject: [PATCH 0094/4093] fix #2344 --- assets/assets.json | 2 +- src/js/storage.js | 7 +++---- src/js/utils.js | 12 ++++++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/assets/assets.json b/assets/assets.json index e8dc9cd56a40c..305d1b6cdd658 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -529,7 +529,7 @@ "group": "regions", "off": true, "title": "RUS: RU AdList (Дополнительная региональная подписка)", - "lang": "ru", + "lang": "be ru uk", "contentURL": "https://easylist-downloads.adblockplus.org/advblock.txt", "supportURL": "https://forums.lanik.us/viewforum.php?f=102" }, diff --git a/src/js/storage.js b/src/js/storage.js index f914c045237ac..3467282088fa7 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -430,8 +430,7 @@ /******************************************************************************/ µBlock.autoSelectRegionalFilterLists = function(lists) { - var lang = self.navigator.language.slice(0, 2), - selectedListKeys = [ this.userFiltersPath ], + var selectedListKeys = [ this.userFiltersPath ], list; for ( var key in lists ) { if ( lists.hasOwnProperty(key) === false ) { continue; } @@ -440,7 +439,7 @@ selectedListKeys.push(key); continue; } - if ( list.lang === lang ) { + if ( this.matchCurrentLanguage(list.lang) ) { selectedListKeys.push(key); list.off = false; } @@ -1135,7 +1134,7 @@ if ( details.entry.content === 'filters' ) { if ( details.entry.off !== true || - self.navigator.language.startsWith(details.entry.lang) + this.matchCurrentLanguage(details.entry.lang) ) { this.saveSelectedFilterLists([ details.assetKey ], true); } diff --git a/src/js/utils.js b/src/js/utils.js index 04c9a3e41000c..e7834c3de6a6f 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -245,3 +245,15 @@ }; /******************************************************************************/ + +// https://github.com/gorhill/uBlock/issues/2344 + +µBlock.matchCurrentLanguage = function(s) { + if ( typeof s !== 'string' ) { return false; } + if ( this.matchCurrentLanguage.reLang === undefined ) { + this.matchCurrentLanguage.reLang = new RegExp('\\b' + self.navigator.language.slice(0, 2) + '\\b'); + } + return this.matchCurrentLanguage.reLang.test(s); +}; + +/******************************************************************************/ From 0d2e4f56218e77168fbb98ce476359f489db8f61 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 27 Jan 2017 13:46:33 -0500 Subject: [PATCH 0095/4093] new 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 b04c85130c35b..5c2540e7a23da 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.7.101", + "version": "1.10.7.102", "default_locale": "en", "description": "__MSG_extShortDesc__", From a0c172c13e689c9df2a7f57ada1e115fa4d3db25 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 28 Jan 2017 13:42:33 -0500 Subject: [PATCH 0096/4093] to mitigate https://github.com/gorhill/uBO-Extra/issues/7 --- platform/chromium/vapi-background.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index db6b0305ec29c..63290d47b6ab0 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -978,15 +978,20 @@ vAPI.net.registerListeners = function() { // search for "https://github.com/gorhill/uBlock/issues/1497". var onBeforeWebsocketRequest = function(details) { details.type = 'websocket'; - var matches = /url=([^&]+)/.exec(details.url); + var requestURL = details.url; + var matches = /[?&]url=([^&]+)/.exec(requestURL); details.url = decodeURIComponent(matches[1]); var r = onBeforeRequestClient(details); // Blocked? - if ( r && r.cancel ) { - return r; - } - // Returning a 1x1 transparent pixel means "not blocked". - return { redirectUrl: 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' }; + if ( r && r.cancel ) { return r; } + // Try to redirect to the URL of an image already present in the + // document, or a 1x1 data: URL if none is present. + matches = /[?&]r=([^&]+)/.exec(requestURL); + return { + redirectUrl: matches !== null ? + decodeURIComponent(matches[1]) : + 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==' + }; }; var onBeforeRequestClient = this.onBeforeRequest.callback; From ba3127a59b9c46b6c93ee1befd826f3333a8c21f Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 1 Feb 2017 08:59:35 -0500 Subject: [PATCH 0097/4093] 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 5c2540e7a23da..335f79ab51736 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.7.102", + "version": "1.10.7.103", "default_locale": "en", "description": "__MSG_extShortDesc__", From 1ba853df8cabc43d143535b4b65ed63a4fe992cc Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 1 Feb 2017 09:05:41 -0500 Subject: [PATCH 0098/4093] all releases are pre-release by default --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5f15a9782e3b0..a46f7f9c2113a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ env: script: ./tools/make-${BROWSER}.sh all deploy: provider: releases + prerelease: true # https://github.com/travis-ci/travis-ci/issues/6772 edge: branch: releases-fix From 60605033bfd252f6b13966b70b3ce1f45849af79 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 3 Feb 2017 08:12:26 -0500 Subject: [PATCH 0099/4093] fix #1871? ("blind" fix, need confirmation) --- src/js/messaging.js | 16 ++++++++++++---- src/js/storage.js | 8 ++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index 4549d6c5ba18b..20ec0e3ca11c4 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -793,6 +793,10 @@ var backupUserData = function(callback) { var restoreUserData = function(request) { var userData = request.userData; + var restart = function() { + vAPI.app.restart(); + }; + var onAllRemoved = function() { µBlock.saveLocalSettings(); vAPI.storage.set(userData.userSettings); @@ -811,13 +815,17 @@ var restoreUserData = function(request) { // '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) ) { - µb.saveSelectedFilterLists(userData.selectedFilterLists); + listKeys = userData.selectedFilterLists; } else if ( userData.filterLists instanceof Object ) { - µb.saveSelectedFilterLists(µb.newListKeysFromOldData(userData.filterLists)); + listKeys = µb.newListKeysFromOldData(userData.filterLists); + } + if ( listKeys !== undefined ) { + µb.saveSelectedFilterLists(listKeys, restart); + } else { + restart(); } - - vAPI.app.restart(); }; // https://github.com/chrisaljoudi/uBlock/issues/1102 diff --git a/src/js/storage.js b/src/js/storage.js index 3467282088fa7..66203f733d2fb 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -196,7 +196,11 @@ }); }; -µBlock.saveSelectedFilterLists = function(newKeys, append) { +µBlock.saveSelectedFilterLists = function(newKeys, append, callback) { + if ( typeof append === 'function' ) { + callback = append; + append = false; + } var oldKeys = this.selectedFilterLists.slice(); if ( append ) { newKeys = newKeys.concat(oldKeys); @@ -214,7 +218,7 @@ remoteBlacklists: this.oldDataFromNewListKeys(newKeys) }; this.selectedFilterLists = newKeys; - vAPI.storage.set(bin); + vAPI.storage.set(bin, callback); }; // TODO(seamless migration): From f321ea31c1b6ab6771bf059cbde2673a9cf0f32a Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 3 Feb 2017 08:13:08 -0500 Subject: [PATCH 0100/4093] 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 335f79ab51736..9b2af15813fd9 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.7.103", + "version": "1.10.7.104", "default_locale": "en", "description": "__MSG_extShortDesc__", From 1bceca9cbbddc23e8f02b6f6c81a1cf17939355e Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 5 Feb 2017 07:43:28 -0500 Subject: [PATCH 0101/4093] fix var name (https://github.com/el1t/uBlock-Safari/issues/25#issuecomment-277506943)) --- src/js/storage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/storage.js b/src/js/storage.js index 66203f733d2fb..bfba9d2ea560c 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -1121,7 +1121,7 @@ this.loadFilterLists(); } if ( this.userSettings.autoUpdate ) { - this.scheduleAssetUpdater(this.hiddenSettings.assetAutoUpdatePeriod * 3600000 || 25200000); + this.scheduleAssetUpdater(this.hiddenSettings.autoUpdatePeriod * 3600000 || 25200000); } else { this.scheduleAssetUpdater(0); } From e4e6d8d8c3dc3daf1481f5f8043926e5a549315f Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 5 Feb 2017 15:25:00 -0500 Subject: [PATCH 0102/4093] new stable version --- 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 9b2af15813fd9..b17c8120e87d7 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.10.7.104", + "version": "1.11.0", "default_locale": "en", "description": "__MSG_extShortDesc__", From 2852f9be197ec20387a669df9ce01ef7d6df1c84 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 5 Feb 2017 15:27:21 -0500 Subject: [PATCH 0103/4093] translation work from https://crowdin.com/project/ublock --- src/_locales/fy/messages.json | 10 +++++----- src/_locales/it/messages.json | 2 +- src/_locales/pt_BR/messages.json | 6 +++--- src/_locales/uk/messages.json | 6 +++--- src/_locales/zh_CN/messages.json | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index 059edde5c7c16..d4cdd3a6bf802 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -40,7 +40,7 @@ "description":"appears as tab name in dashboard" }, "advancedSettingsPageName":{ - "message":"Advanced settings", + "message":"Avansearre ynstellingen", "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ @@ -212,7 +212,7 @@ "description":"" }, "settingsAdvancedUserSettings":{ - "message":"advanced settings", + "message":"avansearre ynstellingen", "description":"For the tooltip of a link which gives access to advanced settings" }, "settingsPrefetchingDisabledPrompt":{ @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Jo list fan hostnammen wêrop uBlock₀ útskeakele is. Ien per rigel ynjaan. Unjildige hostnammen wurde stil negearre.", + "message":"De whitelist-ynstruksjes skriuwe foar op hokker websiden uBlock Origin útskeakele wurde moat. Ien fermelding per rigel. Unjildige ynstruksjes wurde sûnder meidieling negearre en útskeakele.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ @@ -680,7 +680,7 @@ "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":"Warskôging! Wizigje dizze avansearre ynstellingen op eigen risiko.", "description":"A warning to users at the top of 'Advanced settings' page" }, "genericSubmit":{ @@ -688,7 +688,7 @@ "description":"for generic 'Submit' buttons" }, "genericApplyChanges":{ - "message":"Apply changes", + "message":"Wizigingen tapasse", "description":"for generic 'Apply changes' buttons" }, "genericRevert":{ diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 594851c70ddd8..a8bf2552f51a9 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -680,7 +680,7 @@ "description":"used as a prompt for the user to provide a custom device name" }, "advancedSettingsWarning":{ - "message":"Attenzione! Cambia queste impostazioni avanzate a tuo rischio e pericolo.", + "message":"Attenzione! Modifica queste impostazioni avanzate a tuo rischio e pericolo.", "description":"A warning to users at the top of 'Advanced settings' page" }, "genericSubmit":{ diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index ecdf542ae01a0..42fcae42696b7 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -8,7 +8,7 @@ "description":"this will be in the chrome web store: must be 132 characters or less" }, "dashboardName":{ - "message":"uBlock₀ — Painel", + "message":"uBlock₀ — Painel de controle", "description":"English: uBlock₀ — Dashboard" }, "settingsPageName":{ @@ -200,7 +200,7 @@ "description":"English: Make use of context menu where appropriate" }, "settingsColorBlindPrompt":{ - "message":"Cores amigáveis para daltônicos", + "message":"Modo Daltonismo", "description":"English: Color-blind friendly" }, "settingsCloudStorageEnabledPrompt":{ @@ -612,7 +612,7 @@ "description":"English: {{value}} days ago" }, "showDashboardButton":{ - "message":"Mostrar Painel", + "message":"Mostrar Painel de Controle", "description":"Firefox\/Fennec-specific: Show Dashboard" }, "showNetworkLogButton":{ diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index 5757874f75717..7f0ae5f73f0fb 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -84,7 +84,7 @@ "description":"Tooltip for the no-popups per-site switch" }, "popupTipNoLargeMedia":{ - "message":"Перемкнути блокування великих медіа елементів на цьому сайті", + "message":"Увімк\/Вимк блокування великих медіа елементів на цьому сайті", "description":"Tooltip for the no-large-media per-site switch" }, "popupTipNoCosmeticFiltering":{ @@ -292,7 +292,7 @@ "description":"This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo":{ - "message":"

      Загальні косметичні фільтри — це косметичні фільтри, які повинні застосовуватися до всіх веб-сторінок.

      Хоча uBlock і звертається з ними ефективно, вони все одно можуть вимагати значну кількість ресурсів на деяких, особливо великих, сторінках.

      Увімкнення цього параметра знизить споживання ресурсів таких сторінок від застосування загальних косметичних фільтрів, а також знизить споживання пам'яті самого uBlock.

      Рекомендується увімкнути цей параметр на слабких пристроях.", + "message":"

      Загальні косметичні фільтри — це косметичні фільтри, які застосовуються до всіх веб-сторінок.

      Хоча й uBlock обробує фільтри ефективно, вони все одно можуть вимагати значну кількість ресурсів на деяких, особливо навантаженних сторінках.

      Увімкнення цього параметра знизить споживання ресурсів на таких сторінках від застосування загальних косметичних фільтрів, а також знизить споживання пам'яті самого uBlock.

      Рекомендується увімкнути цей параметр на слабких пристроях.", "description":"Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pListsOfBlockedHostsHeader":{ @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Список тих доменів, для яких µBlock буде вимикатись. Один запис на рядок. Недопустимі назви будуть ігноруватись.", + "message":"Ваш список адрес сайтів, для яких µBlock буде неактивним. Додайте по одному запису на рядок. Невірні адреси будуть проігноровані без зауважень.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 1d5fe1c75f8c7..7cd9edb6802fc 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -224,7 +224,7 @@ "description":"English: " }, "settingsWebRTCIPAddressHiddenPrompt":{ - "message":"防止 WebRTC 泄露本地IP地址", + "message":"避免 WebRTC 泄露本地IP地址", "description":"English: " }, "settingPerSiteSwitchGroup":{ From a742f09dd4ba37d748c962bed171ddd84bf046ea Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 6 Feb 2017 15:34:31 -0500 Subject: [PATCH 0104/4093] fix #2360 --- src/js/traffic.js | 62 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 29918bfbe0073..76c5ae73d4fa6 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -443,17 +443,22 @@ var processCSP = function(pageStore, details) { blockInlineScript = µb.isBlockResult(inlineScriptResult); } - context.requestType = 'websocket'; µb.staticNetFilteringEngine.matchStringExactType(context, requestURL, 'websocket'); var websocketResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled), blockWebsocket = µb.isBlockResult(websocketResult); + // https://github.com/gorhill/uBlock/issues/2360 + µb.staticNetFilteringEngine.matchStringExactType(context, 'blob:', 'script'); + var workerResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled), + blockWorker = µb.isBlockResult(workerResult); + var headersChanged; - if ( blockInlineScript || blockWebsocket ) { + if ( blockInlineScript || blockWebsocket || blockWorker ) { headersChanged = foilWithCSP( details.responseHeaders, blockInlineScript, - blockWebsocket + blockWebsocket, + blockWorker ); } @@ -480,6 +485,17 @@ var processCSP = function(pageStore, details) { context.pageHostname ); } + if ( workerResult !== '' ) { + µb.logger.writeOne( + tabId, + 'net', + workerResult, + 'worker', + requestURL, + context.rootHostname, + context.pageHostname + ); + } } context.dispose(); @@ -524,26 +540,38 @@ var foilLargeMediaElement = function(pageStore, details) { /******************************************************************************/ -var foilWithCSP = function(headers, noInlineScript, noWebsocket) { - var i = headerIndexFromName('content-security-policy', headers), +var foilWithCSP = function(headers, noInlineScript, noWebsocket, noWorker) { + var me = foilWithCSP, + i = headerIndexFromName('content-security-policy', headers), before = i === -1 ? '' : headers[i].value.trim(), after = before; if ( noInlineScript ) { after = foilWithCSPDirective( after, - /script-src[^;]*;?\s*/, + me.reScriptSrc, "script-src 'unsafe-eval' *", - /'unsafe-inline'\s*|'nonce-[^']+'\s*/g + me.reScriptSrcRemove ); } if ( noWebsocket ) { after = foilWithCSPDirective( after, - /connect-src[^;]*;?\s*/, + me.reConnectSrc, 'connect-src http:', - /wss?:[^\s]*\s*/g + me.reConnectSrcRemove + ); + } + + // https://www.w3.org/TR/CSP2/#directive-child-src + // https://www.w3.org/TR/CSP3/#directive-worker-src + if ( noWorker ) { + after = foilWithCSPDirective( + after, + me.reWorkerSrc, + 'child-src http:', + me.reWorkerSrcRemove ); } @@ -556,9 +584,9 @@ var foilWithCSP = function(headers, noInlineScript, noWebsocket) { // https://w3c.github.io/webappsec-csp/#directive-frame-src after = foilWithCSPDirective( after, - /frame-src[^;]*;?\s*/, + me.reFrameSrc, 'frame-src http:', - /data:[^\s]*\s*|blob:[^\s]*\s*/g + me.reFrameSrcRemove ); } @@ -573,6 +601,18 @@ var foilWithCSP = function(headers, noInlineScript, noWebsocket) { return changed; }; +(function() { + var fn = foilWithCSP; + fn.reScriptSrc = /script-src[^;]*;?\s*/; + fn.reScriptSrcRemove = /'unsafe-inline'\s*|'nonce-[^']+'\s*/g; + fn.reConnectSrc = /connect-src[^;]*;?\s*/; + fn.reConnectSrcRemove = /wss?:[^\s]*\s*/g; + fn.reWorkerSrc = /child-src[^;]*;?\s*/; + fn.reWorkerSrcRemove = /blob:[^\s]*\s*/g; + fn.reFrameSrc = /frame-src[^;]*;?\s*/; + fn.reFrameSrcRemove = /data:[^\s]*\s*|blob:[^\s]*\s*/g; +})(); + /******************************************************************************/ // Past issues to keep in mind: From 1d705485e9c855b7c113f6cd7114dcd8e0abeeac Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 6 Feb 2017 15:36:34 -0500 Subject: [PATCH 0105/4093] update max version info --- platform/firefox/install.rdf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/firefox/install.rdf b/platform/firefox/install.rdf index 2307fcac62f09..8cb7a2edd6beb 100644 --- a/platform/firefox/install.rdf +++ b/platform/firefox/install.rdf @@ -39,7 +39,7 @@ {{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}} 2.21 - 2.40.* + * @@ -48,7 +48,7 @@ {{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}} 25.0 - 26.* + 27.* From 48b3ba161b405be6b59e0ac6d4a0f57b20ad59ab Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 6 Feb 2017 15:38:39 -0500 Subject: [PATCH 0106/4093] new version 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 b17c8120e87d7..b6f4067097a5a 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.0", + "version": "1.11.1.0", "default_locale": "en", "description": "__MSG_extShortDesc__", From 7176ecb3e73522396acc1db498ab2af44ff0f6c6 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 7 Feb 2017 08:05:39 -0500 Subject: [PATCH 0107/4093] code review of fix to #2360 --- src/js/traffic.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 76c5ae73d4fa6..303a6872fa1e2 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -430,28 +430,29 @@ var processCSP = function(pageStore, details) { loggerEnabled = µb.logger.isEnabled(); var context = pageStore.createContextFromPage(); - context.requestURL = requestURL; context.requestHostname = µb.URI.hostnameFromURI(requestURL); if ( details.type !== 'main_frame' ) { context.pageHostname = context.pageDomain = context.requestHostname; } - var inlineScriptResult, blockInlineScript; + var inlineScriptResult, blockInlineScript, + workerResult, blockWorker; if ( details.type !== 'script' ) { context.requestType = 'inline-script'; + context.requestURL = requestURL; inlineScriptResult = pageStore.filterRequestNoCache(context); blockInlineScript = µb.isBlockResult(inlineScriptResult); + // https://github.com/gorhill/uBlock/issues/2360 + context.requestType = 'script'; + context.requestURL = 'blob:'; + workerResult = pageStore.filterRequestNoCache(context); + blockWorker = µb.isBlockResult(workerResult); } µb.staticNetFilteringEngine.matchStringExactType(context, requestURL, 'websocket'); var websocketResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled), blockWebsocket = µb.isBlockResult(websocketResult); - // https://github.com/gorhill/uBlock/issues/2360 - µb.staticNetFilteringEngine.matchStringExactType(context, 'blob:', 'script'); - var workerResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled), - blockWorker = µb.isBlockResult(workerResult); - var headersChanged; if ( blockInlineScript || blockWebsocket || blockWorker ) { headersChanged = foilWithCSP( From cbca48307c50d4e243462c4c86af03453bbdf766 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 9 Feb 2017 13:33:32 -0500 Subject: [PATCH 0108/4093] fix https://github.com/gorhill/uBlock/pull/2314#issuecomment-278716960 --- src/js/messaging.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/js/messaging.js b/src/js/messaging.js index 20ec0e3ca11c4..776cf192838c9 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -987,6 +987,10 @@ var onMessage = function(request, sender, callback) { case 'purgeCache': µb.assets.purge(request.assetKey); µb.assets.remove('compiled/' + request.assetKey); + // https://github.com/gorhill/uBlock/pull/2314#issuecomment-278716960 + if ( request.assetKey === 'ublock-filters' ) { + µb.assets.purge('ublock-resources'); + } break; case 'readHiddenSettings': From 1e1508cdd2d639cc8e669053a5dcec39d887224b Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 11 Feb 2017 11:44:18 -0500 Subject: [PATCH 0109/4093] code review of dynamic URL filtering engine --- src/js/url-net-filtering.js | 258 +++++++++++++----------------------- 1 file changed, 94 insertions(+), 164 deletions(-) diff --git a/src/js/url-net-filtering.js b/src/js/url-net-filtering.js index a027cec69e047..2891a268b6a26 100644 --- a/src/js/url-net-filtering.js +++ b/src/js/url-net-filtering.js @@ -1,7 +1,7 @@ /******************************************************************************* - uBlock - a browser extension to black/white list requests. - Copyright (C) 2015 Raymond Hill + uBlock Origin - a browser extension to black/white list requests. + 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 @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* global µBlock */ +'use strict'; /******************************************************************************/ @@ -28,16 +28,12 @@ µBlock.URLNetFiltering = (function() { -'use strict'; - /******************************************************************************* -buckets: map of [origin + urlkey + type] - bucket: array of rule entry, sorted from shorter to longer url - +buckets: map of [hostname + type] + bucket: array of rule entries, sorted from shorter to longer url rule entry: { url, action } - *******************************************************************************/ /******************************************************************************/ @@ -63,13 +59,13 @@ var RuleEntry = function(url, action) { /******************************************************************************/ -var indexOfURL = function(urls, url) { +var indexOfURL = function(entries, url) { // TODO: binary search -- maybe, depends on common use cases - var urlLen = url.length; - var entry; - // urls must be ordered by increasing length. - for ( var i = 0; i< urls.length; i++ ) { - entry = urls[i]; + var urlLen = url.length, + entry; + // URLs must be ordered by increasing length. + for ( var i = 0; i < entries.length; i++ ) { + entry = entries[i]; if ( entry.url.length > urlLen ) { break; } @@ -82,30 +78,31 @@ var indexOfURL = function(urls, url) { /******************************************************************************/ -var indexOfMatch = function(urls, url) { - // TODO: binary search -- maybe, depends on common use cases - var urlLen = url.length; - var i = urls.length; - var entry; +var indexOfMatch = function(entries, url) { + var urlLen = url.length, + i = entries.length; while ( i-- ) { - entry = urls[i]; - if ( entry.url.length > urlLen ) { - continue; - } - if ( url.startsWith(entry.url) ) { - return i; + if ( entries[i].url.length <= urlLen ) { + break; } } + if ( i !== -1 ) { + do { + if ( url.startsWith(entries[i].url) ) { + return i; + } + } while ( i-- ); + } return -1; }; /******************************************************************************/ -var indexFromLength = function(urls, len) { +var indexFromLength = function(entries, len) { // TODO: binary search -- maybe, depends on common use cases - // urls must be ordered by increasing length. - for ( var i = 0; i< urls.length; i++ ) { - if ( urls[i].url.length > len ) { + // URLs must be ordered by increasing length. + for ( var i = 0; i < entries.length; i++ ) { + if ( entries[i].url.length > len ) { return i; } } @@ -114,43 +111,26 @@ var indexFromLength = function(urls, len) { /******************************************************************************/ -var addRuleEntry = function(urls, url, action) { - var entry = new RuleEntry(url, action); - var i = indexFromLength(urls, url.length); +var addRuleEntry = function(entries, url, action) { + var entry = new RuleEntry(url, action), + i = indexFromLength(entries, url.length); if ( i === -1 ) { - urls.push(entry); + entries.push(entry); } else { - urls.splice(i, 0, entry); + entries.splice(i, 0, entry); } }; /******************************************************************************/ -var urlKeyFromURL = function(url) { - // Experimental: running benchmarks first - //if ( url === '*' ) { - // return url; - //} - var match = reURLKey.exec(url); - return match !== null ? match[0] : ''; -}; - -var reURLKey = /^[a-z]+:\/\/[^\/?#]+/; - -/******************************************************************************/ - var URLNetFiltering = function() { this.reset(); }; /******************************************************************************/ -// rules: -// origin + urlkey + type => urls -// urls = collection of urls to match - URLNetFiltering.prototype.reset = function() { - this.rules = Object.create(null); + this.rules = new Map(); // registers, filled with result of last evaluation this.context = ''; this.url = ''; @@ -161,20 +141,24 @@ URLNetFiltering.prototype.reset = function() { /******************************************************************************/ URLNetFiltering.prototype.assign = function(other) { - var thisRules = this.rules; - var otherRules = other.rules; - var k; - + var thisRules = this.rules, + otherRules = other.rules, + iter, item; // Remove rules not in other - for ( k in thisRules ) { - if ( otherRules[k] === undefined ) { - delete thisRules[k]; + iter = thisRules.entries(); + for (;;) { + item = iter.next(); + if ( item.done ) { break; } + if ( otherRules.has(item.value) === false ) { + thisRules.delete(item.value); } } - // Add/change rules in other - for ( k in otherRules ) { - thisRules[k] = otherRules[k].slice(); + iter = otherRules.entries(); + for (;;) { + item = iter.next(); + if ( item.done ) { break; } + thisRules.set(item.value[0], item.value[1].slice()); } }; @@ -184,117 +168,77 @@ URLNetFiltering.prototype.setRule = function(srcHostname, url, type, action) { if ( action === 0 ) { return this.removeRule(srcHostname, url, type); } - - var urlKey = urlKeyFromURL(url); - if ( urlKey === '' ) { - return false; + var bucketKey = srcHostname + ' ' + type, + entries = this.rules.get(bucketKey); + if ( entries === undefined ) { + entries = []; + this.rules.set(bucketKey, entries); } - - var bucketKey = srcHostname + ' ' + urlKey + ' ' + type; - var urls = this.rules[bucketKey]; - if ( urls === undefined ) { - urls = this.rules[bucketKey] = []; - } - - var entry; - var i = indexOfURL(urls, url); + var i = indexOfURL(entries, url), + entry; if ( i !== -1 ) { - entry = urls[i]; - if ( entry.action === action ) { - return false; - } + entry = entries[i]; + if ( entry.action === action ) { return false; } entry.action = action; - return true; + } else { + addRuleEntry(entries, url, action); } - - addRuleEntry(urls, url, action); return true; }; /******************************************************************************/ URLNetFiltering.prototype.removeRule = function(srcHostname, url, type) { - var urlKey = urlKeyFromURL(url); - if ( urlKey === '' ) { - return false; - } - - var bucketKey = srcHostname + ' ' + urlKey + ' ' + type; - var urls = this.rules[bucketKey]; - if ( urls === undefined ) { + var bucketKey = srcHostname + ' ' + type, + entries = this.rules.get(bucketKey); + if ( entries === undefined ) { return false; } - - var i = indexOfURL(urls, url); + var i = indexOfURL(entries, url); if ( i === -1 ) { return false; } - - urls.splice(i, 1); - if ( urls.length === 0 ) { - delete this.rules[bucketKey]; + entries.splice(i, 1); + if ( entries.length === 0 ) { + this.rules.delete(bucketKey); } - return true; }; /******************************************************************************/ URLNetFiltering.prototype.evaluateZ = function(context, target, type) { - var urlKey = urlKeyFromURL(target); - if ( urlKey === '' ) { - this.r = 0; + this.r = 0; + if ( this.rules.size === 0 ) { return this; } - - var urls, pos, i, entry, keyShard; - + var entries, pos, i, entry; for (;;) { this.context = context; - keyShard = context + ' ' + urlKey; - if ( (urls = this.rules[keyShard + ' ' + type]) ) { - i = indexOfMatch(urls, target); + if ( (entries = this.rules.get(context + ' ' + type)) ) { + i = indexOfMatch(entries, target); if ( i !== -1 ) { - entry = urls[i]; + entry = entries[i]; this.url = entry.url; this.type = type; this.r = entry.action; return this; } } - if ( (urls = this.rules[keyShard + ' *']) ) { - i = indexOfMatch(urls, target); + if ( (entries = this.rules.get(context + ' *')) ) { + i = indexOfMatch(entries, target); if ( i !== -1 ) { - entry = urls[i]; + entry = entries[i]; this.url = entry.url; this.type = '*'; this.r = entry.action; return this; } } - /* Experimental: running benchmarks first - if ( urls = this.rules[context + ' * ' + type] ) { - entry = urls[0]; - this.url = '*'; - this.type = type; - this.r = entry.action; - return this; - } - if ( urls = this.rules[context + ' * *'] ) { - entry = urls[0]; - this.url = this.type = '*'; - this.r = entry.action; - return this; - } - */ - if ( context === '*' ) { - break; - } + if ( context === '*' ) { break; } pos = context.indexOf('.'); context = pos !== -1 ? context.slice(pos + 1) : '*'; } - - this.r = 0; return this; }; @@ -350,16 +294,20 @@ URLNetFiltering.prototype.copyRules = function(other, context, urls, type) { // "url-filtering:" hostname url type action URLNetFiltering.prototype.toString = function() { - var out = []; - var pos, hn, type, urls, i, entry; - for ( var bucketKey in this.rules ) { - pos = bucketKey.indexOf(' '); - hn = bucketKey.slice(0, pos); - pos = bucketKey.lastIndexOf(' '); - type = bucketKey.slice(pos + 1); - urls = this.rules[bucketKey]; - for ( i = 0; i < urls.length; i++ ) { - entry = urls[i]; + var out = [], + iter = this.rules.entries(), + item, key, pos, hn, type, entries, i, entry; + for (;;) { + item = iter.next(); + if ( item.done ) { break; } + key = item.value[0]; + pos = key.indexOf(' '); + hn = key.slice(0, pos); + pos = key.lastIndexOf(' '); + type = key.slice(pos + 1); + entries = item.value[1]; + for ( i = 0; i < entries.length; i++ ) { + entry = entries[i]; out.push( hn + ' ' + entry.url + ' ' + @@ -374,46 +322,28 @@ URLNetFiltering.prototype.toString = function() { /******************************************************************************/ URLNetFiltering.prototype.fromString = function(text) { - var textEnd = text.length; - var lineBeg = 0, lineEnd; - var line, fields; - this.reset(); - while ( lineBeg < textEnd ) { - lineEnd = text.indexOf('\n', lineBeg); - if ( lineEnd < 0 ) { - lineEnd = text.indexOf('\r', lineBeg); - if ( lineEnd < 0 ) { - lineEnd = textEnd; - } - } - line = text.slice(lineBeg, lineEnd).trim(); - lineBeg = lineEnd + 1; - - if ( line === '' ) { - continue; - } - + var lineIter = new µBlock.LineIterator(text), + line, fields; + while ( lineIter.eot() === false ) { + line = lineIter.next().trim(); + if ( line === '' ) { continue; } // Coarse test if ( line.indexOf('://') === -1 ) { continue; } - fields = line.split(/\s+/); if ( fields.length !== 4 ) { continue; } - // Finer test if ( fields[1].indexOf('://') === -1 ) { continue; } - if ( nameToActionMap.hasOwnProperty(fields[3]) === false ) { continue; } - this.setRule(fields[0], fields[1], fields[2], nameToActionMap[fields[3]]); } }; From 28084e1dc9bbe4d615347c474aea64f8e2f1a193 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 12 Feb 2017 15:53:40 -0500 Subject: [PATCH 0110/4093] code review: marginal performance improvement --- src/js/contentscript.js | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index bf3729b708ce5..c4d29e5748fe5 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.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 @@ -1533,36 +1533,48 @@ vAPI.domSurveyor = (function() { var surveyPhase1 = function(addedNodes) { var t0 = window.performance.now(), - nodes = selectNodes('[class],[id]', addedNodes), + rews = reWhitespace, qq = queriedSelectors, ll = lowGenericSelectors, - node, v, vv, j, - i = nodes.length; + lli = ll.length, + nodes, i, node, v, vv, j; + nodes = selectNodes('[id]', addedNodes); + i = nodes.length; while ( i-- ) { node = nodes[i]; - if ( node.nodeType !== 1 ) { continue; } v = node.id; - if ( v !== '' && typeof v === 'string' ) { - v = '#' + v.trim(); - if ( v !== '#' && !qq.has(v) ) { ll.push(v); qq.add(v); } + if ( typeof v !== 'string' ) { continue; } + v = '#' + v.trim(); + if ( !qq.has(v) && v.length !== 1 ) { + ll[lli] = v; lli++; qq.add(v); } + } + nodes = selectNodes('[class]', addedNodes); + i = nodes.length; + while ( i-- ) { + node = nodes[i]; vv = node.className; - if ( vv === '' || typeof vv !== 'string' ) { continue; } - if ( /\s/.test(vv) === false ) { + if ( typeof vv !== 'string' ) { continue; } + if ( !rews.test(vv) ) { v = '.' + vv; - if ( !qq.has(v) ) { ll.push(v); qq.add(v); } + if ( !qq.has(v) && v.length !== 1 ) { + ll[lli] = v; lli++; qq.add(v); + } } else { vv = node.classList; j = vv.length; while ( j-- ) { v = '.' + vv[j]; - if ( !qq.has(v) ) { ll.push(v); qq.add(v); } + if ( !qq.has(v) ) { + ll[lli] = v; lli++; qq.add(v); + } } } } surveyCost += window.performance.now() - t0; surveyPhase2(addedNodes); }; + var reWhitespace = /\s/; var domChangedHandler = function(addedNodes) { if ( cosmeticSurveyingMissCount > 255 ) { From 1c4347d69d540916b609835dd63c56a11ab7b2e5 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 13 Feb 2017 08:33:10 -0500 Subject: [PATCH 0111/4093] element picker improvement: to not discard class information when an id is available Use class(es) whenever available instead of the id when selecting a broad cosmetic filter (ctrl-click). When asking for a broad cosmetic filter, using the id instead of whatever available class(es) is limiting usefulness. The change here address this. Example of use case: open . Now how to remove all signature widgets from all posts? Without the change here, this was not possible without opening the browser's inspector, finding out and manually typing whatever class is used to identify the signature's root element. With this commit, ctrl-click will now use whatever class information exist instead of the id. --- src/js/scriptlets/element-picker.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index 80d0b4aa49fda..1ab7e12d7f7b2 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -489,13 +489,11 @@ var cosmeticFilterFromElement = function(elem) { } // Class(es) - if ( selector === '' ) { - v = elem.classList; - if ( v ) { - i = v.length || 0; - while ( i-- ) { - selector += '.' + CSS.escape(v.item(i)); - } + v = elem.classList; + if ( v ) { + i = v.length || 0; + while ( i-- ) { + selector += '.' + CSS.escape(v.item(i)); } } @@ -1036,13 +1034,22 @@ var candidateFromFilterChoice = function(filterChoice) { // - Do not compute exact path. // - Discard narrowing directives. if ( filterChoice.modifier ) { - return filter.replace(/:nth-of-type\(\d+\)/, ''); + filter = filter.replace(/:nth-of-type\(\d+\)/, ''); + // Remove the id if one or more classes exist. + if ( filter.charAt(2) === '#' && filter.indexOf('.') !== -1 ) { + filter = filter.replace(/#[^#.]+/, ''); + } + return filter; } // Return path: the target element, then all siblings prepended var selector = '', joiner = ''; for ( ; slot < filters.length; slot++ ) { filter = filters[slot]; + // Remove all classes when an id exists. + if ( filter.charAt(2) === '#' ) { + filter = filter.replace(/\..+$/, ''); + } selector = filter.slice(2) + joiner + selector; // Stop at any element with an id: these are unique in a web page if ( filter.lastIndexOf('###', 0) === 0 ) { From ea8cca144560546ee649a377b3fd5af5338fff09 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 13 Feb 2017 08:56:42 -0500 Subject: [PATCH 0112/4093] 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 b6f4067097a5a..af3c1f9761745 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.1.0", + "version": "1.11.1.1", "default_locale": "en", "description": "__MSG_extShortDesc__", From 2d3cc2be264eec9c9f5d638f1bbc559784a05268 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 16 Feb 2017 12:47:12 -0500 Subject: [PATCH 0113/4093] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72b4f7f996708..189232fcb4dca 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ You can install the latest version [manually](https://github.com/gorhill/uBlock/ It is expected that uBlock Origin is compatible with any Chromium-based browsers. -**Important:** Chromium-based browsers do not relay [websocket connections](https://en.wikipedia.org/wiki/WebSocket) to the extension API. This means websites can use websocket connections to bypass uBO (or any other blocker). This can be remediated by installing uBO's companion extension [uBO-WebSocket](https://github.com/gorhill/uBO-WebSocket). +**Important:** Chromium-based browsers do not relay [websocket connections](https://en.wikipedia.org/wiki/WebSocket) to the extension API. This means websites can use websocket connections to bypass uBO (or any other blocker). This can be remediated by installing uBO's companion extension [uBO-Extra](https://github.com/gorhill/uBO-Extra). #### Firefox / Firefox for Android From 39f9d11ec52fa0da5ae96b9f670c08b651d26925 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 18 Feb 2017 09:01:36 -0500 Subject: [PATCH 0114/4093] prepare uBO for fix to https://github.com/gorhill/uBO-Extra/issues/29 --- platform/chromium/vapi-background.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 63290d47b6ab0..2a4fdd66dfc61 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -976,16 +976,23 @@ vAPI.net.registerListeners = function() { // logger, etc. // Counterpart of following block of code is found in "vapi-client.js" -- // search for "https://github.com/gorhill/uBlock/issues/1497". + // + // Once uBO 1.11.1 and uBO-Extra 2.12 are widespread, the image-based + // handling code can be removed. var onBeforeWebsocketRequest = function(details) { + if ( (details.type !== 'image') && + (details.method !== 'HEAD' || details.type !== 'xmlhttprequest') + ) { + return; + } + var requestURL = details.url, + matches = /[?&]u(?:rl)?=([^&]+)/.exec(requestURL); + if ( matches === null ) { return; } details.type = 'websocket'; - var requestURL = details.url; - var matches = /[?&]url=([^&]+)/.exec(requestURL); details.url = decodeURIComponent(matches[1]); var r = onBeforeRequestClient(details); - // Blocked? if ( r && r.cancel ) { return r; } - // Try to redirect to the URL of an image already present in the - // document, or a 1x1 data: URL if none is present. + // Redirect to the provided URL, or a 1x1 data: URI if none provided. matches = /[?&]r=([^&]+)/.exec(requestURL); return { redirectUrl: matches !== null ? @@ -997,11 +1004,9 @@ vAPI.net.registerListeners = function() { var onBeforeRequestClient = this.onBeforeRequest.callback; var onBeforeRequest = function(details) { // https://github.com/gorhill/uBlock/issues/1497 - if ( - details.type === 'image' && - details.url.endsWith('ubofix=f41665f3028c7fd10eecf573336216d3') - ) { - return onBeforeWebsocketRequest(details); + if ( details.url.endsWith('ubofix=f41665f3028c7fd10eecf573336216d3') ) { + var r = onBeforeWebsocketRequest(details); + if ( r !== undefined ) { return r; } } normalizeRequestDetails(details); From 414f3a98b79098796123813f7c85aac164f08211 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 18 Feb 2017 09:03:02 -0500 Subject: [PATCH 0115/4093] 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 af3c1f9761745..8e29fb8c56c35 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.1.1", + "version": "1.11.1.100", "default_locale": "en", "description": "__MSG_extShortDesc__", From e2fd7e48c81886f2dba42020891e070a0daae846 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 19 Feb 2017 10:06:35 -0500 Subject: [PATCH 0116/4093] fix #2388 --- dist/description/description-da.txt | 6 +++--- src/_locales/ar/messages.json | 2 +- src/_locales/da/messages.json | 6 +++--- src/_locales/eo/messages.json | 4 ++-- src/_locales/he/messages.json | 2 +- src/_locales/pt_BR/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dist/description/description-da.txt b/dist/description/description-da.txt index 320a7bb9a460f..0eb6f46f4ba62 100644 --- a/dist/description/description-da.txt +++ b/dist/description/description-da.txt @@ -1,8 +1,8 @@ En effektiv blocker: let på hukommelse og CPU forbrug,. Kan indlæse og anvende tusindvis af flere filtre end andre populære blockere derude. -Illustreret oversigt over effektiviteten: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP :-Efficiency-compared +Illustreret oversigt over effektiviteten: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-Efficiency-compared -Anvendelse: Den Store power knap i pop-up-vinduet kan permanent deaktivere/aktivere uBlock på det aktuelle websted. Dette gælder kun for det aktuelle websted, det er ikke en global afbryderknap. +Anvendelse: Den store tænd-sluk-knap i pop op-vinduet bruges til permanent at deaktivere/aktivere uBlock på det aktuelle websted. Dette gælder kun for det aktuelle websted, det er ikke en global afbryderknap. *** @@ -24,7 +24,7 @@ Flere lister er tilgængelige hvis du ønsker det: - Spam404 - Osv. -Selvfølgelig vil flere aktive filtre betyde højere hukommelsesforbrug. Selv efter tilføjelse af Fanboys to ekstra lister, og hpHosts’s Ad and tracking servers, har uBlock₀ stadig et lavere hukommelsesforbrug end andre blokere derude. +Selvfølgelig vil flere aktive filtre betyde højere hukommelsesforbrug. Men selv efter tilføjelse af Fanboys to ekstra lister, samt hpHosts’s Ad and tracking servers, har uBlock stadig et lavere hukommelsesforbrug end andre meget populære blockere derude. Vær desuden opmærksom på, at hvis du vælger nogle af disse ekstra lister kan det føre til højere sandsynlighed for, at webstedet bliver vist forkert - især de lister der normalt anvendes som hosts-fil. diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 82cdca5574306..4171b441bc8ec 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -208,7 +208,7 @@ "description":"" }, "settingsAdvancedUserPrompt":{ - "message":"أنا مستخدم متقدم (قراءة إجبارية<\/a>)", + "message":"أنا مستخدم ذو خبرة (قراءة إجبارية<\/a>)", "description":"" }, "settingsAdvancedUserSettings":{ diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 68b994610c549..17dc2f4e25ae0 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -224,7 +224,7 @@ "description":"English: " }, "settingsWebRTCIPAddressHiddenPrompt":{ - "message":"Forhindre WebRTC i at lække lokale IP-adresser", + "message":"Forhindr WebRTC i at lække lokale IP-adresser", "description":"English: " }, "settingPerSiteSwitchGroup":{ @@ -232,7 +232,7 @@ "description":"" }, "settingPerSiteSwitchGroupSynopsis":{ - "message":"Disse standardindstillinger kan overskrives på en per-side basis", + "message":"Disse standardindstillinger kan tilsidesættes for hver enkelt websted", "description":"" }, "settingsNoCosmeticFilteringPrompt":{ @@ -240,7 +240,7 @@ "description":"" }, "settingsNoLargeMediaPrompt":{ - "message":"Bloker medieelementer større end {{input: nummer}} kB", + "message":"Bloker medieelementer større end {{input: number}} kB", "description":"" }, "settingsNoRemoteFontsPrompt":{ diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index 90b5bfe67d937..ad8ac35cad8f6 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -40,7 +40,7 @@ "description":"appears as tab name in dashboard" }, "advancedSettingsPageName":{ - "message":"Advanced settings", + "message":"Altgradaj agordoj", "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ @@ -212,7 +212,7 @@ "description":"" }, "settingsAdvancedUserSettings":{ - "message":"advanced settings", + "message":"altgradaj agordoj", "description":"For the tooltip of a link which gives access to advanced settings" }, "settingsPrefetchingDisabledPrompt":{ diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index 1d8022796e6b3..c4b6e28935064 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"רשימת שמות המתחם שלך בהם uBlock₀ לא יהיה פעיל. רשומה אחת בכל שורה. שמות מתחם לא חוקיים לא יפורשו ולא תהיה התראה לכך.", + "message":"הרשומות ברשימה הלבנה מציינות באילו דפי אינטרנט uBlock Origin לא יהיה פעיל. רשומה אחת בכל שורה. רשומות לא חוקיות תתעלמנה בשקט ויסומנו כהערות.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 42fcae42696b7..28d6050f26ed8 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -200,7 +200,7 @@ "description":"English: Make use of context menu where appropriate" }, "settingsColorBlindPrompt":{ - "message":"Modo Daltonismo", + "message":"Modo daltonismo", "description":"English: Color-blind friendly" }, "settingsCloudStorageEnabledPrompt":{ diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index f7f8996fefbf3..51b40c8c852ba 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -76,7 +76,7 @@ "description":"English: Enter element picker mode" }, "popupTipLog":{ - "message":"Öppna loggaren", + "message":"Öppna loggen", "description":"Tooltip used for the logger icon in the panel" }, "popupTipNoPopups":{ From 2a2cbdec1f07272f6cc1fa37eb9b45c1fb47e0cd Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 19 Feb 2017 10:20:48 -0500 Subject: [PATCH 0117/4093] #2388: forgot to also remove extraneous space --- src/_locales/da/messages.json | 2 +- src/_locales/pt_PT/messages.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 17dc2f4e25ae0..0c48870bff159 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -240,7 +240,7 @@ "description":"" }, "settingsNoLargeMediaPrompt":{ - "message":"Bloker medieelementer større end {{input: number}} kB", + "message":"Bloker medieelementer større end {{input:number}} kB", "description":"" }, "settingsNoRemoteFontsPrompt":{ diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index e2d63b466d109..6e110e1c4d89e 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -560,7 +560,7 @@ "description":"English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton":{ - "message":"Restaurar por um ficheiro...", + "message":"Restaurar a partir de um ficheiro...", "description":"English: Restore from file..." }, "aboutResetDataButton":{ @@ -568,7 +568,7 @@ "description":"English: Reset to default settings..." }, "aboutRestoreDataConfirm":{ - "message":"Todas as suas definições serão substituídas utilizando os dados da cópia de segurança de {{time}}, e uBlock₀ irá reiniciar.\n\nSubstituir todas as definições existentes utilizando os dados da cópia de segurança?", + "message":"Todas as suas definições serão substituídas utilizando os dados da cópia de segurança de {{time}}, e o uBlock₀ irá reiniciar.\n\nSubstituir todas as definições existentes utilizando os dados da cópia de segurança?", "description":"Message asking user to confirm restore" }, "aboutRestoreDataError":{ From 798e21de3640b9684d504e4c44384196e0e1e014 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 27 Feb 2017 16:29:36 -0500 Subject: [PATCH 0118/4093] fix #2414 --- src/epicker.html | 6 +++--- src/js/scriptlets/element-picker.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/epicker.html b/src/epicker.html index fc1f34e520a3a..42df6414195b6 100644 --- a/src/epicker.html +++ b/src/epicker.html @@ -78,7 +78,7 @@ resize: none; width: 100%; } -section > div:first-child > textarea + div { +#resultsetCount { background-color: #aaa; bottom: 0; color: white; @@ -86,7 +86,7 @@ position: absolute; right: 0; } -section.invalidFilter > div:first-child > textarea + div { +section.invalidFilter #resultsetCount { background-color: red; } section > div:first-child + div { @@ -183,7 +183,7 @@

      -
      +
      + + + {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}} + 52.0a1 + * + + + + diff --git a/src/js/background.js b/src/js/background.js index 5cc6d8915a601..ca7fe704b39f5 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -19,54 +19,19 @@ Home: https://github.com/gorhill/uBlock */ -'use strict'; - -/******************************************************************************/ -var µBlock = (function() { // jshint ignore:line - -/******************************************************************************/ +/* global objectAssign */ -var oneSecond = 1000; -var oneMinute = 60 * oneSecond; +'use strict'; /******************************************************************************/ -var defaultExternalLists = [ - '! Examples:', - '! https://easylist-downloads.adblockplus.org/fb_annoyances_full.txt', - '! https://easylist-downloads.adblockplus.org/yt_annoyances_full.txt', - '' -].join('\n'); +var µBlock = (function() { // jshint ignore:line -/******************************************************************************/ + var oneSecond = 1000, + oneMinute = 60 * oneSecond; -return { - firstInstall: false, - - userSettings: { - advancedUserEnabled: false, - alwaysDetachLogger: false, - autoUpdate: true, - cloudStorageEnabled: false, - collapseBlocked: true, - colorBlindFriendly: false, - contextMenuEnabled: true, - dynamicFilteringEnabled: false, - externalLists: defaultExternalLists, - firewallPaneMinimized: true, - hyperlinkAuditingDisabled: true, - ignoreGenericCosmeticFilters: false, - largeMediaSize: 50, - parseAllABPHideFilters: true, - prefetchingDisabled: true, - requestLogMaxEntries: 1000, - showIconBadge: true, - tooltipsDisabled: false, - webrtcIPAddressHidden: false - }, - - hiddenSettingsDefault: { + var hiddenSettingsDefault = { assetFetchTimeout: 30, autoUpdateAssetFetchPeriod: 120, autoUpdatePeriod: 7, @@ -75,86 +40,131 @@ return { manualUpdateAssetFetchPeriod: 2000, popupFontSize: 'unset', suspendTabsUntilReady: false - }, - // This will be filled ASAP: - hiddenSettings: {}, - - // Features detection. - privacySettingsSupported: vAPI.browserSettings instanceof Object, - cloudStorageSupported: vAPI.cloud instanceof Object, - - // https://github.com/chrisaljoudi/uBlock/issues/180 - // Whitelist directives need to be loaded once the PSL is available - netWhitelist: {}, - netWhitelistModifyTime: 0, - netWhitelistDefault: [ - 'about-scheme', - 'behind-the-scene', - 'chrome-extension-scheme', - 'chrome-scheme', - 'loopconversation.about-scheme', - 'moz-extension-scheme', - 'opera-scheme', - 'vivaldi-scheme', - '' - ].join('\n'), - - localSettings: { - blockedRequestCount: 0, - allowedRequestCount: 0 - }, - localSettingsLastModified: 0, - localSettingsLastSaved: 0, - - // read-only - systemSettings: { - compiledMagic: 'fxtcjjhbhyiw', - selfieMagic: 'fxtcjjhbhyiw' - }, - - restoreBackupSettings: { - lastRestoreFile: '', - lastRestoreTime: 0, - lastBackupFile: '', - lastBackupTime: 0 - }, - - // Allows to fully customize uBO's assets, typically set through admin - // settings. The content of 'assets.json' will also tell which filter - // lists to enable by default when uBO is first installed. - assetsBootstrapLocation: 'assets/assets.json', - - userFiltersPath: 'user-filters', - pslAssetKey: 'public_suffix_list.dat', - - selectedFilterLists: [], - availableFilterLists: {}, - - selfieAfter: 23 * oneMinute, - - pageStores: {}, - pageStoresToken: 0, - - storageQuota: vAPI.storage.QUOTA_BYTES, - storageUsed: 0, - - noopFunc: function(){}, - - apiErrorCount: 0, - mouseX: -1, - mouseY: -1, - mouseURL: '', - epickerTarget: '', - epickerEprom: null, - - scriptlets: { - }, - - // so that I don't have to care for last comma - dummy: 0 -}; - -/******************************************************************************/ + }; + + return { + firstInstall: false, + + onBeforeStartQueue: [], + onStartCompletedQueue: [], + + userSettings: { + advancedUserEnabled: false, + alwaysDetachLogger: false, + autoUpdate: true, + cloudStorageEnabled: false, + collapseBlocked: true, + colorBlindFriendly: false, + contextMenuEnabled: true, + dynamicFilteringEnabled: false, + externalLists: [], + firewallPaneMinimized: true, + hyperlinkAuditingDisabled: true, + ignoreGenericCosmeticFilters: false, + largeMediaSize: 50, + parseAllABPHideFilters: true, + prefetchingDisabled: true, + requestLogMaxEntries: 1000, + showIconBadge: true, + tooltipsDisabled: false, + webrtcIPAddressHidden: false + }, + + hiddenSettingsDefault: hiddenSettingsDefault, + hiddenSettings: (function() { + var out = objectAssign({}, hiddenSettingsDefault), + json = vAPI.localStorage.getItem('hiddenSettings'); + if ( typeof json === 'string' ) { + try { + var o = JSON.parse(json); + if ( o instanceof Object ) { + for ( var k in o ) { + if ( out.hasOwnProperty(k) ) { + out[k] = o[k]; + } + } + } + } + catch(ex) { + } + } + return out; + })(), + + // Features detection. + privacySettingsSupported: vAPI.browserSettings instanceof Object, + cloudStorageSupported: vAPI.cloud instanceof Object, + + // https://github.com/chrisaljoudi/uBlock/issues/180 + // Whitelist directives need to be loaded once the PSL is available + netWhitelist: {}, + netWhitelistModifyTime: 0, + netWhitelistDefault: [ + 'about-scheme', + 'behind-the-scene', + 'chrome-extension-scheme', + 'chrome-scheme', + 'loopconversation.about-scheme', + 'moz-extension-scheme', + 'opera-scheme', + 'vivaldi-scheme', + '' + ].join('\n'), + + localSettings: { + blockedRequestCount: 0, + allowedRequestCount: 0 + }, + localSettingsLastModified: 0, + localSettingsLastSaved: 0, + + // read-only + systemSettings: { + compiledMagic: 'fxtcjjhbhyiw', + selfieMagic: 'fxtcjjhbhyiw' + }, + + restoreBackupSettings: { + lastRestoreFile: '', + lastRestoreTime: 0, + lastBackupFile: '', + lastBackupTime: 0 + }, + + // Allows to fully customize uBO's assets, typically set through admin + // settings. The content of 'assets.json' will also tell which filter + // lists to enable by default when uBO is first installed. + assetsBootstrapLocation: 'assets/assets.json', + + userFiltersPath: 'user-filters', + pslAssetKey: 'public_suffix_list.dat', + + selectedFilterLists: [], + availableFilterLists: {}, + + selfieAfter: 23 * oneMinute, + + pageStores: {}, + pageStoresToken: 0, + + storageQuota: vAPI.storage.QUOTA_BYTES, + storageUsed: 0, + + noopFunc: function(){}, + + apiErrorCount: 0, + mouseX: -1, + mouseY: -1, + mouseURL: '', + epickerTarget: '', + epickerEprom: null, + + scriptlets: { + }, + + // so that I don't have to care for last comma + dummy: 0 + }; })(); diff --git a/src/js/start.js b/src/js/start.js index 8c37217f9b439..4fabbbac234e0 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* global objectAssign, publicSuffixList */ +/* global publicSuffixList */ 'use strict'; @@ -51,6 +51,20 @@ vAPI.app.onShutdown = function() { /******************************************************************************/ +var processCallbackQueue = function(queue, callback) { + var processOne = function() { + var fn = queue.pop(); + if ( fn ) { + fn(processOne); + } else if ( typeof callback === 'function' ) { + callback(); + } + }; + processOne(); +}; + +/******************************************************************************/ + // Final initialization steps after all needed assets are in memory. // - Initialize internal state with maybe already existing tabs. // - Schedule next update operation. @@ -76,7 +90,7 @@ var onAllReady = function() { µb.contextMenu.update(null); µb.firstInstall = false; - vAPI.net.onReady(); + processCallbackQueue(µb.onStartCompletedQueue); }; /******************************************************************************/ @@ -278,31 +292,11 @@ var onAdminSettingsRestored = function() { /******************************************************************************/ -µb.hiddenSettings = (function() { - var out = objectAssign({}, µb.hiddenSettingsDefault), - json = vAPI.localStorage.getItem('hiddenSettings'); - if ( typeof json === 'string' ) { - try { - var o = JSON.parse(json); - if ( o instanceof Object ) { - for ( var k in o ) { - if ( out.hasOwnProperty(k) ) { - out[k] = o[k]; - } - } - } - } - catch(ex) { - } - } - return out; -})(); - -/******************************************************************************/ - return function() { - // https://github.com/gorhill/uBlock/issues/531 - µb.restoreAdminSettings(onAdminSettingsRestored); + processCallbackQueue(µb.onBeforeStartQueue, function() { + // https://github.com/gorhill/uBlock/issues/531 + µb.restoreAdminSettings(onAdminSettingsRestored); + }); }; /******************************************************************************/ diff --git a/src/js/traffic.js b/src/js/traffic.js index 303a6872fa1e2..da56f1a0f11f4 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -34,47 +34,41 @@ var exports = {}; /******************************************************************************/ // https://github.com/gorhill/uBlock/issues/2067 -// Experimental: Suspend tabs until uBO is fully ready. - -vAPI.net.onReady = function() { - if ( µBlock.hiddenSettings.suspendTabsUntilReady !== true ) { +// Experimental: Block everything until uBO is fully ready. +// TODO: re-work vAPI code to match more closely how listeners are +// registered with the webRequest API. This will simplify implementing +// the feature here: we could have a temporary onBeforeRequest listener +// which blocks everything until all is ready. +// This would allow to avoid the permanent special test at the top of +// the main onBeforeRequest just to implement this. +var onBeforeReady = null; + +if ( µBlock.hiddenSettings.suspendTabsUntilReady ) { + onBeforeReady = (function() { + var suspendedTabs = new Set(); + µBlock.onStartCompletedQueue.push(function(callback) { + onBeforeReady = null; + var iter = suspendedTabs.values(), + entry; + for (;;) { + entry = iter.next(); + if ( entry.done ) { break; } + vAPI.tabs.reload(entry.value); + } + callback(); + }); + return function(tabId) { + if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; } + suspendedTabs.add(tabId); + return true; + }; + })(); +} else { + µBlock.onStartCompletedQueue.push(function(callback) { vAPI.onLoadAllCompleted(); - } - var fn = onBeforeReady; - onBeforeReady = null; - if ( fn !== null ) { - fn('ready'); - } -}; - -var onBeforeReady = (function() { - var suspendedTabs = new Set(); - - var forceReloadSuspendedTabs = function() { - var iter = suspendedTabs.values(), - entry; - for (;;) { - entry = iter.next(); - if ( entry.done ) { break; } - vAPI.tabs.reload(entry.value); - } - }; - - return function(tabId) { - if ( - vAPI.isBehindTheSceneTabId(tabId) || - µBlock.hiddenSettings.suspendTabsUntilReady !== true - ) { - return; - } - if ( tabId === 'ready' ) { - forceReloadSuspendedTabs(); - return; - } - suspendedTabs.add(tabId); - return true; - }; -})(); + callback(); + }); +} /******************************************************************************/ diff --git a/tools/make-firefox-meta.py b/tools/make-firefox-meta.py index c63d48c9b3ec7..773f6a95d8128 100644 --- a/tools/make-firefox-meta.py +++ b/tools/make-firefox-meta.py @@ -31,23 +31,17 @@ def mkdirs(path): locale_path = pj(source_locale_dir, alpha2, 'messages.json') with open(locale_path, encoding='utf-8') as f: strings = json.load(f, object_pairs_hook=OrderedDict) - alpha2 = alpha2.replace('_', '-') descriptions[alpha2] = strings['extShortDesc']['message'] del strings['extShortDesc'] - language_codes.append(alpha2) - mkdirs(pj(target_locale_dir, alpha2)) - locale_path = pj(target_locale_dir, alpha2, 'messages.properties') with open(locale_path, 'wt', encoding='utf-8', newline='\n') as f: for string_name in strings: string = strings[string_name]['message'] - if alpha2 == 'en' and string_name in title_case_strings: string = string.title() - f.write(string_name) f.write(u'=') f.write(string.replace('\n', r'\n')) @@ -58,11 +52,9 @@ def mkdirs(path): with open(chrome_manifest, 'at', encoding='utf-8', newline='\n') as f: f.write(u'\nlocale ublock0 en ./locale/en/\n') - for alpha2 in language_codes: if alpha2 == 'en': continue - f.write(u'locale ublock0 ' + alpha2 + ' ./locale/' + alpha2 + '/\n') rmtree(source_locale_dir) @@ -89,15 +81,13 @@ def mkdirs(path): manifest['homepage'] = 'https://github.com/gorhill/uBlock' manifest['description'] = descriptions['en'] del descriptions['en'] -manifest['localized'] = [] +manifest['localized'] = [] t = ' ' t3 = 3 * t - for alpha2 in descriptions: if alpha2 == 'en': continue - manifest['localized'].append( '\n' + t*2 + '\n' + t3 + '' + alpha2 + '\n' + @@ -108,12 +98,10 @@ def mkdirs(path): t3 + '' + manifest['homepage'] + '\n' + t*2 + '' ) - manifest['localized'] = '\n'.join(manifest['localized']) install_rdf = pj(build_dir, 'install.rdf') with open(install_rdf, 'r+t', encoding='utf-8', newline='\n') as f: install_rdf = f.read() f.seek(0) - f.write(install_rdf.format(**manifest)) diff --git a/tools/make-firefox.sh b/tools/make-firefox.sh index 6199fb46a567c..c393e1738e55a 100755 --- a/tools/make-firefox.sh +++ b/tools/make-firefox.sh @@ -17,9 +17,6 @@ cp -R src/lib $DES/ cp -R src/_locales $DES/ cp src/*.html $DES/ -# AMO review feedback: avoid "unnecessary files or folders" in package -cat src/background.html | sed -e '/vapi-polyfill\.js/d' > $DES/background.html - mv $DES/img/icon_128.png $DES/icon.png cp platform/firefox/css/* $DES/css/ cp platform/firefox/polyfill.js $DES/js/ diff --git a/tools/make-webext-meta.py b/tools/make-webext-meta.py old mode 100755 new mode 100644 index 15df315fba517..dd21761cdc041 --- a/tools/make-webext-meta.py +++ b/tools/make-webext-meta.py @@ -2,7 +2,10 @@ import os import json +import re import sys +from io import open as uopen +from collections import OrderedDict if len(sys.argv) == 1 or not sys.argv[1]: raise SystemExit('Build dir missing.') @@ -10,7 +13,7 @@ proj_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], '..') build_dir = os.path.abspath(sys.argv[1]) -# Import version number from chromium platform +# Import data from chromium platform chromium_manifest = {} webext_manifest = {} @@ -18,7 +21,8 @@ with open(chromium_manifest_file) as f1: chromium_manifest = json.load(f1) -webext_manifest_file = os.path.join(build_dir, 'manifest.json') +# WebExtension part +webext_manifest_file = os.path.join(build_dir, 'webextension', 'manifest.json') with open(webext_manifest_file) as f2: webext_manifest = json.load(f2) @@ -27,3 +31,52 @@ with open(webext_manifest_file, 'w') as f2: json.dump(webext_manifest, f2, indent=2, separators=(',', ': '), sort_keys=True) f2.write('\n') + +# Legacy part +descriptions = OrderedDict({}) +source_locale_dir = os.path.join(build_dir, 'webextension', '_locales') +for alpha2 in sorted(os.listdir(source_locale_dir)): + locale_path = os.path.join(source_locale_dir, alpha2, 'messages.json') + with uopen(locale_path, encoding='utf-8') as f: + strings = json.load(f, object_pairs_hook=OrderedDict) + alpha2 = alpha2.replace('_', '-') + descriptions[alpha2] = strings['extShortDesc']['message'] + +webext_manifest['author'] = chromium_manifest['author']; +webext_manifest['homepage'] = 'https://github.com/gorhill/uBlock' +webext_manifest['description'] = descriptions['en'] +del descriptions['en'] + +match = re.search('^(\d+\.\d+\.\d+)(\.\d+)$', chromium_manifest['version']) +if match: + buildtype = int(match.group(2)[1:]) + if buildtype < 100: + builttype = 'b' + str(buildtype) + else: + builttype = 'rc' + str(buildtype - 100) + webext_manifest['version'] = match.group(1) + builttype + +webext_manifest['localized'] = [] +t = ' ' +t3 = 3 * t +for alpha2 in descriptions: + if alpha2 == 'en': + continue + webext_manifest['localized'].append( + '\n' + t*2 + '\n' + + t3 + '' + alpha2 + '\n' + + t3 + '' + webext_manifest['name'] + '\n' + + t3 + '' + descriptions[alpha2] + '\n' + + t3 + '' + webext_manifest['author'] + '\n' + + # t3 + '' + ??? + '\n' + + t3 + '' + webext_manifest['homepage'] + '\n' + + t*2 + '' + ) +webext_manifest['localized'] = '\n'.join(webext_manifest['localized']) + +install_rdf = os.path.join(build_dir, 'install.rdf') +with uopen(install_rdf, 'r+t', encoding='utf-8', newline='\n') as f: + install_rdf = f.read() + f.seek(0) + f.write(install_rdf.format(**webext_manifest)) + f.truncate() diff --git a/tools/make-webext.sh b/tools/make-webext.sh index 8304b721bfd30..6166bcef25b1e 100755 --- a/tools/make-webext.sh +++ b/tools/make-webext.sh @@ -7,32 +7,39 @@ echo "*** uBlock0.webext: Copying files" DES=dist/build/uBlock0.webext rm -rf $DES -mkdir -p $DES +mkdir -p $DES/webextension -bash ./tools/make-assets.sh $DES +bash ./tools/make-assets.sh $DES/webextension -cp -R src/css $DES/ -cp -R src/img $DES/ -cp -R src/js $DES/ -cp -R src/lib $DES/ -cp -R src/_locales $DES/ -cp -R $DES/_locales/nb $DES/_locales/no -cp src/*.html $DES/ -cp platform/chromium/*.js $DES/js/ -cp -R platform/chromium/img $DES/ -cp platform/chromium/*.html $DES/ -cp platform/chromium/*.json $DES/ -cp platform/webext/polyfill.js $DES/js/ -cp platform/webext/manifest.json $DES/ -cp LICENSE.txt $DES/ +cp -R src/css $DES/webextension/ +cp -R src/img $DES/webextension/ +cp -R src/js $DES/webextension/ +cp -R src/lib $DES/webextension/ +cp -R src/_locales $DES/webextension/ +cp -R $DES/webextension/_locales/nb $DES/webextension/_locales/no +cp src/*.html $DES/webextension/ +cp platform/chromium/*.js $DES/webextension/js/ +cp -R platform/chromium/img $DES/webextension/ +cp platform/chromium/*.html $DES/webextension/ +cp platform/chromium/*.json $DES/webextension/ +cp platform/webext/polyfill.js $DES/webextension/js/ +cp LICENSE.txt $DES/webextension/ + +cp platform/webext/background.html $DES/webextension/ +cp platform/webext/from-legacy.js $DES/webextension/js/ +cp platform/webext/manifest.json $DES/webextension/ +cp platform/webext/bootstrap.js $DES/ +cp platform/webext/chrome.manifest $DES/ +cp platform/webext/install.rdf $DES/ +mv $DES/webextension/img/icon_128.png $DES/icon.png echo "*** uBlock0.webext: Generating meta..." python tools/make-webext-meta.py $DES/ if [ "$1" = all ]; then echo "*** uBlock0.webext: Creating package..." - pushd $(dirname $DES/) > /dev/null - zip uBlock0.webext.zip -qr $(basename $DES/)/* + pushd $DES > /dev/null + zip ../$(basename $DES).zip -qr * popd > /dev/null fi From d7c1f2f919c2077607f558f0aba4a2d21029bb24 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 5 Mar 2017 12:12:58 -0500 Subject: [PATCH 0123/4093] fix sync storage for Firefox webext (#622) --- platform/chromium/vapi-background.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 99e5e6b9247d1..307f9e513bde3 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -1312,10 +1312,13 @@ vAPI.cloud = (function() { var maxChunkCountPerItem = Math.floor(512 * 0.75) & ~(chunkCountPerFetch - 1); // Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing) - var maxChunkSize = Math.floor(chrome.storage.sync.QUOTA_BYTES_PER_ITEM * 0.75); + var maxChunkSize = Math.floor(chrome.storage.sync.QUOTA_BYTES_PER_ITEM * 0.75 || 6144); // Mind chrome.storage.sync.QUOTA_BYTES (128 kB at time of writing) - var maxStorageSize = chrome.storage.sync.QUOTA_BYTES; + // Firefox: + // https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage/sync + // > You can store up to 100KB of data using this API/ + var maxStorageSize = chrome.storage.sync.QUOTA_BYTES || 102400; var options = { defaultDeviceName: window.navigator.platform, From 50158265469d68efd45e7ddfb221e7f9d7a65896 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 5 Mar 2017 12:54:47 -0500 Subject: [PATCH 0124/4093] fix #2267 --- src/js/assets.js | 62 +++++++++++++++++++++++--------------------- src/js/background.js | 3 ++- src/js/storage.js | 22 +++++++++++++--- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 1e13848cfec66..eb44ac1c32ce5 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -64,10 +64,8 @@ var fireNotification = function(topic, details) { /******************************************************************************/ -var getTextFileFromURL = function(url, onLoad, onError) { - if ( reIsExternalPath.test(url) === false ) { - url = vAPI.getURL(url); - } +api.fetchText = function(url, onLoad, onError) { + var actualUrl = reIsExternalPath.test(url) ? url : vAPI.getURL(url); if ( typeof onError !== 'function' ) { onError = onLoad; @@ -77,28 +75,34 @@ var getTextFileFromURL = function(url, onLoad, onError) { var onResponseReceived = function() { this.onload = this.onerror = this.ontimeout = null; // xhr for local files gives status 0, but actually succeeds - var status = this.status || 200; - if ( status < 200 || status >= 300 ) { - return onError.call(this); + var details = { + url: url, + content: '', + statusCode: this.status || 200, + statusText: this.statusText || '' + }; + if ( details.statusCode < 200 || details.statusCode >= 300 ) { + return onError.call(null, details); } // consider an empty result to be an error if ( stringIsNotEmpty(this.responseText) === false ) { - return onError.call(this); + return onError.call(null, details); } // we never download anything else than plain text: discard if response // appears to be a HTML document: could happen when server serves // some kind of error page I suppose var text = this.responseText.trim(); if ( text.startsWith('<') && text.endsWith('>') ) { - return onError.call(this); + return onError.call(null, details); } - return onLoad.call(this); + details.content = this.responseText; + return onLoad.call(null, details); }; var onErrorReceived = function() { this.onload = this.onerror = this.ontimeout = null; - µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', url)); - onError.call(this); + µBlock.logger.writeOne('', 'error', errorCantConnectTo.replace('{{msg}}', actualUrl)); + onError.call(null, { url: url, content: '' }); }; // Be ready for thrown exceptions: @@ -106,7 +110,7 @@ var getTextFileFromURL = function(url, onLoad, onError) { // `file:///` on Chromium 40 results in an exception being thrown. var xhr = new XMLHttpRequest(); try { - xhr.open('get', url, true); + xhr.open('get', actualUrl, true); xhr.timeout = µBlock.hiddenSettings.assetFetchTimeout * 1000 || 30000; xhr.onload = onResponseReceived; xhr.onerror = onErrorReceived; @@ -405,10 +409,10 @@ var getAssetSourceRegistry = function(callback) { // First-install case. var createRegistry = function() { - getTextFileFromURL( + api.fetchText( µBlock.assetsBootstrapLocation || 'assets/assets.json', - function() { - updateAssetSourceRegistry(this.responseText, true); + function(details) { + updateAssetSourceRegistry(details.content, true); registryReady(); } ); @@ -757,21 +761,21 @@ api.get = function(assetKey, options, callback) { if ( !contentURL ) { return reportBack('', 'E_NOTFOUND'); } - getTextFileFromURL(contentURL, onContentLoaded, onContentNotLoaded); + api.fetchText(contentURL, onContentLoaded, onContentNotLoaded); }; - var onContentLoaded = function() { - if ( stringIsNotEmpty(this.responseText) === false ) { + var onContentLoaded = function(details) { + if ( stringIsNotEmpty(details.content) === false ) { onContentNotLoaded(); return; } if ( reIsExternalPath.test(contentURL) && options.dontCache !== true ) { assetCacheWrite(assetKey, { - content: this.responseText, + content: details.content, url: contentURL }); } - reportBack(this.responseText); + reportBack(details.content); }; var onCachedContentLoaded = function(details) { @@ -811,23 +815,23 @@ var getRemote = function(assetKey, callback) { callback(details); }; - var onRemoteContentLoaded = function() { - if ( stringIsNotEmpty(this.responseText) === false ) { + var onRemoteContentLoaded = function(details) { + if ( stringIsNotEmpty(details.content) === false ) { registerAssetSource(assetKey, { error: { time: Date.now(), error: 'No content' } }); tryLoading(); return; } assetCacheWrite(assetKey, { - content: this.responseText, + content: details.content, url: contentURL }); registerAssetSource(assetKey, { error: undefined }); - reportBack(this.responseText); + reportBack(details.content); }; - var onRemoteContentError = function() { - var text = this.statusText; - if ( this.status === 0 ) { + var onRemoteContentError = function(details) { + var text = details.statusText; + if ( details.statusCode === 0 ) { text = 'network error'; } registerAssetSource(assetKey, { error: { time: Date.now(), error: text } }); @@ -841,7 +845,7 @@ var getRemote = function(assetKey, callback) { if ( !contentURL ) { return reportBack('', 'E_NOTFOUND'); } - getTextFileFromURL(contentURL, onRemoteContentLoaded, onRemoteContentError); + api.fetchText(contentURL, onRemoteContentLoaded, onRemoteContentError); }; getAssetSourceRegistry(function(registry) { diff --git a/src/js/background.js b/src/js/background.js index ca7fe704b39f5..4e69a949855f3 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -39,7 +39,8 @@ var µBlock = (function() { // jshint ignore:line ignoreScriptInjectFilters: false, manualUpdateAssetFetchPeriod: 2000, popupFontSize: 'unset', - suspendTabsUntilReady: false + suspendTabsUntilReady: false, + userResourcesLocation: 'unset' }; return { diff --git a/src/js/storage.js b/src/js/storage.js index bfba9d2ea560c..d76c5c9bb3144 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -818,17 +818,33 @@ /******************************************************************************/ µBlock.loadRedirectResources = function(callback) { - var µb = this; + var µb = this, + content = ''; if ( typeof callback !== 'function' ) { callback = this.noopFunc; } + var onDone = function() { + µb.redirectEngine.resourcesFromString(content); + callback(); + }; + + var onUserResourcesLoaded = function(details) { + if ( details.content !== '' ) { + content += '\n\n' + details.content; + } + onDone(); + }; + var onResourcesLoaded = function(details) { if ( details.content !== '' ) { - µb.redirectEngine.resourcesFromString(details.content); + content = details.content; } - callback(); + if ( µb.hiddenSettings.userResourcesLocation === 'unset' ) { + return onDone(); + } + µb.assets.fetchText(µb.hiddenSettings.userResourcesLocation, onUserResourcesLoaded); }; this.assets.get('ublock-resources', onResourcesLoaded); From 4c832718d3c2348f347774441a15919d3a19c87f Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 5 Mar 2017 14:32:53 -0500 Subject: [PATCH 0125/4093] 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 0eb0f71835b0c..90a216018c51e 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.2", + "version": "1.11.3.0", "default_locale": "en", "description": "__MSG_extShortDesc__", From c271fbbd0d2b4ae6a74a5aa387e549f68fea1064 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 5 Mar 2017 17:20:28 -0500 Subject: [PATCH 0126/4093] code review: remove old cruft --- platform/chromium/vapi-background.js | 88 ++++++++++------------------ 1 file changed, 32 insertions(+), 56 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 307f9e513bde3..c028540bc99ed 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -1092,64 +1092,40 @@ vAPI.net.registerListeners = function() { return onHeadersReceivedClient(details); }; + var urls, types; + + if ( onBeforeRequest ) { + urls = this.onBeforeRequest.urls || ['']; + types = this.onBeforeRequest.types || undefined; + wrApi.onBeforeRequest.addListener( + onBeforeRequest, + { urls: urls, types: types }, + this.onBeforeRequest.extra + ); + } - var installListeners = (function() { - //listener = function(details) { - // quickProfiler.start('onBeforeRequest'); - // var r = onBeforeRequest(details); - // quickProfiler.stop(); - // return r; - //}; - var urls, types; - - if ( - onBeforeRequest && - wrApi.onBeforeRequest.hasListener(onBeforeRequest) === false - ) { - urls = this.onBeforeRequest.urls || ['']; - types = this.onBeforeRequest.types || undefined; - wrApi.onBeforeRequest.addListener( - onBeforeRequest, - { urls: urls, types: types }, - this.onBeforeRequest.extra - ); - } - - // Chromium 48 and lower does not support `ping` type. - // Chromium 56 and higher does support `csp_report` stype. - if ( - onBeforeSendHeaders && - wrApi.onBeforeSendHeaders.hasListener(onBeforeSendHeaders) === false - ) { - wrApi.onBeforeSendHeaders.addListener( - onBeforeSendHeaders, - { - 'urls': [ '' ], - 'types': [ 'ping' ] - }, - [ 'blocking', 'requestHeaders' ] - ); - } - - if ( - onHeadersReceived && - wrApi.onHeadersReceived.hasListener(onHeadersReceived) === false - ) { - urls = this.onHeadersReceived.urls || ['']; - types = onHeadersReceivedTypes; - wrApi.onHeadersReceived.addListener( - onHeadersReceived, - { urls: urls, types: types }, - this.onHeadersReceived.extra - ); - } - - // https://github.com/gorhill/uBlock/issues/675 - // Experimental: keep polling to be sure our listeners are still installed. - //setTimeout(installListeners, 20000); - }).bind(this); + // Chromium 48 and lower does not support `ping` type. + // Chromium 56 and higher does support `csp_report` stype. + if ( onBeforeSendHeaders ) { + wrApi.onBeforeSendHeaders.addListener( + onBeforeSendHeaders, + { + 'urls': [ '' ], + 'types': [ 'ping' ] + }, + [ 'blocking', 'requestHeaders' ] + ); + } - installListeners(); + if ( onHeadersReceived ) { + urls = this.onHeadersReceived.urls || ['']; + types = onHeadersReceivedTypes; + wrApi.onHeadersReceived.addListener( + onHeadersReceived, + { urls: urls, types: types }, + this.onHeadersReceived.extra + ); + } }; /******************************************************************************/ From 2213b005a0dbb5786d541f96d085f1ac3411748d Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 6 Mar 2017 17:34:46 -0500 Subject: [PATCH 0127/4093] forgot to add "ws://*/*"/"wss://*/*" to enable support for websocket type" --- platform/chromium/manifest.json | 4 +++- src/js/traffic.js | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 90a216018c51e..180aa84a5374a 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -55,7 +55,9 @@ "webRequest", "webRequestBlocking", "http://*/*", - "https://*/*" + "https://*/*", + "ws://*/*", + "wss://*/*" ], "short_name": "uBlock₀", "storage": { diff --git a/src/js/traffic.js b/src/js/traffic.js index da56f1a0f11f4..0f570429b534e 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -679,7 +679,9 @@ var headerIndexFromName = function(headerName, headers) { vAPI.net.onBeforeRequest = { urls: [ 'http://*/*', - 'https://*/*' + 'https://*/*', + 'ws://*/*', + 'wss://*/*' ], extra: [ 'blocking' ], callback: onBeforeRequest From 7e55ddf16b602dad264bb74e83700a9b353fd26a Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 6 Mar 2017 17:53:25 -0500 Subject: [PATCH 0128/4093] re. websocket: mind backward compatibility --- platform/chromium/vapi-background.js | 12 ++++++++++++ src/js/traffic.js | 4 +--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index c028540bc99ed..2032ba2c1b165 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -1097,6 +1097,18 @@ vAPI.net.registerListeners = function() { if ( onBeforeRequest ) { urls = this.onBeforeRequest.urls || ['']; types = this.onBeforeRequest.types || undefined; + if ( + (validTypes.websocket) && + (types === undefined || types.indexOf('websocket') !== -1) && + (urls.indexOf('') === -1) + ) { + if ( urls.indexOf('ws://*/*') === -1 ) { + urls.push('ws://*/*'); + } + if ( urls.indexOf('wss://*/*') === -1 ) { + urls.push('wss://*/*'); + } + } wrApi.onBeforeRequest.addListener( onBeforeRequest, { urls: urls, types: types }, diff --git a/src/js/traffic.js b/src/js/traffic.js index 0f570429b534e..da56f1a0f11f4 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -679,9 +679,7 @@ var headerIndexFromName = function(headerName, headers) { vAPI.net.onBeforeRequest = { urls: [ 'http://*/*', - 'https://*/*', - 'ws://*/*', - 'wss://*/*' + 'https://*/*' ], extra: [ 'blocking' ], callback: onBeforeRequest From 9f26776c11cf0d22fb52b9b1bb5c373fc06eca7a Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 7 Mar 2017 13:51:23 -0500 Subject: [PATCH 0129/4093] 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 180aa84a5374a..277e007b23804 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.3.0", + "version": "1.11.3.1", "default_locale": "en", "description": "__MSG_extShortDesc__", From fafe4c8abc4eeae6058e2d3a54d5c9e6d7e2ad2f Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 8 Mar 2017 13:44:01 -0500 Subject: [PATCH 0130/4093] fix #2436 --- tools/make-opera-meta.py | 29 +++++++++++++++++++++++++++++ tools/make-opera.sh | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tools/make-opera-meta.py diff --git a/tools/make-opera-meta.py b/tools/make-opera-meta.py new file mode 100644 index 0000000000000..15df315fba517 --- /dev/null +++ b/tools/make-opera-meta.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +import os +import json +import sys + +if len(sys.argv) == 1 or not sys.argv[1]: + raise SystemExit('Build dir missing.') + +proj_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], '..') +build_dir = os.path.abspath(sys.argv[1]) + +# Import version number from chromium platform +chromium_manifest = {} +webext_manifest = {} + +chromium_manifest_file = os.path.join(proj_dir, 'platform', 'chromium', 'manifest.json') +with open(chromium_manifest_file) as f1: + chromium_manifest = json.load(f1) + +webext_manifest_file = os.path.join(build_dir, 'manifest.json') +with open(webext_manifest_file) as f2: + webext_manifest = json.load(f2) + +webext_manifest['version'] = chromium_manifest['version'] + +with open(webext_manifest_file, 'w') as f2: + json.dump(webext_manifest, f2, indent=2, separators=(',', ': '), sort_keys=True) + f2.write('\n') diff --git a/tools/make-opera.sh b/tools/make-opera.sh index 918f681e17b19..d12887ce85993 100755 --- a/tools/make-opera.sh +++ b/tools/make-opera.sh @@ -27,7 +27,7 @@ cp platform/opera/manifest.json $DES/ cp LICENSE.txt $DES/ echo "*** uBlock0.opera: Generating meta..." -python tools/make-webext-meta.py $DES/ +python tools/make-opera-meta.py $DES/ rm -r $DES/_locales/cv rm -r $DES/_locales/hi From e8375f91cdfedc51029bb28d76bb1d9ca1f76f8f Mon Sep 17 00:00:00 2001 From: Andrei Petcu Date: Fri, 10 Mar 2017 14:01:57 +0200 Subject: [PATCH 0131/4093] #2433 Changed the XML namespaces to please jpm sign (#2434) * #2433 changed the legacy install.rdf to match MDN https://developer.mozilla.org/en-US/Add-ons/Install_Manifests * #2433 changed webext install.rdf to match MDN docs https://developer.mozilla.org/en-US/Add-ons/Install_Manifests * #2433 changed property added by mistake * #2433 changed localization XML namespaces to match install.rdf * #2433 small fixes --- platform/firefox/install.rdf | 106 +++++++++++++++++------------------ platform/webext/install.rdf | 51 ++++++++--------- tools/make-firefox-meta.py | 14 ++--- tools/make-webext-meta.py | 14 ++--- 4 files changed, 93 insertions(+), 92 deletions(-) diff --git a/platform/firefox/install.rdf b/platform/firefox/install.rdf index 8cb7a2edd6beb..21bf2861a34a3 100644 --- a/platform/firefox/install.rdf +++ b/platform/firefox/install.rdf @@ -1,64 +1,64 @@ - - - - uBlock0@raymondhill.net - {version} - {name} - {description} - {homepage} - {author} - Deathamns - Alex Vallat - Manuel Reimer - 2 - true - true - 2 + + + + uBlock0@raymondhill.net + {version} + {name} + {description} + {homepage} + {author} + Deathamns + Alex Vallat + Manuel Reimer + 2 + true + true + 2 {localized} - - - {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}} - 24.0 - * - - + + + {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}} + 24.0 + * + + - - - {{aa3c5121-dab2-40e2-81ca-7ea25febc110}} - 24.0 - * - - + + + {{aa3c5121-dab2-40e2-81ca-7ea25febc110}} + 24.0 + * + + - - - {{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}} - 2.21 - * - - + + + {{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}} + 2.21 + * + + - - - {{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}} - 25.0 - 27.* - - + + + {{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}} + 25.0 + 27.* + + - - - {{3550f703-e582-4d05-9a08-453d09bdfdc6}} - 31.0 - 45.* - - - - + + + {{3550f703-e582-4d05-9a08-453d09bdfdc6}} + 31.0 + 45.* + + + + diff --git a/platform/webext/install.rdf b/platform/webext/install.rdf index 35a548e72d574..5039da2c7f3ad 100644 --- a/platform/webext/install.rdf +++ b/platform/webext/install.rdf @@ -1,28 +1,29 @@ - - - - uBlock0@raymondhill.net - {version} - {name} - {description} - https://github.com/gorhill/uBlock - {author} - Deathamns - Alex Vallat - Manuel Reimer - 2 - true - true - true + + + + uBlock0@raymondhill.net + {version} + {name} + {description} + https://github.com/gorhill/uBlock + {author} + Deathamns + Alex Vallat + Manuel Reimer + 2 + true + true + true {localized} - - - {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}} - 52.0a1 - * - - - - + + + {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}} + 52.0a1 + * + + + + + diff --git a/tools/make-firefox-meta.py b/tools/make-firefox-meta.py index 773f6a95d8128..875885fdb6355 100644 --- a/tools/make-firefox-meta.py +++ b/tools/make-firefox-meta.py @@ -89,14 +89,14 @@ def mkdirs(path): if alpha2 == 'en': continue manifest['localized'].append( - '\n' + t*2 + '\n' + - t3 + '' + alpha2 + '\n' + - t3 + '' + manifest['name'] + '\n' + - t3 + '' + descriptions[alpha2] + '\n' + - t3 + '' + manifest['author'] + '\n' + + '\n' + t*2 + '\n' + + t3 + '' + alpha2 + '\n' + + t3 + '' + manifest['name'] + '\n' + + t3 + '' + descriptions[alpha2] + '\n' + + t3 + '' + manifest['author'] + '\n' + # t3 + '' + ??? + '\n' + - t3 + '' + manifest['homepage'] + '\n' + - t*2 + '' + t3 + '' + manifest['homepage'] + '\n' + + t*2 + '' ) manifest['localized'] = '\n'.join(manifest['localized']) diff --git a/tools/make-webext-meta.py b/tools/make-webext-meta.py index dd21761cdc041..5e299206fa252 100644 --- a/tools/make-webext-meta.py +++ b/tools/make-webext-meta.py @@ -63,14 +63,14 @@ if alpha2 == 'en': continue webext_manifest['localized'].append( - '\n' + t*2 + '\n' + - t3 + '' + alpha2 + '\n' + - t3 + '' + webext_manifest['name'] + '\n' + - t3 + '' + descriptions[alpha2] + '\n' + - t3 + '' + webext_manifest['author'] + '\n' + + '\n' + t*2 + '\n' + + t3 + '' + alpha2 + '\n' + + t3 + '' + webext_manifest['name'] + '\n' + + t3 + '' + descriptions[alpha2] + '\n' + + t3 + '' + webext_manifest['author'] + '\n' + # t3 + '' + ??? + '\n' + - t3 + '' + webext_manifest['homepage'] + '\n' + - t*2 + '' + t3 + '' + webext_manifest['homepage'] + '\n' + + t*2 + '' ) webext_manifest['localized'] = '\n'.join(webext_manifest['localized']) From a4e20ae3ad869a18f66eeb940273a625d342578e Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 11 Mar 2017 13:55:47 -0500 Subject: [PATCH 0132/4093] new filter option: "badfilter" (see https://github.com/uBlockOrigin/uAssets/issues/192) --- src/js/cosmetic-filtering.js | 15 +++--- src/js/static-net-filtering.js | 98 +++++++++++++++++++++++++++++----- src/js/utils.js | 62 +++++++++++++-------- 3 files changed, 134 insertions(+), 41 deletions(-) diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 8782fb9ff8377..32b13ef95035d 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -1148,10 +1148,11 @@ FilterContainer.prototype.fromCompiledContent = function(lineIter, skipGenericCo fieldIter = new µb.FieldIterator('\v'); while ( lineIter.eot() === false ) { - if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x63 /* 'c' */ ) { + line = lineIter.next(); + if ( line.charCodeAt(0) !== 0x63 /* 'c' */ ) { + lineIter.rewind(); return; } - line = lineIter.next(); this.acceptedCount += 1; if ( this.duplicateBuster.has(line) ) { @@ -1274,10 +1275,11 @@ FilterContainer.prototype.skipGenericCompiledContent = function(lineIter) { fieldIter = new µb.FieldIterator('\v'); while ( lineIter.eot() === false ) { - if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x63 /* 'c' */ ) { + line = lineIter.next(); + if ( line.charCodeAt(0) !== 0x63 /* 'c' */ ) { + lineIter.rewind(); return; } - line = lineIter.next(); this.acceptedCount += 1; if ( this.duplicateBuster.has(line) ) { @@ -1336,10 +1338,11 @@ FilterContainer.prototype.skipCompiledContent = function(lineIter) { fieldIter = new µb.FieldIterator('\v'); while ( lineIter.eot() === false ) { - if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x63 /* 'c' */ ) { + line = lineIter.next(); + if ( line.charCodeAt(0) !== 0x63 /* 'c' */ ) { + lineIter.rewind(); return; } - line = lineIter.next(); this.acceptedCount += 1; if ( this.duplicateBuster.has(line) ) { diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index dfa6f3a7a62ad..f1e0e1a8297d6 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1078,6 +1078,17 @@ FilterBucket.prototype.add = function(a) { this.filters.push(a); }; +FilterBucket.prototype.remove = function(fclass, fdata) { + var i = this.filters.length, + filter; + while ( i-- ) { + filter = this.filters[i]; + if ( filter.fid === fclass && filter.toSelfie() === fdata ) { + this.filters.splice(i, 1); + } + } +}; + // Promote hit filters so they can be found faster next time. FilterBucket.prototype.promote = function(i) { var filters = this.filters; @@ -1181,6 +1192,7 @@ FilterParser.prototype.toNormalizedType = { FilterParser.prototype.reset = function() { this.action = BlockAction; this.anchor = 0; + this.badFilter = false; this.elemHiding = false; this.f = ''; this.firstParty = false; @@ -1334,6 +1346,11 @@ FilterParser.prototype.parseOptions = function(s) { if ( opt === 'empty' ) { continue; } + // https://github.com/uBlockOrigin/uAssets/issues/192 + if ( opt === 'badfilter' ) { + this.badFilter = true; + continue; + } // Unrecognized filter option: ignore whole filter. this.unsupported = true; break; @@ -1598,10 +1615,10 @@ FilterContainer.prototype.reset = function() { this.allowFilterCount = 0; this.blockFilterCount = 0; this.discardedCount = 0; + this.badFilters = new Set(); this.duplicateBuster = new Set(); this.categories = new Map(); this.filterParser.reset(); - this.filterCounts = {}; // Reuse filter instances whenever possible at load time. this.fclassLast = null; @@ -1618,6 +1635,7 @@ FilterContainer.prototype.reset = function() { FilterContainer.prototype.freeze = function() { histogram('allFilters', this.categories); + this.removeBadFilters(); this.duplicateBuster = new Set(); this.filterParser.reset(); this.fclassLast = null; @@ -1853,7 +1871,7 @@ FilterContainer.prototype.compile = function(raw, out) { return false; } - // Pure hostnames, use more efficient liquid dict + // Pure hostnames, use more efficient dictionary lookup // https://github.com/chrisaljoudi/uBlock/issues/665 // Create a dict keyed on request type etc. if ( parsed.hostnamePure && this.compileHostnameOnlyFilter(parsed, out) ) { @@ -1880,8 +1898,11 @@ FilterContainer.prototype.compileHostnameOnlyFilter = function(parsed, out) { // return; //} - var party = AnyParty; - if ( parsed.firstParty !== parsed.thirdParty ) { + var route = parsed.badFilter ? 'n-\v' : 'n\v', + party; + if ( parsed.firstParty === parsed.thirdParty ) { + party = AnyParty; + } else { party = parsed.firstParty ? FirstParty : ThirdParty; } var keyShard = parsed.action | parsed.important | party; @@ -1889,7 +1910,7 @@ FilterContainer.prototype.compileHostnameOnlyFilter = function(parsed, out) { var type = parsed.types; if ( type === 0 ) { out.push( - 'n\v' + + route + toHex(keyShard) + '\v' + '.\v' + parsed.f @@ -1901,7 +1922,7 @@ FilterContainer.prototype.compileHostnameOnlyFilter = function(parsed, out) { do { if ( type & 1 ) { out.push( - 'n\v' + + route + toHex(keyShard | (bitOffset << 4)) + '\v' + '.\v' + parsed.f @@ -1934,11 +1955,12 @@ FilterContainer.prototype.compileFilter = function(parsed, out) { /******************************************************************************/ FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed, party, out) { - var bits = parsed.action | parsed.important | party; - var type = parsed.types; + var route = parsed.badFilter ? 'n-\v' : 'n\v', + bits = parsed.action | parsed.important | party, + type = parsed.types; if ( type === 0 ) { out.push( - 'n\v' + + route + toHex(bits) + '\v' + parsed.token + '\v' + filterClass.fid + '\v' + @@ -1950,7 +1972,7 @@ FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed, do { if ( type & 1 ) { out.push( - 'n\v' + + route + toHex(bits | (bitOffset << 4)) + '\v' + parsed.token + '\v' + filterClass.fid + '\v' + @@ -1967,6 +1989,10 @@ FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed, return; } + if ( parsed.badFilter ) { + return; + } + var redirects = µb.redirectEngine.compileRuleFromStaticFilter(parsed.raw); if ( Array.isArray(redirects) === false ) { return; @@ -1985,12 +2011,17 @@ FilterContainer.prototype.fromCompiledContent = function(lineIter) { fieldIter = new µb.FieldIterator('\v'); while ( lineIter.eot() === false ) { - if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x6E /* 'n' */ ) { + line = lineIter.next(); + if ( line.charCodeAt(0) !== 0x6E /* 'n' */ ) { + lineIter.rewind(); return; } - line = lineIter.next(); - fieldIter.first(line); + if ( fieldIter.first(line) === 'n-' ) { + this.badFilters.add(line); + continue; + } + hash = fieldIter.next(); token = fieldIter.next(); fclass = fieldIter.next(); @@ -2046,6 +2077,47 @@ FilterContainer.prototype.fromCompiledContent = function(lineIter) { /******************************************************************************/ +FilterContainer.prototype.removeBadFilters = function() { + var lines = µb.setToArray(this.badFilters), + fieldIter = new µb.FieldIterator('\v'), + hash, token, fclass, fdata, bucket, entry, + i = lines.length; + while ( i-- ) { + fieldIter.first(lines[i]); + hash = fieldIter.next(); + token = fieldIter.next(); + fclass = fieldIter.next(); + fdata = fieldIter.next(); + bucket = this.categories.get(hash); + if ( bucket === undefined ) { + continue; + } + entry = bucket.get(token); + if ( entry === undefined ) { + continue; + } + if ( entry instanceof FilterHostnameDict ) { + entry.delete(fclass); // 'fclass' is hostname + if ( entry.dict.size === 0 ) { + this.categories.delete(hash); + } + continue; + } + if ( entry instanceof FilterBucket ) { + entry.remove(fclass, fdata); + if ( entry.filters.length === 1 ) { + bucket.set(token, entry.filters[0]); + } + continue; + } + if ( entry.fid === fclass && entry.toSelfie() === fdata ) { + this.categories.delete(hash); + } + } +}; + +/******************************************************************************/ + FilterContainer.prototype.filterStringFromCompiled = function(compiled) { var opts = []; var vfields = compiled.split('\v'); diff --git a/src/js/utils.js b/src/js/utils.js index e7834c3de6a6f..8d3814565d491 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -163,6 +163,20 @@ return line; }; +µBlock.LineIterator.prototype.rewind = function() { + if ( this.offset <= 1 ) { + this.offset = 0; + return; + } + var lineEnd = this.text.lastIndexOf('\n', this.offset - 2); + if ( lineEnd !== -1 ) { + this.offset = lineEnd + 1; + } else { + lineEnd = this.text.lastIndexOf('\r', this.offset - 2); + this.offset = lineEnd !== -1 ? lineEnd + 1 : 0; + } +}; + µBlock.LineIterator.prototype.eot = function() { return this.offset >= this.textLen; }; @@ -197,33 +211,37 @@ /******************************************************************************/ -µBlock.mapToArray = function(map) { - var out = [], - entries = map.entries(), - entry; - for (;;) { - entry = entries.next(); - if ( entry.done ) { break; } - out.push([ entry.value[0], entry.value[1] ]); - } - return out; -}; +µBlock.mapToArray = typeof Array.from === 'function' + ? Array.from + : function(map) { + var out = [], + entries = map.entries(), + entry; + for (;;) { + entry = entries.next(); + if ( entry.done ) { break; } + out.push([ entry.value[0], entry.value[1] ]); + } + return out; + }; µBlock.mapFromArray = function(arr) { return new Map(arr); }; -µBlock.setToArray = function(dict) { - var out = [], - entries = dict.values(), - entry; - for (;;) { - entry = entries.next(); - if ( entry.done ) { break; } - out.push(entry.value); - } - return out; -}; +µBlock.setToArray = typeof Array.from === 'function' + ? Array.from + : function(dict) { + var out = [], + entries = dict.values(), + entry; + for (;;) { + entry = entries.next(); + if ( entry.done ) { break; } + out.push(entry.value); + } + return out; + }; µBlock.setFromArray = function(arr) { return new Set(arr); From bed2e99e661f675a198a36588850307d7bdda23a Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 11 Mar 2017 16:54:47 -0500 Subject: [PATCH 0133/4093] 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 277e007b23804..33fe11b39fbe9 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.3.1", + "version": "1.11.3.2", "default_locale": "en", "description": "__MSG_extShortDesc__", From 7a023077b34e0eef509638a6db866f4326e5ff7b Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 12 Mar 2017 10:22:46 -0400 Subject: [PATCH 0134/4093] performance work for pseudo-user styles code --- src/js/contentscript.js | 80 +++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index c4d29e5748fe5..e11d868cb86b4 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -229,55 +229,75 @@ var platformHideNode = vAPI.hideNode, } var uid, - timerId, + timer, observer, - changedNodes = []; - var observerOptions = { + changedNodes = [], + observerOptions = { attributes: true, attributeFilter: [ 'style' ] }; - var overrideInlineStyle = function(node) { - var style = window.getComputedStyle(node), - display = style.getPropertyValue('display'), - attr = node.getAttribute('style') || ''; - if ( node[uid] === undefined ) { - node[uid] = node.hasAttribute('style') && attr; - } - if ( display !== '' && display !== 'none' ) { - if ( attr !== '' ) { attr += '; '; } - node.setAttribute('style', attr + 'display: none !important;'); - } - }; - - var timerHandler = function() { - timerId = undefined; - var nodes = changedNodes, - i = nodes.length, node; + // https://jsperf.com/clientheight-and-clientwidth-vs-getcomputedstyle + // Avoid getComputedStyle(), detecting whether a node is visible can be + // achieved with clientWidth/clientHeight. + // https://gist.github.com/paulirish/5d52fb081b3570c81e3a + // Do not interleave read-from/write-to the DOM. Write-to DOM + // operations would cause the first read-from to be expensive, and + // interleaving means that potentially all single read-from operation + // would be expensive rather than just the 1st one. + // Benchmarking toggling off/on cosmetic filtering confirms quite an + // improvement when: + // - batching as much as possible handling of all nodes; + // - avoiding to interleave read-from/write-to operations. + // However, toggling off/on cosmetic filtering repeatedly is not + // a real use case, but this shows this will help performance + // on sites which try to use inline styles to bypass blockers. + var batchProcess = function() { + timer.clear(); + var cNodes = changedNodes, i = cNodes.length, + vNodes = [], j = 0, + node; while ( i-- ) { - node = nodes[i]; - if ( node[uid] !== undefined ) { - overrideInlineStyle(node); + node = cNodes[i]; + if ( node[uid] !== undefined && node.clientHeight && node.clientWidth ) { + vNodes[j++] = node; + } + } + cNodes.length = 0; + while ( j-- ) { + node = vNodes[j]; + var attr = node.getAttribute('style'); + if ( !attr ) { + attr = ''; + } else { + attr += '; '; } + node.setAttribute('style', attr + 'display: none !important;'); } - nodes.length = 0; }; var observerHandler = function(mutations) { - var i = mutations.length; + var i = mutations.length, + cNodes = changedNodes, + j = cNodes.length; while ( i-- ) { - changedNodes.push(mutations[i].target); - } - if ( timerId === undefined ) { - timerId = vAPI.setTimeout(timerHandler, 1); + cNodes[j++] = mutations[i].target; } + timer.start(); }; platformHideNode = function(node) { if ( uid === undefined ) { uid = vAPI.randomToken(); + timer = new vAPI.SafeAnimationFrame(batchProcess); + } + if ( node[uid] === undefined ) { + node[uid] = node.hasAttribute('style') && (node.getAttribute('style') || ''); } - overrideInlineStyle(node); + // Performance: batch-process nodes to hide. + var cNodes = changedNodes; + cNodes[cNodes.length] = node; + timer.start(); if ( observer === undefined ) { observer = new MutationObserver(observerHandler); } From 0d2c0a5ba31fe2f105ec577fb479f086ecd1fc77 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 13 Mar 2017 10:07:26 -0400 Subject: [PATCH 0135/4093] fix #2450 --- src/js/storage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/storage.js b/src/js/storage.js index d76c5c9bb3144..135bff65270e7 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -180,8 +180,7 @@ var listKeys = []; if ( bin.selectedFilterLists ) { listKeys = bin.selectedFilterLists; - } - if ( bin.remoteBlacklists ) { + } else if ( bin.remoteBlacklists ) { var oldListKeys = µb.newListKeysFromOldData(bin.remoteBlacklists); if ( oldListKeys.sort().join() !== listKeys.sort().join() ) { listKeys = oldListKeys; From fba8ea2e75ee26af4963e2670ce0eb881a88104e Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 13 Mar 2017 10:27:13 -0400 Subject: [PATCH 0136/4093] 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 33fe11b39fbe9..e383a97f50e30 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.3.2", + "version": "1.11.5.0", "default_locale": "en", "description": "__MSG_extShortDesc__", From cf123b926497414cdaab83e265ae85891f372ab6 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 13 Mar 2017 13:03:51 -0400 Subject: [PATCH 0137/4093] fix #2448 --- src/js/contentscript.js | 30 +++++++++---------- src/js/cosmetic-filtering.js | 45 ++++++++++++++++++++++------ src/js/scriptlets/cosmetic-logger.js | 15 ++++------ 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index e11d868cb86b4..7547b6e9e8170 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -233,9 +233,9 @@ var platformHideNode = vAPI.hideNode, observer, changedNodes = [], observerOptions = { - attributes: true, - attributeFilter: [ 'style' ] - }; + attributes: true, + attributeFilter: [ 'style' ] + }; // https://jsperf.com/clientheight-and-clientwidth-vs-getcomputedstyle // Avoid getComputedStyle(), detecting whether a node is visible can be @@ -489,9 +489,9 @@ var domFilterer = { hiddenNodeEnforcer: false, loggerEnabled: undefined, - newHideSelectorBuffer: [], // Hide style filter buffer - newStyleRuleBuffer: [], // Non-hide style filter buffer - simpleHideSelectors: { // Hiding filters: simple selectors + newHideSelectorBuffer: [], // Hide style filter buffer + newStyleRuleBuffer: [], // Non-hide style filter buffer + simpleHideSelectors: { // Hiding filters: simple selectors entries: [], matchesProp: vAPI.matchesProp, selector: undefined, @@ -513,7 +513,7 @@ var domFilterer = { } } }, - complexHideSelectors: { // Hiding filters: complex selectors + complexHideSelectors: { // Hiding filters: complex selectors entries: [], selector: undefined, add: function(selector) { @@ -531,13 +531,8 @@ var domFilterer = { } } }, - styleSelectors: { // Style filters - entries: [], - add: function(o) { - this.entries.push(o); - } - }, - proceduralSelectors: { // Hiding filters: procedural + nqsSelectors: [], // Non-querySelector-able filters + proceduralSelectors: { // Hiding filters: procedural entries: [], add: function(o) { this.entries.push(new PSelector(o)); @@ -578,7 +573,12 @@ var domFilterer = { var o = JSON.parse(selector); if ( o.style ) { this.newStyleRuleBuffer.push(o.style.join(' ')); - this.styleSelectors.add(o); + this.nqsSelectors.push(o.raw); + return; + } + if ( o.pseudoclass ) { + this.newHideSelectorBuffer.push(o.raw); + this.nqsSelectors.push(o.raw); return; } if ( o.tasks ) { diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 32b13ef95035d..44cb62421ab95 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.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 @@ -732,7 +732,8 @@ FilterContainer.prototype.freeze = function() { // https://github.com/gorhill/uBlock/issues/1752 FilterContainer.prototype.compileSelector = (function() { - var reStyleSelector = /^(.+?):style\((.+?)\)$/, + var reAfterBeforeSelector = /^(.+?)(::?after|::?before)$/, + reStyleSelector = /^(.+?):style\((.+?)\)$/, reStyleBad = /url\([^)]+\)/, reScriptSelector = /^script:(contains|inject)\((.+)\)$/, div = document.createElement('div'); @@ -751,17 +752,43 @@ FilterContainer.prototype.compileSelector = (function() { } // We rarely reach this point. - var matches; + var matches, + selector = raw, + pseudoclass, + style; // `:style` selector? - if ( - (matches = reStyleSelector.exec(raw)) !== null && - isValidCSSSelector(matches[1]) && - isValidStyleProperty(matches[2]) - ) { + 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, - style: [ matches[1], '{' + matches[2] + '}' ] + pseudoclass: true }); } diff --git a/src/js/scriptlets/cosmetic-logger.js b/src/js/scriptlets/cosmetic-logger.js index efcf6110512c7..bbbd637f74a85 100644 --- a/src/js/scriptlets/cosmetic-logger.js +++ b/src/js/scriptlets/cosmetic-logger.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 @@ -51,14 +51,11 @@ vAPI.domFilterer.simpleHideSelectors.entries.forEach(evaluateSelector); // Complex CSS selector-based cosmetic filters. vAPI.domFilterer.complexHideSelectors.entries.forEach(evaluateSelector); -// Style cosmetic filters. -vAPI.domFilterer.styleSelectors.entries.forEach(function(filter) { - if ( - loggedSelectors.hasOwnProperty(filter.raw) === false && - document.querySelector(filter.style[0]) !== null - ) { - loggedSelectors[filter.raw] = true; - matchedSelectors.push(filter.raw); +// Non-querySelector-able filters. +vAPI.domFilterer.nqsSelectors.forEach(function(filter) { + if ( loggedSelectors.hasOwnProperty(filter) === false ) { + loggedSelectors[filter] = true; + matchedSelectors.push(filter); } }); From 53b0db3d250caa6ec33577bc514b3ef4192bd260 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 14 Mar 2017 15:09:40 -0400 Subject: [PATCH 0138/4093] fix #2447 --- src/css/logger-ui.css | 1 + src/js/messaging.js | 4 ++-- src/js/url-net-filtering.js | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index 949754500f406..c95fb76273ddf 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -454,6 +454,7 @@ body[dir="rtl"] #netFilteringDialog .dialog > div.headers > span.tools { border: 1px solid #ddc; border-radius: 4px; color: #888; + cursor: pointer; font-size: 1.8em; margin: 0.1em; padding: 0.25em 0.5em; diff --git a/src/js/messaging.js b/src/js/messaging.js index 776cf192838c9..c8b47c20a0cc5 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1079,13 +1079,13 @@ var getURLFilteringData = function(details) { colorEntry = colors[url] = { r: 0, own: false }; if ( suf.evaluateZ(context, url, type).r !== 0 ) { colorEntry.r = suf.r; - colorEntry.own = suf.context === context && suf.url === url && suf.type === type; + colorEntry.own = suf.r !== 0 && suf.context === context && suf.url === url && suf.type === type; } if ( response.dirty ) { continue; } puf.evaluateZ(context, url, type); - response.dirty = colorEntry.own !== (puf.context === context && puf.url === url && puf.type === type); + response.dirty = colorEntry.own !== (puf.r !== 0 && puf.context === context && puf.url === url && puf.type === type); } return response; }; diff --git a/src/js/url-net-filtering.js b/src/js/url-net-filtering.js index 2891a268b6a26..e5be493cca195 100644 --- a/src/js/url-net-filtering.js +++ b/src/js/url-net-filtering.js @@ -274,9 +274,9 @@ URLNetFiltering.prototype.copyRules = function(other, context, urls, type) { while ( i-- ) { url = urls[i]; other.evaluateZ(context, url, type); - otherOwn = other.context === context && other.url === url && other.type === type; + otherOwn = other.r !== 0 && other.context === context && other.url === url && other.type === type; this.evaluateZ(context, url, type); - thisOwn = this.context === context && this.url === url && this.type === type; + thisOwn = this.r !== 0 && this.context === context && this.url === url && this.type === type; if ( otherOwn && !thisOwn ) { this.setRule(context, url, type, other.r); changed = true; From e0a511634897ebf129714f03d5e56ae667db7770 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 14 Mar 2017 16:01:35 -0400 Subject: [PATCH 0139/4093] fix #2454 --- platform/webext/install.rdf | 2 +- tools/make-webext-meta.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/webext/install.rdf b/platform/webext/install.rdf index 5039da2c7f3ad..0d0e9b3bf24f2 100644 --- a/platform/webext/install.rdf +++ b/platform/webext/install.rdf @@ -1,7 +1,7 @@ - uBlock0@raymondhill.net + uBlock0-webext@raymondhill.net {version} {name} {description} diff --git a/tools/make-webext-meta.py b/tools/make-webext-meta.py index 5e299206fa252..694f63006796e 100644 --- a/tools/make-webext-meta.py +++ b/tools/make-webext-meta.py @@ -43,6 +43,7 @@ descriptions[alpha2] = strings['extShortDesc']['message'] webext_manifest['author'] = chromium_manifest['author']; +webext_manifest['name'] = chromium_manifest['name'] + '/webext'; webext_manifest['homepage'] = 'https://github.com/gorhill/uBlock' webext_manifest['description'] = descriptions['en'] del descriptions['en'] From 23fa686f46fe07277fc83da3d5a72adfe3c9d8d0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 14 Mar 2017 16:05:44 -0400 Subject: [PATCH 0140/4093] 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 e383a97f50e30..f63533c262b2d 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.0", + "version": "1.11.5.1", "default_locale": "en", "description": "__MSG_extShortDesc__", From d7f751d42bcf23391adc19878daa12de3f8b1a93 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 16 Mar 2017 19:14:25 -0400 Subject: [PATCH 0141/4093] fix #2440 --- src/js/traffic.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index da56f1a0f11f4..601bf60bc85f6 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -437,9 +437,11 @@ var processCSP = function(pageStore, details) { inlineScriptResult = pageStore.filterRequestNoCache(context); blockInlineScript = µb.isBlockResult(inlineScriptResult); // https://github.com/gorhill/uBlock/issues/2360 + // https://github.com/gorhill/uBlock/issues/2440 context.requestType = 'script'; context.requestURL = 'blob:'; - workerResult = pageStore.filterRequestNoCache(context); + µb.staticNetFilteringEngine.matchString(context); + workerResult = µb.staticNetFilteringEngine.toResultString(loggerEnabled); blockWorker = µb.isBlockResult(workerResult); } @@ -469,7 +471,7 @@ var processCSP = function(pageStore, details) { context.pageHostname ); } - if ( websocketResult !== '' ) { + if ( websocketResult ) { µb.logger.writeOne( tabId, 'net', @@ -480,7 +482,7 @@ var processCSP = function(pageStore, details) { context.pageHostname ); } - if ( workerResult !== '' ) { + if ( workerResult ) { µb.logger.writeOne( tabId, 'net', From 64c92a96a3c56ffc2e8b263325e6ac85fe45ab96 Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 16 Mar 2017 19:31:14 -0400 Subject: [PATCH 0142/4093] fix https://github.com/nikrolls/uBlock-Edge/issues/64 --- src/js/contentscript.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 7547b6e9e8170..5d313d6df39ed 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -98,6 +98,8 @@ vAPI.matchesProp = (function() { return 'mozMatchesSelector'; } else if ( typeof docElem.webkitMatchesSelector === 'function' ) { return 'webkitMatchesSelector'; + } else if ( typeof docElem.msMatchesSelector === 'function' ) { + return 'msMatchesSelector'; } } return 'matches'; From dde8598ab51a351c877365bcb38f5f212659b97e Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 18 Mar 2017 10:04:49 -0400 Subject: [PATCH 0143/4093] translation work from https://crowdin.com/project/ublock --- dist/description/description-pt_PT.txt | 8 ++++---- dist/description/description-tr.txt | 2 +- src/_locales/ca/messages.json | 2 +- src/_locales/el/messages.json | 2 +- src/_locales/es/messages.json | 4 ++-- src/_locales/et/messages.json | 2 +- src/_locales/fa/messages.json | 4 ++-- src/_locales/fi/messages.json | 2 +- src/_locales/it/messages.json | 2 +- src/_locales/nl/messages.json | 2 +- src/_locales/pt_PT/messages.json | 10 +++++----- src/_locales/sk/messages.json | 6 +++--- src/_locales/sr/messages.json | 6 +++--- src/_locales/tr/messages.json | 10 +++++----- src/_locales/uk/messages.json | 6 +++--- 15 files changed, 34 insertions(+), 34 deletions(-) diff --git a/dist/description/description-pt_PT.txt b/dist/description/description-pt_PT.txt index 51f9510d7c39c..12c8ad7605279 100644 --- a/dist/description/description-pt_PT.txt +++ b/dist/description/description-pt_PT.txt @@ -3,11 +3,11 @@ Um bloqueador eficiente: leve na memória e CPU e, no entanto, consegue carregar Visão geral ilustradora da sua eficiência: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared -Utilização: O botão grande de energia na janela serve para desativar ou ativar, permanentemente, o uBlock para o web site atual. Aplica-se unicamente ao web site atual, não sendo um botão de energia global. +Utilização: O botão grande de energia na janela serve para desativar/ativar permanentemente o uBlock para o web site atual. Aplica-se unicamente ao web site atual, não sendo um botão de energia global. *** -Flexível, é mais do que um bloqueador de anúncios. Pode também ler e criar filtros a partir de ficheiros de servidores. +Flexível, é mais do que um "bloqueador de anúncios": pode também ler e criar filtros a partir de ficheiros de servidores. Por predefinição, estas listas de filtros são carregadas e aplicadas: @@ -27,7 +27,7 @@ Se quiser, estão disponíveis mais listas para seleção: Obviamente que quanto maior o número de filtros ativos, maior será o consumo de memória. No entanto, mesmo após adicionar as duas listas extra do Fanboy, hpHosts Ad and tracking servers, o uBlock₀ continua a consumir menos memória do que outros bloqueadores populares disponíveis. -Esteja ciente de que se selecionar algumas destas listas extra pode resultar numa probabilidade acrescida de rutura em alguns web sites -- especialmente nas listas que são normalmente utilizadas como ficheiro de servidores. +Esteja ciente de que selecionar algumas destas listas extra pode resultar numa probabilidade acrescida de rutura em alguns web sites -- especialmente as listas que são normalmente utilizadas como ficheiro de servidores. *** @@ -35,7 +35,7 @@ Sem as listas de filtros predefinidas, esta extensão não é nada. Se realmente *** -Grátis. +Livre. Código aberto com licença pública (GPLv3) De utilizadores para utilizadores. diff --git a/dist/description/description-tr.txt b/dist/description/description-tr.txt index 35fd585663a92..ce9b49b4b2df6 100644 --- a/dist/description/description-tr.txt +++ b/dist/description/description-tr.txt @@ -44,7 +44,7 @@ Katkıda bulunanlar @ Crowdin: https://crowdin.net/project/ublock *** -Bu çok erken bir sürüm, gözden geçirirken, bunu göz önünde bulundurun. +Bu çok erken bir sürüm, gözden geçirirken bunu göz önünde bulundurun. Proje değişiklik günlüğü: https://github.com/gorhill/uBlock/releases diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index 21e295279c0f8..0ad5e21d224e1 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -584,7 +584,7 @@ "description":"English: Network error: unable to connect to {{url}}" }, "subscriberConfirm":{ - "message":"µBlock: Voleu afegir aquest enllaç a la llista de filtres personalitzats?\n\nTítol: \"{{title}}\"\nURL: {{url}}", + "message":"uBlock₀: Voleu afegir aquest enllaç a la llista de filtres personalitzats?\n\nTítol: \"{{title}}\"\nURL: {{url}}", "description":"English: The message seen by the user to confirm subscription to a ABP filter list" }, "elapsedOneMinuteAgo":{ diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index 1cea6f934bc98..b81709644fc5e 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -284,7 +284,7 @@ "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ - "message":"

      Η επιλογή αυτή επιτρέπει την ανάλυση και επιβολή των συμβατών με τα “κρυμμένα στοιχεία” του Adblock Plus φίλτρων<\/a>. Τα φίλτρα αυτά είναι ουσιαστικά κοσμητικά, χρησιμεύουν στην απόκρυψη στοιχείων από μια σελίδα στην οποία θεωρούνται οπτική όχληση και δεν μπορούν να αποκλειστούν με την, βασισμένη σε αιτήματα δικτύου, μηχανή φιλτραρίσματος.<\/p>

      Η ενεργοποίηση αυτής της λειτουργίας αυξάνει το αποτύπωμα μνήμης του µBlock<\/i>.<\/p>", + "message":"

      Η επιλογή αυτή επιτρέπει την ανάλυση και επιβολή των συμβατών με τα “κρυμμένα στοιχεία” του Adblock Plus φίλτρων<\/a>. Τα φίλτρα αυτά είναι ουσιαστικά κοσμητικά, χρησιμεύουν στην απόκρυψη στοιχείων από μια σελίδα στην οποία θεωρούνται οπτική όχληση και δεν μπορούν να αποκλειστούν με την, βασισμένη σε αιτήματα δικτύου, μηχανή φιλτραρίσματος.<\/p>

      Η ενεργοποίηση αυτής της λειτουργίας αυξάνει το αποτύπωμα μνήμης του uBlock₀<\/i>.<\/p>", "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index 57757a32b1e34..a221b9d2d5210 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -100,7 +100,7 @@ "description":"Tooltip when hovering the top-most cell of the global-rules column." }, "popupTipLocalRules":{ - "message":"Reglas locales: las reglas en esta columna aplican solo al sitio actual.\nLas reglas locales tienen preferencia sobre las reglas globales.", + "message":"Reglas locales: las reglas en esta columna aplican solo al sitio actual.\nLas reglas locales anulan las reglas globales.", "description":"Tooltip when hovering the top-most cell of the local-rules column." }, "popupTipSaveRules":{ @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Lista de nombres de servidores para los cuales se deshabilitará uBlock₀. Una entrada por línea. Se omitirán nombres de servidores no válidos.", + "message":"Lista de servidores para los cuales se deshabilitará uBlock₀. Una entrada por línea. Se omitirán nombres de servidores inválidos.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index de3eade6be014..e780fe77b869b 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -32,7 +32,7 @@ "description":"appears as tab name in dashboard" }, "statsPageName":{ - "message":"uBlock — Võrgutaotluste logi", + "message":"uBlock₀ — Võrgutaotluste logi", "description":"Title for the logger window" }, "aboutPageName":{ diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index cd6a577f1f9ae..7b9ec145db41f 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":{ @@ -584,7 +584,7 @@ "description":"English: Network error: unable to connect to {{url}}" }, "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/fi/messages.json b/src/_locales/fi/messages.json index efd1f28cc400a..ebd5ab029dae6 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -284,7 +284,7 @@ "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ - "message":"

      Tämä asetus ottaa käyttöön Adblock Plus-yhteensopivat “elementtejä piilottavat” suodattimet<\/a>. Nämä suodattimet ovat pääasiassa ehosteita. Ne piilottavat verkkosivuilta elementtejä, jotka ovat visuaalisesti häiritseviä eikä niitä voi estää verkkopyyntöihin pohjautuvalla suodatinmoottorilla. <\/p>

      Tämä ominaisuus nostaa uBlock<\/i>in muistinkäyttöä.<\/p>", + "message":"

      Tämä asetus ottaa käyttöön Adblock Plus-yhteensopivat “elementtejä piilottavat” suodattimet<\/a>. Nämä suodattimet ovat pääasiassa ehosteita. Ne piilottavat verkkosivuilta elementtejä, jotka ovat visuaalisesti häiritseviä eikä niitä voi estää verkkopyyntöihin pohjautuvalla suodatinmoottorilla. <\/p>

      Tämä ominaisuus nostaa uBlock₀<\/i>in muistinkäyttöä.<\/p>", "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index a8bf2552f51a9..500dce64b4ed9 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -284,7 +284,7 @@ "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ - "message":"

      Questa opzione abilita i filtri di Adblock Plus-compatible “element hiding” filters<\/a>. Questi filtri sono essenzialmente estetici, servono a nascondere elementi fastidiosi in una pagina web , e che non possono essere bloccati normalmente.<\/p>

      Questa funziona aumenta l'uso della memoria da parte di uBlock<\/i>.<\/p>", + "message":"

      Questa opzione abilita i filtri di Adblock Plus-compatible “element hiding” filters<\/a>. Questi filtri sono essenzialmente estetici, servono a nascondere elementi fastidiosi in una pagina web , e che non possono essere bloccati normalmente.<\/p>

      Questa funziona aumenta l'uso della memoria da parte di uBlock₀<\/i>.<\/p>", "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 36f2f5146fda0..05e61e712b3df 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"De whitelist-instructies schrijven voor op welke webpagina’s uBlock Origin dient te worden uitgeschakeld. Eén vermelding per regel. Ongeldige instructies worden zonder mededeling genegeerd en uitgeschakeld.", + "message":"De whitelist-instructies schrijven voor op welke webpagina’s uBlock Origin dient te worden uitgeschakeld. Eén vermelding per regel. Ongeldige instructies worden zonder mededeling genegeerd en uitgecommentarieerd.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 6e110e1c4d89e..fe76427e905ac 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -292,7 +292,7 @@ "description":"This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo":{ - "message":"

      Os filtros cosméticos genéricos são os filtros cosméticos que são destinados a ser aplicados em todos os web sites.

      Embora manuseados eficientemente pelo uBlock₀, os filtros cosméticos genéricos podem ainda contribuir para uma memória mensurável e overhead da CPU em algumas páginas web, especialmente os grandes e duradouros.

      Ao ativar esta opção irá eliminar a overhead da memória e CPU adicionada a páginas web como resultado do manuseamento de filtros cosméticos genéricos, e também irá diminuir a pegada de memória do próprio uBlock₀.

      É recomendado que ative esta opção em dispositivos menos poderosos.", + "message":"

      Os filtros cosméticos genéricos são aqueles filtros cosméticos destinados a ser aplicados em todos os web sites.

      Embora manuseados eficientemente pelo uBlock₀, os filtros cosméticos genéricos podem ainda contribuir para uma memória mensurável e overhead da CPU em algumas páginas web, especialmente os grandes e duradouros.

      Ao ativar esta opção irá eliminar a overhead da memória e CPU adicionada a páginas web como resultado do manuseamento de filtros cosméticos genéricos, e também irá diminuir a pegada de memória do próprio uBlock₀.

      É recomendado que ative esta opção em dispositivos menos poderosos.", "description":"Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pListsOfBlockedHostsHeader":{ @@ -584,7 +584,7 @@ "description":"English: Network error: unable to connect to {{url}}" }, "subscriberConfirm":{ - "message":"uBlock₀: Adicionar o seguinte URL à lista de filtros personalizados?\n\nTítulo: \"{{title}}\"\nURL: {{url}}", + "message":"uBlock₀: Adicionar o seguinte URL à sua lista de filtros personalizados?\n\nTítulo: \"{{title}}\"\nURL: {{url}}", "description":"English: The message seen by the user to confirm subscription to a ABP filter list" }, "elapsedOneMinuteAgo":{ @@ -592,7 +592,7 @@ "description":"English: a minute ago" }, "elapsedManyMinutesAgo":{ - "message":"há {{value}} minuto(s)", + "message":"há {{value}} minutos", "description":"English: {{value}} minutes ago" }, "elapsedOneHourAgo":{ @@ -600,7 +600,7 @@ "description":"English: an hour ago" }, "elapsedManyHoursAgo":{ - "message":"há {{value}} hora(s)", + "message":"há {{value}} horas", "description":"English: {{value}} hours ago" }, "elapsedOneDayAgo":{ @@ -608,7 +608,7 @@ "description":"English: a day ago" }, "elapsedManyDaysAgo":{ - "message":"há {{value}} dia(s)", + "message":"há {{value}} dias", "description":"English: {{value}} days ago" }, "showDashboardButton":{ diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 31edd775975dc..7ad779b9de074 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -44,7 +44,7 @@ "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ - "message":"Kliknutie: zakázať\/povoliť uBlock pre túto stránku.\n\nCtrl+kliknutie: zakázať uBlock len pre túto stránku.", + "message":"Kliknutie: zakázať\/povoliť uBlock pre túto stránku.\n\nCtrl+kliknutie: zakázať uBlock₀ len pre túto stránku.", "description":"English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupBlockedRequestPrompt":{ @@ -284,7 +284,7 @@ "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ - "message":"

      Táto možnosť povoľuje analyzovanie a vynútenie filtrov “skrývajúcich prvky” a kompatibilných s Adblock Plus<\/a>. Tieto filtre sú prevažne kozmetické. Skrývajú prvky webových stránok, ktoré sú vizuálne otravné a nemôžu byť zablokované filtrovaním sieťových požiadavkov.<\/p>

      Povolenie tejto funkcie zvyšujte nároky uBlock<\/i>na pamäť.<\/p>", + "message":"

      Táto možnosť povoľuje analyzovanie a vynútenie filtrov “skrývajúcich prvky” a kompatibilných s Adblock Plus<\/a>. Tieto filtre sú prevažne kozmetické. Skrývajú prvky webových stránok, ktoré sú vizuálne otravné a nemôžu byť zablokované filtrovaním sieťových požiadavkov.<\/p>

      Povolenie tejto funkcie zvyšujte nároky uBlock₀<\/i>na pamäť.<\/p>", "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ @@ -584,7 +584,7 @@ "description":"English: Network error: unable to connect to {{url}}" }, "subscriberConfirm":{ - "message":"uBlock: Pridať nasledujúcu URL do zoznamu vlastných filtrov?\n\nNázov: \"{{title}}\"\nURL: {{url}}", + "message":"uBlock₀: Pridať nasledujúcu URL do zoznamu vlastných filtrov?\n\nNázov: \"{{title}}\"\nURL: {{url}}", "description":"English: The message seen by the user to confirm subscription to a ABP filter list" }, "elapsedOneMinuteAgo":{ diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index aef2d92d94501..cc15861f7357e 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -528,7 +528,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":{ @@ -628,7 +628,7 @@ "description":"English: uBlock₀ has prevented the following page from loading:" }, "docblockedPrompt2":{ - "message":"Због следећег филтера", + "message":"због следећег филтера", "description":"English: Because of the following filter" }, "docblockedNoParamsPrompt":{ @@ -636,7 +636,7 @@ "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":"Нађен у:", + "message":"Пронађен у:", "description":"English: List of filter list names follows" }, "docblockedBack":{ diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 3c4b42f7f8967..561636d132024 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -424,7 +424,7 @@ "description":"English: dynamic rule syntax and full documentation." }, "whitelistPrompt":{ - "message":"Hangi alan adları için uBlock'un devre dışı olacağını belirten listeniz. Satır başına bir girdi. Geçersiz alan adları sessizce yok sayılır.", + "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.", "description":"English: An overview of the content of the dashboard's Whitelist pane." }, "whitelistImport":{ @@ -556,7 +556,7 @@ "description":"Text for button to create a backup of all settings" }, "aboutBackupFilename":{ - "message":"ublock-yedek_{{datetime}}.txt", + "message":"ublock-yedeğim_{{datetime}}.txt", "description":"English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton":{ @@ -624,7 +624,7 @@ "description":"Firefox-specific: appears as 'uBlock₀ (off)'" }, "docblockedPrompt1":{ - "message":"uBlock aşağıdaki sayfanın yüklenmesini engelledi:", + "message":"uBlock₀ aşağıdaki sayfanın yüklenmesini engelledi:", "description":"English: uBlock₀ has prevented the following page from loading:" }, "docblockedPrompt2":{ @@ -664,11 +664,11 @@ "description":"tooltip" }, "cloudPull":{ - "message":"Bulut depodan al", + "message":"Bulut depodan içe aktar", "description":"tooltip" }, "cloudPullAndMerge":{ - "message":"Bulut depolamadan içeri aktar ve şu anki ayarlarla birleştir", + "message":"Bulut depodan içe aktar ve şu anki ayarlarla birleştir", "description":"tooltip" }, "cloudNoData":{ diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index 7f0ae5f73f0fb..edb33c9fd97ed 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -44,7 +44,7 @@ "description":"Title for the advanced settings page" }, "popupPowerSwitchInfo":{ - "message":"Натиснення: вимикає\/умикає µBlock для поточного сайту.\n\nCtrl+натиснення: вимикає µBlock тільки для цієї сторінки.", + "message":"Натиснення: вимикає\/умикає uBlock₀ для поточного сайту.\n\nCtrl+натиснення: вимикає uBlock₀ тільки для цієї сторінки.", "description":"English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupBlockedRequestPrompt":{ @@ -284,7 +284,7 @@ "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ - "message":"

      Ця опція вмикає аналіз та застосування Adblock Plus-сумісних фільтрів “приховання елементв” <\/a>. По суті це косметичні фільтри – приховують елементи сторінки, які візуально дратують і не можуть бути блоковані механізмом фільтрації мережевих запитів.<\/p>

      Увімкнення цієї функції збільшує споживання пам’яті µBlock<\/i>'ом.<\/p>", + "message":"

      Ця опція вмикає аналіз та застосування Adblock Plus-сумісних фільтрів “приховання елементв” <\/a>. По суті це косметичні фільтри – приховують елементи сторінки, які візуально дратують і не можуть бути блоковані механізмом фільтрації мережевих запитів.<\/p>

      Увімкнення цієї функції збільшує споживання пам’яті uBlock₀<\/i>'ом.<\/p>", "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ @@ -584,7 +584,7 @@ "description":"English: Network error: unable to connect to {{url}}" }, "subscriberConfirm":{ - "message":"µBlock: Додати це посилання до списку ваших фільтрів?\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":{ From 2dcf7b797454b3caa3427bda36a0285bf4bd45f0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 20 Mar 2017 15:54:41 -0400 Subject: [PATCH 0144/4093] fix #2464 --- src/js/static-net-filtering.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f1e0e1a8297d6..ab7a4a8e87405 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -978,6 +978,12 @@ var FilterHostnameDict = function() { this.dict = new Set(); }; +Object.defineProperty(FilterHostnameDict.prototype, 'size', { + get: function() { + return this.dict.size; + } +}); + FilterHostnameDict.prototype.add = function(hn) { if ( this.dict.has(hn) ) { return false; @@ -986,6 +992,10 @@ FilterHostnameDict.prototype.add = function(hn) { return true; }; +FilterHostnameDict.prototype.remove = function(hn) { + return this.dict.delete(hn); +}; + FilterHostnameDict.prototype.match = function() { // TODO: mind IP addresses var pos, @@ -2097,8 +2107,8 @@ FilterContainer.prototype.removeBadFilters = function() { continue; } if ( entry instanceof FilterHostnameDict ) { - entry.delete(fclass); // 'fclass' is hostname - if ( entry.dict.size === 0 ) { + entry.remove(fclass); // 'fclass' is hostname + if ( entry.size === 0 ) { this.categories.delete(hash); } continue; From b19dfb2dce0efec96be75467955419ba9c7341b3 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 20 Mar 2017 16:25:40 -0400 Subject: [PATCH 0145/4093] smaller tab buttons for small screens (https://github.com/gorhill/uBlock/issues/1890#issuecomment-287016408) --- src/css/dashboard.css | 11 +++++++---- src/dashboard.html | 14 +++++++------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/css/dashboard.css b/src/css/dashboard.css index ea8f0573ecce1..f55ca5caa1043 100644 --- a/src/css/dashboard.css +++ b/src/css/dashboard.css @@ -41,7 +41,7 @@ html, body { cursor: pointer; display: inline-block; font-size: 110%; - margin: 0; + margin: 0 1px 0 0; padding: 4px; position: relative; text-decoration: none; @@ -70,15 +70,18 @@ iframe { position: relative; } #dashboard-nav-widgets { - padding: 0; + padding: 1px 0 0 0; + white-space: normal; } [data-i18n="extName"] { display: none; } .tabButton { border-radius: 0; - border-top: 0; - display: block; + display: inline-block; + font-size: 100%; + margin-bottom: 1px; + top: 0; } .tabButton.selected { border-bottom: 1px solid #ccc; diff --git a/src/dashboard.html b/src/dashboard.html index 8826d40869efb..14285be521b08 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -12,13 +12,13 @@

      - - - - - - - +
      From 15714ad22ba5f8313b5cd5b8c03bccb0bc53848b Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 20 Mar 2017 16:30:30 -0400 Subject: [PATCH 0146/4093] 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 f63533c262b2d..0ed48933982f4 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.1", + "version": "1.11.5.2", "default_locale": "en", "description": "__MSG_extShortDesc__", From 51ade9f04399f836a4b09ce73cfdd52fc2d39dc1 Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 21 Mar 2017 08:23:21 -0400 Subject: [PATCH 0147/4093] code review re. https://github.com/uBlockOrigin/uAssets/issues/192#issuecomment-285904675 --- src/js/static-net-filtering.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index ab7a4a8e87405..a33f370ba387e 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2106,13 +2106,6 @@ FilterContainer.prototype.removeBadFilters = function() { if ( entry === undefined ) { continue; } - if ( entry instanceof FilterHostnameDict ) { - entry.remove(fclass); // 'fclass' is hostname - if ( entry.size === 0 ) { - this.categories.delete(hash); - } - continue; - } if ( entry instanceof FilterBucket ) { entry.remove(fclass, fdata); if ( entry.filters.length === 1 ) { @@ -2120,8 +2113,22 @@ FilterContainer.prototype.removeBadFilters = function() { } continue; } + if ( entry instanceof FilterHostnameDict ) { + entry.remove(fclass); // 'fclass' is hostname + if ( entry.size === 0 ) { + bucket.delete(token); + if ( bucket.size === 0 ) { + this.categories.delete(hash); + } + } + continue; + } if ( entry.fid === fclass && entry.toSelfie() === fdata ) { - this.categories.delete(hash); + bucket.delete(token); + if ( bucket.size === 0 ) { + this.categories.delete(hash); + } + continue; } } }; From 52d580aabcf887efdd822b8369d04de579196d75 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 22 Mar 2017 17:15:38 -0400 Subject: [PATCH 0148/4093] add alt URLs for EasyList/EasyPrivacy (https://github.com/gorhill/uBlock/issues/2037#issuecomment-287532208) --- assets/assets.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/assets.json b/assets/assets.json index 305d1b6cdd658..b98670998d4ca 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -97,6 +97,7 @@ "title": "EasyList", "contentURL": [ "https://easylist.to/easylist/easylist.txt", + "https://secure.fanboy.co.nz/easylist.txt", "https://easylist-downloads.adblockplus.org/easylist.txt", "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/thirdparties/easylist-downloads.adblockplus.org/easylist.txt", "assets/thirdparties/easylist-downloads.adblockplus.org/easylist.txt" @@ -124,6 +125,7 @@ "title": "EasyPrivacy", "contentURL": [ "https://easylist.to/easylist/easyprivacy.txt", + "https://secure.fanboy.co.nz/easyprivacy.txt", "https://easylist-downloads.adblockplus.org/easyprivacy.txt", "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt", "assets/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt" From 556de1a9b4fabaf1f6da5d53a41daa0b0d35b74a Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 23 Mar 2017 08:36:38 -0400 Subject: [PATCH 0149/4093] 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 0ed48933982f4..242a22ef20c59 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.2", + "version": "1.11.5.100", "default_locale": "en", "description": "__MSG_extShortDesc__", From 554ff4d60356d4436b95338b4d45e9b98e59425f Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 24 Mar 2017 12:57:45 -0400 Subject: [PATCH 0150/4093] translation work from https://crowdin.com/project/ublock --- src/_locales/nb/messages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 0597f82b08c82..3b2d254246be3 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -76,7 +76,7 @@ "description":"English: Enter element picker mode" }, "popupTipLog":{ - "message":"Åpne loggeren", + "message":"Åpne loggen", "description":"Tooltip used for the logger icon in the panel" }, "popupTipNoPopups":{ @@ -616,7 +616,7 @@ "description":"Firefox\/Fennec-specific: Show Dashboard" }, "showNetworkLogButton":{ - "message":"Vis loggeren", + "message":"Vis logg", "description":"Firefox\/Fennec-specific: Show Logger" }, "fennecMenuItemBlockingOff":{ From ea99f5fd076391f9afc5fe198323b99cb3d55839 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 25 Mar 2017 09:18:19 -0400 Subject: [PATCH 0151/4093] fix #2435 --- platform/chromium/manifest.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 242a22ef20c59..ad6d04c0e4927 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.100", + "version": "1.11.5.101", "default_locale": "en", "description": "__MSG_extShortDesc__", @@ -54,10 +54,7 @@ "webNavigation", "webRequest", "webRequestBlocking", - "http://*/*", - "https://*/*", - "ws://*/*", - "wss://*/*" + "" ], "short_name": "uBlock₀", "storage": { From 1b1e9828352d5ba5d6c758eccf1cdac4e2a33510 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 25 Mar 2017 09:19:57 -0400 Subject: [PATCH 0152/4093] minor code review --- platform/chromium/vapi-common.js | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/chromium/vapi-common.js b/platform/chromium/vapi-common.js index 6020d66652025..803081a281ba7 100644 --- a/platform/chromium/vapi-common.js +++ b/platform/chromium/vapi-common.js @@ -56,6 +56,7 @@ vAPI.download = function(details) { var a = document.createElement('a'); a.href = details.url; a.setAttribute('download', details.filename || ''); + a.setAttribute('type', 'text/plain'); a.dispatchEvent(new MouseEvent('click')); }; From 419fad084ead4f330dbe60c1d19a01d42dfe6d04 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 25 Mar 2017 10:57:11 -0400 Subject: [PATCH 0153/4093] change of heart on how to migrate legacy storage --- platform/webext/bootstrap.js | 10 +++------- platform/webext/from-legacy.js | 9 ++++++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/platform/webext/bootstrap.js b/platform/webext/bootstrap.js index 69f6fc7ddda47..ebbbc0b7814df 100644 --- a/platform/webext/bootstrap.js +++ b/platform/webext/bootstrap.js @@ -46,6 +46,9 @@ function startup({ webExtension }) { }); return true; } + if ( message.what === 'webext:storageMigrateDone' ) { + browser.runtime.onMessage.removeListener(onMessage); + } if ( typeof callback === 'function' ) { callback(); } @@ -262,13 +265,6 @@ var getStorageMigrator = function() { let markAsDone = function() { close(); - let { Services } = Components.utils.import('resource://gre/modules/Services.jsm', null), - path = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile); - path.append('extension-data'); - path.append(hostName + '.sqlite'); - if ( path.exists() && path.isFile() ) { - path.renameTo(null, hostName + '.migrated.sqlite'); - } }; return { diff --git a/platform/webext/from-legacy.js b/platform/webext/from-legacy.js index 456086536b618..93edba2be64c0 100644 --- a/platform/webext/from-legacy.js +++ b/platform/webext/from-legacy.js @@ -52,7 +52,14 @@ }); }; - migrateNext(); + self.browser.storage.local.get('legacyStorageMigrated', bin => { + if ( bin && bin.legacyStorageMigrated ) { + self.browser.runtime.sendMessage({ what: 'webext:storageMigrateDone' }); + return callback(); + } + self.browser.storage.local.set({ legacyStorageMigrated: true }); + migrateNext(); + }); }; µb.onBeforeStartQueue.push(migrateAll); From 69b7dc3289986fa871cecea4ef0e1e2bb33b2013 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 27 Mar 2017 10:09:10 -0400 Subject: [PATCH 0154/4093] leveraging virtuous side-effect of using strictest setting for webrtc local IP address prevention, see https://github.com/uBlockOrigin/uAssets/issues/333#issuecomment-289426678 --- platform/chromium/vapi-background.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 2032ba2c1b165..8e3cc9e791498 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -169,16 +169,12 @@ vAPI.browserSettings = (function() { scope: 'regular' }, this.noopCallback); } else { - // Respect current stricter setting if any. - cpn.webRTCIPHandlingPolicy.get({}, function(details) { - var value = details.value === 'disable_non_proxied_udp' ? - 'disable_non_proxied_udp' : - 'default_public_interface_only'; - cpn.webRTCIPHandlingPolicy.set({ - value: value, - scope: 'regular' - }, this.noopCallback); - }.bind(this)); + // https://github.com/uBlockOrigin/uAssets/issues/333#issuecomment-289426678 + // - Leverage virtuous side-effect of strictest setting. + cpn.webRTCIPHandlingPolicy.set({ + value: 'disable_non_proxied_udp', + scope: 'regular' + }, this.noopCallback); } } catch(ex) { console.error(ex); From d7c8588c16bd9596e5412d264c9757ea732ac379 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 27 Mar 2017 10:10:56 -0400 Subject: [PATCH 0155/4093] 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 ad6d04c0e4927..f241da64c979f 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.101", + "version": "1.11.5.102", "default_locale": "en", "description": "__MSG_extShortDesc__", From dc06d5fa0cbecfc10f7f1e96af04f945e016bc1c Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Fri, 31 Mar 2017 21:17:21 +0100 Subject: [PATCH 0156/4093] Fix #2493 by using the createWindowlessBrowser API when available --- platform/firefox/bootstrap.js | 78 +++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js index 04e43ebfc3aad..3f4660547d367 100644 --- a/platform/firefox/bootstrap.js +++ b/platform/firefox/bootstrap.js @@ -61,6 +61,60 @@ function startup(data/*, reason*/) { let appShell = Cc['@mozilla.org/appshell/appShellService;1'] .getService(Ci.nsIAppShellService); + if ( appShell.createWindowlessBrowser ) { + getWindowlessBrowserFrame(appShell); + } else { + getHiddenWindowBrowserFrame(appShell); + } +} + +function createBgProcess(parentDocument) { + bgProcess = parentDocument.documentElement.appendChild( + parentDocument.createElementNS('http://www.w3.org/1999/xhtml', 'iframe') + ); + bgProcess.setAttribute( + 'src', + 'chrome://' + hostName + '/content/background.html#' + version + ); + + // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener%28%29 + // "If the same listener registers twice for the same message, the + // "second registration is ignored." + restartListener.messageManager.addMessageListener( + hostName + '-restart', + restartListener + ); +} + +let windowlessBrowser; +let windowlessBrowserPL; + +function getWindowlessBrowserFrame(appShell) { + windowlessBrowser = appShell.createWindowlessBrowser(true); + windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor); + let webProgress = windowlessBrowser.getInterface(Ci.nsIWebProgress); + let XPCOMUtils = Components.utils.import('resource://gre/modules/XPCOMUtils.jsm', null).XPCOMUtils; + windowlessBrowserPL = { + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIWebProgressListener, Ci.nsIWebProgressListener2, + Ci.nsISupportsWeakReference]), + onStateChange(wbp, request, stateFlags, status) { + if ( !request ) { + return; + } + if ( stateFlags & Ci.nsIWebProgressListener.STATE_STOP ) { + webProgress.removeProgressListener(windowlessBrowserPL); + windowlessBrowserPL = null; + createBgProcess(windowlessBrowser.document); + } + } + }; + webProgress.addProgressListener(windowlessBrowserPL, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); + windowlessBrowser.document.location = "data:application/vnd.mozilla.xul+xml;charset=utf-8,"; +} + + +function getHiddenWindowBrowserFrame(appShell) { let isReady = function() { var hiddenDoc; @@ -75,23 +129,7 @@ function startup(data/*, reason*/) { if ( !hiddenDoc || hiddenDoc.readyState !== 'complete' ) { return false; } - - bgProcess = hiddenDoc.documentElement.appendChild( - hiddenDoc.createElementNS('http://www.w3.org/1999/xhtml', 'iframe') - ); - bgProcess.setAttribute( - 'src', - 'chrome://' + hostName + '/content/background.html#' + version - ); - - // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener%28%29 - // "If the same listener registers twice for the same message, the - // "second registration is ignored." - restartListener.messageManager.addMessageListener( - hostName + '-restart', - restartListener - ); - + createBgProcess(hiddenDoc); return true; }; @@ -150,6 +188,12 @@ function shutdown(data, reason) { bgProcess = null; } + if ( windowlessBrowser !== null ) { + windowlessBrowser.close(); + windowlessBrowser = null; + windowlessBrowserPL = null; + } + if ( data === undefined ) { return; } From 0112e548348ac0ac598dcbea9852477f3c6d61d0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 09:41:08 -0400 Subject: [PATCH 0157/4093] fix https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 --- platform/chromium/vapi-client.js | 13 +++++++++---- platform/firefox/vapi-client.js | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index 78396637b2adf..efaffeab41bb0 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors + Copyright (C) 2014-2017 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 @@ -27,7 +27,7 @@ /******************************************************************************/ -(function(self) { +(function() { /******************************************************************************/ /******************************************************************************/ @@ -56,7 +56,12 @@ if ( /^image\/|^text\/plain/.test(contentType) ) { /******************************************************************************/ -var vAPI = self.vAPI = self.vAPI || {}; +// https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 +if ( !self.vAPI || !self.vAPI.uBO ) { + self.vAPI = { uBO: true }; +} + +var vAPI = self.vAPI; var chrome = self.chrome; // https://github.com/chrisaljoudi/uBlock/issues/456 @@ -520,6 +525,6 @@ vAPI.executionCost.stop('vapi-client.js'); /******************************************************************************/ /******************************************************************************/ -})(this); +})(); /******************************************************************************/ diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index 90c46334d0866..f5a761923f193 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors + Copyright (C) 2014-2017 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 @@ -29,7 +29,7 @@ /******************************************************************************/ -(function(self) { +(function() { // https://github.com/chrisaljoudi/uBlock/issues/464 if ( document instanceof HTMLDocument === false ) { @@ -45,7 +45,12 @@ if ( document instanceof HTMLDocument === false ) { /******************************************************************************/ -var vAPI = self.vAPI = self.vAPI || {}; +// https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 +if ( !self.vAPI || !self.vAPI.uBO ) { + self.vAPI = { uBO: true }; +} + +var vAPI = self.vAPI; /******************************************************************************/ @@ -537,6 +542,6 @@ vAPI.executionCost.stop('vapi-client.js'); /******************************************************************************/ -})(this); +})(); /******************************************************************************/ From 8e73fb32b58dedf08b3538808b706950ef647a9a Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 10:00:09 -0400 Subject: [PATCH 0158/4093] fix https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 --- platform/chromium/vapi-common.js | 9 +++++++-- platform/firefox/vapi-common.js | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/platform/chromium/vapi-common.js b/platform/chromium/vapi-common.js index 803081a281ba7..70fa49292e175 100644 --- a/platform/chromium/vapi-common.js +++ b/platform/chromium/vapi-common.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors + Copyright (C) 2014-2017 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 @@ -28,7 +28,12 @@ (function() { -var vAPI = self.vAPI = self.vAPI || {}; +// https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 +if ( !self.vAPI || !self.vAPI.uBO ) { + self.vAPI = { uBO: true }; +} + +var vAPI = self.vAPI; var chrome = self.chrome; /******************************************************************************/ diff --git a/platform/firefox/vapi-common.js b/platform/firefox/vapi-common.js index 5ba6ba7952fce..6fc7f5b852244 100644 --- a/platform/firefox/vapi-common.js +++ b/platform/firefox/vapi-common.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2016 The uBlock Origin authors + Copyright (C) 2014-2017 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 @@ -36,7 +36,12 @@ const {Services} = Components.utils.import( null ); -var vAPI = self.vAPI = self.vAPI || {}; +// https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 +if ( !self.vAPI || !self.vAPI.uBO ) { + self.vAPI = { uBO: true }; +} + +var vAPI = self.vAPI; /******************************************************************************/ From e4973e738be4c076c9c07bbcf053ff45ee0abe8f Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 11:08:57 -0400 Subject: [PATCH 0159/4093] 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 f241da64c979f..9e4a54c1ebb51 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.102", + "version": "1.11.5.103", "default_locale": "en", "description": "__MSG_extShortDesc__", From 66635c44c66fe139a90cd44f68e42ce74f92e10b Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 12:13:29 -0400 Subject: [PATCH 0160/4093] fix warning on older firefox versions --- platform/firefox/bootstrap.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js index 3f4660547d367..e53bf08dae485 100644 --- a/platform/firefox/bootstrap.js +++ b/platform/firefox/bootstrap.js @@ -31,6 +31,8 @@ const {classes: Cc, interfaces: Ci} = Components; // Accessing the context of the background page: // var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=ublock0]').contentWindow; +let windowlessBrowser = null; +let windowlessBrowserPL = null; let bgProcess = null; let version; const hostName = 'ublock0'; @@ -86,9 +88,6 @@ function createBgProcess(parentDocument) { ); } -let windowlessBrowser; -let windowlessBrowserPL; - function getWindowlessBrowserFrame(appShell) { windowlessBrowser = appShell.createWindowlessBrowser(true); windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor); @@ -98,7 +97,7 @@ function getWindowlessBrowserFrame(appShell) { QueryInterface: XPCOMUtils.generateQI([ Ci.nsIWebProgressListener, Ci.nsIWebProgressListener2, Ci.nsISupportsWeakReference]), - onStateChange(wbp, request, stateFlags, status) { + onStateChange(wbp, request, stateFlags/*, status*/) { if ( !request ) { return; } @@ -189,7 +188,10 @@ function shutdown(data, reason) { } if ( windowlessBrowser !== null ) { - windowlessBrowser.close(); + // close() does not exist for older versions of Firefox. + if ( typeof windowlessBrowser.close === 'function' ) { + windowlessBrowser.close(); + } windowlessBrowser = null; windowlessBrowserPL = null; } From 08409c5cb3bd31667db3404d17f6e8e416becf20 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 12:14:44 -0400 Subject: [PATCH 0161/4093] fix regression bug introduced with 0112e5 --- platform/firefox/vapi-client.js | 4 ++-- platform/firefox/vapi-common.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index f5a761923f193..e810d79d8e934 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -29,7 +29,7 @@ /******************************************************************************/ -(function() { +(function(self) { // https://github.com/chrisaljoudi/uBlock/issues/464 if ( document instanceof HTMLDocument === false ) { @@ -542,6 +542,6 @@ vAPI.executionCost.stop('vapi-client.js'); /******************************************************************************/ -})(); +})(this); /******************************************************************************/ diff --git a/platform/firefox/vapi-common.js b/platform/firefox/vapi-common.js index 6fc7f5b852244..6b0200d18e24e 100644 --- a/platform/firefox/vapi-common.js +++ b/platform/firefox/vapi-common.js @@ -27,7 +27,7 @@ /******************************************************************************/ -(function() { +(function(self) { /******************************************************************************/ @@ -186,6 +186,6 @@ vAPI.localStorage.init('extensions.' + location.host + '.'); /******************************************************************************/ -})(); +})(this); /******************************************************************************/ From 35f2a932f1f522eef9702d76d94ed88a6d2dad74 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 12:16:13 -0400 Subject: [PATCH 0162/4093] 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 9e4a54c1ebb51..9e98425d17d2e 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.103", + "version": "1.11.5.104", "default_locale": "en", "description": "__MSG_extShortDesc__", From 68ad90616bbbaaf2d9c840590a512cfe13b26999 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 12:40:02 -0400 Subject: [PATCH 0163/4093] fix #2499 --- platform/chromium/manifest.json | 2 +- platform/chromium/vapi-client.js | 4 ++-- platform/chromium/vapi-common.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 9e98425d17d2e..9fa9a35e8ce56 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.104", + "version": "1.11.5.105", "default_locale": "en", "description": "__MSG_extShortDesc__", diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index efaffeab41bb0..c0f6336f1b487 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -27,7 +27,7 @@ /******************************************************************************/ -(function() { +(function(self) { /******************************************************************************/ /******************************************************************************/ @@ -525,6 +525,6 @@ vAPI.executionCost.stop('vapi-client.js'); /******************************************************************************/ /******************************************************************************/ -})(); +})(this); /******************************************************************************/ diff --git a/platform/chromium/vapi-common.js b/platform/chromium/vapi-common.js index 70fa49292e175..a92fe3f4c00d7 100644 --- a/platform/chromium/vapi-common.js +++ b/platform/chromium/vapi-common.js @@ -26,7 +26,7 @@ /******************************************************************************/ /******************************************************************************/ -(function() { +(function(self) { // https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 if ( !self.vAPI || !self.vAPI.uBO ) { @@ -102,6 +102,6 @@ try { /******************************************************************************/ -})(); +})(this); /******************************************************************************/ From 9f4a879bca1e40ad24fb87107d7f37ed3b01aedf Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 15:45:24 -0400 Subject: [PATCH 0164/4093] fix https://github.com/gorhill/uBlock/issues/2502#issuecomment-290939436 --- platform/firefox/vapi-background.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index c9cc54b83c031..4885528a03feb 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -1013,9 +1013,9 @@ vAPI.tabs.open = function(details) { // Open in a standalone window if ( details.popup === true ) { Services.ww.openWindow( - self, + win, details.url, - null, + 'uBO-logger', 'location=1,menubar=1,personalbar=1,resizable=1,toolbar=1', null ); From 5699e85afa6fc26b6be6ec42e09e4ac6784c94c9 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 16:22:34 -0400 Subject: [PATCH 0165/4093] fix https://github.com/gorhill/uBlock/issues/2502#issuecomment-290943606 --- platform/firefox/bootstrap.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js index e53bf08dae485..0659f21c31591 100644 --- a/platform/firefox/bootstrap.js +++ b/platform/firefox/bootstrap.js @@ -95,12 +95,12 @@ function getWindowlessBrowserFrame(appShell) { let XPCOMUtils = Components.utils.import('resource://gre/modules/XPCOMUtils.jsm', null).XPCOMUtils; windowlessBrowserPL = { QueryInterface: XPCOMUtils.generateQI([ - Ci.nsIWebProgressListener, Ci.nsIWebProgressListener2, - Ci.nsISupportsWeakReference]), - onStateChange(wbp, request, stateFlags/*, status*/) { - if ( !request ) { - return; - } + Ci.nsIWebProgressListener, + Ci.nsIWebProgressListener2, + Ci.nsISupportsWeakReference + ]), + onStateChange: function(wbp, request, stateFlags/*, status*/) { + if ( !request ) { return; } if ( stateFlags & Ci.nsIWebProgressListener.STATE_STOP ) { webProgress.removeProgressListener(windowlessBrowserPL); windowlessBrowserPL = null; From 53a794d9b2a8c65406ee7a201cacbc91c297b2f8 Mon Sep 17 00:00:00 2001 From: Gijs Date: Sat, 1 Apr 2017 22:56:50 +0100 Subject: [PATCH 0166/4093] Fix #2502 by waiting for the hidden window even when not using it for the background page (#2503) --- platform/firefox/bootstrap.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js index 0659f21c31591..41107bbcc50cd 100644 --- a/platform/firefox/bootstrap.js +++ b/platform/firefox/bootstrap.js @@ -63,11 +63,7 @@ function startup(data/*, reason*/) { let appShell = Cc['@mozilla.org/appshell/appShellService;1'] .getService(Ci.nsIAppShellService); - if ( appShell.createWindowlessBrowser ) { - getWindowlessBrowserFrame(appShell); - } else { - getHiddenWindowBrowserFrame(appShell); - } + waitForHiddenWindow(appShell); } function createBgProcess(parentDocument) { @@ -113,7 +109,7 @@ function getWindowlessBrowserFrame(appShell) { } -function getHiddenWindowBrowserFrame(appShell) { +function waitForHiddenWindow(appShell) { let isReady = function() { var hiddenDoc; @@ -128,7 +124,20 @@ function getHiddenWindowBrowserFrame(appShell) { if ( !hiddenDoc || hiddenDoc.readyState !== 'complete' ) { return false; } - createBgProcess(hiddenDoc); + + // In theory, it should be possible to create a windowless browser + // immediately, without waiting for the hidden window to have loaded + // completely. However, in practice, on Windows this seems to lead + // to a broken Firefox appearance. To avoid this, we only create the + // windowless browser here. We'll use that rather than the hidden + // window for the actual background page (windowless browsers are + // also what the webextension implementation in Firefox uses for + // background pages). + if ( appShell.createWindowlessBrowser ) { + getWindowlessBrowserFrame(appShell); + } else { + createBgProcess(hiddenDoc); + } return true; }; From c0b0afadecb10169a3419489302a6b72dad2fad0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 1 Apr 2017 19:11:58 -0400 Subject: [PATCH 0167/4093] minor code review + new revision for release candidate --- platform/chromium/manifest.json | 2 +- platform/firefox/bootstrap.js | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 9fa9a35e8ce56..451e2764feb22 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.105", + "version": "1.11.5.106", "default_locale": "en", "description": "__MSG_extShortDesc__", diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js index 41107bbcc50cd..7e2dddc9b067b 100644 --- a/platform/firefox/bootstrap.js +++ b/platform/firefox/bootstrap.js @@ -60,10 +60,7 @@ function startup(data/*, reason*/) { return; } - let appShell = Cc['@mozilla.org/appshell/appShellService;1'] - .getService(Ci.nsIAppShellService); - - waitForHiddenWindow(appShell); + waitForHiddenWindow(); } function createBgProcess(parentDocument) { @@ -109,7 +106,10 @@ function getWindowlessBrowserFrame(appShell) { } -function waitForHiddenWindow(appShell) { +function waitForHiddenWindow() { + let appShell = Cc['@mozilla.org/appshell/appShellService;1'] + .getService(Ci.nsIAppShellService); + let isReady = function() { var hiddenDoc; From e420db1f1f9f1028cc0b40561b8798c3fa35e1a0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 2 Apr 2017 10:55:50 -0400 Subject: [PATCH 0168/4093] keep using `.xpi` for webext --- .travis.yml | 2 +- tools/make-webext.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a46f7f9c2113a..43c8d42cd17ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ env: matrix: - BROWSER=chromium EXT=zip - BROWSER=firefox EXT=xpi - - BROWSER=webext EXT=zip + - BROWSER=webext EXT=xpi script: ./tools/make-${BROWSER}.sh all deploy: provider: releases diff --git a/tools/make-webext.sh b/tools/make-webext.sh index 6166bcef25b1e..e9fda83149acf 100755 --- a/tools/make-webext.sh +++ b/tools/make-webext.sh @@ -39,7 +39,7 @@ python tools/make-webext-meta.py $DES/ if [ "$1" = all ]; then echo "*** uBlock0.webext: Creating package..." pushd $DES > /dev/null - zip ../$(basename $DES).zip -qr * + zip ../$(basename $DES).xpi -qr * popd > /dev/null fi From 98041b44a1da18f6d730265053eaaf7055d4c624 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 2 Apr 2017 11:35:30 -0400 Subject: [PATCH 0169/4093] minor css tuning --- src/css/dashboard.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/css/dashboard.css b/src/css/dashboard.css index f55ca5caa1043..142c3bd9a4b56 100644 --- a/src/css/dashboard.css +++ b/src/css/dashboard.css @@ -41,7 +41,7 @@ html, body { cursor: pointer; display: inline-block; font-size: 110%; - margin: 0 1px 0 0; + margin: 0 0.2em 0 0; padding: 4px; position: relative; text-decoration: none; @@ -81,6 +81,7 @@ iframe { display: inline-block; font-size: 100%; margin-bottom: 1px; + margin-right: 1px; top: 0; } .tabButton.selected { From f6563be542cd9917ce032ef835a39f43abf1fbca Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 2 Apr 2017 13:29:08 -0400 Subject: [PATCH 0170/4093] fix #2506 --- platform/firefox/install.rdf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/firefox/install.rdf b/platform/firefox/install.rdf index 21bf2861a34a3..dcca93b826f19 100644 --- a/platform/firefox/install.rdf +++ b/platform/firefox/install.rdf @@ -20,7 +20,7 @@ {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}} - 24.0 + 27.0 * @@ -29,7 +29,7 @@ {{aa3c5121-dab2-40e2-81ca-7ea25febc110}} - 24.0 + 27.0 * @@ -38,7 +38,7 @@ {{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}} - 2.21 + 2.24 * @@ -47,7 +47,7 @@ {{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}} - 25.0 + 26.0 27.* From 43e6a7599d3e8a86618ef65487ffac9fad646d6b Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 2 Apr 2017 16:23:13 -0400 Subject: [PATCH 0171/4093] fix #2506 --- platform/firefox/bootstrap.js | 16 +++++++++------- platform/firefox/install.rdf | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js index 7e2dddc9b067b..f6f85e814decd 100644 --- a/platform/firefox/bootstrap.js +++ b/platform/firefox/bootstrap.js @@ -1,7 +1,7 @@ /******************************************************************************* - µBlock - a browser extension to block requests. - Copyright (C) 2014 The µBlock authors + uBlock Origin - a browser extension to block requests. + Copyright (C) 2014-2017 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 @@ -26,7 +26,7 @@ /******************************************************************************/ -const {classes: Cc, interfaces: Ci} = Components; +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; // Accessing the context of the background page: // var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=ublock0]').contentWindow; @@ -85,7 +85,7 @@ function getWindowlessBrowserFrame(appShell) { windowlessBrowser = appShell.createWindowlessBrowser(true); windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor); let webProgress = windowlessBrowser.getInterface(Ci.nsIWebProgress); - let XPCOMUtils = Components.utils.import('resource://gre/modules/XPCOMUtils.jsm', null).XPCOMUtils; + let XPCOMUtils = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null).XPCOMUtils; windowlessBrowserPL = { QueryInterface: XPCOMUtils.generateQI([ Ci.nsIWebProgressListener, @@ -133,7 +133,8 @@ function waitForHiddenWindow() { // window for the actual background page (windowless browsers are // also what the webextension implementation in Firefox uses for // background pages). - if ( appShell.createWindowlessBrowser ) { + let { Services } = Cu.import('resource://gre/modules/Services.jsm', null); + if ( Services.vc.compare(Services.appinfo.platformVersion, '27') >= 0 ) { getWindowlessBrowserFrame(appShell); } else { createBgProcess(hiddenDoc); @@ -242,8 +243,9 @@ function uninstall(aData, aReason) { // To cleanup vAPI.localStorage in vapi-common.js // As I get more familiar with FF API, will find out whetehr there was // a better way to do this. - Components.utils.import('resource://gre/modules/Services.jsm', null) - .Services.prefs.getBranch('extensions.' + hostName + '.').deleteBranch(''); + Cu.import('resource://gre/modules/Services.jsm', null) + .Services.prefs.getBranch('extensions.' + hostName + '.') + .deleteBranch(''); } /******************************************************************************/ diff --git a/platform/firefox/install.rdf b/platform/firefox/install.rdf index dcca93b826f19..8ead1412962b9 100644 --- a/platform/firefox/install.rdf +++ b/platform/firefox/install.rdf @@ -20,7 +20,7 @@ {{ec8030f7-c20a-464f-9b0e-13a3a9e97384}} - 27.0 + 24.0 * From 209f79535b18cc5f5c89c6eb41ff8ecfbb259eb8 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 2 Apr 2017 22:48:11 -0400 Subject: [PATCH 0172/4093] new revision for release candidate --- platform/chromium/manifest.json | 2 +- platform/firefox/bootstrap.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index 451e2764feb22..4e1588b285342 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.106", + "version": "1.11.5.107", "default_locale": "en", "description": "__MSG_extShortDesc__", diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js index f6f85e814decd..c11454041d6ff 100644 --- a/platform/firefox/bootstrap.js +++ b/platform/firefox/bootstrap.js @@ -105,7 +105,6 @@ function getWindowlessBrowserFrame(appShell) { windowlessBrowser.document.location = "data:application/vnd.mozilla.xul+xml;charset=utf-8,"; } - function waitForHiddenWindow() { let appShell = Cc['@mozilla.org/appshell/appShellService;1'] .getService(Ci.nsIAppShellService); From 7aa44cf8f8cecf9259b19b010f9c64a403ab8334 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 3 Apr 2017 07:51:25 -0400 Subject: [PATCH 0173/4093] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 267f1d05faea5..3d2b36d9518e1 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,5 @@ +**Important:** If you are having issue with uBlock Origin ("uBO") on Nightly, please install the most recent developer version of uBO on AMO: . + Read first: ### Describe the issue From f804c7503e43b814cf855d7a4111f9db3d660058 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 3 Apr 2017 07:51:45 -0400 Subject: [PATCH 0174/4093] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 3d2b36d9518e1..787e5539973dd 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,4 @@ -**Important:** If you are having issue with uBlock Origin ("uBO") on Nightly, please install the most recent developer version of uBO on AMO: . +**Important:** If you are having issue with uBlock Origin ("uBO") on **Firefox Nightly**, please install the most recent developer version of uBO on AMO: . Read first: From 15feb55fc5bf4977b0efd2f39232ad3126ccf4ff Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 3 Apr 2017 09:44:53 -0400 Subject: [PATCH 0175/4093] translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 4 ++-- src/_locales/nl/messages.json | 2 +- src/_locales/pt_BR/messages.json | 4 ++-- src/_locales/sr/messages.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 4171b441bc8ec..fdaa9037aa5a6 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -516,7 +516,7 @@ "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyOrigin":{ - "message":"في أي مكان", + "message":"من أي مكان", "description":"Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant":{ @@ -592,7 +592,7 @@ "description":"English: a minute ago" }, "elapsedManyMinutesAgo":{ - "message":"منذ {{value}} دقيقة", + "message":"منذ {{value}} دقائق", "description":"English: {{value}} minutes ago" }, "elapsedOneHourAgo":{ diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 05e61e712b3df..afd272209c04a 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -284,7 +284,7 @@ "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ - "message":"

      Deze optie schakelt het inlezen en toepassen van Adblock Plus-compatibele ‘elementverbergende’ filters<\/a> in. Deze filters zijn puur cosmetisch; ze verbergen elementen in de webpagina die visueel storend kunnen zijn en niet door de op netwerkaanvragen gebaseerde filterengine kunnen worden geblokkeerd.<\/p>

      Het inschakelen van deze functie verhoogt het geheugengebruik van uBlock₀.<\/p>", + "message":"

      Deze optie schakelt het inlezen en toepassen van Adblock Plus-compatibele ‘elementverbergende’ filters<\/a> in. Deze filters zijn puur cosmetisch; ze verbergen elementen in de webpagina die visueel storend kunnen zijn en niet door de op netwerkaanvragen gebaseerde filter-engine kunnen worden geblokkeerd.<\/p>

      Het inschakelen van deze functie verhoogt het geheugengebruik van uBlock₀.<\/p>", "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 28d6050f26ed8..da9df3b6f005e 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -284,7 +284,7 @@ "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ - "message":"

      Esta opção permite a análise e aplicação de filtros de “ocultação de elementos” compatíveis com o Adblock Plus<\/a>. Estes filtros são essencialmente cosméticos, servindo para ocultar elementos em uma página da web que são consideradas um incômodo visual, e que não podem ser bloqueados pelo motor de filtragem baseado em pedidos de rede.<\/p>

      Ativando este recurso aumenta o consumo de memória do uBlock₀.<\/p>", + "message":"

      Esta opção permite a análise e aplicação de filtros para “ocultação de elementos” compatíveis com o Adblock Plus<\/a>. Estes filtros são essencialmente cosméticos, servindo para ocultar elementos em uma página da web que são consideradas um incômodo visual, e que não podem ser bloqueados pelo motor de filtragem baseado em pedidos de rede.<\/p>

      Ativando este recurso aumenta o consumo de memória do uBlock₀.<\/p>", "description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters":{ @@ -292,7 +292,7 @@ "description":"This will cause uBO to ignore all generic cosmetic filters." }, "3pIgnoreGenericCosmeticFiltersInfo":{ - "message":"

      Os filtros cosméticos genéricos são os filtros cosméticos que são destinados a serem aplicados em todos os sites.

      Embora manuseados eficientemente pelo uBlock₀, os filtros cosméticos genéricos podem ainda contribuir para um grande uso de memória e CPU em algumas páginas, especialmente as grandes e duradouras.

      Habilitando esta opção irá eliminar a sobrecarga da memória e cpu adicionadas nas páginas de web como resultado da aplicação dos filtros cosméticos genéricos e também irá reduzir o consumo da memória pelo uBlock₀ em si.

      É recomendado que ative esta opção em dispositivos menos poderosos.", + "message":"

      Os filtros cosméticos genéricos são os filtros cosméticos que são destinados a serem aplicados em todos os sites.

      Embora manuseados eficientemente pelo uBlock₀, os filtros cosméticos genéricos podem ainda contribuir para um grande uso de memória e CPU em algumas páginas, especialmente as grandes e duradouras.

      Ativando esta opção irá eliminar a sobrecarga da memória e cpu adicionadas nas páginas de web como resultado da aplicação dos filtros cosméticos genéricos e também irá reduzir o consumo da memória pelo uBlock₀ em si.

      É recomendado que ative esta opção em dispositivos menos poderosos.", "description":"Describes the purpose of the 'Ignore generic cosmetic filters' feature." }, "3pListsOfBlockedHostsHeader":{ diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index cc15861f7357e..5bee7e60c152a 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -188,7 +188,7 @@ "description":"English: Hide placeholders of blocked elements" }, "settingsIconBadgePrompt":{ - "message":"Покажи број блокираних захтева на икони", + "message":"Прикажи број блокираних захтева на иконици", "description":"English: Show the number of blocked requests on the icon" }, "settingsTooltipsPrompt":{ From bad345ea232c4e85a44816ebcd0f45de8f115477 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 3 Apr 2017 10:07:59 -0400 Subject: [PATCH 0176/4093] new languages available from https://crowdin.com/translate/ublock --- src/_locales/kn/messages.json | 710 ++++++++++++++++++++++++++++++++++ src/_locales/ml/messages.json | 710 ++++++++++++++++++++++++++++++++++ tools/import-crowdin.sh | 2 + 3 files changed, 1422 insertions(+) create mode 100644 src/_locales/kn/messages.json create mode 100644 src/_locales/ml/messages.json diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json new file mode 100644 index 0000000000000..a7da010fcb000 --- /dev/null +++ b/src/_locales/kn/messages.json @@ -0,0 +1,710 @@ +{ + "extName":{ + "message":"ಯುಬ್ಲಾಕ್", + "description":"extension name." + }, + "extShortDesc":{ + "message":"ಕೊನೆಗೂ, ಒಂದು ದಕ್ಷ ನಿರ್ಬಂಧಕ. ಮಿತವಾದ ಸಿಪಿಯೂ ಹಾಗು ಮೆಮೊರಿ ಬಳಕೆ.", + "description":"this will be in the chrome web store: must be 132 characters or less" + }, + "dashboardName":{ + "message":"uBlock₀ - ಡ್ಯಾಶ್ಬೋರ್ಡು", + "description":"English: uBlock₀ — Dashboard" + }, + "settingsPageName":{ + "message":"ಬದಲಾವಣೆ", + "description":"appears as tab name in dashboard" + }, + "3pPageName":{ + "message":"೩ನೇ ವ್ಯಕ್ತಿ ಶೋಧಕಗಳು", + "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₀ — ಜಾಲ ವಿನಂತಿಗಳ ದಾಖಲೆ", + "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":"ಕ್ಲಿಕ್: ಈ ಜಾಲತಾಣಕ್ಕೆ ನಿಷ್ಕ್ರಿಯ\/ಸಕ್ರಿಯಗೊಳಿಸಲು.\nಕಂಟ್ರೋಲ್ + ಕ್ಲಿಕ್: ಈ ಜಾಲತಾಣಕ್ಕೆ ಮಾತ್ರ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು.", + "description":"English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." + }, + "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":"ಡ್ಯಾಶ್ಬೋರ್ಡು ತೆರೆಯಲು ಕ್ಲಿಕಿಸಿ", + "description":"English: Click to open the dashboard" + }, + "popupTipPicker":{ + "message":"ಅಂಶ ಆಯ್ವಿಕೆ ಕ್ರಮಕ್ಕೆ ದಾಖಲಾಗಿ", + "description":"English: Enter element picker mode" + }, + "popupTipLog":{ + "message":"ಜಾಲ ವಿನಂತಿಗಳ ದಾಖಲೆಗೆ ಹೋಗಿ", + "description":"Tooltip used for the logger icon in the panel" + }, + "popupTipNoPopups":{ + "message":"ಈ ಜಾಲತಾಣದ ಎಲ್ಲಾ ಪಾಪ್ಅಪ್ ಗಳ ನಿರ್ಬಂಧವನ್ನು ಟಾಗಲ್ ಮಾಡಿ", + "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" + }, + "popupTipNoCosmeticFiltering":{ + "message":"ಈ ಜಾಲತಾಣಕ್ಕೆ ಕಾಸ್ಮೆಟಿಕ್ ಫಿಲ್ಟರಿಂಗನ್ನು ಟಾಗಲ್ ಮಾಡಿ", + "description":"Tooltip for the no-cosmetic-filtering per-site switch" + }, + "popupTipNoRemoteFonts":{ + "message":"ಈ ಜಾಲತಾಣಕ್ಕೆ ದೂರಸ್ಥ ಅಕ್ಷರಶೈಲಿಯ ನಿರ್ಬಂಧವನ್ನು ಟಾಗಲ್ ಮಾಡಿ", + "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":"೩ನೇ ವ್ಯಕ್ತಿ", + "description":"" + }, + "popup3pPassiveRulePrompt":{ + "message":"೩ನೇ ವ್ಯಕ್ತಿ ಸಿಎಸ್ಸೇಸು\/ಚಿತ್ರಗಳು", + "description":"" + }, + "popupInlineScriptRulePrompt":{ + "message":"ಒಳಸಾಲಿನ ಸ್ಕ್ರಿಪ್ಟುಗಳು", + "description":"" + }, + "popup1pScriptRulePrompt":{ + "message":"೧ನೇ ವ್ಯಕ್ತಿ ಸ್ಕ್ರಿಪ್ಟುಗಳು", + "description":"" + }, + "popup3pScriptRulePrompt":{ + "message":"೩ನೇ ವ್ಯಕ್ತಿ ಸ್ಕ್ರಿಪ್ಟುಗಳು", + "description":"" + }, + "popup3pFrameRulePrompt":{ + "message":"೩ನೇ ವ್ಯಕ್ತಿ ಫ್ರೇಮುಗಳು", + "description":"" + }, + "popupHitDomainCountPrompt":{ + "message":"ಸಂಪರ್ಕ ಹೊಂದಿರುವ ಡೊಮೈನ್ಗಳು", + "description":"appears in popup" + }, + "popupHitDomainCount":{ + "message":"{{total}} ರಲ್ಲಿ {{count}}", + "description":"appears in popup" + }, + "pickerCreate":{ + "message":"ರಚಿಸಿ", + "description":"English: Create" + }, + "pickerPick":{ + "message":"ಆಯ್ಕೆ", + "description":"English: Pick" + }, + "pickerQuit":{ + "message":"ತ್ಯಜಿಸಿ", + "description":"English: Quit" + }, + "pickerPreview":{ + "message":"Preview", + "description":"Element picker preview mode: will cause the elements matching the current filter to be removed from the page" + }, + "pickerNetFilters":{ + "message":"ನಿವ್ವಳ ಫಿಲ್ಟರುಗಳು", + "description":"English: header for a type of filter in the element picker dialog" + }, + "pickerCosmeticFilters":{ + "message":"ಕಾಸ್ಮೆಟಿಕ್ ಫಿಲ್ಟರುಗಳು", + "description":"English: Cosmetic filters" + }, + "pickerCosmeticFiltersHint":{ + "message":"ಕ್ಲಿಕ್, ಕಂಟ್ರೋಲ್-ಕ್ಲಿಕ್", + "description":"English: Click, Ctrl-click" + }, + "pickerContextMenuEntry":{ + "message":"ಭಾಗ ತಡೆಹಿಡಿಯಿರಿ", + "description":"English: Block element" + }, + "settingsCollapseBlockedPrompt":{ + "message":"ನಿರ್ಬಂಧಿಸಿದ ಭಾಗಗಳಿರುವ ಪ್ಲೇಸ್ಹೋಲ್ಡರ್ಸ್ ಮರೆಮಾಡಿ", + "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":"ಬಣ್ಣ ಅಂಧತೆ ಉಪಯುಕ್ತ", + "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":"" + }, + "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":"ಶೋಧಕ ಪಟ್ಟಿಗಳನ್ನು ಸ್ವಯಂ ನವೀಕರಿಸಿ.", + "description":"A checkbox in the _3rd-party filters_ pane" + }, + "3pUpdateNow":{ + "message":"ಈಗ ನವೀಕರಿಸಿ", + "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" + }, + "3pGroupSocial":{ + "message":"ಸಾಮಾಜಿಕ", + "description":"English: Social" + }, + "3pGroupMultipurpose":{ + "message":"ಬಹುಪಯೋಗಿ", + "description":"English: Multipurpose" + }, + "3pGroupRegions":{ + "message":"ಪ್ರದೇಶಗಳು, ಭಾಷೆಗಳು", + "description":"English: Regions, languages" + }, + "3pGroupCustom":{ + "message":"ಇಚ್ಛೆಯ", + "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." + }, + "3pExternalListsApply":{ + "message":"Parse", + "description":"English: Parse" + }, + "3pExternalListPurge":{ + "message":"purge cache", + "description":"English: purge cache" + }, + "3pExternalListNew":{ + "message":"new version available", + "description":"English: new version available" + }, + "3pExternalListObsolete":{ + "message":"ಹಳೆಯದು", + "description":"a filter list is 'out of date' (possibly needs to be updated)" + }, + "3pLastUpdate":{ + "message":"Last update: {{ago}}", + "description":"English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'" + }, + "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":"ಆಮದಿಸಿ ಸೇರ್ಪಡಿಸು", + "description":"English: Import and append" + }, + "1pExport":{ + "message":"ರಫ್ತು", + "description":"English: Export" + }, + "1pExportFilename":{ + "message":"my-ublock-static-filters_{{datetime}}.txt", + "description":"English: my-ublock-static-filters_{{datetime}}.txt" + }, + "1pApplyChanges":{ + "message":"ಬದಲಾವಣೆಗಳನ್ನು ಅನ್ವಯಿಸಿ", + "description":"English: Apply changes" + }, + "rulesPermanentHeader":{ + "message":"ಶಾಶ್ವತ ನಿಯಮಗಳು", + "description":"header" + }, + "rulesTemporaryHeader":{ + "message":"ತಾತ್ಕಾಲಿಕ ನಿಯಮಗಳು", + "description":"header" + }, + "rulesRevert":{ + "message":"ಹಿಂತಿರುಗಿಸಿ", + "description":"This will remove all temporary rules" + }, + "rulesCommit":{ + "message":"ಕಮಿಟ್", + "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":"Import from file...", + "description":"" + }, + "rulesExport":{ + "message":"Export to file", + "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":"Export", + "description":"English: Export" + }, + "whitelistExportFilename":{ + "message":"my-ublock-whitelist_{{datetime}}.txt", + "description":"English: my-ublock-whitelist_{{datetime}}.txt" + }, + "whitelistApply":{ + "message":"Apply changes", + "description":"English: Apply changes" + }, + "logRequestsHeaderType":{ + "message":"Type", + "description":"English: Type" + }, + "logRequestsHeaderDomain":{ + "message":"Domain", + "description":"English: Domain" + }, + "logRequestsHeaderURL":{ + "message":"URL", + "description":"English: URL" + }, + "logRequestsHeaderFilter":{ + "message":"Filter", + "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" + }, + "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":"Type:", + "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":"ವಿಕಿ", + "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":"ನೀಡುಗರು", + "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":"Unable to connect to {{url}}", + "description":"English: Network error: unable to connect to {{url}}" + }, + "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":"ಒಂದು ನಿಮಿಷದ ಹಿಂದೆ", + "description":"English: a minute ago" + }, + "elapsedManyMinutesAgo":{ + "message":"{{value}} ನಿಮಿಷದ ಹಿಂದೆ", + "description":"English: {{value}} minutes ago" + }, + "elapsedOneHourAgo":{ + "message":"ಒಂದು ತಾಸಿನ ಹಿಂದೆ", + "description":"English: an hour ago" + }, + "elapsedManyHoursAgo":{ + "message":"{{value}} ತಾಸಿನ ಹಿಂದೆ", + "description":"English: {{value}} hours ago" + }, + "elapsedOneDayAgo":{ + "message":"ಒಂದು ದಿನದ ಹಿಂದೆ", + "description":"English: a day ago" + }, + "elapsedManyDaysAgo":{ + "message":"{{value}} ದಿನದ ಹಿಂದೆ", + "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":"ಹಿಂದೆ ಹೋಗಿ", + "description":"English: Go back" + }, + "docblockedClose":{ + "message":"ಈ ವಿಂಡೋವನ್ನು ಮುಚ್ಚಿ", + "description":"English: Close this window" + }, + "docblockedProceed":{ + "message":"Disable strict blocking for {{hostname}}", + "description":"English: Disable strict blocking for {{hostname}} ..." + }, + "docblockedDisableTemporary":{ + "message":"ತಾತ್ಕಾಲಿಕವಾಗಿ", + "description":"English: Temporarily" + }, + "docblockedDisablePermanent":{ + "message":"ಶಾಶ್ವತವಾಗಿ", + "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":"ಈ ಸಾಧನದ ಹೆಸರು:", + "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":"ಸಲ್ಲಿಸಿ", + "description":"for generic 'Submit' buttons" + }, + "genericApplyChanges":{ + "message":"Apply changes", + "description":"for generic 'Apply changes' buttons" + }, + "genericRevert":{ + "message":"ಹಿಂತಿರುಗಿಸಿ", + "description":"for generic 'Revert' buttons" + }, + "genericBytes":{ + "message":"ಬೈಟ್ಗಳು", + "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/src/_locales/ml/messages.json b/src/_locales/ml/messages.json new file mode 100644 index 0000000000000..d8d20fd38c8ab --- /dev/null +++ b/src/_locales/ml/messages.json @@ -0,0 +1,710 @@ +{ + "extName":{ + "message":"uBlock₀", + "description":"extension name." + }, + "extShortDesc":{ + "message":"അവസാനം, ഒരു കാര്യക്ഷമമായ ബ്ലോക്കര്‍. ലഘുവായ CPU, memory ഉപയോഗം.", + "description":"this will be in the chrome web store: must be 132 characters or less" + }, + "dashboardName":{ + "message":"യുബ്ലോക്ക്ഒ - ഡാഷ്ബോര്‍ഡ്", + "description":"English: uBlock₀ — Dashboard" + }, + "settingsPageName":{ + "message":"സെറ്റിംഗ്സ്", + "description":"appears as tab name in dashboard" + }, + "3pPageName":{ + "message":"തേര്‍ഡ് പാര്‍ട്ടി ഫില്‍ട്ടറുകള്‍", + "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":"യുബ്ലോക്ക്ഒ - നെറ്റ്‌വര്‍ക്ക് അപേക്ഷാ ലോഗ്", + "description":"Title for the logger window" + }, + "aboutPageName":{ + "message":"ഇതിനെ കുറിച്ച്", + "description":"appears as tab name in dashboard" + }, + "advancedSettingsPageName":{ + "message":"Advanced settings", + "description":"Title for the advanced settings page" + }, + "popupPowerSwitchInfo":{ + "message":"ക്ലിക്ക്: ഈ സൈറ്റില്‍ യുബ്ലോക്ക്ഒ ഡിസേബിള്‍\/എനെബിള്‍ ചെയ്യാന്‍.\nCtrl + ക്ലിക്ക്: ഈ പേജില്‍ യുബ്ലോക്ക്ഒ ഡിസേബിള്‍\/എനെബിള്‍ ചെയ്യാന്‍.", + "description":"English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." + }, + "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":"ഡാഷ്ബോര്‍ഡ് തുറക്കാന്‍ ക്ലിക്ക് ചെയ്യുക", + "description":"English: Click to open the dashboard" + }, + "popupTipPicker":{ + "message":"എലമെന്‍ഡ് പിക്കര്‍ മോഡില്‍ കടക്കുക", + "description":"English: Enter element picker mode" + }, + "popupTipLog":{ + "message":"അപേക്ഷാ ലോഗിലേക്ക് പോകുക", + "description":"Tooltip used for the logger icon in the panel" + }, + "popupTipNoPopups":{ + "message":"ഈ സൈറ്റിലെ എല്ലാ പോപ്‌അപ്പുകളും ബ്ലോക്ക്‌ ചെയ്യുന്നത് ടോഗ്ഗിള്‍ ചെയ്യുക", + "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" + }, + "popupTipNoCosmeticFiltering":{ + "message":"ഈ സൈറ്റില്‍ സൗന്ദര്യ ഫില്‍ട്ടറുകള്‍ ടോഗ്ഗിള്‍ ചെയ്യുക", + "description":"Tooltip for the no-cosmetic-filtering per-site switch" + }, + "popupTipNoRemoteFonts":{ + "message":"ഈ സൈറ്റില്‍ റിമോട്ട് ഫോണ്ടുകള്‍ ബ്ലോക്ക് ചെയ്യുന്നത് ടോഗ്ഗിള്‍ ചെയ്യുക", + "description":"Tooltip for the no-remote-fonts per-site switch" + }, + "popupTipGlobalRules":{ + "message":"ആഗോള നിയമങ്ങൾ: ഈ കോളത്തിലെ നിയമങ്ങൾ എല്ലാ സൈറ്റുകളിലും പ്രയോഗിക്കുന്ന നിയമങ്ങള് ആണ്.", + "description":"Tooltip when hovering the top-most cell of the global-rules column." + }, + "popupTipLocalRules":{ + "message":"പ്രാദേശിക നിയമങ്ങൾ: ഈ കോളത്തിലെ നിയമങ്ങൾ നിലവിലെ സൈറ്റിലെ മാത്രം പ്രയോഗിക്കുന്ന.\nപ്രാദേശിക നിയമങ്ങൾ ആഗോള നിയമങ്ങൾ അതിലംഘിച്ച് പ്രവർത്തിക്കുന്നതായിരിക്കും.", + "description":"Tooltip when hovering the top-most cell of the local-rules column." + }, + "popupTipSaveRules":{ + "message":"നിങ്ങളുടെ മാറ്റങ്ങൾ സ്ഥിരപെടുത്താന്‍ ക്ലിക്കുചെയ്യുക.", + "description":"Tooltip when hovering over the padlock in the dynamic filtering pane." + }, + "popupTipRevertRules":{ + "message":"നിങ്ങളുടെ മാറ്റങ്ങൾ പഴയപടി ആകാന്‍ ക്ലിക്കുചെയ്യുക.", + "description":"Tooltip when hovering over the eraser in the dynamic filtering pane." + }, + "popupAnyRulePrompt":{ + "message":"എല്ലാം", + "description":"" + }, + "popupImageRulePrompt":{ + "message":"ചിത്രങ്ങള്‍", + "description":"" + }, + "popup3pAnyRulePrompt":{ + "message":"തേര്‍ഡ് പാര്‍ട്ടി", + "description":"" + }, + "popup3pPassiveRulePrompt":{ + "message":"മൂന്നാം പാര്‍ട്ടി സിഎസ്എസ്\/ ചിത്രങ്ങള്‍", + "description":"" + }, + "popupInlineScriptRulePrompt":{ + "message":"ഇന്‍ലൈന്‍ സ്ക്രിപ്റ്റുകള്‍", + "description":"" + }, + "popup1pScriptRulePrompt":{ + "message":"ഫസ്റ്റ് പാര്‍ട്ടി സ്ക്രിപ്റ്റുകള്‍", + "description":"" + }, + "popup3pScriptRulePrompt":{ + "message":"തേര്‍ഡ് പാര്‍ട്ടി സ്ക്രിപ്റ്റുകള്‍", + "description":"" + }, + "popup3pFrameRulePrompt":{ + "message":"തേര്‍ഡ് പാര്‍ട്ടി ഫ്രെയിമുകള്‍", + "description":"" + }, + "popupHitDomainCountPrompt":{ + "message":"കണക്റ്റ് ചെയ്യപ്പെട്ട ഡൊമൈനുകള്‍", + "description":"appears in popup" + }, + "popupHitDomainCount":{ + "message":"{{total}} ല്‍ നിന്നും {{count}}", + "description":"appears in popup" + }, + "pickerCreate":{ + "message":"ക്രിയേറ്റ് ചെയ്യുക", + "description":"English: Create" + }, + "pickerPick":{ + "message":"പിക്ക് ചെയ്യുക", + "description":"English: Pick" + }, + "pickerQuit":{ + "message":"പുറത്ത് കടക്കുക", + "description":"English: Quit" + }, + "pickerPreview":{ + "message":"Preview", + "description":"Element picker preview mode: will cause the elements matching the current filter to be removed from the page" + }, + "pickerNetFilters":{ + "message":"നെറ്റ് ഫില്‍ട്ടറുകള്‍", + "description":"English: header for a type of filter in the element picker dialog" + }, + "pickerCosmeticFilters":{ + "message":"സൗന്ദര്യ ഫില്‍ട്ടറുകള്‍", + "description":"English: Cosmetic filters" + }, + "pickerCosmeticFiltersHint":{ + "message":"ക്ലിക്ക്, Ctrl - ക്ലിക്ക്", + "description":"English: Click, Ctrl-click" + }, + "pickerContextMenuEntry":{ + "message":"എലമെന്‍ഡ് ബ്ലോക്ക് ചെയ്യുക", + "description":"English: Block element" + }, + "settingsCollapseBlockedPrompt":{ + "message":"ബ്ലോക്ക് ചെയ്യപ്പെട്ട എലമെന്‍ഡുകള്‍ക്ക് പകരമായുള്ള പ്ലയ്സ്ഹോള്‍ഡറുകള്‍ മറയ്ക്കുക", + "description":"English: Hide placeholders of blocked elements" + }, + "settingsIconBadgePrompt":{ + "message":"ഐക്കണില്‍ ബ്ലോക്ക് ചെയ്യപ്പെട്ട അപേക്ഷകളുടെ എണ്ണം കാണിക്കുക", + "description":"English: Show the number of blocked requests on the icon" + }, + "settingsTooltipsPrompt":{ + "message":"ടൂള്ടിപ്പ് പ്രവർത്തനരഹിതമാക്കുക", + "description":"A checkbox in the Settings pane" + }, + "settingsContextMenuPrompt":{ + "message":"ആവശ്യമായ ഇടങ്ങളില്‍ കോണ്‍ടെക്സ്റ്റ്‌ മെനു ഉപയോഗിക്കുക", + "description":"English: Make use of context menu where appropriate" + }, + "settingsColorBlindPrompt":{ + "message":"വർണ്ണാന്ധതാ സൗഹാര്‍ദ്ദപരമായത്", + "description":"English: Color-blind friendly" + }, + "settingsCloudStorageEnabledPrompt":{ + "message":"ക്ലൌഡ് സ്റ്റോറെജ് സപ്പോര്‍ട്ട് എനേബിള്‍ ചെയ്യുക", + "description":"" + }, + "settingsAdvancedUserPrompt":{ + "message":"ഞാന്‍ ഒരു അഡ്വാന്‍സ്ഡ് യൂസര്‍ ആണ് ( വായിക്കേണ്ടത്<\/a>)", + "description":"" + }, + "settingsAdvancedUserSettings":{ + "message":"advanced settings", + "description":"For the tooltip of a link which gives access to advanced settings" + }, + "settingsPrefetchingDisabledPrompt":{ + "message":"പ്രീ-ഫെച്ചിംഗ് ഡിസേബിള്‍ ചെയ്യുക (ബ്ലോക്ക് ചെയ്ത നെറ്റ്‌വര്‍ക്ക് അപേക്ഷകള്‍ക്ക് കണക്ഷന്‍ ലഭിക്കുന്നത് തടയുന്നതിന്)", + "description":"English: " + }, + "settingsHyperlinkAuditingDisabledPrompt":{ + "message":"ഹൈപര്‍ ലിങ്ക് ഓഡിറ്റിങ്ങ്\/ബീക്കന്‍ ഡിസേബിള്‍ ചെയ്യുക", + "description":"English: " + }, + "settingsWebRTCIPAddressHiddenPrompt":{ + "message":"വെബ്‌ആര്‍ടിസി ലോക്കല്‍ ഐ പി അഡ്രസുകള്‍ ലീക്ക് ചെയ്യുന്നത് തടയുക", + "description":"English: " + }, + "settingPerSiteSwitchGroup":{ + "message":"സ്ഥിര രീതി", + "description":"" + }, + "settingPerSiteSwitchGroupSynopsis":{ + "message":"These default behaviors can be overridden on a per-site basis", + "description":"" + }, + "settingsNoCosmeticFilteringPrompt":{ + "message":"കോസ്മെറ്റിക് ഫിൽട്ടറിംഗ് പ്രവർത്തനരഹിതമാക്കുക", + "description":"" + }, + "settingsNoLargeMediaPrompt":{ + "message":"Block media elements larger than {{input:number}} kB", + "description":"" + }, + "settingsNoRemoteFontsPrompt":{ + "message":"വിദൂര ഫോണ്ടുകൾ തടയുക", + "description":"" + }, + "settingsStorageUsed":{ + "message":"സ്റ്റോറേജ് ഉപയോഗം: {{value}} ബൈറ്റുകള്‍", + "description":"English: Storage used: {{}} bytes" + }, + "settingsLastRestorePrompt":{ + "message":"അവസാന റീസ്റ്റോര്‍:", + "description":"English: Last restore:" + }, + "settingsLastBackupPrompt":{ + "message":"അവസാന ബാക്ക്അപ്:", + "description":"English: Last backup:" + }, + "3pListsOfBlockedHostsPrompt":{ + "message":"{{netFilterCount}} നെറ്റ്‌വര്‍ക്ക് ഫില്‍ട്ടറുകള്‍ + {{cosmeticFilterCount}} സൗന്ദര്യ ഫില്‍ട്ടറുകള്‍ ഇവിടെ നിന്നും:", + "description":"Appears at the top of the _3rd-party filters_ pane" + }, + "3pListsOfBlockedHostsPerListStats":{ + "message":"{{total}} ല്‍ നിന്നും {{used}} ഉപയോഗിക്കുന്നു", + "description":"Appears aside each filter list in the _3rd-party filters_ pane" + }, + "3pAutoUpdatePrompt1":{ + "message":"ഫില്‍ട്ടര്‍ ലിസ്റ്റുകള്‍ ഓട്ടോ-അപ്ഡേറ്റ് ചെയ്യുക.", + "description":"A checkbox in the _3rd-party filters_ pane" + }, + "3pUpdateNow":{ + "message":"ഇപ്പോള്‍ അപ്ഡേറ്റ് ചെയ്യുക", + "description":"A button in the in the _3rd-party filters_ pane" + }, + "3pPurgeAll":{ + "message":"ക്യാഷ് ശുദ്ധീകരിക്കുക", + "description":"A button in the in the _3rd-party filters_ pane" + }, + "3pParseAllABPHideFiltersPrompt1":{ + "message":"ശുദ്ധീകരിച്ച് സൗന്ദര്യ ഫില്‍ട്ടറുകള്‍ എന്‍ഫോര്‍സ് ചെയ്യുക.", + "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":"മാല്‍വെയര്‍ ഡൊമൈനുകള്‍", + "description":"English: Malware domains" + }, + "3pGroupSocial":{ + "message":"സാമൂഹിക", + "description":"English: Social" + }, + "3pGroupMultipurpose":{ + "message":"മള്‍ട്ടിപര്‍പ്പസ്", + "description":"English: Multipurpose" + }, + "3pGroupRegions":{ + "message":"പ്രാദേശികം, ഭാഷകള്‍", + "description":"English: Regions, languages" + }, + "3pGroupCustom":{ + "message":"കസ്റ്റം", + "description":"English: Custom" + }, + "3pExternalListsHint":{ + "message":"ഒരു വരിയില്‍ ഒരു യുആര്‍എല്‍ എന്ന രീതിയില്‍. ‘!’ എന്നിവയില്‍ തുടങ്ങുന്ന വരികള്‍ ഇഗ്നോര്‍ ചെയ്യപ്പെടും. ഇന്‍വാലിഡ്‌ ആയ യുആര്‍എല്ലുകള്‍ നിശബ്ദമായി ഇഗ്നോര്‍ ചെയ്യപ്പെടും.", + "description":"English: One URL per line. Lines prefixed with ‘!’ will be ignored. Invalid URLs will be silently ignored." + }, + "3pExternalListsApply":{ + "message":"പാര്‍സ്", + "description":"English: Parse" + }, + "3pExternalListPurge":{ + "message":"ക്യാഷ് ശുദ്ധീകരിക്കുക", + "description":"English: purge cache" + }, + "3pExternalListNew":{ + "message":"പുതിയ അപ്ഡേറ്റ് ലഭ്യം", + "description":"English: new version available" + }, + "3pExternalListObsolete":{ + "message":"കാലഹരണപ്പെട്ടത്", + "description":"a filter list is 'out of date' (possibly needs to be updated)" + }, + "3pLastUpdate":{ + "message":"അവസാന അപ്ഡേറ്റ്:{{ago}}", + "description":"English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'" + }, + "1pFormatHint":{ + "message":"ഒരു വരിയില്‍ ഒരു ഫില്‍റ്റര്‍ എന്ന രീതിയില്‍. ഒരു ഫില്‍റ്റര്‍ എന്നത്, ഹോസ്റ്റ് നെയിം, അല്ലെങ്കില്‍ ആഡ് ബ്ലോക്ക് പ്ലസ്‌-നോട്‌ കംപാറ്റബിള്‍ ആയ ഫില്‍റ്റര്‍ എന്നിവ ആകാം. ‘!’ എന്നിവയില്‍ ആരംഭിക്കുന്ന വരികള്‍ ഇഗ്നോര്‍ ചെയ്യപ്പെടും.", + "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":"ഇമ്പോര്‍ട്ടും കൂട്ടിചേര്‍ക്കലും ചെയ്യുക", + "description":"English: Import and append" + }, + "1pExport":{ + "message":"എക്സ്പോര്‍ട്ട്‌", + "description":"English: Export" + }, + "1pExportFilename":{ + "message":"എന്‍റെ-യുബ്ലോക്ക്-സ്റ്റാറ്റിക്ക്-ഫില്‍ട്ടറുകള്‍_{{datetime}}.txt", + "description":"English: my-ublock-static-filters_{{datetime}}.txt" + }, + "1pApplyChanges":{ + "message":"മാറ്റങ്ങള്‍ അപ്ലേ ചെയ്യുക", + "description":"English: Apply changes" + }, + "rulesPermanentHeader":{ + "message":"സ്ഥിര നിയമങ്ങള്‍", + "description":"header" + }, + "rulesTemporaryHeader":{ + "message":"താല്‍ക്കാലിക നിയമങ്ങള്‍", + "description":"header" + }, + "rulesRevert":{ + "message":"റിവേര്‍ട്ട്", + "description":"This will remove all temporary rules" + }, + "rulesCommit":{ + "message":"കമ്മിറ്റ്", + "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":"എന്‍റെ-യുബ്ലോക്ക്-ഡൈനാമിക്-നിയമങ്ങള്‍_{{datetime}}.txt", + "description":"default file name to use" + }, + "rulesHint":{ + "message":"താങ്കളുടെ ഡൈനാമിക് ഫില്‍റ്റര്‍ നിയമങ്ങളുടെ ലിസ്റ്റ്.", + "description":"English: List of your dynamic filtering rules." + }, + "rulesFormatHint":{ + "message":"നിയമത്തിന്‍റെ സിന്‍റ്റാക്സ്‌: സോര്‍സ് ഡെസ്റ്റിനേഷന്‍ ടൈപ്പ് ആക്ഷന്‍<\/code> ( മുഴുനീള പ്രമാണം<\/a>).", + "description":"English: dynamic rule syntax and full documentation." + }, + "whitelistPrompt":{ + "message":"യുബ്ലോക്ക്ഒ ഡിസേബിള്‍ ചെയ്യപ്പെടേണ്ട ഹോസ്റ്റ് നെയിമുകള്‍. ഒരു വരിയില്‍ ഒരു എന്‍ട്രി എന്ന രീതിയില്‍ ചേര്‍ക്കുക. ഇന്‍വാലിഡ്‌ ഹോസ്റ്റ് നെയിമുകള്‍ നിശബ്ദമായി ഇഗ്നോര്‍ ചെയ്യപെടും.", + "description":"English: An overview of the content of the dashboard's Whitelist pane." + }, + "whitelistImport":{ + "message":"ഇമ്പോര്‍ട്ടും കൂട്ടിചേര്‍ക്കലും ചെയ്യുക", + "description":"English: Import and append" + }, + "whitelistExport":{ + "message":"എക്സ്പോര്‍ട്ട്‌", + "description":"English: Export" + }, + "whitelistExportFilename":{ + "message":"എന്‍റെ-യുബ്ലോക്ക്-വൈറ്റ്ലിസ്റ്റ്_{{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":"യുആര്‍എല്‍", + "description":"English: URL" + }, + "logRequestsHeaderFilter":{ + "message":"ഫില്‍ട്ടര്‍", + "description":"English: Filter" + }, + "logAll":{ + "message":"എല്ലാം", + "description":"Appears in the logger's tab selector" + }, + "logBehindTheScene":{ + "message":"സീനിനു പിന്നില്‍", + "description":"Pretty name for behind-the-scene network requests" + }, + "logFilterPrompt":{ + "message":"ലോഗ് എന്‍ട്രി ഫില്‍ട്ടര്‍ ചെയ്യുക", + "description":"English: filter log entries" + }, + "logMaxEntriesTip":{ + "message":"ലോഗ് എന്‍ട്രികളുടെ മാക്സിമം എണ്ണം", + "description":"Tooltip informaing that the input field is to set the maximum number of entries in the log" + }, + "loggerURLFilteringContextLabel":{ + "message":"കോണ്‍ടെക്സ്റ്റ്:", + "description":"Label for the context selector" + }, + "loggerURLFilteringTypeLabel":{ + "message":"ടൈപ്പ്:", + "description":"Label for the type selector" + }, + "loggerURLFilteringHeader":{ + "message":"ഡൈനാമിക് യുആര്‍എല്‍ ഫില്‍ട്ടറിങ്ങ്", + "description":"Small header to identify the dynamic URL filtering section" + }, + "loggerStaticFilteringHeader":{ + "message":"സ്റ്റാറ്റിക് ഫില്‍ട്ടറിങ്ങ്", + "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":"ബ്ലോക്ക്‌ ചെയ്യുക", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartAllow":{ + "message":"അനുവദിക്കുക", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartType":{ + "message":"ടൈപ്പ് \"{{type}}\"", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartAnyType":{ + "message":"ഏതു ടൈപ്പും", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartOrigin":{ + "message":"\"{{origin}}\"ല്‍ നിന്നും", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartAnyOrigin":{ + "message":"എവിടെ നിന്നും", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartNotImportant":{ + "message":"അങ്ങിനെ അല്ലെങ്കില്‍", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringSentencePartImportant":{ + "message":"എന്നിരുന്നാലും", + "description":"Used in the static filtering wizard" + }, + "loggerStaticFilteringFinderSentence1":{ + "message":"സ്റ്റാറ്റിക് ഫില്‍ട്ടര്‍ {{filter}} ഇതില്‍ കണ്ടെത്തി:", + "description":"Below this sentence, the filter lists in which the filter was found" + }, + "aboutChangelog":{ + "message":"മാറ്റങ്ങളുടെ ലോഗ്", + "description":"English: Change log" + }, + "aboutWiki":{ + "message":"വിക്കി", + "description":"English: project' wiki on Github" + }, + "aboutSupport":{ + "message":"Support", + "description":"A link for where to get support" + }, + "aboutCode":{ + "message":"സോര്‍സ് കോഡ് (ജിപിഎല്‍വി3)", + "description":"English: Source code (GPLv3)" + }, + "aboutContributors":{ + "message":"കോണ്‍ട്രിബ്യൂട്ടര്‍മാര്‍", + "description":"English: Contributors" + }, + "aboutBackupDataButton":{ + "message":"ഫയലിലേക്ക് ബാക്അപ്", + "description":"Text for button to create a backup of all settings" + }, + "aboutBackupFilename":{ + "message":"എന്‍റെ-യുബ്ലോക്ക്-ബാക്ക്അപ്_{{datetime}}.txt", + "description":"English: my-ublock-backup_{{datetime}}.txt" + }, + "aboutRestoreDataButton":{ + "message":"ഫയലില്‍ നിന്നും റീസ്റ്റോര്‍ ചെയ്യുക...", + "description":"English: Restore from file..." + }, + "aboutResetDataButton":{ + "message":"ഡീഫാള്‍ട്ട് സെറ്റിംഗ്സിലേക്ക് റീസ്റ്റോര്‍ ചെയ്യുക...", + "description":"English: Reset to default settings..." + }, + "aboutRestoreDataConfirm":{ + "message":"{{time}}ല്‍ ബാക്ക്അപ് ചെയ്യപ്പെട്ട ഡേറ്റ ഉപയോഗിച്ച് താങ്കളുടെ എല്ലാ സെറ്റിംഗ്സും ഓവര്‍ റൈറ്റ് ചെയ്യപ്പെടും. അതിനു ശേഷം യുബ്ലോക്ക്‌ഒ റീസ്റ്റാര്‍ട്ട്‌ ചെയ്യപ്പെടും.\n\nനിലവിലുള്ള എല്ലാ സെറ്റിങ്ങുകളും ഓവര്‍റൈറ്റ് ചെയ്യട്ടെയോ?", + "description":"Message asking user to confirm restore" + }, + "aboutRestoreDataError":{ + "message":"ഡേറ്റ വായിക്കാന്‍ ഒക്കുന്നില്ല അല്ലെങ്കില്‍ അത് ഇന്‍വാലിഡ്‌ ആണ്", + "description":"Message to display when an error occurred during restore" + }, + "aboutResetDataConfirm":{ + "message":"എല്ലാ സെറ്റിങ്ങുകളും റിമൂവ് ചെയ്യപ്പെടും. അതിനു ശേഷം യുബ്ലോക്ക്ഒ റീസ്റ്റാര്‍ട്ട്‌ ചെയ്യപ്പെടും.\n\nയുബ്ലോക്ക്ഒ-യെ ഫാക്ടറി സെറ്റിങ്ങുകളിലേക്ക് റീസെറ്റ് ചെയ്യട്ടെയോ?", + "description":"Message asking user to confirm reset" + }, + "errorCantConnectTo":{ + "message":"{{url}} എന്നതിലേക്ക് കണക്റ്റ് ചെയ്യാനാകുന്നില്ല", + "description":"English: Network error: unable to connect to {{url}}" + }, + "subscriberConfirm":{ + "message":"യുബ്ലോക്ക്ഒ: താഴെ പറയുന്ന യുആര്‍എല്‍ താങ്കളുടെ കസ്റ്റം ഫില്‍റ്ററില്‍ ചേര്‍ക്കട്ടേയോ?\n\nടൈറ്റില്‍: \"{{title}}\"\nയുആര്‍എല്‍: {{url}}", + "description":"English: The message seen by the user to confirm subscription to a ABP filter list" + }, + "elapsedOneMinuteAgo":{ + "message":"ഒരു മിനിറ്റ് മുന്‍പ്", + "description":"English: a minute ago" + }, + "elapsedManyMinutesAgo":{ + "message":"{{value}} മിനിറ്റുകള്‍ മുന്‍പ്", + "description":"English: {{value}} minutes ago" + }, + "elapsedOneHourAgo":{ + "message":"ഒരു മണിക്കൂര്‍ മുന്‍പ്", + "description":"English: an hour ago" + }, + "elapsedManyHoursAgo":{ + "message":"{{value}} മണിക്കൂറുകള്‍ മുന്‍പ്", + "description":"English: {{value}} hours ago" + }, + "elapsedOneDayAgo":{ + "message":"ഒരു ദിവസം മുന്‍പ്", + "description":"English: a day ago" + }, + "elapsedManyDaysAgo":{ + "message":"{{value}} ദിവസങ്ങള്‍ക്ക്മുന്‍പ്", + "description":"English: {{value}} days ago" + }, + "showDashboardButton":{ + "message":"ഡാഷ്ബോര്‍ഡ് കാണിക്കുക", + "description":"Firefox\/Fennec-specific: Show Dashboard" + }, + "showNetworkLogButton":{ + "message":"ലോഗ്ഗര്‍ കാണിക്കുക", + "description":"Firefox\/Fennec-specific: Show Logger" + }, + "fennecMenuItemBlockingOff":{ + "message":"ഓഫ്", + "description":"Firefox-specific: appears as 'uBlock₀ (off)'" + }, + "docblockedPrompt1":{ + "message":"താഴെ പറയുന്ന പേജ് ലോഡ് ചെയ്യുന്നത് യുബ്ലോക്ക് ഒറിജിന്‍ തടഞ്ഞിരിക്കുന്നു:", + "description":"English: uBlock₀ has prevented the following page from loading:" + }, + "docblockedPrompt2":{ + "message":"ഈ ഫില്‍റ്റര്‍ കാരണം", + "description":"English: Because of the following filter" + }, + "docblockedNoParamsPrompt":{ + "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":"ഇതില്‍ കണ്ടെത്തി:", + "description":"English: List of filter list names follows" + }, + "docblockedBack":{ + "message":"പിന്നിലേക്ക്‌ പോകുക", + "description":"English: Go back" + }, + "docblockedClose":{ + "message":"ഈ വിന്‍ഡോ ക്ലോസ് ചെയ്യുക", + "description":"English: Close this window" + }, + "docblockedProceed":{ + "message":"{{hostname}}ലേക്ക് കര്‍ശന ബ്ലോക്കിംഗ് ഡിസേബിള്‍ ചെയ്യുക", + "description":"English: Disable strict blocking for {{hostname}} ..." + }, + "docblockedDisableTemporary":{ + "message":"താല്‍ക്കാലികമായി", + "description":"English: Temporarily" + }, + "docblockedDisablePermanent":{ + "message":"സ്ഥിരമായി", + "description":"English: Permanently" + }, + "cloudPush":{ + "message":"ക്ലൌഡ് സ്റ്റോറേജിലേക്ക് എക്സ്പോര്‍ട്ട്‌ ചെയ്യുക", + "description":"tooltip" + }, + "cloudPull":{ + "message":"ക്ലൌഡ് സ്റ്റോറേജില്‍ നിന്ന് ഇമ്പോര്‍ട്ട് ചെയ്യുക", + "description":"tooltip" + }, + "cloudPullAndMerge":{ + "message":"ക്ലൗഡ് സംഭരനിയില്‍ നിന്ന് ഇറക്കുമതി ചെയ്ത് നിലവിലെ ക്രമീകരണത്തില്‍ ലയിപ്പിക്കും", + "description":"tooltip" + }, + "cloudNoData":{ + "message":"...\n...", + "description":"" + }, + "cloudDeviceNamePrompt":{ + "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.", + "description":"A warning to users at the top of 'Advanced settings' page" + }, + "genericSubmit":{ + "message":"സബ്മിറ്റ്", + "description":"for generic 'Submit' buttons" + }, + "genericApplyChanges":{ + "message":"Apply changes", + "description":"for generic 'Apply changes' buttons" + }, + "genericRevert":{ + "message":"റിവേര്‍ട്ട്", + "description":"for generic 'Revert' buttons" + }, + "genericBytes":{ + "message":"ബൈറ്റുകള്‍", + "description":"" + }, + "contextMenuTemporarilyAllowLargeMediaElements":{ + "message":"താൽക്കാലികമായി വലിയ മീഡിയ അനുവദിക്കുക", + "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 50da11c5e8344..72743d3c5cc99 100755 --- a/tools/import-crowdin.sh +++ b/tools/import-crowdin.sh @@ -35,9 +35,11 @@ cp $SRC/hu/messages.json $DES/hu/messages.json 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/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 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 From b3d210c866b8055b294c59b3c0407d828a4875a8 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 3 Apr 2017 10:10:27 -0400 Subject: [PATCH 0177/4093] minor code review --- platform/chromium/vapi-client.js | 2 +- platform/chromium/vapi-common.js | 2 +- platform/firefox/vapi-client.js | 2 +- platform/firefox/vapi-common.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index c0f6336f1b487..8e659499d24b9 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -57,7 +57,7 @@ if ( /^image\/|^text\/plain/.test(contentType) ) { /******************************************************************************/ // https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 -if ( !self.vAPI || !self.vAPI.uBO ) { +if ( !self.vAPI || self.vAPI.uBO !== true ) { self.vAPI = { uBO: true }; } diff --git a/platform/chromium/vapi-common.js b/platform/chromium/vapi-common.js index a92fe3f4c00d7..5a2c2799394b1 100644 --- a/platform/chromium/vapi-common.js +++ b/platform/chromium/vapi-common.js @@ -29,7 +29,7 @@ (function(self) { // https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 -if ( !self.vAPI || !self.vAPI.uBO ) { +if ( !self.vAPI || self.vAPI.uBO !== true ) { self.vAPI = { uBO: true }; } diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index e810d79d8e934..606f36f12b2eb 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -46,7 +46,7 @@ if ( document instanceof HTMLDocument === false ) { /******************************************************************************/ // https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 -if ( !self.vAPI || !self.vAPI.uBO ) { +if ( !self.vAPI || self.vAPI.uBO !== true ) { self.vAPI = { uBO: true }; } diff --git a/platform/firefox/vapi-common.js b/platform/firefox/vapi-common.js index 6b0200d18e24e..1ee7699879478 100644 --- a/platform/firefox/vapi-common.js +++ b/platform/firefox/vapi-common.js @@ -37,7 +37,7 @@ const {Services} = Components.utils.import( ); // https://bugs.chromium.org/p/project-zero/issues/detail?id=1225&desc=6#c10 -if ( !self.vAPI || !self.vAPI.uBO ) { +if ( !self.vAPI || self.vAPI.uBO !== true ) { self.vAPI = { uBO: true }; } From 510eba6bc4a6c2714704ce597d388e97de3796af Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 4 Apr 2017 16:45:50 -0400 Subject: [PATCH 0178/4093] fix #2477 --- src/js/messaging.js | 9 +++------ src/js/settings.js | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index c8b47c20a0cc5..f99bfa8cdfa4d 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -776,15 +776,12 @@ var backupUserData = function(callback) { var filename = vAPI.i18n('aboutBackupFilename') .replace('{{datetime}}', µb.dateNowToSensibleString()) .replace(/ +/g, '_'); - - vAPI.download({ - 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(userData, null, ' ')), - 'filename': filename - }); µb.restoreBackupSettings.lastBackupFile = filename; µb.restoreBackupSettings.lastBackupTime = Date.now(); vAPI.storage.set(µb.restoreBackupSettings); - getLocalData(callback); + getLocalData(function(localData) { + callback({ localData: localData, userData: userData }); + }); }; µb.assets.get(µb.userFiltersPath, onUserFiltersReady); diff --git a/src/js/settings.js b/src/js/settings.js index 16582955916ed..c7e24297a9f46 100644 --- a/src/js/settings.js +++ b/src/js/settings.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 @@ -105,7 +105,20 @@ var startImportFilePicker = function() { /******************************************************************************/ var exportToFile = function() { - messaging.send('dashboard', { what: 'backupUserData' }, onLocalDataReceived); + messaging.send('dashboard', { what: 'backupUserData' }, function(response) { + if ( + response instanceof Object === false || + response.userData instanceof Object === false + ) { + return; + } + vAPI.download({ + 'url': 'data:text/plain;charset=utf-8,' + + encodeURIComponent(JSON.stringify(response.userData, null, ' ')), + 'filename': response.localData.lastBackupFile + }); + onLocalDataReceived(response.localData); + }); }; /******************************************************************************/ From 6a4466f8afab90c7dd8a8a85c26f10f25007c20c Mon Sep 17 00:00:00 2001 From: gorhill Date: Tue, 4 Apr 2017 16:47:37 -0400 Subject: [PATCH 0179/4093] 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 4e1588b285342..17c910593ec1d 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.107", + "version": "1.11.5.108", "default_locale": "en", "description": "__MSG_extShortDesc__", From b52f4e90d77da14b284e0075e4f5d7524d86ea38 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 5 Apr 2017 17:21:55 -0400 Subject: [PATCH 0180/4093] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 787e5539973dd..1c4a90222dfea 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -23,6 +23,7 @@ Read first: [If you fail to provide this info, I will mark the issue as invalid. Lists all settings which differs from default settings] +- OS/version: - Browser/version: - uBlock Origin version: From c2208f8f74449fdc9d9a856f4db735496ff5a224 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 7 Apr 2017 07:24:57 -0400 Subject: [PATCH 0181/4093] translation work from https://crowdin.com/project/ublock --- src/_locales/pt_PT/messages.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index fe76427e905ac..641ae1be24ff9 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -204,7 +204,7 @@ "description":"English: Color-blind friendly" }, "settingsCloudStorageEnabledPrompt":{ - "message":"Ativar suporte à 'cloud'", + "message":"Ativar suporte à nuvem", "description":"" }, "settingsAdvancedUserPrompt":{ @@ -660,15 +660,15 @@ "description":"English: Permanently" }, "cloudPush":{ - "message":"Exportar para a 'cloud'", + "message":"Exportar para a nuvem", "description":"tooltip" }, "cloudPull":{ - "message":"Importar da 'cloud'", + "message":"Importar a partir da nuvem", "description":"tooltip" }, "cloudPullAndMerge":{ - "message":"Importar do armazenamento 'cloud' e juntar às definições atuais", + "message":"Importar a partir da nuvem e juntar às definições atuais", "description":"tooltip" }, "cloudNoData":{ From 582edd5e62253738d68ac9f092e0ed6571774b89 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 8 Apr 2017 10:40:38 -0400 Subject: [PATCH 0182/4093] fix #2519 --- src/js/scriptlets/element-picker.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index 6d43a6df36a30..9c14dfb2f0de2 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -1061,6 +1061,15 @@ var candidateFromFilterChoice = function(filterChoice) { } joiner = ' > '; } + + // https://github.com/gorhill/uBlock/issues/2519 + if ( + slot === filters.length && + document.querySelectorAll(selector).length > 1 + ) { + selector = 'body > ' + selector; + } + return '##' + selector; }; From 248395fda4d44b3138cd60eb722811d26889c969 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 9 Apr 2017 09:16:35 -0400 Subject: [PATCH 0183/4093] translation work from https://crowdin.com/project/ublock --- dist/description/description-ja.txt | 6 +++--- src/_locales/ja/messages.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/dist/description/description-ja.txt b/dist/description/description-ja.txt index 67e1387d6f73d..71559013f1bb3 100644 --- a/dist/description/description-ja.txt +++ b/dist/description/description-ja.txt @@ -3,13 +3,13 @@ 他ソフトとの比較: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared -使用法: ポップアップに表示される大きな電源ボタンは、現在のサイトでuBlockの有効/無効を切り替えます。 現在のサイトのみに適用されます、グローバルボタンではありません。 +使用法: ポップアップに表示される大きな電源ボタンは、現在のサイトでuBlockの有効/無効を切り替えます。 変更は現在のサイトへのみ適用されます。他のサイトとの共通ボタンではありません。 *** ただの「広告ブロッカー」より柔軟です:ホストファイルを読み込みフィルターを作成できます。 -要するに、以下のフィルターが読み込まれ、適用されます: +初回起動時の設定では、以下のフィルターが読み込まれ、適用されています: - EasyList - Peter Lowe’s Ad server list @@ -23,7 +23,7 @@ - hpHosts’s Ad and tracking servers - MVPS HOSTS - Spam404 -- その他たくさん +- などなど もちろん、多くのフィルターを適用すれば使用メモリーは増えます。 ただ、それでも、Fanboy's Special Blocking List、Fanboy's Enhanced Tracking List、hpHost's Ad and tracking serversの三つのリストを追加で適用しても、uBlockは他の人気のブロッカーより少ないメモリー消費を実現しています。 diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 251dfd9514e54..4ed78eb520d60 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -280,7 +280,7 @@ "description":"A button in the in the _3rd-party filters_ pane" }, "3pParseAllABPHideFiltersPrompt1":{ - "message":"要素隠蔽フィルタを解析、施行する。", + "message":"要素隠蔽フィルターを解析、施行する", "description":"English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo":{ @@ -356,7 +356,7 @@ "description":"English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'" }, "1pFormatHint":{ - "message":"一つのフィルターにつき一行です。フィルターはただのホストネームでもAdblock Plus適合のフィルターでも構いません。‘!’ですでに固定されている行は無視されます。", + "message":"1行につき1つのフィルターです。フィルターはただのホストネームでもAdblock Plus適合のフィルターでも構いません。‘!’ですでに固定されている行は無視されます。", "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":{ From f4f52c32209348dc050b77b6e0d84b9be5897541 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sun, 9 Apr 2017 11:32:13 -0400 Subject: [PATCH 0184/4093] new version 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 17c910593ec1d..de1576cd56a4d 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.11.5.108", + "version": "1.12.0", "default_locale": "en", "description": "__MSG_extShortDesc__", From 9e4c54b874a0e20d2f613396ea3958464cf699d0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 12 Apr 2017 07:45:50 -0400 Subject: [PATCH 0185/4093] Update ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 1c4a90222dfea..f28e375e1809c 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,6 +1,4 @@ -**Important:** If you are having issue with uBlock Origin ("uBO") on **Firefox Nightly**, please install the most recent developer version of uBO on AMO: . - -Read first: +Filter issues **MUST NOT** be reported here. Read first: ### Describe the issue From 749b31c97efc3bf3161b7ea4da851266ad719fc7 Mon Sep 17 00:00:00 2001 From: gorhill Date: Fri, 14 Apr 2017 16:36:51 -0400 Subject: [PATCH 0186/4093] possible workaround fix for https://github.com/nikrolls/uBlock-Edge/issues/69 --- assets/assets.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assets/assets.json b/assets/assets.json index b98670998d4ca..94262b4cd8b42 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -163,8 +163,8 @@ "contentURL": [ "https://mirror.cedia.org.ec/malwaredomains/justdomains", "https://mirror1.malwaredomains.com/files/justdomains", - "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains", - "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains.txt" + "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains.txt", + "assets/thirdparties/mirror1.malwaredomains.com/files/justdomains" ], "supportURL": "http://www.malwaredomains.com/" }, @@ -268,8 +268,8 @@ "title": "Peter Lowe’s Ad and tracking server list", "contentURL": [ "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext", - "assets/thirdparties/pgl.yoyo.org/as/serverlist", - "assets/thirdparties/pgl.yoyo.org/as/serverlist.txt" + "assets/thirdparties/pgl.yoyo.org/as/serverlist.txt", + "assets/thirdparties/pgl.yoyo.org/as/serverlist" ], "supportURL": "https://pgl.yoyo.org/adservers/" }, From d3cd49777bd276004c4ef4b0099e768c0c2a6356 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 15 Apr 2017 08:43:24 -0400 Subject: [PATCH 0187/4093] new revision --- 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 de1576cd56a4d..8ce765031d210 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "uBlock Origin", - "version": "1.12.0", + "version": "1.12.1", "default_locale": "en", "description": "__MSG_extShortDesc__", From 774faa1c8c44f54726866b1818a8a53a11186b56 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 22 Apr 2017 08:37:47 -0400 Subject: [PATCH 0188/4093] fix #2553 --- assets/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/assets.json b/assets/assets.json index 94262b4cd8b42..65230ad805eda 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -369,7 +369,7 @@ "off": true, "title": "FIN: Finnish Addition to Easylist", "lang": "fi", - "contentURL": "http://adb.juvander.net/Finland_adb.txt", + "contentURL": "https://adb.juvander.net/Finland_adb.txt", "supportURL": "http://www.juvander.fi/AdblockFinland" }, "FRA-0": { From 0b3d1477f245eda143c5b55a57d106001e73d6b9 Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 22 Apr 2017 12:57:56 -0400 Subject: [PATCH 0189/4093] add basic mitigation to potential abuse of IDN --- src/css/popup.css | 62 ++++++++++++++++++++++++++--------------------- src/js/popup.js | 24 +++++++++++++++--- src/popup.html | 2 +- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/src/css/popup.css b/src/css/popup.css index 2d47a0a95d6d3..66cbc3a9b8067 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -33,7 +33,7 @@ h2 { padding: 0.2em; text-align: center; } -h2:nth-of-type(1) { +h2:first-of-type { margin-top: 0; } a { @@ -124,7 +124,7 @@ body.portrait[dir="ltr"] #panes > div:nth-of-type(2) { #panes:not(.dfEnabled) > div:nth-of-type(2) { display: none; } -#panes > div:nth-of-type(1) { +#panes > div:first-of-type { min-width: 11em; padding: 0; } @@ -304,24 +304,30 @@ body[dir="rtl"] #tooltip { color: #000; display: inline-block; height: 1.9em; - line-height: 1.9em; + line-height: 1.9; overflow: hidden; position: relative; vertical-align: top; } -#firewallContainer > div:nth-of-type(1) > span:nth-of-type(1) { +#firewallContainer > div:first-of-type > span:first-of-type { cursor: pointer; } -#firewallContainer > div > span:nth-of-type(1) { +#firewallContainer > div > span:first-of-type { padding-right: 2px; position: relative; text-overflow: ellipsis; width: calc(100% - 4em); } +#firewallContainer > div.isDomain > span.isIDN:first-of-type > sup:before { + color: #666; + content: 'idn\2002'; + font-size: 80%; + font-weight: normal; + } #firewallContainer > div > span:nth-of-type(2) { display: none; } -#firewallContainer > div > span:nth-of-type(1) ~ span { +#firewallContainer > div > span:first-of-type ~ span { border-left: 1px solid white; width: 4em; } @@ -333,15 +339,15 @@ body[dir="rtl"] #tooltip { #firewallContainer > div > span:nth-of-type(4) { display: none; } -#firewallContainer > div.isDomain > span:nth-of-type(1) { +#firewallContainer > div.isDomain > span:first-of-type { font-weight: bold; } -#firewallContainer > div:nth-of-type(1) > span:nth-of-type(1):before { +#firewallContainer > div:first-of-type > span:first-of-type:before { color: #aaa; content: '\2012'; padding-right: 0.25em; } -#firewallContainer.minimized > div:nth-of-type(1) > span:nth-of-type(1):before { +#firewallContainer.minimized > div:first-of-type > span:first-of-type:before { content: '+'; } #firewallContainer.minimized > div.isDomain > span:nth-of-type(3) { @@ -379,7 +385,7 @@ body[dir="rtl"] #tooltip { content: '\2212\2212\2212'; } -body.advancedUser #firewallContainer > div > span:nth-of-type(1) { +body.advancedUser #firewallContainer > div > span:first-of-type { width: calc(100% - 8em); } body.advancedUser #firewallContainer > div > span:nth-of-type(2) { @@ -388,17 +394,17 @@ body.advancedUser #firewallContainer > div > span:nth-of-type(2) { body.advancedUser #firewallContainer > div:first-child ~ div:not([class]) { display: block; } -body.advancedUser #firewallContainer > div > span:nth-of-type(1) ~ span { +body.advancedUser #firewallContainer > div > span:first-of-type ~ span { cursor: pointer; } /** Small coloured label at the left of a row */ -#firewallContainer > div.allowed > span:nth-of-type(1):before, -#firewallContainer > div.blocked > span:nth-of-type(1):before, -#firewallContainer.minimized > div.isDomain.totalAllowed > span:nth-of-type(1):before, -#firewallContainer.minimized > div.isDomain.totalBlocked > span:nth-of-type(1):before { +#firewallContainer > div.allowed > span:first-of-type:before, +#firewallContainer > div.blocked > span:first-of-type:before, +#firewallContainer.minimized > div.isDomain.totalAllowed > span:first-of-type:before, +#firewallContainer.minimized > div.isDomain.totalBlocked > span:first-of-type:before { box-sizing: border-box; content: ''; display: inline-block; @@ -412,24 +418,24 @@ body.advancedUser #firewallContainer > div > span:nth-of-type(1) ~ span { Source for color-blind color scheme from https://github.com/WyohKnott: https://github.com/chrisaljoudi/uBlock/issues/467#issuecomment-95177219 */ -#firewallContainer > div.allowed > span:nth-of-type(1):before, -#firewallContainer.minimized > div.isDomain.totalAllowed > span:nth-of-type(1):before { +#firewallContainer > div.allowed > span:first-of-type:before, +#firewallContainer.minimized > div.isDomain.totalAllowed > span:first-of-type:before { background-color: rgb(0, 160, 0); } -#firewallContainer.colorBlind > div.allowed > span:nth-of-type(1):before, -#firewallContainer.colorBlind.minimized > div.isDomain.totalAllowed > span:nth-of-type(1):before { +#firewallContainer.colorBlind > div.allowed > span:first-of-type:before, +#firewallContainer.colorBlind.minimized > div.isDomain.totalAllowed > span:first-of-type:before { background-color: rgb(255, 194, 57); } -#firewallContainer > div.blocked > span:nth-of-type(1):before, -#firewallContainer.minimized > div.isDomain.totalBlocked > span:nth-of-type(1):before { +#firewallContainer > div.blocked > span:first-of-type:before, +#firewallContainer.minimized > div.isDomain.totalBlocked > span:first-of-type:before { background-color: rgb(192, 0, 0); } -#firewallContainer.colorBlind > div.blocked > span:nth-of-type(1):before, -#firewallContainer.colorBlind.minimized > div.isDomain.totalBlocked > span:nth-of-type(1):before { +#firewallContainer.colorBlind > div.blocked > span:first-of-type:before, +#firewallContainer.colorBlind.minimized > div.isDomain.totalBlocked > span:first-of-type:before { background-color: rgb(0, 19, 110); } -#firewallContainer > div.allowed.blocked > span:nth-of-type(1):before, -#firewallContainer.minimized > div.isDomain.totalAllowed.totalBlocked > span:nth-of-type(1):before { +#firewallContainer > div.allowed.blocked > span:first-of-type:before, +#firewallContainer.minimized > div.isDomain.totalAllowed.totalBlocked > span:first-of-type:before { background-color: rgb(192, 160, 0); } /* Rule cells */ @@ -483,7 +489,7 @@ body.advancedUser #firewallContainer > div > span.nRule.ownRule { height: 100%; opacity: 0.2; } -#actionSelector > span:nth-of-type(1) { +#actionSelector > span:first-of-type { width: 33%; } #actionSelector > span:nth-of-type(2) { @@ -495,10 +501,10 @@ body.advancedUser #firewallContainer > div > span.nRule.ownRule { #actionSelector > span:hover { opacity: 0.75; } -#actionSelector > span:nth-of-type(1) { +#actionSelector > span:first-of-type { background-color: rgb(0, 160, 0); } -#actionSelector.colorBlind > span:nth-of-type(1) { +#actionSelector.colorBlind > span:first-of-type { background-color: rgb(255, 194, 57); } #actionSelector > span:nth-of-type(2) { diff --git a/src/js/popup.js b/src/js/popup.js index f99747c7b4a64..f88627802a2bc 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-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 @@ -94,6 +94,7 @@ var rowsToRecycle = uDom(); var cachedPopupHash = ''; var statsStr = vAPI.i18n('popupBlockedStats'); var domainsHitStr = vAPI.i18n('popupHitDomainCount'); +var reHasAsciiAndUnicode = /[A-Za-z]+[^\x00-\x7F]|[^\x00-\x7F]+[A-Za-z]/; /******************************************************************************/ @@ -196,10 +197,25 @@ var addFirewallRow = function(des) { } row.descendants('[data-des]').attr('data-des', des); - row.descendants('span:nth-of-type(1)').text(punycode.toUnicode(des)); - var hnDetails = popupData.hostnameDict[des] || {}; - var isDomain = des === hnDetails.domain; + var hnDetails = popupData.hostnameDict[des] || {}, + isDomain = des === hnDetails.domain; + + var prettyDomainName = punycode.toUnicode(des), + isPunycoded = prettyDomainName !== des, + mixedDomainName = false; + if ( isDomain && isPunycoded ) { + var pos = prettyDomainName.indexOf('.'); + if ( pos !== -1 ) { + mixedDomainName = reHasAsciiAndUnicode.test(prettyDomainName.slice(0, pos)); + } + } + + var span = row.nodeAt(0).querySelector('span:first-of-type'); + span.classList.toggle('isIDN', mixedDomainName); + span.querySelector('span').textContent = prettyDomainName; + span.title = isDomain && isPunycoded ? des : ''; + row.toggleClass('isDomain', isDomain) .toggleClass('isSubDomain', !isDomain) .toggleClass('allowed', hnDetails.allowCount !== 0) diff --git a/src/popup.html b/src/popup.html index e27f253e15507..5f8f17ef729fe 100644 --- a/src/popup.html +++ b/src/popup.html @@ -48,7 +48,7 @@

       

      -
      -
      +
      +
      -
      +
      @@ -45,7 +45,7 @@
      -
      -
      -
      +
      +
      -
      +
      @@ -45,7 +45,7 @@
      -
      -
        +
        • {{netFilters}}
          • diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index 362f07c1072ae..182a215a9df61 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -1009,17 +1009,28 @@ const onCandidateChanged = (function() { /******************************************************************************/ const candidateFromFilterChoice = function(filterChoice) { - let slot = filterChoice.slot; - let filters = filterChoice.filters; + let { slot, filters } = filterChoice; let filter = filters[slot]; + // https://github.com/uBlockOrigin/uBlock-issues/issues/47 + for ( const elem of dialog.querySelectorAll('#candidateFilters li') ) { + elem.classList.remove('active'); + } + if ( filter === undefined ) { return ''; } // For net filters there no such thing as a path - if ( filter.startsWith('##') === false ) { return filter; } + if ( filter.startsWith('##') === false ) { + dialog.querySelector(`#netFilters li:nth-of-type(${slot+1})`) + .classList.add('active'); + return filter; + } // At this point, we have a cosmetic filter + dialog.querySelector(`#cosmeticFilters li:nth-of-type(${slot+1})`) + .classList.add('active'); + // Modifier means "target broadly". Hence: // - Do not compute exact path. // - Discard narrowing directives. @@ -1079,8 +1090,8 @@ const filterChoiceFromEvent = function(ev) { slot: 0, modifier: ev.ctrlKey || ev.metaKey }; - while ( li.previousSibling !== null ) { - li = li.previousSibling; + while ( li.previousElementSibling !== null ) { + li = li.previousElementSibling; r.slot += 1; } return r; @@ -1138,7 +1149,7 @@ const onDialogClicked = function(ev) { highlightElements(targetElements, true); } - else if ( ev.target.parentNode.classList.contains('changeFilter') ) { + else if ( ev.target.closest('.changeFilter') !== null ) { taCandidate.value = candidateFromFilterChoice(filterChoiceFromEvent(ev)); onCandidateChanged(); } From fae65534161c8288dff3836b1058308b3669c17e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 26 Aug 2020 11:03:06 -0400 Subject: [PATCH 3538/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 52c0e5857a3b9..0a730cff06c38 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.29.3.5 +1.29.3.6 From e9c496e8589963f8dba04db483c8c571671ca0e6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 26 Aug 2020 11:10:37 -0400 Subject: [PATCH 3539/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d63ac11acf0e2..0c94f7a6a4300 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.29.3.5", + "version": "1.29.3.6", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b5", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b5/uBlock0_1.29.3b5.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b6", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b6/uBlock0_1.29.3b6.firefox.signed.xpi" } ] } From ef62398fd9e420f4e9914fcd473b3b20e292e872 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Aug 2020 06:36:29 -0400 Subject: [PATCH 3540/4093] Add translations for ne wsort widget in "My rules" Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1055 --- src/_locales/en/messages.json | 16 ++++++++++++++++ src/dyna-rules.html | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 06b55a740b69f..ba9b006fbb9cf 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/dyna-rules.html b/src/dyna-rules.html index dfd2729a1d565..de125bf3978b6 100644 --- a/src/dyna-rules.html +++ b/src/dyna-rules.html @@ -35,7 +35,7 @@
        - filter  Sort: double-angle-up + filter  double-angle-up
        From 8f308f9a8248cc6b3fc5150eeba072220ab64037 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Aug 2020 06:39:18 -0400 Subject: [PATCH 3541/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0a730cff06c38..abc301cde66ec 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.29.3.6 +1.29.3.7 From 45cce88d8bd347ccfebed93e39b53fe938d8b07d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Aug 2020 06:41:58 -0400 Subject: [PATCH 3542/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/de/messages.json | 8 ++++---- src/_locales/hy/messages.json | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 9a8e7e7a94769..bb3773d252ed0 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -180,7 +180,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { - "message": "Pop-Up Fenster", + "message": "Pop-Up-Fenster", "description": "Caption for the no-popups per-site switch" }, "popupNoLargeMedia_v2": { @@ -296,7 +296,7 @@ "description": "English: Click, Ctrl-click" }, "pickerContextMenuEntry": { - "message": "Element blockieren", + "message": "Element blockieren...", "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { @@ -356,7 +356,7 @@ "description": "" }, "settingsNoLargeMediaPrompt": { - "message": "Blockiere Medienelemente größer als {{input}} KB", + "message": "Medienelemente größer als {{input}} KB blockieren", "description": "" }, "settingsNoRemoteFontsPrompt": { @@ -1024,7 +1024,7 @@ "description": "Label for keyboard shortcut used to toggle blocking profile" }, "relaxBlockingMode": { - "message": "Lockere Blockiermodus", + "message": "Blockiermodus lockern", "description": "Label for keyboard shortcut used to relax blocking mode (meant to replace 'Toggle blocking profile')" }, "storageUsed": { diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index e30c12191a81f..fa76ebf29801e 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -20,7 +20,7 @@ "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { - "message": "Ignore", + "message": "Անտեսել", "description": "Label for button to ignore unsaved changes" }, "settingsPageName": { @@ -108,7 +108,7 @@ "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { - "message": "Open the dashboard", + "message": "Բացել վահանակը", "description": "English: Click to open the dashboard" }, "popupTipZapper": { @@ -184,11 +184,11 @@ "description": "Caption for the no-popups per-site switch" }, "popupNoLargeMedia_v2": { - "message": "Large media elements", + "message": "Մեծ մեդիա էլեմենտներ", "description": "Caption for the no-large-media per-site switch" }, "popupNoCosmeticFiltering_v2": { - "message": "Cosmetic filtering", + "message": "Կոսմետիկ զտում", "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { @@ -352,7 +352,7 @@ "description": "" }, "settingsNoCosmeticFilteringPrompt": { - "message": "Disable cosmetic filtering", + "message": "Անջատել կոսմետիկ զտումը", "description": "" }, "settingsNoLargeMediaPrompt": { @@ -388,7 +388,7 @@ "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "3pAutoUpdatePrompt1": { - "message": "Auto-update filter lists", + "message": "Ինքնին թարմացնել ցանկերը", "description": "A checkbox in the _3rd-party filters_ pane" }, "3pUpdateNow": { @@ -408,7 +408,7 @@ "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": { @@ -448,7 +448,7 @@ "description": "English: Multipurpose" }, "3pGroupRegions": { - "message": "Regions, languages", + "message": "Երկրներ, լեզուներ", "description": "English: Regions, languages" }, "3pGroupCustom": { @@ -488,7 +488,7 @@ "description": "Short information about how to create custom filters" }, "1pImport": { - "message": "Import and append", + "message": "Ներմուծել և կցել", "description": "English: Import and append" }, "1pExport": { @@ -820,7 +820,7 @@ "description": "" }, "aboutWiki": { - "message": "Wiki", + "message": "Վիկի", "description": "English: project' wiki on GitHub" }, "aboutSupport": { From d2195b42464b4ec9ba6b785bab5c5c37f35d86a4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 27 Aug 2020 07:04:37 -0400 Subject: [PATCH 3543/4093] Fix rule sorting quirk in "My rules" pane Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1055 --- src/js/dyna-rules.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/js/dyna-rules.js b/src/js/dyna-rules.js index 20ea3ec1055c1..7d1df962470b5 100644 --- a/src/js/dyna-rules.js +++ b/src/js/dyna-rules.js @@ -385,28 +385,30 @@ const onPresentationChanged = (( ) => { const reSwRule = /^([^/]+): ([^/ ]+) ([^ ]+)/; const reRule = /^([^ ]+) ([^/ ]+) ([^ ]+ [^ ]+)/; - const reUrlRule = /^([^ ]+) ([^ ]+) ([^ ]+ [^ ]+)/; + const reUrlRule = /^([^ ]+) ([^ ]+) ([^ ]+ [^ ]+)/; const reverseHn = function(hn) { return hn.split('.').reverse().join('.'); }; const slotFromRule = rule => { - let type, srcHn, desHn, extra = ''; + let type, srcHn, desHn, extra; let match = reSwRule.exec(rule); if ( match !== null ) { type = ' ' + match[1]; srcHn = reverseHn(match[2]); desHn = srcHn; + extra = match[3]; } else if ( (match = reRule.exec(rule)) !== null ) { type = '\x10FFFE'; srcHn = reverseHn(match[1]); desHn = reverseHn(match[2]); + extra = match[3]; } else if ( (match = reUrlRule.exec(rule)) !== null ) { type = '\x10FFFF'; srcHn = reverseHn(match[1]); desHn = reverseHn(vAPI.hostnameFromURI(match[2])); - extra = rule; + extra = match[3]; } if ( sortType === 0 ) { return { rule, token: `${type} ${srcHn} ${desHn} ${extra}` }; From 1369b137d55166a4967d9f7c44fab228358e94c8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Aug 2020 15:34:14 -0400 Subject: [PATCH 3544/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 16 ++++++++++++++++ src/_locales/az/messages.json | 16 ++++++++++++++++ src/_locales/bg/messages.json | 18 +++++++++++++++++- src/_locales/bn/messages.json | 16 ++++++++++++++++ src/_locales/bs/messages.json | 18 +++++++++++++++++- src/_locales/ca/messages.json | 16 ++++++++++++++++ src/_locales/cs/messages.json | 16 ++++++++++++++++ src/_locales/cv/messages.json | 18 +++++++++++++++++- src/_locales/da/messages.json | 18 +++++++++++++++++- src/_locales/de/messages.json | 16 ++++++++++++++++ src/_locales/el/messages.json | 16 ++++++++++++++++ src/_locales/en_GB/messages.json | 18 +++++++++++++++++- src/_locales/eo/messages.json | 16 ++++++++++++++++ src/_locales/es/messages.json | 22 +++++++++++++++++++--- src/_locales/et/messages.json | 16 ++++++++++++++++ src/_locales/eu/messages.json | 16 ++++++++++++++++ src/_locales/fa/messages.json | 16 ++++++++++++++++ src/_locales/fi/messages.json | 16 ++++++++++++++++ src/_locales/fil/messages.json | 18 +++++++++++++++++- src/_locales/fr/messages.json | 18 +++++++++++++++++- src/_locales/fy/messages.json | 16 ++++++++++++++++ src/_locales/gl/messages.json | 16 ++++++++++++++++ src/_locales/he/messages.json | 16 ++++++++++++++++ src/_locales/hi/messages.json | 16 ++++++++++++++++ src/_locales/hr/messages.json | 24 ++++++++++++++++++++---- src/_locales/hu/messages.json | 16 ++++++++++++++++ src/_locales/hy/messages.json | 18 +++++++++++++++++- src/_locales/id/messages.json | 26 +++++++++++++++++++++----- src/_locales/it/messages.json | 16 ++++++++++++++++ src/_locales/ja/messages.json | 18 +++++++++++++++++- src/_locales/ka/messages.json | 16 ++++++++++++++++ src/_locales/kk/messages.json | 18 +++++++++++++++++- src/_locales/kn/messages.json | 18 +++++++++++++++++- src/_locales/ko/messages.json | 16 ++++++++++++++++ src/_locales/lt/messages.json | 16 ++++++++++++++++ src/_locales/lv/messages.json | 16 ++++++++++++++++ src/_locales/ml/messages.json | 16 ++++++++++++++++ src/_locales/mr/messages.json | 16 ++++++++++++++++ src/_locales/ms/messages.json | 18 +++++++++++++++++- src/_locales/nb/messages.json | 18 +++++++++++++++++- src/_locales/nl/messages.json | 18 +++++++++++++++++- src/_locales/oc/messages.json | 18 +++++++++++++++++- src/_locales/pl/messages.json | 18 +++++++++++++++++- src/_locales/pt_BR/messages.json | 16 ++++++++++++++++ src/_locales/pt_PT/messages.json | 20 ++++++++++++++++++-- src/_locales/ro/messages.json | 16 ++++++++++++++++ src/_locales/ru/messages.json | 18 +++++++++++++++++- src/_locales/sk/messages.json | 16 ++++++++++++++++ src/_locales/sl/messages.json | 16 ++++++++++++++++ src/_locales/sq/messages.json | 16 ++++++++++++++++ src/_locales/sr/messages.json | 26 +++++++++++++++++++++----- src/_locales/sv/messages.json | 16 ++++++++++++++++ src/_locales/ta/messages.json | 16 ++++++++++++++++ src/_locales/te/messages.json | 16 ++++++++++++++++ src/_locales/th/messages.json | 18 +++++++++++++++++- src/_locales/tr/messages.json | 18 +++++++++++++++++- src/_locales/uk/messages.json | 16 ++++++++++++++++ src/_locales/ur/messages.json | 18 +++++++++++++++++- src/_locales/vi/messages.json | 16 ++++++++++++++++ src/_locales/zh_CN/messages.json | 18 +++++++++++++++++- src/_locales/zh_TW/messages.json | 20 ++++++++++++++++++-- 61 files changed, 1018 insertions(+), 42 deletions(-) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index 42ebeec29e5a8..29bac6ceb18c3 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -551,6 +551,22 @@ "message": "صياغة القواعد: مصدر نوع وجهة العمل (تعليمات كاملة).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "القائمة الخاصه التي سوف يتم تجاهلها من قبل uBlock₀. رابط واحد كل سطر.\nالروابط الغير صحيحه سوف يتم تجاهلها.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/az/messages.json b/src/_locales/az/messages.json index b81e447f29013..a61ae24abc11d 100644 --- a/src/_locales/az/messages.json +++ b/src/_locales/az/messages.json @@ -551,6 +551,22 @@ "message": "Qaydalar sintaksisi: mənbə təyinat növ əməliyyat (bütün sənədlər).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Çeşidlə:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Qayda növü", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Mənbə", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Hedef", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "İstisnalar siyahısındakı təlimatlar uBlock Origin-in hansı veb-səhifələrdə işləyişinin dayandırılmasını təmin edir. Hər sətirdə yalnız bir təlimat ola bilər. Yanlış təlimatlar xəbərdarlıq edilmədən nəzərə alınmayacaq və şərhə çeviriləcəkdir.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 7162e0fc5e5e9..63540d91d940e 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Един филтър на ред. Може да е обикновено име на хост или съвместим с Adblock Plus филтър. Редовете с представка ! ще бъдат игнорирани.", + "message": "Един филтър на ред. Това може да бъде обикновен адрес или съвместим с Adblock Plus филтър. Редовете с представка ‘!’ ще бъдат игнорирани.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Синтаксис на правилото: източник цел тип действие (пълна документация).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Сортиране:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Тип правило", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Източник", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Дестинация", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Списък на адресите, за които uBlock Origin ще бъде изключен. Един елемент на ред. Недействителните адреси ще бъдат пренебрегнати.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/bn/messages.json b/src/_locales/bn/messages.json index 634fed3e6bab8..1de9b7038fa24 100644 --- a/src/_locales/bn/messages.json +++ b/src/_locales/bn/messages.json @@ -551,6 +551,22 @@ "message": "শব্দবিন্যাসের নিয়ম: source destination type action (সম্পূর্ণ নথিপত্র)।", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "আপনার হোস্টের তালিকা যাদের জন্য uBlock₀ নিষ্ক্রিয় করা হবে। লাইন প্রতি একটি ভুক্তি। অবৈধ হোস্ট নাম নিশব্দে উপেক্ষা করা হবে।", "description": "The name of the trusted sites pane." diff --git a/src/_locales/bs/messages.json b/src/_locales/bs/messages.json index 8a683de2da4ee..a622b464f9a7f 100644 --- a/src/_locales/bs/messages.json +++ b/src/_locales/bs/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Sintaksa pravila: izvor destinaija tip akcija (puna dokumentacija).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Direktive bijele liste govore na kojim web stranicama bi uBlock Origin trebao biti isključen. Jedna stavka po redu. Netačne direktive će biti ignorisane i zakomentirane.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index c7bd1059d0af2..237a12497ad25 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -551,6 +551,22 @@ "message": "Sintaxi de les regles: origen destinació tipus acció (documentació).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Ordena:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Tipus de regla", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Origen", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destinació", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Llistat dels noms de servidor amb que s'inhabilitarà l'µBlock. Una entrada per línia. S'ignoraran els noms de servidor no vàlids.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index 4a48f9d3aa1f4..0f7736aa88da2 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -551,6 +551,22 @@ "message": "Syntaxe pravidel: zdroj destinace typ akce (kompletní dokumentace).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Pravidla v seznamu povolených domén určují, pro které webové stránky bude uBlock Origin vypnutý. Jedna položka na řádek. Neplatná pravidla budou tiše ignorována a zakomentována.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/cv/messages.json b/src/_locales/cv/messages.json index f9af8f2f403b5..c7321bc7db607 100644 --- a/src/_locales/cv/messages.json +++ b/src/_locales/cv/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index 7dbe47bcd6653..740704703537d 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Ét filter pr. linje. Et filter kan være et almindeligt værtsnavn eller et Adblock Plus-kompatibelt filter. Linjer startende med ! ignoreres.", + "message": "Ét filter pr. linje. Et filter kan være et almindeligt værtsnavn eller et EasyList-kompatibelt filter. Linjer startende med ! ignoreres.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Regelsyntaks: kilde destination type handling (fuld dokumentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sortering:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Regeltype", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Kilde", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "De betroede webstedsdirektiver dikterer, på hvilke websider uBlock Origin skal deaktiveres. Én post pr. linje. Ugyldige direktiver ignoreres og kommenteres ud.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index bb3773d252ed0..f6ed5a20d22da 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -551,6 +551,22 @@ "message": "Regel-Syntax: Quelle Ziel Typ Action ( vollständige Dokumentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sortieren:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Regeltyp", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Quelle", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Ziel", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Ausnahmeregeln bestimmen, auf welchen Webseiten uBlock₀ nicht aktiv sein soll. Ein Eintrag pro Zeile. Ungültige Regeln werden stillschweigend ignoriert und auskommentiert.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index af292c397239d..62b09ec3c3d0f 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -551,6 +551,22 @@ "message": "Συντακτικό κανόνων: προέλευση προορισμός τύπος ενέργεια (πλήρης τεκμηρίωση).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Η λίστα σας με τα ονόματα κόμβων (host names) στα οποία το uBlock θα είναι απενεργοποιημένο. Μια καταχώρηση ανά γραμμή. Άκυρα ονόματα κόμβων θα παρακάμπτονται σιωπηλά.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/en_GB/messages.json b/src/_locales/en_GB/messages.json index 6b9ec186e75fd..a779e9b5adba2 100644 --- a/src/_locales/en_GB/messages.json +++ b/src/_locales/en_GB/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index a0faee5da80cc..41bceab6d48e8 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -551,6 +551,22 @@ "message": "Sintakso de reguloj: fonto celo tipo ago (kompleta dokumentado).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Via listo de gastignomoj por kiuj uBlock₀ estos malŝaltata. Unu gastignomo por ĉiu linio. Nevalidaj gastignomoj estos ignorataj silente.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index 9c34a998f22d4..98c1639987bb0 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -460,7 +460,7 @@ "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "Una URL por línea. Las URL inválidas serán ignoradas.", + "message": "Una URL por línea. Las URL no válidas serán ignoradas.", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Un filtro por línea. El filtro puede ser un nombre de dominio, o un filtro compatible con Adblock Plus. Las líneas que comiencen con ! serán ignoradas.", + "message": "Un filtro por línea. El filtro puede ser un nombre de dominio, o un filtro compatible con EasyList. Las líneas que comiencen con ! serán ignoradas.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Sintaxis de las reglas: origen destino tipo acción (documentación completa).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Ordenar:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Tipo de regla", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Fuente", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destino", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Sitios de confianza para los cuales se deshabilitará uBlock Origin. Una entrada por línea. Las entradas no válidas serán ignoradas.", "description": "The name of the trusted sites pane." @@ -876,7 +892,7 @@ "description": "Message asking user to confirm restore" }, "aboutRestoreDataError": { - "message": "Los datos no se pueden leer o son inválidos", + "message": "Los datos no se pueden leer o no son válidos", "description": "Message to display when an error occurred during restore" }, "aboutResetDataConfirm": { diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 3792e55745a11..02ef096f50ca5 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -551,6 +551,22 @@ "message": "Reegli süntaks: allikas sihtkoht tüüp tegevus (kogu dokumentatsioon).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sorteeri:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Reegli tüüp", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Allikas", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Sihtkoht", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Usaldatud saitide direktiivid määravad, millistel veebilehtedel peaks uBlock Origin keelatud olema. Üks kirje rea kohta. Sobimatuid direktiive vaikselt ignoreeritakse ja kommenteeritakse välja.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index ab3b3499d8e30..7eea4f0e55e8b 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -551,6 +551,22 @@ "message": "Arauen sintaxia: jatorria helburua mota ekintza(Dokumentazio osoa).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Zerrenda zuriaren direktibek uBlock zeintzu web orrietan desgaituko den zehazten dute. Sarrera bat lerroko. Baliogabeko ostalari izenak ezikusiko dira.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index cd0b2c2dde88c..f93cd5fcc0d40 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -551,6 +551,22 @@ "message": "نحوه استفاده: source destination type action (مستندات کامل).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "دستور العمل‌های لیست سفید حکم می‌کند که uBlock₀ باید بر روی کدام یک از صفحات وب غیر فعال باشد. در هر خط فقط یک مورد. دستور العمل‌های نامعتبر بدون اشاره ای نادیده گرفته شده و بیرون انداخته می‌شوند.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index 74dcaad58768d..ccbc5e62121ea 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -551,6 +551,22 @@ "message": "Säännön syntaksi: lähde kohde tyyppi toiminto (täysi dokumentaatio).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Lista osoitteista, joissa haluat kytkeä uBlock₀:n pois päältä. Kirjoita yksi osoite kullekin riville. Virheelliset säännöt ohitetaan.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index c60352f3dd2cf..91d2a0cc99b8f 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index ac259b35152a9..9b490fefd2540 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Une règle par ligne. Une règle peut être un simple nom d'hôte, ou encore un filtre respectant la syntaxe des filtres Adblock Plus. Les lignes débutant par ! seront ignorées.", + "message": "Une règle par ligne. Une règle peut être un simple nom d'hôte, ou encore une règle compatible EasyList. Les lignes débutant par ! seront ignorées.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Syntaxe : source destination type action (Documentation complète en anglais)", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Trier :", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Type de règle", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Les sites Web déclarés comme fiables auprès d'uBlock Origin ne seront pas traités par ce dernier.\nUne entrée par ligne. Les entrées invalides seront silencieusement ignorées et commentées.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index c4eabe8a5bc83..ee052b26f0a77 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -551,6 +551,22 @@ "message": "Rigelsyntaks: boarne bestimming type aksje (folsleine dokumintaasje).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "De whitelist-ynstruksjes skriuwe foar op hokker websiden uBlock Origin útskeakele wurde moat. Ien fermelding per rigel. Unjildige ynstruksjes wurde sûnder meidieling negearre en útskeakele.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index 12ca71f9c252d..af9f40be5fa40 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -551,6 +551,22 @@ "message": "Sintaxe das regras: orixe destino tipo acción (documentación completa).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "A lista branca é a túa lista de nomes de servidor nos que o uBlock₀ estará desactivado. Ten unha entrada por cada liña, e os nomes de servidor non válidos serán ignorados.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index 944608f8bbf90..f184015499333 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -551,6 +551,22 @@ "message": "סינטקס חוק: פעולה סוג יעד מקור (תיעוד מלא).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "רשימת שמות המתחם שלך בהם uBlock₀ לא יהיה פעיל. רשומה אחת בכל שורה. שמות מתחם לא חוקיים לא יפורשו ולא תהיה התראה לכך.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index e94ff0a555c09..35b5fd4acf192 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -551,6 +551,22 @@ "message": "सिंटेक्स नियम: सोर्स डेस्टिनेशन टाइप एक्शन ( पूरी डॉक्यूमेंटेशन).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "वाइटलिस्ट के नियम यह बताते हैं कि uBlock Origin को किस वेब पेज पर बंद करना चाहिए। एक एंट्री हर लाइन में। अमान्य नियमों को सीधे-सीधे नजरअंदाज कर दिया जाएगा।", "description": "The name of the trusted sites pane." diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index f5f836e40a510..d517c3d794f0b 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "Popis dopuštenih", + "message": "Pouzdane stranice", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Jedan filter po retku. Filter može biti običan hostname ili filter kompatibilan sa Adblock Plus-om. Linije sa prefiksom ! zanemarit će se.", + "message": "Jedan filter po retku. Filter može biti običan hostname ili filter kompatibilan sa EasyList-om. Linije sa prefiksom ! zanemarit će se.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,8 +551,24 @@ "message": "Pravila sintakse: akcija tipa izvornog odredišta (sva dokumentacija).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sortiraj:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Vrsta pravila", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Izvor", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destinacija", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { - "message": "Popis host naziva za koje će uBlock Origin biti onemogućen. Jedan zapis po retku. Nevaljani host nazivi biti će tiho ignorirani.", + "message": "Direktiva pouzdanih stranica govori za koje bi web stranice uBlock Origin trebao biti onemogućen. Jedan zapis po retku. Nevaljane direktive biti će tiho ignorirane i komentirane.", "description": "The name of the trusted sites pane." }, "whitelistImport": { @@ -564,7 +580,7 @@ "description": "English: Export" }, "whitelistExportFilename": { - "message": "moja-ublock-lista_dopustenog_{{datetime}}.txt", + "message": "moje-ublock-pouzdane-stranice_{{datetime}}.txt", "description": "The default filename to use for import/export purpose" }, "whitelistApply": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 1f481d940921b..84e3db5d5475b 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -551,6 +551,22 @@ "message": "Szabály szintaxis: forrás cél típus művelet (Teljes dokumentáció).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Rendezés:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Szabály típusa", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Forrás", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Az alábbi listában felsorolt hostokon a kiegészítő nem fog működni. Soronként egy bejegyzés. Érvénytelen hostnevek figyelmen kívül maradnak.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index fa76ebf29801e..ffb826203279b 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index bcded2c8e3e41..2de75ef98d2c7 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -64,7 +64,7 @@ "description": "Title for the advanced settings page" }, "popupPowerSwitchInfo": { - "message": "Klik: nonfungsi/fungsikan uBlock₀ untuk situs ini.\n\nCtrl+klik: nonfungsikan uBlock₀ hanya di halaman ini.", + "message": "Klik: nonaktif/aktifkan uBlock₀ untuk situs ini.\n\nCtrl+klik: nonaktifkan uBlock₀ hanya di laman ini.", "description": "English: Click: disable/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page." }, "popupPowerSwitchInfo1": { @@ -332,11 +332,11 @@ "description": "For the tooltip of a link which gives access to advanced settings" }, "settingsPrefetchingDisabledPrompt": { - "message": "Nonfungsikan pra-ambil (untuk mencegah sambungan apapun untuk permintaan jaringan yang diblokir)", + "message": "Nonaktifkan pra-ambil (untuk mencegah sambungan apapun untuk permintaan jaringan yang diblokir)", "description": "English: " }, "settingsHyperlinkAuditingDisabledPrompt": { - "message": "Nonfungsikan pengauditan pranala", + "message": "Nonaktifkan pengauditan pranala", "description": "English: " }, "settingsWebRTCIPAddressHiddenPrompt": { @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Satu filter per baris. Filter dapat berupa nama host, atau filter yang kompatibel dengan Adblock Plus. Baris yang diawali dengan ! akan diabaikan.", + "message": "Satu filter per baris. Filter dapat berupa nama hos, atau filter yang kompatibel dengan EasyList. Baris yang diawali dengan ! akan diabaikan.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Aturan sintaksis: sumber tujuan jenis tindakan (dokumentasi lengkap).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Urutan:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Tipe aturan", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Sumber", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Tujuan", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Berisi domain atau halaman situs terpercaya, uBlock Origin akan dinonaktifkan. Satu entri per baris. Entri yang tidak valid akan diabaikan dan dijadikan komentar.", "description": "The name of the trusted sites pane." @@ -952,7 +968,7 @@ "description": "English: Close this window" }, "docblockedProceed": { - "message": "Nonfungsikan pemblokiran ketat untuk {{hostname}}", + "message": "Nonaktifkan pemblokiran ketat untuk {{hostname}}", "description": "English: Disable strict blocking for {{hostname}} ..." }, "docblockedDisableTemporary": { diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 6f2acdb59b13a..fcb45ba314f83 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -551,6 +551,22 @@ "message": "Sintassi per le regole: sorgente destinazione tipo azione (documentazione completa).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Ordina:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Tipo regola", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Fonte", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destinazione", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Lista dei siti dove uBlock è disattivato. Un sito per ogni riga. Le voci non valide verranno silenziosamente ignorate.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 4bd0d53f8a8fe..e218ac2a62381 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "1行につき1つのフィルターです。フィルターはただのホストネームでもAdblock Plus書式のフィルターでも構いません。! が先頭にある行は無視されます。", + "message": "1行につき1つのフィルターです。フィルターはただのホストネームでもAdblock Plus適合のフィルターでも構いません。‘!’ですでに固定されている行は無視されます。", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "ルールの書き方: source destination type action (詳しい説明)", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "並べ替え", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "種類", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "発信源", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "通信先", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "uBlock₀が無効になるホスト名の一覧です。1行につき1つのエントリで、無効なホスト名は無視されます。", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 77d033af66c28..6535c9ea0e257 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -551,6 +551,22 @@ "message": "წესის აგებულება: წყარო დანიშნულება მოთხოვნის სახე მოქმედება (მასალები სრულად).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "დალაგება:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "წესის სახეობა", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "წყარო", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "დანიშნულება", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "მითითებული გამონაკლისები განსაზღვრავს, თუ რომელ ვებგვერდზე უნდა გამოირთოს uBlock Origin. თითოეული ჩანაწერი ცალკეულ ხაზზე უნდა იყოს. არასწორი მითითებები უგულებელყოფილი იქნება გაფრთხილების გარეშე და აღინიშნება კომენტარად.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/kk/messages.json b/src/_locales/kk/messages.json index fd895e508f63c..120295bf72b0f 100644 --- a/src/_locales/kk/messages.json +++ b/src/_locales/kk/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json index fa7c4a3dfc49f..64218733ad9c3 100644 --- a/src/_locales/kn/messages.json +++ b/src/_locales/kn/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index 590f74dcfa612..fa5ae5f17e43e 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -551,6 +551,22 @@ "message": "규칙 문법: 소스 페이지, 필터링 대상, 필터링할 타입, 적용할 조치 (전체 설명서).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "목록에 있는 호스트들은 uBlock₀에서 비활성화됩니다. 한 줄에 한 개씩 입력하세요. 존재하지 않는 호스트는 자동으로 무시됩니다.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index ab545fc7acea4..505b89810dcd3 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -551,6 +551,22 @@ "message": "Taisyklės sintaksė: šaltinis paskirtis tipas veiksmas (dokumentacija).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Baltojo sąrašo direktyvos nurodo, kurioms svetainėms uBlock Origin turėtų būti išjungtas. Vienas įrašas eilutėje. Neteisingos direktyvos bus tyliai ignoruotos ir užkomentuotos.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index 0bc186aa351ea..029c3af183d09 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -551,6 +551,22 @@ "message": "Noteikumu sintakse: avots mērķis tips rīcība (pilna dokumentācija).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Saraksts ar interneta resursiem, kuriem tiks atslēgts uBlock Origin. Viens ieraksts katrā rindiņā. Nederīgi interneta resursu nosaukumi tiks ignorēti bez brīdinājuma.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ml/messages.json b/src/_locales/ml/messages.json index d17b24562a37c..9fddc114e7abf 100644 --- a/src/_locales/ml/messages.json +++ b/src/_locales/ml/messages.json @@ -551,6 +551,22 @@ "message": "നിയമത്തിന്‍റെ സിന്‍റ്റാക്സ്‌: സോര്‍സ് ഡെസ്റ്റിനേഷന്‍ ടൈപ്പ് ആക്ഷന്‍ ( മുഴുനീള പ്രമാണം).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "യുബ്ലോക്ക്ഒ ഡിസേബിള്‍ ചെയ്യപ്പെടേണ്ട ഹോസ്റ്റ് നെയിമുകള്‍. ഒരു വരിയില്‍ ഒരു എന്‍ട്രി എന്ന രീതിയില്‍ ചേര്‍ക്കുക. ഇന്‍വാലിഡ്‌ ഹോസ്റ്റ് നെയിമുകള്‍ നിശബ്ദമായി ഇഗ്നോര്‍ ചെയ്യപെടും.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/mr/messages.json b/src/_locales/mr/messages.json index a57af3ea71d20..9b156f643f2c3 100644 --- a/src/_locales/mr/messages.json +++ b/src/_locales/mr/messages.json @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "आपली होस्ट नावांची यादी ज्याचासाठी म्यूब्लॉक अक्षम केले जाईल. प्रति ओळ एक प्रविष्टी. अवैध होस्ट नावांकडे शांतपणे दुर्लक्ष केले जाईल.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ms/messages.json b/src/_locales/ms/messages.json index 3d2b81224e8cb..2e62f9a71421f 100644 --- a/src/_locales/ms/messages.json +++ b/src/_locales/ms/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 418f0b4a9bb7c..33373e388887f 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Ett filter per linje. Et filter kan være et vanlig vertsnavn eller et Adblock Plus-kompatibelt filter. Linjer med prefikset ! blir ignorert.", + "message": "Ett filter per linje. Et filter kan være et vanlig vertsnavn eller et EasyList-kompatibelt filter. Linjer med prefikset ! blir ignorert.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Regelsyntaks: kilde destinasjon type handling (full dokumentasjon).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sorter:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Regeltype", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Kilde", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destinasjon", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Direktivene for betrodde sider bestemmer hvilke nettsider uBlock Origin ikke skal være aktiv på. Én oppføring per linje. Ugyldige direktiver blir stille ignorert og kommentert ut.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index 404a4fe90497e..3645718151050 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Eén filter per regel. Een filter kan een gewone hostnaam of een Adblock Plus-compatibel filter zijn. Regels beginnend met ! worden genegeerd.", + "message": "Eén filter per regel. Een filter kan een gewone hostnaam of een EasyList-compatibel filter zijn. Regels beginnend met ! worden genegeerd.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Regelsyntaxis: bron bestemming type actie (volledige documentatie).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sorteren:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Regeltype", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Bron", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Bestemming", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "De vertrouwde-website-instructies schrijven voor op welke webpagina’s uBlock Origin dient te worden uitgeschakeld. Eén vermelding per regel. Ongeldige instructies worden zonder mededeling genegeerd en uitgecommentarieerd.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/oc/messages.json b/src/_locales/oc/messages.json index 8ce5af3222276..06e76821b87da 100644 --- a/src/_locales/oc/messages.json +++ b/src/_locales/oc/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index 07cdea2b8be4b..684406473932e 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Jeden filtr na linię. Filtrem może być nazwa hosta lub filtr kompatybilny z dodatkiem Adblock Plus. Wiersze poprzedzone ! będą pomijane.", + "message": "W wierszu może być tylko jeden filtr. Filtrem może być nazwa hosta lub filtr kompatybilny z EasyList. Wiersze poprzedzone znakiem ! będą pomijane.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Składnia reguły: źródło cel typ akcja (pełna dokumentacja).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sortowanie:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Typ reguły", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Źródło", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Miejsce docelowe", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Wytyczne zaufanych witryn nakazują, na których stronach uBlock Origin powinien zostać wyłączony. Jeden wpis na linię. Nieprawidłowe linie zostaną bez powiadomienia zignorowane i wykomentowane.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 331dd262f6453..d74dc58d9ddce 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -551,6 +551,22 @@ "message": "Regras de sintaxe: origem destino tipo ação (documentação completa).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Ordenar:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Tipo de regra", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Fonte", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destinação", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "As diretivas de sites confiáveis ditam em quais páginas da Web o uBlock Origin deve ser desativado. Uma entrada por linha. As diretivas inválidas serão silenciosamente ignoradas e comentadas.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index f7a3b8ace57af..8e8f08b5f4ee6 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Um filtro por linha. Um filtro pode ser um simples nome de servidor ou um filtro compatível com o Adblock Plus. Linhas começadas por ! serão ignoradas.", + "message": "Um filtro por linha. Um filtro pode ser um simples nome de anfitrião, ou um filtro compatível com a EasyList. Linhas começadas por ! serão ignoradas.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,8 +551,24 @@ "message": "Regra de sintaxe: origem destino tipo ação (documentação completa).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Ordenar:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Tipo de regra", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Fonte", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destino", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { - "message": "As diretivas de sites confiáveis determinam em quais páginas da web o uBlock Origin deve ser desativado. Uma entrada por linha. Diretivas inválidas serão ignoradas e comentadas silenciosamente.", + "message": "As diretivas de sites confiáveis determinam em quais páginas web o uBlock Origin deve ser desativado. Uma entrada por linha. Diretivas inválidas serão ignoradas e comentadas silenciosamente.", "description": "The name of the trusted sites pane." }, "whitelistImport": { diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index 7b2a5909e04bb..a9e0a87fe16dd 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -551,6 +551,22 @@ "message": "Sintaxa pentru regulă: sursă destinație tip acțiune (documentație).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sortare:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Tipul regulei", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Sursa", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destinaţia", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Lista ta de nume de gazde unde uBlock va fi dezactivat. Fiecare adresă pe o singură linie. Adresele nevalide vor fi ignorate automat.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index e049c99e8a46a..0b71e9b7e4e86 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Одно правило на строку. Правилом может быть обычное имя сайта, или Adblock Plus-совместимый фильтр. Строки, начинающиеся с !, будут проигнорированы.", + "message": "Одно правило на строку. Правилом может быть имя сайта, или EasyList-совместимый фильтр. Строки, начинающиеся с !, будут проигнорированы.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Синтаксис правил: источник назначение тип действие (полная документация).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Сортировка:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Тип правила", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Источник", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Цель", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Записи доверенных сайтов указывают на каких веб-страницах uBlock Origin должен быть отключен. Одна запись на строку. Некорректные записи будут проигнорированы и закомментированы без предупреждений.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 3f33431339cf5..2bd8d3fb3e7bb 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -551,6 +551,22 @@ "message": "Syntax pravidla: zdroj cieľ typ akcia (úplná dokumentácia).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Zoradenie:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Typ pravidla", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Zdroj", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Doména", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Zoznam hostiteľov, pre ktoré webové stránky bude uBlock Origin zakázaný. Jedna položka na riadok. Neplatní hostitelia budú ignorovaní a zakomentovaní.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index c7968ae9c0881..21809a0a586c7 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -551,6 +551,22 @@ "message": "Sintaksa pravil: vir destinacija tip akcija (polna dokumentacija (v angleščini)).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Vaš seznam gostiteljskih naslovov, za katere želite, da je uBlock₀ izklopljen. En vnos na vrstico. Neveljavna gostiteljska imena bodo brez opozoril ignorirana.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 92a186cc1a5c3..c37d1c2691e64 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -551,6 +551,22 @@ "message": "Rregullat e sintaksës: burimi destinacioni lloji veprimi (dokumentimi i plotë).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Kjo listë paraqet faqet e besuara në të cilat uBlock Origin duhet çaktivizuar. Një element për rresht. Nuk do të merren parasysh udhëzimet e pasakta.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index 8fb3eae356529..83b906b5a6355 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "Бела листа", + "message": "Поуздани сајтови", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { @@ -388,7 +388,7 @@ "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "3pAutoUpdatePrompt1": { - "message": "Аутоматско ажурирање листе филтера", + "message": "Аутоматски ажурирај листе филтера", "description": "A checkbox in the _3rd-party filters_ pane" }, "3pUpdateNow": { @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Један филтер по реду. Филтер може бити назив хоста или филтер компатибилан са Adblock Plus форматом. Редови са префиксом ! ће бити игнорисани.", + "message": "Један филтер по реду. Филтер може бити назив хоста или филтер компатибилан са EasyList форматом. Редови са префиксом ! ће бити игнорисани.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,8 +551,24 @@ "message": "Правила синтаксе: извор одредиште тип акција(сва документација).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Сортирај:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Тип правила", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Извор", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Одредиште", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { - "message": "Смернице дозвољених домена диктирају на којим веб страницама би uBlock Origin требало бити онемогућен. Један унос по реду. Неисправне смернице ће бити тихо занемарене.", + "message": "Смернице поузданих сајтова диктирају на којим веб страницама би uBlock Origin требало бити онемогућен. Један унос по реду. Неисправне смернице ће бити тихо занемарене.", "description": "The name of the trusted sites pane." }, "whitelistImport": { @@ -564,7 +580,7 @@ "description": "English: Export" }, "whitelistExportFilename": { - "message": "моја-ublock-листа_дозвољених_домена{{datetime}}.txt", + "message": "моји-ublock-поуздани-сајтови_{{datetime}}.txt", "description": "The default filename to use for import/export purpose" }, "whitelistApply": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 9487a14ca8d31..9f0ab3dba4ade 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -551,6 +551,22 @@ "message": "Regelsyntax: källa mål typ åtgärd (full dokumentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sortera:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Regeltyp", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Källa", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Mål", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "De pålitliga webbplatsdirektiven dikterar på vilka webbplatser uBlock Origin bör inaktiveras. En post per rad. Ogiltiga direktiv ignoreras och kommenteras ut.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ta/messages.json b/src/_locales/ta/messages.json index b2ee49d8e1487..b39bf960f94ce 100644 --- a/src/_locales/ta/messages.json +++ b/src/_locales/ta/messages.json @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "எந்தெந்தப் பக்கங்களில் uBlock₀ முடக்கப்பட வேண்டுமென்பதை அனுமதிப்பட்ட கட்டளைகள் ஆணையிடுகின்றன. வரிக்கு ஒரு இடுகை மட்டுமே. தவறான கட்டளைகள் அமைதியாகப் புறக்கணிக்கப்படும்.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index 31b19ad8b1b65..def734821c188 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -551,6 +551,22 @@ "message": "నియమనిర్మాన పద్ధతి: మూలం గమ్యం రకం చర్య (పూర్తి వివరాలు).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "uBlock Origin అచేతనబడించిన హోస్టూల జాబితా, ఇది మీరు కూర్చినది,. ఒక్కో పంక్తికి ఒక్కో నమోదు. చెల్లని నమోదులు మౌనంగా విస్మరించబడుతాయి.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index a79ccc9aa6173..9f6d9b192649d 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 213281cbf635d..75ef37609b229 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Her satırda bir filtre. Bir filtre yalın bir alan adı veya Adblock Plus uyumlu bir filtre olabilir. ! ile başlayan satırlar yok sayılacaktır.", + "message": "Her satırda bir filtre. Bir filtre yalın bir alan adı veya EasyList-uyumlu bir filtre olabilir. ! ile başlayan satırlar yok sayılacaktır.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Kural sözdizimi: kaynak hedefi türü işlemi (bütün belgeler).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sıralama:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Kural türü", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Kaynak", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Hedef", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Güvenilen site yönergeleri, uBlock Origin'in devre dışı bırakılması gerektiği web sayfalarını belirler. Her satırda bir girdi. Geçersiz yönergeler sessizce yok sayılacak ve yoruma dönüştürülecektir.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index 61fde9db298b6..9b7b9a7107101 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -551,6 +551,22 @@ "message": "Синтаксис правил: джерело призначення тип дія (повна документація).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Ваш список адрес сайтів, для яких µBlock буде неактивним. Додайте по одному запису на рядок. Невірні адреси будуть проігноровані без попереджень та закоментовані.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/ur/messages.json b/src/_locales/ur/messages.json index 0daf83bc5d14d..8b9d89fafe57c 100644 --- a/src/_locales/ur/messages.json +++ b/src/_locales/ur/messages.json @@ -484,7 +484,7 @@ "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.", + "message": "One filter per line. A filter can be a plain hostname, or an EasyList-compatible filter. Lines prefixed with ! will be ignored.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "Rule syntax: source destination type action (full documentation).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sort:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Rule type", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Source", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Destination", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "The trusted site 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": "The name of the trusted sites pane." diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index ce0a08c4feb38..d799ebf75075e 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -551,6 +551,22 @@ "message": "Quy tắc cú pháp: nguồn đích loại hành động (tài liệu đầy đủ).", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "Sắp xếp:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "Loại quy tắc", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "Nguồn", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "Đích", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "Danh sách tên các máy chủ mà uBlock Origin sẽ bị chặn. Một mục nhập trên mỗi dòng. Tên máy chủ không hợp lệ sẽ được tự động bỏ qua.", "description": "The name of the trusted sites pane." diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 7f47005f4cf2a..a20700947f8e6 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "一行一条过滤规则。每条规则可以是一个普通的主机名或者是一条 Adblock Plus 兼容的过滤规则。以 ! 开头的行将被忽略。", + "message": "一行一条过滤规则。每条规则可以是一个普通的主机名或者是一条与 EasyList 兼容的过滤规则。以 ! 开头的行将被忽略。", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "规则语法:来源主机名称 目标主机名称 连接请求类型 操作完整说明)。", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "排列:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "规则类型", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "来源", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "目标", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "您的列表中针对 µBlock 的主机名将被禁用。每行一条规则。无效的主机名将直接被忽略。", "description": "The name of the trusted sites pane." diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 1548d707c956b..326b4642b9ab5 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -4,7 +4,7 @@ "description": "extension name." }, "extShortDesc": { - "message": "一個高效率的網路資源過濾器,使用不多的 CPU 及記憶體資源。", + "message": "終於有套使用不多的 CPU 及記憶體資源的高效率阻擋器。", "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "每行一個過濾規則。規則可以單純是主機名稱,或是與 Adblock Plus 相容的過濾規則。以 ! 開頭的行將被忽略。", + "message": "每行一個過濾規則。規則可以單純是主機名稱,或是與 EasyList 相容的過濾規則。以 ! 開頭的行將被忽略。", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -551,6 +551,22 @@ "message": "規則語法:來源主機名稱 目標主機名稱 連線請求類型 操作完整說明)。", "description": "English: dynamic rule syntax and full documentation." }, + "rulesSort": { + "message": "排序:", + "description": "English: label for sort option." + }, + "rulesSortByType": { + "message": "規則類型", + "description": "English: a sort option for list of rules." + }, + "rulesSortBySource": { + "message": "來源", + "description": "English: a sort option for list of rules." + }, + "rulesSortByDestination": { + "message": "目標", + "description": "English: a sort option for list of rules." + }, "whitelistPrompt": { "message": "列表裡的主機名稱將被 uBlock₀ 停用。每行一個規則。無效的主機名稱將被忽略掉。", "description": "The name of the trusted sites pane." From aadbdccbbec1ee450d3096e3d600f34f3a7e122a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 28 Aug 2020 15:40:55 -0400 Subject: [PATCH 3545/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 0c94f7a6a4300..d313be24f9155 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.29.3.6", + "version": "1.29.3.7", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b6", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b6/uBlock0_1.29.3b6.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b7", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b7/uBlock0_1.29.3b7.firefox.signed.xpi" } ] } From 3bbed7be330a581d1ddf15c5283aac2864c66620 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 29 Aug 2020 08:32:40 -0400 Subject: [PATCH 3546/4093] Do not use `letter-spacing` for non-bolded text Related feedback: - https://github.com/uBlockOrigin/uBlock-issues/issues/1044#issuecomment-682920219 --- src/css/common.css | 2 +- src/css/dashboard.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/css/common.css b/src/css/common.css index f1c26292edf6d..4a4ef2633e52e 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -98,7 +98,6 @@ button { fill: var(--button-ink); font-size: 14px; justify-content: center; - letter-spacing: 0.5px; min-height: 36px; padding: 0 var(--font-size); vertical-align: middle; @@ -276,6 +275,7 @@ select { :root.hidpi button { font-family: Metropolis, sans-serif; font-weight: 600; + letter-spacing: 0.5px; } :root.hidpi .fieldset-header { font-family: Metropolis, sans-serif; diff --git a/src/css/dashboard.css b/src/css/dashboard.css index a3672077f8eb9..136a2035eb2db 100644 --- a/src/css/dashboard.css +++ b/src/css/dashboard.css @@ -36,7 +36,6 @@ html, body { border-top: 3px solid transparent; color: var(--dashboard-tab-ink); fill: var(--dashboard-tab-ink); - letter-spacing: 0.5px; padding: 0.5em 1.4em calc(0.5em - 3px); text-decoration: none; user-select: none; @@ -87,6 +86,7 @@ body:not(.canUpdateShortcuts) .tabButton[data-pane="shortcuts.html"] { :root.hidpi .tabButton { font-family: Metropolis, sans-serif; font-weight: 600; + letter-spacing: 0.5px; } /* hover-able devices */ From db008f45dca8187d23ffb4d28cf9330adad3c0ea Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 30 Aug 2020 07:56:11 -0400 Subject: [PATCH 3547/4093] Import translation work from https://crowdin.com/project/ublock --- dist/description/description-fil.txt | 2 +- src/_locales/az/messages.json | 2 +- src/_locales/fil/messages.json | 18 +++++++++--------- src/_locales/sv/messages.json | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dist/description/description-fil.txt b/dist/description/description-fil.txt index 73ac8c4e5af3d..24942883f327d 100644 --- a/dist/description/description-fil.txt +++ b/dist/description/description-fil.txt @@ -1,4 +1,4 @@ -Isang episyenteng blocker: magaan sa memorya at CPU footprint, pero naka-loload at nag-eenforce ng libo-libong mga filters kaysa sa mga ibang sikat na blockers. +Isang episyenteng blocker: magaan sa memorya at CPU footprint, ngunit nakakapag-loload at nakakapag-enforce ng libo-libong mga filters kumpara sa mga ibang sikat na blockers. Isinalarawan pangkalahatang-ideya ng kahusayan ng uBlock: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared diff --git a/src/_locales/az/messages.json b/src/_locales/az/messages.json index a61ae24abc11d..1eace1f8676b7 100644 --- a/src/_locales/az/messages.json +++ b/src/_locales/az/messages.json @@ -564,7 +564,7 @@ "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { - "message": "Hedef", + "message": "Hədəf", "description": "English: a sort option for list of rules." }, "whitelistPrompt": { diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index 91d2a0cc99b8f..8dae3e2654da6 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -56,7 +56,7 @@ "description": "appears as tab name in dashboard" }, "assetViewerPageName": { - "message": "uBlock₀ — Asset viewer", + "message": "uBlock₀ — Pang-tingin ng Asset\n", "description": "Title for the asset viewer page" }, "advancedSettingsPageName": { @@ -104,7 +104,7 @@ "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { - "message": "Domains connected", + "message": "Mga konektado na domain", "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { @@ -172,11 +172,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { - "message": "Click to disable JavaScript on this site", + "message": "Pindutin para hindi paganahin ang Javascript sa site na ito", "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { - "message": "Click to no longer disable JavaScript on this site", + "message": "Pindutin upang paganahin na ang Javascript sa site na ito", "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { @@ -372,11 +372,11 @@ "description": "background information: https://github.com/gorhill/uBlock/issues/3150" }, "settingsLastRestorePrompt": { - "message": "Last restore:", + "message": "Huling pag-restore:", "description": "English: Last restore:" }, "settingsLastBackupPrompt": { - "message": "Last backup:", + "message": "Huling backup:", "description": "English: Last backup:" }, "3pListsOfBlockedHostsPrompt": { @@ -384,7 +384,7 @@ "description": "Appears at the top of the _3rd-party filters_ pane" }, "3pListsOfBlockedHostsPerListStats": { - "message": "{{used}} used out of {{total}}", + "message": "{{used}} na ang nagamit sa {{total}}", "description": "Appears aside each filter list in the _3rd-party filters_ pane" }, "3pAutoUpdatePrompt1": { @@ -392,7 +392,7 @@ "description": "A checkbox in the _3rd-party filters_ pane" }, "3pUpdateNow": { - "message": "Update now", + "message": "I-update ngayon", "description": "A button in the in the _3rd-party filters_ pane" }, "3pPurgeAll": { @@ -448,7 +448,7 @@ "description": "English: Multipurpose" }, "3pGroupRegions": { - "message": "Regions, languages", + "message": "Rehiyon, mga Lenggwahe", "description": "English: Regions, languages" }, "3pGroupCustom": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 9f0ab3dba4ade..463246c5e3cda 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -484,7 +484,7 @@ "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Ett filter per rad. Ett filter kan vara ett vanligt värdnamn eller ett Adblock Plus-kompatibelt filter. Rader med prefixet ! kommer att ignoreras.", + "message": "Ett filter per rad. Ett filter kan vara ett vanligt värdnamn eller ett EasyList-kompatibelt filter. Rader med prefixet ! kommer att ignoreras.", "description": "Short information about how to create custom filters" }, "1pImport": { From 90743532f18d115671134b4eafb3a2e39c96a3b7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Sep 2020 09:52:44 -0400 Subject: [PATCH 3548/4093] Add missing colon --- src/_locales/en/messages.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index ba9b006fbb9cf..e8f9bd5034d23 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -945,11 +945,11 @@ }, "docblockedPrompt1": { "message": "uBlock Origin has prevented the following page from loading:", - "description": "English: uBlock₀ has prevented the following page from loading:" + "description": "Used in the strict-blocking page" }, "docblockedPrompt2": { - "message": "Because of the following filter", - "description": "English: Because of the following filter" + "message": "Because of the following filter:", + "description": "Used in the strict-blocking page" }, "docblockedNoParamsPrompt": { "message": "without parameters", From 9ab631b4c4c002a10c910b53d7fbf2579461bced Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Sep 2020 09:53:35 -0400 Subject: [PATCH 3549/4093] Ensure port instance exists when waiting for connection requests --- platform/chromium/vapi-client-extra.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/chromium/vapi-client-extra.js b/platform/chromium/vapi-client-extra.js index f18280e041e54..383678f84b1f1 100644 --- a/platform/chromium/vapi-client-extra.js +++ b/platform/chromium/vapi-client-extra.js @@ -151,8 +151,9 @@ vAPI.MessagingConnection = class { static addListener(listener) { listeners.add(listener); + vAPI.messaging.getPort(); // Ensure a port instance exists } - static async connectTo(from, to, handler) { + static connectTo(from, to, handler) { const port = vAPI.messaging.getPort(); if ( port === null ) { return; } const connection = new vAPI.MessagingConnection(handler, { From 43dba2bd0e93a141bf84afaf12d7476435b7dd6a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Sep 2020 09:57:38 -0400 Subject: [PATCH 3550/4093] StaticFilteringParser.analyzeExtra() has no argument --- src/js/codemirror/ubo-static-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 4a7e9d026a6bb..24d257d60eb74 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -277,7 +277,7 @@ CodeMirror.defineMode('ubo-static-filtering', function() { token: function(stream) { if ( stream.sol() ) { parser.analyze(stream.string); - parser.analyzeExtra(stream.string); + parser.analyzeExtra(); parserSlot = 0; netOptionValueMode = false; } From 9eb455ab5eb2d5640f4188e71011b7aa22ab8b43 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Sep 2020 12:32:12 -0400 Subject: [PATCH 3551/4093] Isolate element picker dialog from page content world Related issues: - https://github.com/gorhill/uBlock/issues/3497 - https://github.com/uBlockOrigin/uBlock-issues/issues/1215 To solve above issues, the element picker's dialog is now isolated from the page content in which it is embedded. The highly interactive, mouse-driven part of the element picker is still visible by the page content. --- platform/chromium/vapi-client.js | 8 +- src/css/epicker-dialog.css | 184 +++++ src/css/epicker.css | 60 ++ src/epicker.html | 250 ------ src/js/epicker-dialog.js | 499 ++++++++++++ src/js/messaging.js | 36 +- .../{element-picker.js => epicker.js} | 771 ++++++------------ src/js/ublock.js | 2 +- .../epicker-dialog.html | 48 ++ 9 files changed, 1041 insertions(+), 817 deletions(-) create mode 100644 src/css/epicker-dialog.css create mode 100644 src/css/epicker.css delete mode 100644 src/epicker.html create mode 100644 src/js/epicker-dialog.js rename src/js/scriptlets/{element-picker.js => epicker.js} (69%) create mode 100644 src/web_accessible_resources/epicker-dialog.html diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index c8a934dec85ae..491e503ab1b3c 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -102,8 +102,14 @@ vAPI.messaging = { }, disconnectListenerBound: null, + // 2020-09-01: + // In Firefox, `details instanceof Object` resolves to `false` despite + // `details` being a valid object. Consequently, falling back to use + // `typeof details`. + // This is an issue which surfaced when the element picker code was + // revisited to isolate the picker dialog DOM from the page DOM. messageListener: function(details) { - if ( details instanceof Object === false ) { return; } + if ( typeof details !== 'object' || details === null ) { return; } // Response to specific message previously sent if ( details.msgId !== undefined ) { diff --git a/src/css/epicker-dialog.css b/src/css/epicker-dialog.css new file mode 100644 index 0000000000000..5b0c0e44fd8ad --- /dev/null +++ b/src/css/epicker-dialog.css @@ -0,0 +1,184 @@ +html#ublock0-epicker, +#ublock0-epicker body { + background: transparent; + color: black; + cursor: not-allowed; + font: 12px sans-serif; + height: 100vh; + margin: 0; + overflow: hidden; + width: 100vw; +} +#ublock0-epicker :focus { + outline: none; +} +#ublock0-epicker ul, +#ublock0-epicker li, +#ublock0-epicker div { + display: block; +} +#ublock0-epicker #toolbar { + cursor: grab; + display: flex; + justify-content: space-between; +} +#ublock0-epicker aside.moving #toolbar { + cursor: grabbing; +} +#ublock0-epicker ul { + margin: 0.25em 0 0 0; +} +#ublock0-epicker button { + background-color: #ccc; + border: 1px solid #aaa; + border-radius: 3px; + box-sizing: border-box; + box-shadow: none; + color: #000; + cursor: pointer; + opacity: 0.7; + padding: 4px 6px; +} +#ublock0-epicker button:disabled { + color: #999; + background-color: #ccc; +} +#ublock0-epicker button:not(:disabled):hover { + opacity: 1; +} +#ublock0-epicker #create:not(:disabled) { + background-color: hsl(36, 100%, 83%); + border-color: hsl(36, 50%, 60%); +} +#ublock0-epicker #preview { + float: left; +} +#ublock0-epicker body.preview #preview { + background-color: hsl(204, 100%, 83%); + border-color: hsl(204, 50%, 60%); +} +#ublock0-epicker section { + border: 0; + box-sizing: border-box; + display: inline-block; + width: 100%; +} +#ublock0-epicker section > div:first-child { + border: 1px solid #aaa; + margin: 0; + position: relative; +} +#ublock0-epicker section.invalidFilter > div:first-child { + border-color: red; +} +#ublock0-epicker section > div:first-child > textarea { + background-color: #fff; + border: none; + box-sizing: border-box; + color: #000; + font: 11px monospace; + height: 8em; + margin: 0; + overflow: hidden; + overflow-y: auto; + padding: 2px; + resize: none; + width: 100%; + word-break: break-all; +} +#ublock0-epicker #resultsetCount { + background-color: #aaa; + bottom: 0; + color: white; + padding: 2px 4px; + position: absolute; + right: 0; +} +#ublock0-epicker section.invalidFilter #resultsetCount { + background-color: red; +} +#ublock0-epicker section > div:first-child + div { + direction: ltr; + margin: 2px 0; + text-align: right; +} +#ublock0-epicker ul { + padding: 0; + list-style-type: none; + text-align: left; + overflow: hidden; +} +#ublock0-epicker #candidateFilters { + max-height: 16em; + overflow-y: auto; +} +#ublock0-epicker #candidateFilters > li:first-of-type { + margin-bottom: 0.5em; +} +#ublock0-epicker .changeFilter > li > span:nth-of-type(1) { + font-weight: bold; +} +#ublock0-epicker .changeFilter > li > span:nth-of-type(2) { + font-size: smaller; + color: gray; +} +#ublock0-epicker #candidateFilters .changeFilter { + list-style-type: none; + margin: 0 0 0 1em; + overflow: hidden; + text-align: left; +} +#ublock0-epicker #candidateFilters .changeFilter li { + border: 1px solid transparent; + cursor: pointer; + direction: ltr; + font: 11px monospace; + white-space: nowrap; +} +#ublock0-epicker #candidateFilters .changeFilter li.active { + border: 1px dotted gray; + } +#ublock0-epicker #candidateFilters .changeFilter li:hover { + background-color: white; +} +#ublock0-epicker aside { + background-color: #eee; + border: 1px solid #aaa; + bottom: 4px; + box-sizing: border-box; + cursor: default; + min-width: 24em; + padding: 4px; + position: fixed; + right: 4px; + width: calc(40% - 4px); +} +/** + https://github.com/gorhill/uBlock/issues/3449 + https://github.com/uBlockOrigin/uBlock-issues/issues/55 +**/ +@keyframes startDialog { + 0% { opacity: 1.0; } + 60% { opacity: 1.0; } + 100% { opacity: 0.1; } +} +#ublock0-epicker body.paused > aside { + opacity: 0.1; + visibility: visible; + z-index: 100; +} +#ublock0-epicker body.paused > aside:not(:hover):not(.show) { + animation-duration: 1.6s; + animation-name: startDialog; + animation-timing-function: linear; +} +#ublock0-epicker body.paused > aside:hover { + opacity: 1; +} +#ublock0-epicker body.paused > aside.show { + opacity: 1; +} +#ublock0-epicker body.paused > aside.hide { + opacity: 0.1; +} + diff --git a/src/css/epicker.css b/src/css/epicker.css new file mode 100644 index 0000000000000..1010fb24eb98e --- /dev/null +++ b/src/css/epicker.css @@ -0,0 +1,60 @@ +html#ublock0-epicker, +#ublock0-epicker body { + background: transparent !important; + box-sizing: border-box !important; + color: black !important; + font: 12px sans-serif !important; + height: 100vh !important; + margin: 0 !important; + overflow: hidden !important; + position: fixed !important; + width: 100vw !important; +} +#ublock0-epicker :focus { + outline: none !important; +} +#ublock0-epicker svg { + cursor: crosshair !important; + box-sizing: border-box; + height: 100% !important; + left: 0 !important; + position: absolute !important; + top: 0 !important; + width: 100% !important; +} +#ublock0-epicker .paused > svg { + cursor: not-allowed !important; +} +#ublock0-epicker svg > path:first-child { + fill: rgba(0,0,0,0.5) !important; + fill-rule: evenodd !important; +} +#ublock0-epicker svg > path + path { + stroke: #F00 !important; + stroke-width: 0.5px !important; + fill: rgba(255,63,63,0.20) !important; +} +#ublock0-epicker body.zap svg > path + path { + stroke: #FF0 !important; + stroke-width: 0.5px !important; + fill: rgba(255,255,63,0.20) !important; +} +#ublock0-epicker body.preview svg > path { + fill: rgba(0,0,0,0.10) !important; +} +#ublock0-epicker body.preview svg > path + path { + stroke: none !important; +} +#ublock0-epicker body > iframe { + border: 0 !important; + box-sizing: border-box !important; + display: none !important; + height: 100% !important; + left: 0 !important; + position: absolute !important; + top: 0 !important; + width: 100% !important; +} +#ublock0-epicker body.paused > iframe { + display: initial !important; +} diff --git a/src/epicker.html b/src/epicker.html deleted file mode 100644 index 795526e37b794..0000000000000 --- a/src/epicker.html +++ /dev/null @@ -1,250 +0,0 @@ - - -uBlock Origin Element Picker - - - - - - - diff --git a/src/js/epicker-dialog.js b/src/js/epicker-dialog.js new file mode 100644 index 0000000000000..cea9d43970092 --- /dev/null +++ b/src/js/epicker-dialog.js @@ -0,0 +1,499 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2014-present 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'; + +/******************************************************************************/ +/******************************************************************************/ + +(( ) => { + +/******************************************************************************/ + +if ( typeof vAPI !== 'object' ) { return; } + +const epickerId = (( ) => { + const url = new URL(self.location.href); + return url.searchParams.get('epid'); +})(); +if ( epickerId === null ) { return; } + +let epickerConnectionId; +let filterHostname = ''; +let filterOrigin = ''; +let filterResultset = []; + +/******************************************************************************/ + +const $id = id => document.getElementById(id); +const $stor = selector => document.querySelector(selector); +const $storAll = selector => document.querySelectorAll(selector); + +/******************************************************************************/ + +const filterFromTextarea = function() { + const s = taCandidate.value.trim(); + if ( s === '' ) { return ''; } + const pos = s.indexOf('\n'); + const filter = pos === -1 ? s.trim() : s.slice(0, pos).trim(); + staticFilteringParser.analyze(filter); + staticFilteringParser.analyzeExtra(); + return staticFilteringParser.shouldDiscard() ? '!' : filter; +}; + +/******************************************************************************/ + +const userFilterFromCandidate = function(filter) { + if ( filter === '' || filter === '!' ) { return; } + + // Cosmetic filter? + if ( filter.startsWith('##') ) { + return filterHostname + filter; + } + + // Assume net filter + const opts = []; + + // If no domain included in filter, we need domain option + if ( filter.startsWith('||') === false ) { + opts.push(`domain=${filterHostname}`); + } + + if ( filterResultset.length !== 0 ) { + const item = filterResultset[0]; + if ( item.opts ) { + opts.push(item.opts); + } + } + + if ( opts.length ) { + filter += '$' + opts.join(','); + } + + return filter; +}; + +/******************************************************************************/ + +const candidateFromFilterChoice = function(filterChoice) { + let { slot, filters } = filterChoice; + let filter = filters[slot]; + + // https://github.com/uBlockOrigin/uBlock-issues/issues/47 + for ( const elem of $storAll('#candidateFilters li') ) { + elem.classList.remove('active'); + } + + if ( filter === undefined ) { return ''; } + + // For net filters there no such thing as a path + if ( filter.startsWith('##') === false ) { + $stor(`#netFilters li:nth-of-type(${slot+1})`) + .classList.add('active'); + return filter; + } + + // At this point, we have a cosmetic filter + + $stor(`#cosmeticFilters li:nth-of-type(${slot+1})`) + .classList.add('active'); + + // Modifier means "target broadly". Hence: + // - Do not compute exact path. + // - Discard narrowing directives. + // - Remove the id if one or more classes exist + // TODO: should remove tag name too? ¯\_(ツ)_/¯ + if ( filterChoice.modifier ) { + filter = filter.replace(/:nth-of-type\(\d+\)/, ''); + // https://github.com/uBlockOrigin/uBlock-issues/issues/162 + // Mind escaped periods: they do not denote a class identifier. + if ( filter.charAt(2) === '#' ) { + const pos = filter.search(/[^\\]\./); + if ( pos !== -1 ) { + filter = '##' + filter.slice(pos + 1); + } + } + return filter; + } + + // Return path: the target element, then all siblings prepended + let selector = '', joiner = ''; + for ( ; slot < filters.length; slot++ ) { + filter = filters[slot]; + // Remove all classes when an id exists. + // https://github.com/uBlockOrigin/uBlock-issues/issues/162 + // Mind escaped periods: they do not denote a class identifier. + if ( filter.charAt(2) === '#' ) { + filter = filter.replace(/([^\\])\..+$/, '$1'); + } + selector = filter.slice(2) + joiner + selector; + // Stop at any element with an id: these are unique in a web page + if ( filter.startsWith('###') ) { break; } + // Stop if current selector matches only one element on the page + if ( document.querySelectorAll(selector).length === 1 ) { break; } + joiner = ' > '; + } + + // https://github.com/gorhill/uBlock/issues/2519 + // https://github.com/uBlockOrigin/uBlock-issues/issues/17 + if ( + slot === filters.length && + selector.startsWith('body > ') === false && + document.querySelectorAll(selector).length > 1 + ) { + selector = 'body > ' + selector; + } + + return '##' + selector; +}; + +/******************************************************************************/ + +const onCandidateChanged = function() { + const filter = filterFromTextarea(); + const bad = filter === '!'; + $stor('section').classList.toggle('invalidFilter', bad); + $id('create').disabled = bad; + if ( bad ) { + $id('resultsetCount').textContent = 'E'; + $id('create').setAttribute('disabled', ''); + } + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'dialogSetFilter', + filter, + compiled: filter.startsWith('##') + ? staticFilteringParser.result.compiled + : undefined, + }); +}; + +/******************************************************************************/ + +const onPreviewClicked = function() { + const state = pickerBody.classList.toggle('preview'); + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'dialogPreview', + state, + }); +}; + +/******************************************************************************/ + +const onCreateClicked = function() { + const candidate = filterFromTextarea(); + const filter = userFilterFromCandidate(candidate); + if ( filter !== undefined ) { + vAPI.messaging.send('elementPicker', { + what: 'createUserFilter', + autoComment: true, + filters: filter, + origin: filterOrigin, + pageDomain: filterHostname, + killCache: /^#[$?]?#/.test(candidate) === false, + }); + } + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'dialogCreate', + filter: candidate, + compiled: candidate.startsWith('##') + ? staticFilteringParser.result.compiled + : undefined, + }); +}; + +/******************************************************************************/ + +const onPickClicked = function(ev) { + if ( + (ev instanceof MouseEvent) && + (ev.type === 'mousedown') && + (ev.which !== 1 || ev.target !== document.body) + ) { + return; + } + pickerBody.classList.remove('paused'); + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'dialogPick' + }); +}; + +/******************************************************************************/ + +const onQuitClicked = function() { + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'dialogQuit' + }); +}; + +/******************************************************************************/ + +const onCandidateClicked = function(ev) { + let li = ev.target.closest('li'); + const ul = li.closest('.changeFilter'); + if ( ul === null ) { return; } + const choice = { + filters: Array.from(ul.querySelectorAll('li')).map(a => a.textContent), + slot: 0, + modifier: ev.ctrlKey || ev.metaKey + }; + while ( li.previousElementSibling !== null ) { + li = li.previousElementSibling; + choice.slot += 1; + } + taCandidate.value = candidateFromFilterChoice(choice); + onCandidateChanged(); +}; + +/******************************************************************************/ + +const onKeyPressed = function(ev) { + // Esc + if ( ev.key === 'Escape' || ev.which === 27 ) { + onQuitClicked(); + return; + } +}; + +/******************************************************************************/ + +const onStartMoving = (( ) => { + let mx0 = 0, my0 = 0; + let mx1 = 0, my1 = 0; + let r0 = 0, b0 = 0; + let rMax = 0, bMax = 0; + let timer; + + const move = ( ) => { + timer = undefined; + let r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax); + let b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax); + dialog.style.setProperty('right', `${r1}px`, 'important'); + dialog.style.setProperty('bottom', `${b1}px`, 'important'); + }; + + const moveAsync = ev => { + if ( ev.isTrusted === false ) { return; } + eatEvent(ev); + if ( timer !== undefined ) { return; } + mx1 = ev.pageX; + my1 = ev.pageY; + timer = self.requestAnimationFrame(move); + }; + + const stop = ev => { + if ( ev.isTrusted === false ) { return; } + if ( dialog.classList.contains('moving') === false ) { return; } + dialog.classList.remove('moving'); + self.removeEventListener('mousemove', moveAsync, { capture: true }); + self.removeEventListener('mouseup', stop, { capture: true, once: true }); + eatEvent(ev); + }; + + return function(ev) { + if ( ev.isTrusted === false ) { return; } + const target = dialog.querySelector('#toolbar'); + if ( ev.target !== target ) { return; } + if ( dialog.classList.contains('moving') ) { return; } + mx0 = ev.pageX; my0 = ev.pageY; + const style = self.getComputedStyle(dialog); + r0 = parseInt(style.right, 10); + b0 = parseInt(style.bottom, 10); + const rect = dialog.getBoundingClientRect(); + rMax = pickerBody.clientWidth - 4 - rect.width ; + bMax = pickerBody.clientHeight - 4 - rect.height; + dialog.classList.add('moving'); + self.addEventListener('mousemove', moveAsync, { capture: true }); + self.addEventListener('mouseup', stop, { capture: true, once: true }); + eatEvent(ev); + }; +})(); + +/******************************************************************************/ + +const eatEvent = function(ev) { + ev.stopPropagation(); + ev.preventDefault(); +}; + +/******************************************************************************/ + +const showDialog = function(details) { + pickerBody.classList.add('paused'); + + const { netFilters, cosmeticFilters, filter, options } = details; + + // https://github.com/gorhill/uBlock/issues/738 + // Trim dots. + filterHostname = details.hostname; + if ( filterHostname.slice(-1) === '.' ) { + filterHostname = filterHostname.slice(0, -1); + } + filterOrigin = details.origin; + + // Create lists of candidate filters + const populate = function(src, des) { + const root = dialog.querySelector(des); + const ul = root.querySelector('ul'); + while ( ul.firstChild !== null ) { + ul.firstChild.remove(); + } + for ( let i = 0; i < src.length; i++ ) { + const li = document.createElement('li'); + li.textContent = src[i]; + ul.appendChild(li); + } + if ( src.length !== 0 ) { + root.style.removeProperty('display'); + } else { + root.style.setProperty('display', 'none', 'important'); + } + }; + + populate(netFilters, '#netFilters'); + populate(cosmeticFilters, '#cosmeticFilters'); + + dialog.querySelector('ul').style.display = + netFilters.length || cosmeticFilters.length ? '' : 'none'; + dialog.querySelector('#create').disabled = true; + + // Auto-select a candidate filter + + // 2020-09-01: + // In Firefox, `details instanceof Object` resolves to `false` despite + // `details` being a valid object. Consequently, falling back to use + // `typeof details`. + // This is an issue which surfaced when the element picker code was + // revisited to isolate the picker dialog DOM from the page DOM. + if ( typeof filter !== 'object' || filter === null ) { + taCandidate.value = ''; + return; + } + + const filterChoice = { + filters: filter.filters, + slot: filter.slot, + modifier: options.modifier || false + }; + + taCandidate.value = candidateFromFilterChoice(filterChoice); + onCandidateChanged(); +}; + +/******************************************************************************/ + +// Let's have the element picker code flushed from memory when no longer +// in use: to ensure this, release all local references. + +const stopPicker = function() { + vAPI.shutdown.remove(stopPicker); +}; + +/******************************************************************************/ + +const pickerBody = document.body; +const dialog = $stor('aside'); +const taCandidate = $stor('textarea'); +let staticFilteringParser; + +/******************************************************************************/ + +const startDialog = function() { + dialog.addEventListener('click', eatEvent); + taCandidate.addEventListener('input', onCandidateChanged); + $stor('body').addEventListener('mousedown', onPickClicked); + $id('preview').addEventListener('click', onPreviewClicked); + $id('create').addEventListener('click', onCreateClicked); + $id('pick').addEventListener('click', onPickClicked); + $id('quit').addEventListener('click', onQuitClicked); + $id('candidateFilters').addEventListener('click', onCandidateClicked); + $id('toolbar').addEventListener('mousedown', onStartMoving); + self.addEventListener('keydown', onKeyPressed, true); + staticFilteringParser = new vAPI.StaticFilteringParser({ interactive: true }); +}; + +/******************************************************************************/ + +const onPickerMessage = function(msg) { + switch ( msg.what ) { + case 'showDialog': + showDialog(msg); + break; + case 'filterResultset': + filterResultset = msg.resultset; + $id('resultsetCount').textContent = filterResultset.length; + if ( filterResultset.length !== 0 ) { + $id('create').removeAttribute('disabled'); + } else { + $id('create').setAttribute('disabled', ''); + } + break; + } +}; + +/******************************************************************************/ + +const onConnectionMessage = function(msg) { + switch ( msg.what ) { + case 'connectionBroken': + stopPicker(); + break; + case 'connectionMessage': + onPickerMessage(msg.payload); + break; + case 'connectionAccepted': + epickerConnectionId = msg.id; + startDialog(); + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'dialogInit', + }); + break; + } +}; + +vAPI.MessagingConnection.connectTo( + `epickerDialog-${epickerId}`, + `epicker-${epickerId}`, + onConnectionMessage +); + +/******************************************************************************/ + +})(); + + + + + + + + +/******************************************************************************* + + DO NOT: + - Remove the following code + - Add code beyond the following code + Reason: + - https://github.com/gorhill/uBlock/pull/3721 + - uBO never uses the return value from injected content scripts + +**/ + +void 0; diff --git a/src/js/messaging.js b/src/js/messaging.js index af97adfa2d7a3..38f38fda5df10 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -713,34 +713,19 @@ const onMessage = function(request, sender, callback) { switch ( request.what ) { case 'elementPickerArguments': const xhr = new XMLHttpRequest(); - xhr.open('GET', 'epicker.html', true); + xhr.open('GET', 'css/epicker.css', true); xhr.overrideMimeType('text/html;charset=utf-8'); xhr.responseType = 'text'; xhr.onload = function() { this.onload = null; - const i18n = { - bidi_dir: document.body.getAttribute('dir'), - create: vAPI.i18n('pickerCreate'), - pick: vAPI.i18n('pickerPick'), - quit: vAPI.i18n('pickerQuit'), - preview: vAPI.i18n('pickerPreview'), - netFilters: vAPI.i18n('pickerNetFilters'), - cosmeticFilters: vAPI.i18n('pickerCosmeticFilters'), - cosmeticFiltersHint: vAPI.i18n('pickerCosmeticFiltersHint') - }; - const reStrings = /\{\{(\w+)\}\}/g; - const replacer = function(a0, string) { - return i18n[string]; - }; - callback({ - frameContent: this.responseText.replace(reStrings, replacer), + frameCSS: this.responseText, target: µb.epickerArgs.target, mouse: µb.epickerArgs.mouse, zap: µb.epickerArgs.zap, eprom: µb.epickerArgs.eprom, + dialogURL: vAPI.getURL(`/web_accessible_resources/epicker-dialog.html${vAPI.warSecret()}`), }); - µb.epickerArgs.target = ''; }; xhr.send(); @@ -754,21 +739,6 @@ const onMessage = function(request, sender, callback) { let response; switch ( request.what ) { - case 'compileCosmeticFilterSelector': { - const parser = new vAPI.StaticFilteringParser(); - parser.analyze(request.selector); - if ( (parser.flavorBits & parser.BITFlavorExtCosmetic) !== 0 ) { - response = parser.result.compiled; - } - break; - } - - // https://github.com/gorhill/uBlock/issues/3497 - // This needs to be removed once issue is fixed. - case 'createUserFilter': - µb.createUserFilters(request); - break; - case 'elementPickerEprom': µb.epickerArgs.eprom = request; break; diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/epicker.js similarity index 69% rename from src/js/scriptlets/element-picker.js rename to src/js/scriptlets/epicker.js index 182a215a9df61..0d420b5d4117a 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/epicker.js @@ -26,27 +26,28 @@ /******************************************************************************/ /******************************************************************************/ -(( ) => { +(async ( ) => { /******************************************************************************/ -if ( - window.top !== window || - typeof vAPI !== 'object' || - vAPI.domFilterer instanceof Object === false -) { - return; -} +if ( window.top !== window || typeof vAPI !== 'object' ) { return; } + +/******************************************************************************/ -let pickerRoot = document.getElementById(vAPI.sessionId); -if ( pickerRoot ) { return; } +const epickerId = vAPI.randomToken(); +let epickerConnectionId; +/******************************************************************************/ + +let pickerRoot = document.querySelector(`[${vAPI.sessionId}]`); +if ( pickerRoot !== null ) { return; } + +let pickerBootArgs; let pickerBody = null; let svgOcean = null; let svgIslands = null; let svgRoot = null; let dialog = null; -let taCandidate = null; const netFilterCandidates = []; const cosmeticFilterCandidates = []; @@ -61,18 +62,6 @@ let lastNetFilterUnion = ''; /******************************************************************************/ -// For browsers not supporting `:scope`, it's not the end of the world: the -// suggested CSS selectors may just end up being more verbose. - -let cssScope = ':scope > '; -try { - document.querySelector(':scope *'); -} catch (e) { - cssScope = ''; -} - -/******************************************************************************/ - const safeQuerySelectorAll = function(node, selector) { if ( node !== null ) { try { @@ -85,14 +74,6 @@ const safeQuerySelectorAll = function(node, selector) { /******************************************************************************/ -const rawFilterFromTextarea = function() { - const s = taCandidate.value; - const pos = s.indexOf('\n'); - return pos === -1 ? s.trim() : s.slice(0, pos).trim(); -}; - -/******************************************************************************/ - const getElementBoundingClientRect = function(elem) { let rect = typeof elem.getBoundingClientRect === 'function' ? elem.getBoundingClientRect() @@ -152,9 +133,7 @@ const highlightElements = function(elems, force) { for ( let i = 0; i < elems.length; i++ ) { const elem = elems[i]; - if ( elem === pickerRoot ) { - continue; - } + if ( elem === pickerRoot ) { continue; } const rect = getElementBoundingClientRect(elem); // Ignore if it's not on the screen @@ -480,11 +459,11 @@ const cosmeticFilterFromElement = function(elem) { if ( attr.v.length === 0 ) { continue; } v = elem.getAttribute(attr.k); if ( attr.v === v ) { - selector += '[' + attr.k + '="' + attr.v + '"]'; + selector += `[${attr.k}="${attr.v}"]`; } else if ( v.startsWith(attr.v) ) { - selector += '[' + attr.k + '^="' + attr.v + '"]'; + selector += `[${attr.k}^="${attr.v}"]`; } else { - selector += '[' + attr.k + '*="' + attr.v + '"]'; + selector += `[${attr.k}*="${attr.v}"]`; } } } @@ -495,7 +474,7 @@ const cosmeticFilterFromElement = function(elem) { const parentNode = elem.parentNode; if ( selector === '' || - safeQuerySelectorAll(parentNode, cssScope + selector).length > 1 + safeQuerySelectorAll(parentNode, `:scope > ${selector}`).length > 1 ) { selector = tagName + selector; } @@ -504,7 +483,7 @@ const cosmeticFilterFromElement = function(elem) { // If the selector is still ambiguous at this point, further narrow using // `nth-of-type`. It is preferable to use `nth-of-type` as opposed to // `nth-child`, as `nth-of-type` is less volatile. - if ( safeQuerySelectorAll(parentNode, cssScope + selector).length > 1 ) { + if ( safeQuerySelectorAll(parentNode, `:scope > ${selector}`).length > 1 ) { let i = 1; while ( elem.previousSibling !== null ) { elem = elem.previousSibling; @@ -515,7 +494,7 @@ const cosmeticFilterFromElement = function(elem) { i++; } } - selector += ':nth-of-type(' + i + ')'; + selector += `:nth-of-type(${i})`; } if ( bestCandidateFilter === null ) { @@ -526,7 +505,7 @@ const cosmeticFilterFromElement = function(elem) { }; } - cosmeticFilterCandidates.push('##' + selector); + cosmeticFilterCandidates.push(`##${selector}`); return 1; }; @@ -575,7 +554,7 @@ const filtersFrom = function(x, y) { // https://github.com/gorhill/uBlock/issues/1545 // Network filter candidates from all other elements found at point (x, y). if ( typeof x === 'number' ) { - let attrName = pickerRoot.id + '-clickblind'; + let attrName = vAPI.sessionId + '-clickblind'; let previous; elem = first; while ( elem !== null ) { @@ -587,7 +566,7 @@ const filtersFrom = function(x, y) { } netFilterFromElement(elem); } - let elems = document.querySelectorAll('[' + attrName + ']'); + let elems = document.querySelectorAll(`[${attrName}]`); i = elems.length; while ( i-- ) { elems[i].removeAttribute(attrName); @@ -601,7 +580,7 @@ const filtersFrom = function(x, y) { /******************************************************************************* - filterToDOMInterface.set + filterToDOMInterface.queryAll @desc Look-up all the HTML elements matching the filter passed in argument. @param string, a cosmetic or network filter. @@ -767,24 +746,21 @@ const filterToDOMInterface = (( ) => { return out; }; - let lastFilter, - lastResultset, - lastAction, - appliedStyleTag, - applied = false, - previewing = false; + let lastFilter; + let lastResultset; + let lastAction; + let appliedStyleTag; + let applied = false; + let previewing = false; - const queryAll = async function(filter, callback) { + const queryAll = function(details) { + let { filter, compiled } = details; filter = filter.trim(); - if ( filter === lastFilter ) { - callback(lastResultset); - return; - } + if ( filter === lastFilter ) { return lastResultset; } unapply(); - if ( filter === '' ) { + if ( filter === '' || filter === '!' ) { lastFilter = ''; lastResultset = []; - callback(lastResultset); return; } lastFilter = filter; @@ -792,23 +768,17 @@ const filterToDOMInterface = (( ) => { if ( filter.startsWith('##') === false ) { lastResultset = fromNetworkFilter(filter); if ( previewing ) { apply(); } - callback(lastResultset); - return; + return lastResultset; } - lastResultset = fromPlainCosmeticFilter(filter.slice(2)); + lastResultset = fromPlainCosmeticFilter(compiled); if ( lastResultset ) { if ( previewing ) { apply(); } - callback(lastResultset); - return; + return lastResultset; } // Procedural cosmetic filter - const response = await vAPI.messaging.send('elementPicker', { - what: 'compileCosmeticFilterSelector', - selector: filter, - }); - lastResultset = fromCompiledCosmeticFilter(response); + lastResultset = fromCompiledCosmeticFilter(compiled); if ( previewing ) { apply(); } - callback(lastResultset); + return lastResultset; }; // https://github.com/gorhill/uBlock/issues/1629 @@ -896,278 +866,48 @@ const filterToDOMInterface = (( ) => { // https://www.reddit.com/r/uBlockOrigin/comments/c62irc/ // Support injecting the cosmetic filters into the DOM filterer // immediately rather than wait for the next page load. - const preview = function(rawFilter, permanent = false) { - previewing = rawFilter !== false; + const preview = function(state, permanent = false) { + previewing = state !== false; pickerBody.classList.toggle('preview', previewing); if ( previewing === false ) { return unapply(); } - queryAll(rawFilter, items => { - if ( items === undefined ) { return; } - apply(); - if ( permanent === false ) { return; } - if ( vAPI.domFilterer instanceof Object === false ) { return; } - const cssSelectors = new Set(); - const proceduralSelectors = new Set(); - for ( const item of items ) { - if ( item.type !== 'cosmetic' ) { continue; } - if ( item.raw.startsWith('{') ) { - proceduralSelectors.add(item.raw); - } else { - cssSelectors.add(item.raw); - } - } - if ( cssSelectors.size !== 0 ) { - vAPI.domFilterer.addCSSRule( - Array.from(cssSelectors), - 'display:none!important;' - ); - } - if ( proceduralSelectors.size !== 0 ) { - vAPI.domFilterer.addProceduralSelectors( - Array.from(proceduralSelectors) - ); + if ( lastResultset === undefined ) { return; } + apply(); + if ( permanent === false ) { return; } + if ( vAPI.domFilterer instanceof Object === false ) { return; } + const cssSelectors = new Set(); + const proceduralSelectors = new Set(); + for ( const item of lastResultset ) { + if ( item.type !== 'cosmetic' ) { continue; } + if ( item.raw.startsWith('{') ) { + proceduralSelectors.add(item.raw); + } else { + cssSelectors.add(item.raw); } - }); - }; - - return { - previewing: function() { return previewing; }, - preview: preview, - set: queryAll - }; -})(); - -/******************************************************************************/ - -const userFilterFromCandidate = function(callback) { - let v = rawFilterFromTextarea(); - filterToDOMInterface.set(v, items => { - if ( !items || items.length === 0 ) { - callback(); - return; - } - - // https://github.com/gorhill/uBlock/issues/738 - // Trim dots. - let hostname = window.location.hostname; - if ( hostname.slice(-1) === '.' ) { - hostname = hostname.slice(0, -1); - } - - // Cosmetic filter? - if ( v.startsWith('##') ) { - callback(hostname + v, true); - return; - } - - // Assume net filter - const opts = []; - - // If no domain included in filter, we need domain option - if ( v.startsWith('||') === false ) { - opts.push(`domain=${hostname}`); } - - const item = items[0]; - if ( item.opts ) { - opts.push(item.opts); - } - - if ( opts.length ) { - v += '$' + opts.join(','); + if ( cssSelectors.size !== 0 ) { + vAPI.domFilterer.addCSSRule( + Array.from(cssSelectors), + 'display:none!important;' + ); } - - callback(v); - }); -}; - -/******************************************************************************/ - -const onCandidateChanged = (function() { - const process = function(items) { - const elems = []; - const valid = items !== undefined; - if ( valid ) { - for ( const item of items ) { - elems.push(item.elem); - } + if ( proceduralSelectors.size !== 0 ) { + vAPI.domFilterer.addProceduralSelectors( + Array.from(proceduralSelectors) + ); } - pickerBody.querySelector('#resultsetCount').textContent = valid ? - items.length.toLocaleString() : - 'E'; - dialog.querySelector('section').classList.toggle('invalidFilter', !valid); - dialog.querySelector('#create').disabled = elems.length === 0; - highlightElements(elems, true); }; - return function() { - filterToDOMInterface.set(rawFilterFromTextarea(), process); + return { + get previewing() { return previewing; }, + preview, + queryAll, }; })(); /******************************************************************************/ -const candidateFromFilterChoice = function(filterChoice) { - let { slot, filters } = filterChoice; - let filter = filters[slot]; - - // https://github.com/uBlockOrigin/uBlock-issues/issues/47 - for ( const elem of dialog.querySelectorAll('#candidateFilters li') ) { - elem.classList.remove('active'); - } - - if ( filter === undefined ) { return ''; } - - // For net filters there no such thing as a path - if ( filter.startsWith('##') === false ) { - dialog.querySelector(`#netFilters li:nth-of-type(${slot+1})`) - .classList.add('active'); - return filter; - } - - // At this point, we have a cosmetic filter - - dialog.querySelector(`#cosmeticFilters li:nth-of-type(${slot+1})`) - .classList.add('active'); - - // Modifier means "target broadly". Hence: - // - Do not compute exact path. - // - Discard narrowing directives. - // - Remove the id if one or more classes exist - // TODO: should remove tag name too? ¯\_(ツ)_/¯ - if ( filterChoice.modifier ) { - filter = filter.replace(/:nth-of-type\(\d+\)/, ''); - // https://github.com/uBlockOrigin/uBlock-issues/issues/162 - // Mind escaped periods: they do not denote a class identifier. - if ( filter.charAt(2) === '#' ) { - let pos = filter.search(/[^\\]\./); - if ( pos !== -1 ) { - filter = '##' + filter.slice(pos + 1); - } - } - return filter; - } - - // Return path: the target element, then all siblings prepended - let selector = '', joiner = ''; - for ( ; slot < filters.length; slot++ ) { - filter = filters[slot]; - // Remove all classes when an id exists. - // https://github.com/uBlockOrigin/uBlock-issues/issues/162 - // Mind escaped periods: they do not denote a class identifier. - if ( filter.charAt(2) === '#' ) { - filter = filter.replace(/([^\\])\..+$/, '$1'); - } - selector = filter.slice(2) + joiner + selector; - // Stop at any element with an id: these are unique in a web page - if ( filter.startsWith('###') ) { break; } - // Stop if current selector matches only one element on the page - if ( document.querySelectorAll(selector).length === 1 ) { break; } - joiner = ' > '; - } - - // https://github.com/gorhill/uBlock/issues/2519 - // https://github.com/uBlockOrigin/uBlock-issues/issues/17 - if ( - slot === filters.length && - selector.startsWith('body > ') === false && - document.querySelectorAll(selector).length > 1 - ) { - selector = 'body > ' + selector; - } - - return '##' + selector; -}; - -/******************************************************************************/ - -const filterChoiceFromEvent = function(ev) { - let li = ev.target; - const isNetFilter = li.textContent.startsWith('##') === false; - const r = { - filters: isNetFilter ? netFilterCandidates : cosmeticFilterCandidates, - slot: 0, - modifier: ev.ctrlKey || ev.metaKey - }; - while ( li.previousElementSibling !== null ) { - li = li.previousElementSibling; - r.slot += 1; - } - return r; -}; - -/******************************************************************************/ - -const onDialogClicked = function(ev) { - if ( ev.isTrusted === false ) { return; } - - // If the dialog is hidden, clicking on it force it to become visible. - if ( dialog.classList.contains('hide') ) { - dialog.classList.add('show'); - dialog.classList.remove('hide'); - } - - else if ( ev.target === null ) { - /* do nothing */ - } - - else if ( ev.target.id === 'create' ) { - // We have to exit from preview mode: this guarantees matching elements - // will be found for the candidate filter. - filterToDOMInterface.preview(false); - userFilterFromCandidate((filter = undefined, isCosmetic = false) => { - if ( filter === undefined ) { return; } - vAPI.messaging.send('elementPicker', { - what: 'createUserFilter', - autoComment: true, - filters: filter, - origin: window.location.origin, - pageDomain: window.location.hostname, - killCache: isCosmetic === false, - }); - filterToDOMInterface.preview(rawFilterFromTextarea(), true); - stopPicker(); - }); - } - - else if ( ev.target.id === 'pick' ) { - unpausePicker(); - } - - else if ( ev.target.id === 'quit' ) { - filterToDOMInterface.preview(false); - stopPicker(); - } - - else if ( ev.target.id === 'preview' ) { - if ( filterToDOMInterface.previewing() ) { - filterToDOMInterface.preview(false); - } else { - filterToDOMInterface.preview(rawFilterFromTextarea()); - } - highlightElements(targetElements, true); - } - - else if ( ev.target.closest('.changeFilter') !== null ) { - taCandidate.value = candidateFromFilterChoice(filterChoiceFromEvent(ev)); - onCandidateChanged(); - } - - ev.stopPropagation(); - ev.preventDefault(); -}; - -/******************************************************************************/ - -const removeAllChildren = function(parent) { - while ( parent.firstChild ) { - parent.removeChild(parent.firstChild); - } -}; - -/******************************************************************************/ - const showDialog = function(options) { pausePicker(); @@ -1178,47 +918,15 @@ const showDialog = function(options) { dialog.classList.toggle('show', options.show === true); dialog.classList.remove('hide'); - // Create lists of candidate filters - const populate = function(src, des) { - const root = dialog.querySelector(des); - const ul = root.querySelector('ul'); - removeAllChildren(ul); - for ( let i = 0; i < src.length; i++ ) { - const li = document.createElement('li'); - li.textContent = src[i]; - ul.appendChild(li); - } - if ( src.length !== 0 ) { - root.style.removeProperty('display'); - } else { - root.style.setProperty('display', 'none', 'important'); - } - }; - - populate(netFilterCandidates, '#netFilters'); - populate(cosmeticFilterCandidates, '#cosmeticFilters'); - - dialog.querySelector('ul').style.display = - netFilterCandidates.length || cosmeticFilterCandidates.length - ? '' - : 'none'; - dialog.querySelector('#create').disabled = true; - - // Auto-select a candidate filter - - if ( bestCandidateFilter === null ) { - taCandidate.value = ''; - return; - } - - const filterChoice = { - filters: bestCandidateFilter.filters, - slot: bestCandidateFilter.slot, - modifier: options.modifier || false - }; - - taCandidate.value = candidateFromFilterChoice(filterChoice); - onCandidateChanged(); + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'showDialog', + hostname: self.location.hostname, + origin: self.location.origin, + netFilters: netFilterCandidates, + cosmeticFilters: cosmeticFilterCandidates, + filter: bestCandidateFilter, + options, + }); }; /******************************************************************************/ @@ -1284,7 +992,7 @@ const elementFromPoint = (( ) => { /******************************************************************************/ -const onSvgHovered = (function() { +const onSvgHovered = (( ) => { let timer; let mx = 0, my = 0; @@ -1315,7 +1023,7 @@ const onSvgHovered = (function() { */ -const onSvgTouchStartStop = (function() { +const onSvgTouchStartStop = (( ) => { var startX, startY; return function onTouch(ev) { @@ -1380,8 +1088,8 @@ const onSvgClicked = function(ev) { // If zap mode, highlight element under mouse, this makes the zapper usable // on touch screens. - if ( pickerBody.classList.contains('zap') ) { - var elem = targetElements.lenght !== 0 && targetElements[0]; + if ( pickerBootArgs.zap ) { + let elem = targetElements.lenght !== 0 && targetElements[0]; if ( !elem || ev.target !== svgIslands ) { elem = elementFromPoint(ev.clientX, ev.clientY); if ( elem !== null ) { @@ -1400,7 +1108,7 @@ const onSvgClicked = function(ev) { // - click outside dialog AND // - not in preview mode if ( pickerBody.classList.contains('paused') ) { - if ( filterToDOMInterface.previewing() === false ) { + if ( filterToDOMInterface.previewing === false ) { unpausePicker(); } return; @@ -1417,7 +1125,7 @@ const onSvgClicked = function(ev) { /******************************************************************************/ const svgListening = function(on) { - var action = (on ? 'add' : 'remove') + 'EventListener'; + const action = (on ? 'add' : 'remove') + 'EventListener'; svgRoot[action]('mousemove', onSvgHovered, { passive: true }); }; @@ -1427,7 +1135,7 @@ const onKeyPressed = function(ev) { // Delete if ( (ev.key === 'Delete' || ev.key === 'Backspace') && - pickerBody.classList.contains('zap') + pickerBootArgs.zap ) { ev.stopPropagation(); ev.preventDefault(); @@ -1447,8 +1155,8 @@ const onKeyPressed = function(ev) { /******************************************************************************/ // https://github.com/chrisaljoudi/uBlock/issues/190 -// May need to dynamically adjust the height of the overlay + new position -// of highlighted elements. +// May need to dynamically adjust the height of the overlay + new position +// of highlighted elements. const onScrolled = function() { highlightElements(targetElements, true); @@ -1456,65 +1164,6 @@ const onScrolled = function() { /******************************************************************************/ -const onStartMoving = (( ) => { - let mx0 = 0, my0 = 0; - let mx1 = 0, my1 = 0; - let r0 = 0, b0 = 0; - let rMax = 0, bMax = 0; - let timer; - - const move = ( ) => { - timer = undefined; - let r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax); - let b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax); - dialog.style.setProperty('right', `${r1}px`, 'important'); - dialog.style.setProperty('bottom', `${b1}px`, 'important'); - }; - - const moveAsync = ev => { - if ( ev.isTrusted === false ) { return; } - ev.preventDefault(); - ev.stopPropagation(); - if ( timer !== undefined ) { return; } - mx1 = ev.pageX; - my1 = ev.pageY; - timer = self.requestAnimationFrame(move); - }; - - const stop = ev => { - if ( ev.isTrusted === false ) { return; } - if ( dialog.classList.contains('moving') === false ) { return; } - dialog.classList.remove('moving'); - const pickerWin = pickerRoot.contentWindow; - pickerWin.removeEventListener('mousemove', moveAsync, { capture: true }); - pickerWin.removeEventListener('mouseup', stop, { capture: true, once: true }); - ev.preventDefault(); - ev.stopPropagation(); - }; - - return function(ev) { - if ( ev.isTrusted === false ) { return; } - const target = dialog.querySelector('#toolbar'); - if ( ev.target !== target ) { return; } - if ( dialog.classList.contains('moving') ) { return; } - mx0 = ev.pageX; my0 = ev.pageY; - const pickerWin = pickerRoot.contentWindow; - const style = pickerWin.getComputedStyle(dialog); - r0 = parseInt(style.right, 10); - b0 = parseInt(style.bottom, 10); - const rect = dialog.getBoundingClientRect(); - rMax = pickerBody.clientWidth - 4 - rect.width ; - bMax = pickerBody.clientHeight - 4 - rect.height; - dialog.classList.add('moving'); - pickerWin.addEventListener('mousemove', moveAsync, { capture: true }); - pickerWin.addEventListener('mouseup', stop, { capture: true, once: true }); - ev.preventDefault(); - ev.stopPropagation(); - }; -})(); - -/******************************************************************************/ - const pausePicker = function() { pickerBody.classList.add('paused'); svgListening(false); @@ -1544,91 +1193,36 @@ const stopPicker = function() { // https://github.com/gorhill/uBlock/issues/2060 if ( vAPI.domFilterer instanceof Object ) { - vAPI.userStylesheet.remove(pickerCSS1); - vAPI.userStylesheet.remove(pickerCSS2); + vAPI.userStylesheet.remove(pickerCSS); vAPI.userStylesheet.apply(); + vAPI.domFilterer.unexcludeNode(pickerRoot); } - vAPI.domFilterer.unexcludeNode(pickerRoot); window.removeEventListener('scroll', onScrolled, true); svgListening(false); - pickerRoot.parentNode.removeChild(pickerRoot); - pickerRoot = pickerBody = - svgRoot = svgOcean = svgIslands = - dialog = taCandidate = null; + pickerRoot.remove(); + pickerRoot = pickerBody = svgRoot = svgOcean = svgIslands = dialog = null; window.focus(); }; /******************************************************************************/ -const startPicker = function(details) { - pickerRoot.addEventListener('load', stopPicker); - - const frameDoc = pickerRoot.contentDocument; - const parsedDom = (new DOMParser()).parseFromString( - details.frameContent, - 'text/html' - ); - - // Provide an id users can use as anchor to personalize uBO's element - // picker style properties. - parsedDom.documentElement.id = 'ublock0-epicker'; - - // https://github.com/gorhill/uBlock/issues/2240 - // https://github.com/uBlockOrigin/uBlock-issues/issues/170 - // Remove the already declared inline style tag: we will create a new - // one based on the removed one, and replace the old one. - let style = parsedDom.querySelector('style'); - const styleText = style.textContent; - style.parentNode.removeChild(style); - style = frameDoc.createElement('style'); - style.textContent = styleText; - parsedDom.head.appendChild(style); - - frameDoc.replaceChild( - frameDoc.adoptNode(parsedDom.documentElement), - frameDoc.documentElement - ); - - pickerBody = frameDoc.body; - pickerBody.setAttribute('lang', navigator.language); - pickerBody.classList.toggle('zap', details.zap === true); - - dialog = pickerBody.querySelector('aside'); - dialog.addEventListener('click', onDialogClicked); - - taCandidate = dialog.querySelector('textarea'); - taCandidate.addEventListener('input', onCandidateChanged); +// Auto-select a specific target, if any, and if possible - dialog.querySelector('#toolbar').addEventListener('mousedown', onStartMoving); - - svgRoot = pickerBody.querySelector('svg'); - svgOcean = svgRoot.firstChild; - svgIslands = svgRoot.lastChild; +const startPicker = function() { svgRoot.addEventListener('click', onSvgClicked); svgRoot.addEventListener('touchstart', onSvgTouchStartStop); svgRoot.addEventListener('touchend', onSvgTouchStartStop); svgListening(true); - window.addEventListener('scroll', onScrolled, true); + self.addEventListener('scroll', onScrolled, true); pickerRoot.contentWindow.addEventListener('keydown', onKeyPressed, true); pickerRoot.contentWindow.focus(); - // Restore net filter union data if it originate from the same URL. - const eprom = details.eprom || null; - if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) { - lastNetFilterHostname = eprom.lastNetFilterHostname || ''; - lastNetFilterUnion = eprom.lastNetFilterUnion || ''; - } - - // Auto-select a specific target, if any, and if possible - - highlightElements([], true); - // Try using mouse position if ( - details.mouse && + pickerBootArgs.mouse && typeof vAPI.mouseClick.x === 'number' && vAPI.mouseClick.x > 0 ) { @@ -1639,7 +1233,7 @@ const startPicker = function(details) { } // No mouse position available, use suggested target - const target = details.target || ''; + const target = pickerBootArgs.target || ''; const pos = target.indexOf('\t'); if ( pos === -1 ) { return; } @@ -1660,16 +1254,10 @@ const startPicker = function(details) { if ( elem === pickerRoot ) { continue; } const src = elem[attr]; if ( typeof src !== 'string' ) { continue; } - if ( - (src !== url) && - (src !== '' || url !== 'about:blank') - ) { + if ( (src !== url) && (src !== '' || url !== 'about:blank') ) { continue; } - elem.scrollIntoView({ - behavior: 'smooth', - block: 'start' - }); + elem.scrollIntoView({ behavior: 'smooth', block: 'start' }); filtersFrom(elem); showDialog({ modifier: true }); return; @@ -1681,18 +1269,71 @@ const startPicker = function(details) { /******************************************************************************/ -const bootstrapPicker = async function() { - vAPI.shutdown.add(stopPicker); - const details = await vAPI.messaging.send('elementPicker', { - what: 'elementPickerArguments', - }); - startPicker(details); +const onDialogMessage = function(msg) { + switch ( msg.what ) { + case 'dialogInit': + startPicker(); + break; + case 'dialogPreview': + filterToDOMInterface.preview(msg.state); + break; + case 'dialogCreate': + filterToDOMInterface.queryAll(msg); + filterToDOMInterface.preview(true, true); + stopPicker(); + break; + case 'dialogPick': + unpausePicker(); + break; + case 'dialogQuit': + filterToDOMInterface.preview(false); + stopPicker(); + break; + case 'dialogSetFilter': { + const resultset = filterToDOMInterface.queryAll(msg); + highlightElements(resultset.map(a => a.elem), true); + if ( msg.filter === '!' ) { break; } + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'filterResultset', + resultset: resultset.map(a => { + const o = Object.assign({}, a); + o.elem = undefined; + return o; + }), + }); + break; + } + default: + break; + } +}; + +/******************************************************************************/ + +const onConnectionMessage = function(msg) { + if ( + msg.from !== `epickerDialog-${epickerId}` || + msg.to !== `epicker-${epickerId}` + ) { + return; + } + switch ( msg.what ) { + case 'connectionRequested': + epickerConnectionId = msg.id; + return true; + case 'connectionBroken': + stopPicker(); + break; + case 'connectionMessage': + onDialogMessage(msg.payload); + break; + } }; /******************************************************************************/ pickerRoot = document.createElement('iframe'); -pickerRoot.id = vAPI.sessionId; +pickerRoot.setAttribute(vAPI.sessionId, ''); const pickerCSSStyle = [ 'background: transparent', @@ -1720,38 +1361,104 @@ const pickerCSSStyle = [ pickerRoot.style.cssText = pickerCSSStyle; // https://github.com/uBlockOrigin/uBlock-issues/issues/393 -// This needs to be injected as an inline style, *never* as a user styles, +// This needs to be injected as an inline style, *never* as a user style, // hence why it's not added above as part of the pickerCSSStyle // properties. pickerRoot.style.setProperty('pointer-events', 'auto', 'important'); -const pickerCSS1 = [ - `#${pickerRoot.id} {`, - pickerCSSStyle, - '}' -].join('\n'); -const pickerCSS2 = [ - `[${pickerRoot.id}-clickblind] {`, - 'pointer-events: none !important;', - '}' -].join('\n'); +const pickerCSS = ` +[${vAPI.sessionId}] { + ${pickerCSSStyle} +} +[${vAPI.sessionId}-clickblind] { + pointer-events: none !important; +} +`; + +{ + const pickerRootLoaded = new Promise(resolve => { + pickerRoot.addEventListener('load', ( ) => { resolve(); }, { once: true }); + }); + document.documentElement.append(pickerRoot); + + const results = await Promise.all([ + vAPI.messaging.send('elementPicker', { what: 'elementPickerArguments' }), + pickerRootLoaded, + ]); + + pickerBootArgs = results[0]; + + // The DOM filterer will not be present when cosmetic filtering is + // disabled. + if ( + pickerBootArgs.zap !== true && + vAPI.domFilterer instanceof Object === false + ) { + pickerRoot.remove(); + return; + } + + // Restore net filter union data if origin is the same. + const eprom = pickerBootArgs.eprom || null; + if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) { + lastNetFilterHostname = eprom.lastNetFilterHostname || ''; + lastNetFilterUnion = eprom.lastNetFilterUnion || ''; + } + + const frameDoc = pickerRoot.contentDocument; + + // Provide an id users can use as anchor to personalize uBO's element + // picker style properties. + frameDoc.documentElement.id = 'ublock0-epicker'; + + // https://github.com/gorhill/uBlock/issues/2240 + // https://github.com/uBlockOrigin/uBlock-issues/issues/170 + // Remove the already declared inline style tag: we will create a new + // one based on the removed one, and replace the old one. + const style = frameDoc.createElement('style'); + style.textContent = pickerBootArgs.frameCSS; + frameDoc.head.appendChild(style); + + pickerBody = frameDoc.body; + pickerBody.setAttribute('lang', navigator.language); + pickerBody.classList.toggle('zap', pickerBootArgs.zap === true); + + svgRoot = frameDoc.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svgOcean = frameDoc.createElementNS('http://www.w3.org/2000/svg', 'path'); + svgRoot.append(svgOcean); + svgIslands = frameDoc.createElementNS('http://www.w3.org/2000/svg', 'path'); + svgRoot.append(svgIslands); + pickerBody.append(svgRoot); + + dialog = frameDoc.createElement('iframe'); + pickerBody.append(dialog); +} + +highlightElements([], true); // https://github.com/gorhill/uBlock/issues/1529 // In addition to inline styles, harden the element picker styles by using // dedicated CSS rules. -vAPI.userStylesheet.add(pickerCSS1); -vAPI.userStylesheet.add(pickerCSS2); +vAPI.userStylesheet.add(pickerCSS); vAPI.userStylesheet.apply(); +vAPI.shutdown.add(stopPicker); + +// https://github.com/gorhill/uBlock/issues/3497 +// https://github.com/uBlockOrigin/uBlock-issues/issues/1215 +// Instantiate isolated element picker dialog. +if ( pickerBootArgs.zap === true ) { + startPicker(); + return; +} + // https://github.com/gorhill/uBlock/issues/2060 vAPI.domFilterer.excludeNode(pickerRoot); -pickerRoot.addEventListener( - 'load', - ( ) => { bootstrapPicker(); }, - { once: true } -); -document.documentElement.appendChild(pickerRoot); +if ( await vAPI.messaging.extend() !== true ) { return; } +vAPI.MessagingConnection.addListener(onConnectionMessage); + +dialog.contentWindow.location = `${pickerBootArgs.dialogURL}&epid=${epickerId}`; /******************************************************************************/ diff --git a/src/js/ublock.js b/src/js/ublock.js index e4d720471fe36..1a0c0ac8281c2 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -428,7 +428,7 @@ const matchBucket = function(url, hostname, bucket, start) { }); await vAPI.tabs.executeScript(tabId, { - file: '/js/scriptlets/element-picker.js', + file: '/js/scriptlets/epicker.js', runAt: 'document_end', }); diff --git a/src/web_accessible_resources/epicker-dialog.html b/src/web_accessible_resources/epicker-dialog.html new file mode 100644 index 0000000000000..d87b4656664f9 --- /dev/null +++ b/src/web_accessible_resources/epicker-dialog.html @@ -0,0 +1,48 @@ + + + + + +uBlock Origin Element Picker + + + + + + + + + + + + + + + + From d4f4f82872c063aae958d50bc4dcc42c17d2db24 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Sep 2020 12:38:41 -0400 Subject: [PATCH 3552/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index abc301cde66ec..d8d1b156b141f 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.29.3.7 +1.29.3.8 From c02bba3cfa79ae736a221b30f3cd078d9dfa5638 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Sep 2020 12:56:07 -0400 Subject: [PATCH 3553/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d313be24f9155..52172b5a11789 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.29.3.7", + "version": "1.29.3.8", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b7", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b7/uBlock0_1.29.3b7.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b8", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b8/uBlock0_1.29.3b8.firefox.signed.xpi" } ] } From 3b9218ecc0b5520dadf48dd1784afc4aa0370e4e Mon Sep 17 00:00:00 2001 From: gwarser Date: Tue, 1 Sep 2020 22:19:21 +0200 Subject: [PATCH 3554/4093] uBlock for firefox legacy is auto-updating (#3774) from GitHub after [1.16.4.17](https://github.com/gorhill/uBlock-for-firefox-legacy/releases/tag/firefox-legacy-1.16.4.17). Discussed on Reddit: https://www.reddit.com/r/uBlockOrigin/comments/ikbuog/does_ublock_for_firefox_legacy_update_itself/ --- dist/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/dist/README.md b/dist/README.md index d3c0ab433de22..1874153e9f762 100644 --- a/dist/README.md +++ b/dist/README.md @@ -58,8 +58,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`. -If you want to automatically receive updates for this version, you can also install [uBlock Origin Updater](https://github.com/JustOff/ublock0-updater). - ### 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 d23f9c6a8b620ae0c61aab7fba7477471bd656fb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Sep 2020 10:27:35 -0400 Subject: [PATCH 3555/4093] Isolate element picker's svg layers from page content Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1226 Related commit: - https://github.com/gorhill/uBlock/commit/9eb455ab5eb2d5640f4188e71011b7aa22ab8b43 In the previous commit, the element picker dialog was isolated from the page content. This commit is to also isolate the svg layers from the page content. With this commit, there is no longer a need for an anonymous iframe and the isolated world iframe is now directly embedded in the page. As a result, pages are now unable to interfere with any of the element picker user interface. Pages can now only see an iframe, but are unable to see the content of that iframe. The styles applied to the iframe are from a user stylesheet, so as to ensure pages can't override the iframe's style properties set by uBO. --- platform/chromium/vapi-client-extra.js | 3 + .../{epicker-dialog.css => epicker-ui.css} | 73 +- src/css/epicker.css | 60 -- src/js/{epicker-dialog.js => epicker-ui.js} | 365 ++++++++-- src/js/messaging.js | 30 +- src/js/scriptlets/epicker.js | 667 +++++++----------- .../{epicker-dialog.html => epicker-ui.html} | 5 +- 7 files changed, 603 insertions(+), 600 deletions(-) rename src/css/{epicker-dialog.css => epicker-ui.css} (79%) delete mode 100644 src/css/epicker.css rename src/js/{epicker-dialog.js => epicker-ui.js} (61%) rename src/web_accessible_resources/{epicker-dialog.html => epicker-ui.html} (90%) diff --git a/platform/chromium/vapi-client-extra.js b/platform/chromium/vapi-client-extra.js index 383678f84b1f1..0328ec7a98a18 100644 --- a/platform/chromium/vapi-client-extra.js +++ b/platform/chromium/vapi-client-extra.js @@ -153,6 +153,9 @@ vAPI.MessagingConnection = class { listeners.add(listener); vAPI.messaging.getPort(); // Ensure a port instance exists } + static removeListener(listener) { + listeners.delete(listener); + } static connectTo(from, to, handler) { const port = vAPI.messaging.getPort(); if ( port === null ) { return; } diff --git a/src/css/epicker-dialog.css b/src/css/epicker-ui.css similarity index 79% rename from src/css/epicker-dialog.css rename to src/css/epicker-ui.css index 5b0c0e44fd8ad..0ff6eecd5f05b 100644 --- a/src/css/epicker-dialog.css +++ b/src/css/epicker-ui.css @@ -12,6 +12,22 @@ html#ublock0-epicker, #ublock0-epicker :focus { outline: none; } +#ublock0-epicker aside { + background-color: #eee; + border: 1px solid #aaa; + bottom: 4px; + box-sizing: border-box; + cursor: default; + display: none; + min-width: 24em; + padding: 4px; + position: fixed; + right: 4px; + width: calc(40% - 4px); +} +#ublock0-epicker.paused:not(.zap) aside { + display: block; +} #ublock0-epicker ul, #ublock0-epicker li, #ublock0-epicker div { @@ -53,7 +69,7 @@ html#ublock0-epicker, #ublock0-epicker #preview { float: left; } -#ublock0-epicker body.preview #preview { +#ublock0-epicker.preview #preview { background-color: hsl(204, 100%, 83%); border-color: hsl(204, 50%, 60%); } @@ -141,18 +157,7 @@ html#ublock0-epicker, #ublock0-epicker #candidateFilters .changeFilter li:hover { background-color: white; } -#ublock0-epicker aside { - background-color: #eee; - border: 1px solid #aaa; - bottom: 4px; - box-sizing: border-box; - cursor: default; - min-width: 24em; - padding: 4px; - position: fixed; - right: 4px; - width: calc(40% - 4px); -} + /** https://github.com/gorhill/uBlock/issues/3449 https://github.com/uBlockOrigin/uBlock-issues/issues/55 @@ -162,23 +167,55 @@ html#ublock0-epicker, 60% { opacity: 1.0; } 100% { opacity: 0.1; } } -#ublock0-epicker body.paused > aside { +#ublock0-epicker.paused aside { opacity: 0.1; visibility: visible; z-index: 100; } -#ublock0-epicker body.paused > aside:not(:hover):not(.show) { +#ublock0-epicker.paused:not(.show):not(.hide) aside:not(:hover) { animation-duration: 1.6s; animation-name: startDialog; animation-timing-function: linear; } -#ublock0-epicker body.paused > aside:hover { +#ublock0-epicker.paused aside:hover { opacity: 1; } -#ublock0-epicker body.paused > aside.show { +#ublock0-epicker.paused.show aside { opacity: 1; } -#ublock0-epicker body.paused > aside.hide { +#ublock0-epicker.paused.hide aside { opacity: 0.1; } +#ublock0-epicker svg { + cursor: crosshair; + box-sizing: border-box; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; +} +#ublock0-epicker.paused svg { + cursor: not-allowed; +} +#ublock0-epicker svg > path:first-child { + fill: rgba(0,0,0,0.5); + fill-rule: evenodd; +} +#ublock0-epicker svg > path + path { + stroke: #F00; + stroke-width: 0.5px; + fill: rgba(255,63,63,0.20); +} +#ublock0-epicker.zap svg > path + path { + stroke: #FF0; + stroke-width: 0.5px; + fill: rgba(255,255,63,0.20); +} +#ublock0-epicker.preview svg > path { + fill: rgba(0,0,0,0.10); +} +#ublock0-epicker.preview svg > path + path { + stroke: none; +} diff --git a/src/css/epicker.css b/src/css/epicker.css deleted file mode 100644 index 1010fb24eb98e..0000000000000 --- a/src/css/epicker.css +++ /dev/null @@ -1,60 +0,0 @@ -html#ublock0-epicker, -#ublock0-epicker body { - background: transparent !important; - box-sizing: border-box !important; - color: black !important; - font: 12px sans-serif !important; - height: 100vh !important; - margin: 0 !important; - overflow: hidden !important; - position: fixed !important; - width: 100vw !important; -} -#ublock0-epicker :focus { - outline: none !important; -} -#ublock0-epicker svg { - cursor: crosshair !important; - box-sizing: border-box; - height: 100% !important; - left: 0 !important; - position: absolute !important; - top: 0 !important; - width: 100% !important; -} -#ublock0-epicker .paused > svg { - cursor: not-allowed !important; -} -#ublock0-epicker svg > path:first-child { - fill: rgba(0,0,0,0.5) !important; - fill-rule: evenodd !important; -} -#ublock0-epicker svg > path + path { - stroke: #F00 !important; - stroke-width: 0.5px !important; - fill: rgba(255,63,63,0.20) !important; -} -#ublock0-epicker body.zap svg > path + path { - stroke: #FF0 !important; - stroke-width: 0.5px !important; - fill: rgba(255,255,63,0.20) !important; -} -#ublock0-epicker body.preview svg > path { - fill: rgba(0,0,0,0.10) !important; -} -#ublock0-epicker body.preview svg > path + path { - stroke: none !important; -} -#ublock0-epicker body > iframe { - border: 0 !important; - box-sizing: border-box !important; - display: none !important; - height: 100% !important; - left: 0 !important; - position: absolute !important; - top: 0 !important; - width: 100% !important; -} -#ublock0-epicker body.paused > iframe { - display: initial !important; -} diff --git a/src/js/epicker-dialog.js b/src/js/epicker-ui.js similarity index 61% rename from src/js/epicker-dialog.js rename to src/js/epicker-ui.js index cea9d43970092..3b01cd4c3ee9a 100644 --- a/src/js/epicker-dialog.js +++ b/src/js/epicker-ui.js @@ -30,8 +30,25 @@ if ( typeof vAPI !== 'object' ) { return; } +const $id = id => document.getElementById(id); +const $stor = selector => document.querySelector(selector); +const $storAll = selector => document.querySelectorAll(selector); + +const pickerRoot = document.documentElement; +const dialog = $stor('aside'); +const taCandidate = $stor('textarea'); +let staticFilteringParser; + +const svgRoot = $stor('svg'); +const svgOcean = svgRoot.children[0]; +const svgIslands = svgRoot.children[1]; +const NoPaths = 'M0 0'; + const epickerId = (( ) => { const url = new URL(self.location.href); + if ( url.searchParams.has('zap') ) { + pickerRoot.classList.add('zap'); + } return url.searchParams.get('epid'); })(); if ( epickerId === null ) { return; } @@ -43,12 +60,6 @@ let filterResultset = []; /******************************************************************************/ -const $id = id => document.getElementById(id); -const $stor = selector => document.querySelector(selector); -const $storAll = selector => document.querySelectorAll(selector); - -/******************************************************************************/ - const filterFromTextarea = function() { const s = taCandidate.value.trim(); if ( s === '' ) { return ''; } @@ -121,7 +132,7 @@ const candidateFromFilterChoice = function(filterChoice) { // - Discard narrowing directives. // - Remove the id if one or more classes exist // TODO: should remove tag name too? ¯\_(ツ)_/¯ - if ( filterChoice.modifier ) { + if ( filterChoice.broad ) { filter = filter.replace(/:nth-of-type\(\d+\)/, ''); // https://github.com/uBlockOrigin/uBlock-issues/issues/162 // Mind escaped periods: they do not denote a class identifier. @@ -167,6 +178,127 @@ const candidateFromFilterChoice = function(filterChoice) { /******************************************************************************/ +const onSvgClicked = function(ev) { + // If zap mode, highlight element under mouse, this makes the zapper usable + // on touch screens. + if ( pickerRoot.classList.contains('zap') ) { + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'zapElementAtPoint', + mx: ev.clientX, + my: ev.clientY, + options: { + stay: ev.shiftKey || ev.type === 'touch', + highlight: ev.target !== svgIslands, + }, + }); + return; + } + // https://github.com/chrisaljoudi/uBlock/issues/810#issuecomment-74600694 + // Unpause picker if: + // - click outside dialog AND + // - not in preview mode + if ( pickerRoot.classList.contains('paused') ) { + if ( pickerRoot.classList.contains('preview') === false ) { + unpausePicker(); + } + return; + } + // Force dialog to always be visible when using a touch-driven device. + if ( ev.type === 'touch' ) { + pickerRoot.classList.add('show'); + } + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'filterElementAtPoint', + mx: ev.clientX, + my: ev.clientY, + broad: ev.ctrlKey, + }); +}; + +/******************************************************************************* + + Swipe right: + If picker not paused: quit picker + If picker paused and dialog visible: hide dialog + If picker paused and dialog not visible: quit picker + + Swipe left: + If picker paused and dialog not visible: show dialog + +*/ + +const onSvgTouch = (( ) => { + let startX = 0, startY = 0; + let t0 = 0; + return ev => { + if ( ev.type === 'touchstart' ) { + startX = ev.touches[0].screenX; + startY = ev.touches[0].screenY; + t0 = ev.timeStamp; + return; + } + if ( startX === undefined ) { return; } + if ( ev.cancelable === false ) { return; } + const stopX = ev.changedTouches[0].screenX; + const stopY = ev.changedTouches[0].screenY; + const angle = Math.abs(Math.atan2(stopY - startY, stopX - startX)); + const distance = Math.sqrt( + Math.pow(stopX - startX, 2), + Math.pow(stopY - startY, 2) + ); + // Interpret touch events as a tap if: + // - Swipe is not valid; and + // - The time between start and stop was less than 200ms. + const duration = ev.timeStamp - t0; + if ( distance < 32 && duration < 200 ) { + onSvgClicked({ + type: 'touch', + target: ev.target, + clientX: ev.changedTouches[0].pageX, + clientY: ev.changedTouches[0].pageY, + }); + ev.preventDefault(); + return; + } + if ( distance < 64 ) { return; } + const angleUpperBound = Math.PI * 0.25 * 0.5; + const swipeRight = angle < angleUpperBound; + if ( swipeRight === false && angle < Math.PI - angleUpperBound ) { + return; + } + ev.preventDefault(); + // Swipe left. + if ( swipeRight === false ) { + if ( pickerRoot.classList.contains('paused') ) { + pickerRoot.classList.remove('hide'); + pickerRoot.classList.add('show'); + } + return; + } + // Swipe right. + if ( + pickerRoot.classList.contains('zap') && + svgIslands.getAttribute('d') !== NoPaths + ) { + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'unhighlight' + }); + return; + } + else if ( + pickerRoot.classList.contains('paused') && + pickerRoot.classList.contains('show') + ) { + pickerRoot.classList.remove('show'); + pickerRoot.classList.add('hide'); + return; + } + quitPicker(); + }; +})(); + +/******************************************************************************/ + const onCandidateChanged = function() { const filter = filterFromTextarea(); const bad = filter === '!'; @@ -188,9 +320,9 @@ const onCandidateChanged = function() { /******************************************************************************/ const onPreviewClicked = function() { - const state = pickerBody.classList.toggle('preview'); + const state = pickerRoot.classList.toggle('preview'); vAPI.MessagingConnection.sendTo(epickerConnectionId, { - what: 'dialogPreview', + what: 'togglePreview', state, }); }; @@ -221,38 +353,27 @@ const onCreateClicked = function() { /******************************************************************************/ -const onPickClicked = function(ev) { - if ( - (ev instanceof MouseEvent) && - (ev.type === 'mousedown') && - (ev.which !== 1 || ev.target !== document.body) - ) { - return; - } - pickerBody.classList.remove('paused'); - vAPI.MessagingConnection.sendTo(epickerConnectionId, { - what: 'dialogPick' - }); +const onPickClicked = function() { + unpausePicker(); }; /******************************************************************************/ const onQuitClicked = function() { - vAPI.MessagingConnection.sendTo(epickerConnectionId, { - what: 'dialogQuit' - }); + quitPicker(); }; /******************************************************************************/ const onCandidateClicked = function(ev) { let li = ev.target.closest('li'); + if ( li === null ) { return; } const ul = li.closest('.changeFilter'); if ( ul === null ) { return; } const choice = { filters: Array.from(ul.querySelectorAll('li')).map(a => a.textContent), slot: 0, - modifier: ev.ctrlKey || ev.metaKey + broad: ev.ctrlKey || ev.metaKey }; while ( li.previousElementSibling !== null ) { li = li.previousElementSibling; @@ -275,6 +396,7 @@ const onKeyPressed = function(ev) { /******************************************************************************/ const onStartMoving = (( ) => { + let isTouch = false; let mx0 = 0, my0 = 0; let mx1 = 0, my1 = 0; let r0 = 0, b0 = 0; @@ -290,44 +412,101 @@ const onStartMoving = (( ) => { }; const moveAsync = ev => { - if ( ev.isTrusted === false ) { return; } - eatEvent(ev); if ( timer !== undefined ) { return; } - mx1 = ev.pageX; - my1 = ev.pageY; + if ( isTouch ) { + const touch = ev.touches[0]; + mx1 = touch.pageX; + my1 = touch.pageY; + } else { + mx1 = ev.pageX; + my1 = ev.pageY; + } timer = self.requestAnimationFrame(move); }; const stop = ev => { - if ( ev.isTrusted === false ) { return; } if ( dialog.classList.contains('moving') === false ) { return; } dialog.classList.remove('moving'); - self.removeEventListener('mousemove', moveAsync, { capture: true }); - self.removeEventListener('mouseup', stop, { capture: true, once: true }); + if ( isTouch ) { + self.removeEventListener('touchmove', moveAsync, { capture: true }); + } else { + self.removeEventListener('mousemove', moveAsync, { capture: true }); + } eatEvent(ev); }; return function(ev) { - if ( ev.isTrusted === false ) { return; } const target = dialog.querySelector('#toolbar'); if ( ev.target !== target ) { return; } if ( dialog.classList.contains('moving') ) { return; } - mx0 = ev.pageX; my0 = ev.pageY; + isTouch = ev.type.startsWith('touch'); + if ( isTouch ) { + const touch = ev.touches[0]; + mx0 = touch.pageX; + my0 = touch.pageY; + } else { + mx0 = ev.pageX; + my0 = ev.pageY; + } const style = self.getComputedStyle(dialog); r0 = parseInt(style.right, 10); b0 = parseInt(style.bottom, 10); const rect = dialog.getBoundingClientRect(); - rMax = pickerBody.clientWidth - 4 - rect.width ; - bMax = pickerBody.clientHeight - 4 - rect.height; + rMax = pickerRoot.clientWidth - 4 - rect.width ; + bMax = pickerRoot.clientHeight - 4 - rect.height; dialog.classList.add('moving'); - self.addEventListener('mousemove', moveAsync, { capture: true }); - self.addEventListener('mouseup', stop, { capture: true, once: true }); + if ( isTouch ) { + self.addEventListener('touchmove', moveAsync, { capture: true }); + self.addEventListener('touchend', stop, { capture: true, once: true }); + } else { + self.addEventListener('mousemove', moveAsync, { capture: true }); + self.addEventListener('mouseup', stop, { capture: true, once: true }); + } eatEvent(ev); }; })(); /******************************************************************************/ +const svgListening = (( ) => { + let on = false; + let timer; + let mx = 0, my = 0; + + const onTimer = ( ) => { + timer = undefined; + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'highlightElementAtPoint', + mx, + my, + }); + }; + + const onHover = ev => { + mx = ev.clientX; + my = ev.clientY; + if ( timer === undefined ) { + timer = self.requestAnimationFrame(onTimer); + } + }; + + return state => { + if ( state === on ) { return; } + on = state; + if ( on ) { + document.addEventListener('mousemove', onHover, { passive: true }); + return; + } + document.removeEventListener('mousemove', onHover, { passive: true }); + if ( timer !== undefined ) { + self.cancelAnimationFrame(timer); + timer = undefined; + } + }; +})(); + +/******************************************************************************/ + const eatEvent = function(ev) { ev.stopPropagation(); ev.preventDefault(); @@ -336,9 +515,9 @@ const eatEvent = function(ev) { /******************************************************************************/ const showDialog = function(details) { - pickerBody.classList.add('paused'); + pausePicker(); - const { netFilters, cosmeticFilters, filter, options } = details; + const { netFilters, cosmeticFilters, filter, options = {} } = details; // https://github.com/gorhill/uBlock/issues/738 // Trim dots. @@ -390,7 +569,7 @@ const showDialog = function(details) { const filterChoice = { filters: filter.filters, slot: filter.slot, - modifier: options.modifier || false + broad: options.broad || false }; taCandidate.value = candidateFromFilterChoice(filterChoice); @@ -399,52 +578,83 @@ const showDialog = function(details) { /******************************************************************************/ -// Let's have the element picker code flushed from memory when no longer -// in use: to ensure this, release all local references. - -const stopPicker = function() { - vAPI.shutdown.remove(stopPicker); +const pausePicker = function() { + pickerRoot.classList.add('paused'); + svgListening(false); }; /******************************************************************************/ -const pickerBody = document.body; -const dialog = $stor('aside'); -const taCandidate = $stor('textarea'); -let staticFilteringParser; +const unpausePicker = function() { + pickerRoot.classList.remove('paused', 'preview'); + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'togglePreview', + state: false, + }); + svgListening(true); +}; /******************************************************************************/ -const startDialog = function() { - dialog.addEventListener('click', eatEvent); +const startPicker = function() { + self.addEventListener('keydown', onKeyPressed, true); + const svg = $stor('svg'); + svg.addEventListener('click', onSvgClicked); + svg.addEventListener('touchstart', onSvgTouch); + svg.addEventListener('touchend', onSvgTouch); + + unpausePicker(); + + if ( pickerRoot.classList.contains('zap') ) { return; } + taCandidate.addEventListener('input', onCandidateChanged); - $stor('body').addEventListener('mousedown', onPickClicked); $id('preview').addEventListener('click', onPreviewClicked); $id('create').addEventListener('click', onCreateClicked); $id('pick').addEventListener('click', onPickClicked); $id('quit').addEventListener('click', onQuitClicked); $id('candidateFilters').addEventListener('click', onCandidateClicked); $id('toolbar').addEventListener('mousedown', onStartMoving); - self.addEventListener('keydown', onKeyPressed, true); + $id('toolbar').addEventListener('touchstart', onStartMoving); staticFilteringParser = new vAPI.StaticFilteringParser({ interactive: true }); }; /******************************************************************************/ +const quitPicker = function() { + vAPI.MessagingConnection.sendTo(epickerConnectionId, { what: 'quitPicker' }); + vAPI.MessagingConnection.disconnectFrom(epickerConnectionId); +}; + +/******************************************************************************/ + const onPickerMessage = function(msg) { switch ( msg.what ) { - case 'showDialog': - showDialog(msg); - break; - case 'filterResultset': - filterResultset = msg.resultset; - $id('resultsetCount').textContent = filterResultset.length; - if ( filterResultset.length !== 0 ) { - $id('create').removeAttribute('disabled'); - } else { - $id('create').setAttribute('disabled', ''); + case 'showDialog': + showDialog(msg); + break; + case 'filterResultset': { + filterResultset = msg.resultset; + $id('resultsetCount').textContent = filterResultset.length; + if ( filterResultset.length !== 0 ) { + $id('create').removeAttribute('disabled'); + } else { + $id('create').setAttribute('disabled', ''); + } + break; + } + case 'svgListening': { + svgListening(msg.on); + break; } - break; + case 'svgPaths': { + let { ocean, islands } = msg; + ocean += islands; + svgOcean.setAttribute('d', ocean); + svgIslands.setAttribute('d', islands || NoPaths); + break; + } + default: + break; } }; @@ -452,19 +662,18 @@ const onPickerMessage = function(msg) { const onConnectionMessage = function(msg) { switch ( msg.what ) { - case 'connectionBroken': - stopPicker(); - break; - case 'connectionMessage': - onPickerMessage(msg.payload); - break; - case 'connectionAccepted': - epickerConnectionId = msg.id; - startDialog(); - vAPI.MessagingConnection.sendTo(epickerConnectionId, { - what: 'dialogInit', - }); - break; + case 'connectionBroken': + break; + case 'connectionMessage': + onPickerMessage(msg.payload); + break; + case 'connectionAccepted': + epickerConnectionId = msg.id; + startPicker(); + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'start', + }); + break; } }; diff --git a/src/js/messaging.js b/src/js/messaging.js index 38f38fda5df10..6dac63db543f2 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -711,26 +711,6 @@ const onMessage = function(request, sender, callback) { // Async switch ( request.what ) { - case 'elementPickerArguments': - const xhr = new XMLHttpRequest(); - xhr.open('GET', 'css/epicker.css', true); - xhr.overrideMimeType('text/html;charset=utf-8'); - xhr.responseType = 'text'; - xhr.onload = function() { - this.onload = null; - callback({ - frameCSS: this.responseText, - target: µb.epickerArgs.target, - mouse: µb.epickerArgs.mouse, - zap: µb.epickerArgs.zap, - eprom: µb.epickerArgs.eprom, - dialogURL: vAPI.getURL(`/web_accessible_resources/epicker-dialog.html${vAPI.warSecret()}`), - }); - µb.epickerArgs.target = ''; - }; - xhr.send(); - return; - default: break; } @@ -739,6 +719,16 @@ const onMessage = function(request, sender, callback) { let response; switch ( request.what ) { + case 'elementPickerArguments': + response = { + target: µb.epickerArgs.target, + mouse: µb.epickerArgs.mouse, + zap: µb.epickerArgs.zap, + eprom: µb.epickerArgs.eprom, + pickerURL: vAPI.getURL(`/web_accessible_resources/epicker-ui.html${vAPI.warSecret()}`), + }; + µb.epickerArgs.target = ''; + break; case 'elementPickerEprom': µb.epickerArgs.eprom = request; break; diff --git a/src/js/scriptlets/epicker.js b/src/js/scriptlets/epicker.js index 0d420b5d4117a..7cb13a7e64b74 100644 --- a/src/js/scriptlets/epicker.js +++ b/src/js/scriptlets/epicker.js @@ -30,24 +30,13 @@ /******************************************************************************/ -if ( window.top !== window || typeof vAPI !== 'object' ) { return; } - -/******************************************************************************/ - const epickerId = vAPI.randomToken(); let epickerConnectionId; -/******************************************************************************/ - let pickerRoot = document.querySelector(`[${vAPI.sessionId}]`); if ( pickerRoot !== null ) { return; } let pickerBootArgs; -let pickerBody = null; -let svgOcean = null; -let svgIslands = null; -let svgRoot = null; -let dialog = null; const netFilterCandidates = []; const cosmeticFilterCandidates = []; @@ -103,8 +92,8 @@ const getElementBoundingClientRect = function(elem) { return { height: bottom - top, - left: left, - top: top, + left, + top, width: right - left }; }; @@ -113,45 +102,40 @@ const getElementBoundingClientRect = function(elem) { const highlightElements = function(elems, force) { // To make mouse move handler more efficient - if ( !force && elems.length === targetElements.length ) { - if ( elems.length === 0 || elems[0] === targetElements[0] ) { - return; - } + if ( + (force !== true) && + (elems.length === targetElements.length) && + (elems.length === 0 || elems[0] === targetElements[0]) + ) { + return; } - targetElements = elems; - - const ow = pickerRoot.contentWindow.innerWidth; - const oh = pickerRoot.contentWindow.innerHeight; - const ocean = [ - 'M0 0', - 'h', ow, - 'v', oh, - 'h-', ow, - 'z' - ]; + targetElements = []; + + const ow = self.innerWidth; + const oh = self.innerHeight; const islands = []; - for ( let i = 0; i < elems.length; i++ ) { - const elem = elems[i]; + for ( const elem of elems ) { if ( elem === pickerRoot ) { continue; } + targetElements.push(elem); const rect = getElementBoundingClientRect(elem); - - // Ignore if it's not on the screen - if ( rect.left > ow || rect.top > oh || - rect.left + rect.width < 0 || rect.top + rect.height < 0 ) { + // Ignore offscreen areas + if ( + rect.left > ow || rect.top > oh || + rect.left + rect.width < 0 || rect.top + rect.height < 0 + ) { continue; } - - const poly = 'M' + rect.left + ' ' + rect.top + - 'h' + rect.width + - 'v' + rect.height + - 'h-' + rect.width + - 'z'; - ocean.push(poly); - islands.push(poly); + islands.push( + `M${rect.left} ${rect.top}h${rect.width}v${rect.height}h-${rect.width}z` + ); } - svgOcean.setAttribute('d', ocean.join('')); - svgIslands.setAttribute('d', islands.join('') || 'M0 0'); + + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'svgPaths', + ocean: `M0 0h${ow}v${oh}h-${ow}z`, + islands: islands.join(''), + }); }; /******************************************************************************/ @@ -170,8 +154,6 @@ const mergeStrings = function(urls) { // The differ works at line granularity: we insert a linefeed after // each character to trick the differ to work at character granularity. const diffs = differ.diff_main( - //urls[i].replace(/.(?=.)/g, '$&\n'), - //merged.replace(/.(?=.)/g, '$&\n') urls[i].split('').join('\n'), merged.split('').join('\n') ); @@ -552,24 +534,20 @@ const filtersFrom = function(x, y) { } // https://github.com/gorhill/uBlock/issues/1545 - // Network filter candidates from all other elements found at point (x, y). + // Network filter candidates from all other elements found at + // point (x, y). if ( typeof x === 'number' ) { - let attrName = vAPI.sessionId + '-clickblind'; - let previous; + const attrName = vAPI.sessionId + '-clickblind'; elem = first; while ( elem !== null ) { - previous = elem; + const previous = elem; elem.setAttribute(attrName, ''); elem = elementFromPoint(x, y); - if ( elem === null || elem === previous ) { - break; - } + if ( elem === null || elem === previous ) { break; } netFilterFromElement(elem); } - let elems = document.querySelectorAll(`[${attrName}]`); - i = elems.length; - while ( i-- ) { - elems[i].removeAttribute(attrName); + for ( const elem of document.querySelectorAll(`[${attrName}]`) ) { + elem.removeAttribute(attrName); } netFilterFromElement(document.body); @@ -761,7 +739,7 @@ const filterToDOMInterface = (( ) => { if ( filter === '' || filter === '!' ) { lastFilter = ''; lastResultset = []; - return; + return lastResultset; } lastFilter = filter; lastAction = undefined; @@ -868,7 +846,6 @@ const filterToDOMInterface = (( ) => { // immediately rather than wait for the next page load. const preview = function(state, permanent = false) { previewing = state !== false; - pickerBody.classList.toggle('preview', previewing); if ( previewing === false ) { return unapply(); } @@ -909,15 +886,6 @@ const filterToDOMInterface = (( ) => { /******************************************************************************/ const showDialog = function(options) { - pausePicker(); - - options = options || {}; - - // Typically the dialog will be forced to be visible when using a - // touch-aware device. - dialog.classList.toggle('show', options.show === true); - dialog.classList.remove('hide'); - vAPI.MessagingConnection.sendTo(epickerConnectionId, { what: 'showDialog', hostname: self.location.hostname, @@ -931,42 +899,6 @@ const showDialog = function(options) { /******************************************************************************/ -// https://www.reddit.com/r/uBlockOrigin/comments/bktxtb/scrolling_doesnt_work/emn901o -// Override 'fixed' position property on body element if present. - -const zap = function() { - if ( targetElements.length === 0 ) { return; } - - const getStyleValue = function(elem, prop) { - const style = window.getComputedStyle(elem); - return style ? style[prop] : ''; - }; - - let elem = targetElements[0]; - // Heuristic to detect scroll-locking: remove such lock when detected. - if ( - parseInt(getStyleValue(elem, 'zIndex'), 10) >= 1000 || - getStyleValue(elem, 'position') === 'fixed' - ) { - const doc = document; - if ( getStyleValue(doc.body, 'overflowY') === 'hidden' ) { - doc.body.style.setProperty('overflow', 'auto', 'important'); - } - if ( getStyleValue(doc.body, 'position') === 'fixed' ) { - doc.body.style.setProperty('position', 'static', 'important'); - } - if ( getStyleValue(doc.documentElement, 'overflowY') === 'hidden' ) { - doc.documentElement.style.setProperty('overflow', 'auto', 'important'); - } - } - - elem.parentNode.removeChild(elem); - elem = elementFromPoint(); - highlightElements(elem ? [ elem ] : []); -}; - -/******************************************************************************/ - const elementFromPoint = (( ) => { let lastX, lastY; @@ -979,154 +911,80 @@ const elementFromPoint = (( ) => { return null; } if ( !pickerRoot ) { return null; } - pickerRoot.style.setProperty('pointer-events', 'none', 'important'); + const magicAttr = `${vAPI.sessionId}-clickblind`; + pickerRoot.setAttribute(magicAttr, ''); let elem = document.elementFromPoint(x, y); if ( elem === document.body || elem === document.documentElement ) { elem = null; } // https://github.com/uBlockOrigin/uBlock-issues/issues/380 - pickerRoot.style.setProperty('pointer-events', 'auto', 'important'); + pickerRoot.removeAttribute(magicAttr); return elem; }; })(); /******************************************************************************/ -const onSvgHovered = (( ) => { - let timer; - let mx = 0, my = 0; - - const onTimer = function() { - timer = undefined; - const elem = elementFromPoint(mx, my); - highlightElements(elem ? [elem] : []); - }; +const highlightElementAtPoint = function(mx, my) { + const elem = elementFromPoint(mx, my); + highlightElements(elem ? [ elem ] : []); +}; - return function onMove(ev) { - mx = ev.clientX; - my = ev.clientY; - if ( timer === undefined ) { - timer = vAPI.setTimeout(onTimer, 40); - } - }; -})(); +/******************************************************************************/ -/******************************************************************************* +const filterElementAtPoint = function(mx, my, broad) { + if ( filtersFrom(mx, my) === 0 ) { return; } + showDialog({ broad }); +}; - Swipe right: - If picker not paused: quit picker - If picker paused and dialog visible: hide dialog - If picker paused and dialog not visible: quit picker +/******************************************************************************/ - Swipe left: - If picker paused and dialog not visible: show dialog +// https://www.reddit.com/r/uBlockOrigin/comments/bktxtb/scrolling_doesnt_work/emn901o +// Override 'fixed' position property on body element if present. -*/ +// With touch-driven devices, first highlight the element and remove only +// when tapping again the highlighted area. -const onSvgTouchStartStop = (( ) => { - var startX, - startY; - return function onTouch(ev) { - if ( ev.type === 'touchstart' ) { - startX = ev.touches[0].screenX; - startY = ev.touches[0].screenY; - return; - } - if ( startX === undefined ) { return; } - if ( ev.cancelable === false ) { return; } - var stopX = ev.changedTouches[0].screenX, - stopY = ev.changedTouches[0].screenY, - angle = Math.abs(Math.atan2(stopY - startY, stopX - startX)), - distance = Math.sqrt( - Math.pow(stopX - startX, 2), - Math.pow(stopY - startY, 2) - ); - // Interpret touch events as a click events if swipe is not valid. - if ( distance < 32 ) { - onSvgClicked({ - type: 'touch', - target: ev.target, - clientX: ev.changedTouches[0].pageX, - clientY: ev.changedTouches[0].pageY, - isTrusted: ev.isTrusted - }); - ev.preventDefault(); - return; - } - if ( distance < 64 ) { return; } - var angleUpperBound = Math.PI * 0.25 * 0.5, - swipeRight = angle < angleUpperBound; - if ( swipeRight === false && angle < Math.PI - angleUpperBound ) { - return; - } - ev.preventDefault(); - // Swipe left. - if ( swipeRight === false ) { - if ( pickerBody.classList.contains('paused') ) { - dialog.classList.remove('hide'); - dialog.classList.add('show'); - } - return; - } - // Swipe right. - if ( - pickerBody.classList.contains('paused') && - dialog.classList.contains('show') - ) { - dialog.classList.remove('show'); - dialog.classList.add('hide'); - return; +const zapElementAtPoint = function(mx, my, options) { + if ( options.highlight ) { + const elem = elementFromPoint(mx, my); + if ( elem ) { + highlightElements([ elem ]); } - stopPicker(); - }; -})(); + return; + } -/******************************************************************************/ + let elem = targetElements.length !== 0 && targetElements[0] || null; + if ( elem === null && mx !== undefined ) { + elem = elementFromPoint(mx, my); + } -const onSvgClicked = function(ev) { - if ( ev.isTrusted === false ) { return; } + if ( elem instanceof HTMLElement === false ) { return; } - // If zap mode, highlight element under mouse, this makes the zapper usable - // on touch screens. - if ( pickerBootArgs.zap ) { - let elem = targetElements.lenght !== 0 && targetElements[0]; - if ( !elem || ev.target !== svgIslands ) { - elem = elementFromPoint(ev.clientX, ev.clientY); - if ( elem !== null ) { - highlightElements([elem]); - return; - } + const getStyleValue = function(elem, prop) { + const style = window.getComputedStyle(elem); + return style ? style[prop] : ''; + }; + + // Heuristic to detect scroll-locking: remove such lock when detected. + if ( + parseInt(getStyleValue(elem, 'zIndex'), 10) >= 1000 || + getStyleValue(elem, 'position') === 'fixed' + ) { + const doc = document; + if ( getStyleValue(doc.body, 'overflowY') === 'hidden' ) { + doc.body.style.setProperty('overflow', 'auto', 'important'); } - zap(); - if ( !ev.shiftKey ) { - stopPicker(); + if ( getStyleValue(doc.body, 'position') === 'fixed' ) { + doc.body.style.setProperty('position', 'static', 'important'); } - return; - } - // https://github.com/chrisaljoudi/uBlock/issues/810#issuecomment-74600694 - // Unpause picker if: - // - click outside dialog AND - // - not in preview mode - if ( pickerBody.classList.contains('paused') ) { - if ( filterToDOMInterface.previewing === false ) { - unpausePicker(); + if ( getStyleValue(doc.documentElement, 'overflowY') === 'hidden' ) { + doc.documentElement.style.setProperty('overflow', 'auto', 'important'); } - return; - } - if ( filtersFrom(ev.clientX, ev.clientY) === 0 ) { - return; } - showDialog({ - show: ev.type === 'touch', - modifier: ev.ctrlKey - }); -}; - -/******************************************************************************/ -const svgListening = function(on) { - const action = (on ? 'add' : 'remove') + 'EventListener'; - svgRoot[action]('mousemove', onSvgHovered, { passive: true }); + elem.remove(); + highlightElementAtPoint(mx, my); }; /******************************************************************************/ @@ -1139,7 +997,7 @@ const onKeyPressed = function(ev) { ) { ev.stopPropagation(); ev.preventDefault(); - zap(); + zapElementAtPoint(); return; } // Esc @@ -1147,7 +1005,7 @@ const onKeyPressed = function(ev) { ev.stopPropagation(); ev.preventDefault(); filterToDOMInterface.preview(false); - stopPicker(); + quitPicker(); return; } }; @@ -1158,67 +1016,18 @@ const onKeyPressed = function(ev) { // May need to dynamically adjust the height of the overlay + new position // of highlighted elements. -const onScrolled = function() { +const onViewportChanged = function() { highlightElements(targetElements, true); }; /******************************************************************************/ -const pausePicker = function() { - pickerBody.classList.add('paused'); - svgListening(false); -}; - -/******************************************************************************/ - -const unpausePicker = function() { - filterToDOMInterface.preview(false); - pickerBody.classList.remove('paused'); - svgListening(true); -}; - -/******************************************************************************/ - -// Let's have the element picker code flushed from memory when no longer -// in use: to ensure this, release all local references. - -const stopPicker = function() { - vAPI.shutdown.remove(stopPicker); - - targetElements = []; - candidateElements = []; - bestCandidateFilter = null; - - if ( pickerRoot === null ) { return; } - - // https://github.com/gorhill/uBlock/issues/2060 - if ( vAPI.domFilterer instanceof Object ) { - vAPI.userStylesheet.remove(pickerCSS); - vAPI.userStylesheet.apply(); - vAPI.domFilterer.unexcludeNode(pickerRoot); - } - - window.removeEventListener('scroll', onScrolled, true); - svgListening(false); - pickerRoot.remove(); - pickerRoot = pickerBody = svgRoot = svgOcean = svgIslands = dialog = null; - - window.focus(); -}; - -/******************************************************************************/ - // Auto-select a specific target, if any, and if possible const startPicker = function() { - svgRoot.addEventListener('click', onSvgClicked); - svgRoot.addEventListener('touchstart', onSvgTouchStartStop); - svgRoot.addEventListener('touchend', onSvgTouchStartStop); - svgListening(true); - - self.addEventListener('scroll', onScrolled, true); - pickerRoot.contentWindow.addEventListener('keydown', onKeyPressed, true); - pickerRoot.contentWindow.focus(); + self.addEventListener('scroll', onViewportChanged, { passive: true }); + self.addEventListener('resize', onViewportChanged, { passive: true }); + self.addEventListener('keydown', onKeyPressed, true); // Try using mouse position if ( @@ -1227,8 +1036,7 @@ const startPicker = function() { vAPI.mouseClick.x > 0 ) { if ( filtersFrom(vAPI.mouseClick.x, vAPI.mouseClick.y) !== 0 ) { - showDialog(); - return; + return showDialog(); } } @@ -1259,89 +1067,170 @@ const startPicker = function() { } elem.scrollIntoView({ behavior: 'smooth', block: 'start' }); filtersFrom(elem); - showDialog({ modifier: true }); - return; + return showDialog({ broad: true }); } // A target was specified, but it wasn't found: abort. - stopPicker(); + quitPicker(); +}; + +/******************************************************************************/ + +// Let's have the element picker code flushed from memory when no longer +// in use: to ensure this, release all local references. + +const quitPicker = function() { + self.removeEventListener('scroll', onViewportChanged, { passive: true }); + self.removeEventListener('resize', onViewportChanged, { passive: true }); + self.removeEventListener('keydown', onKeyPressed, true); + vAPI.shutdown.remove(quitPicker); + vAPI.MessagingConnection.disconnectFrom(epickerConnectionId); + vAPI.MessagingConnection.removeListener(onConnectionMessage); + vAPI.userStylesheet.remove(pickerCSS); + vAPI.userStylesheet.apply(); + + if ( pickerRoot === null ) { return; } + + // https://github.com/gorhill/uBlock/issues/2060 + if ( vAPI.domFilterer instanceof Object ) { + vAPI.domFilterer.unexcludeNode(pickerRoot); + } + + pickerRoot.remove(); + pickerRoot = null; + + self.focus(); }; /******************************************************************************/ const onDialogMessage = function(msg) { switch ( msg.what ) { - case 'dialogInit': - startPicker(); - break; - case 'dialogPreview': - filterToDOMInterface.preview(msg.state); - break; - case 'dialogCreate': - filterToDOMInterface.queryAll(msg); - filterToDOMInterface.preview(true, true); - stopPicker(); - break; - case 'dialogPick': - unpausePicker(); - break; - case 'dialogQuit': - filterToDOMInterface.preview(false); - stopPicker(); - break; - case 'dialogSetFilter': { - const resultset = filterToDOMInterface.queryAll(msg); - highlightElements(resultset.map(a => a.elem), true); - if ( msg.filter === '!' ) { break; } - vAPI.MessagingConnection.sendTo(epickerConnectionId, { - what: 'filterResultset', - resultset: resultset.map(a => { - const o = Object.assign({}, a); - o.elem = undefined; - return o; - }), - }); - break; - } - default: - break; + case 'start': + startPicker(); + if ( targetElements.length === 0 ) { + highlightElements([], true); + } + break; + case 'dialogCreate': + filterToDOMInterface.queryAll(msg); + filterToDOMInterface.preview(true, true); + quitPicker(); + break; + case 'dialogSetFilter': { + const resultset = filterToDOMInterface.queryAll(msg); + highlightElements(resultset.map(a => a.elem), true); + if ( msg.filter === '!' ) { break; } + vAPI.MessagingConnection.sendTo(epickerConnectionId, { + what: 'filterResultset', + resultset: resultset.map(a => { + const o = Object.assign({}, a); + o.elem = undefined; + return o; + }), + }); + break; + } + case 'quitPicker': + filterToDOMInterface.preview(false); + quitPicker(); + break; + case 'highlightElementAtPoint': + highlightElementAtPoint(msg.mx, msg.my); + break; + case 'unhighlight': + highlightElements([]); + break; + case 'filterElementAtPoint': + filterElementAtPoint(msg.mx, msg.my, msg.broad); + break; + case 'zapElementAtPoint': + zapElementAtPoint(msg.mx, msg.my, msg.options); + if ( msg.options.highlight !== true && msg.options.stay !== true ) { + quitPicker(); + } + break; + case 'togglePreview': + filterToDOMInterface.preview(msg.state); + break; + default: + break; } }; /******************************************************************************/ const onConnectionMessage = function(msg) { - if ( - msg.from !== `epickerDialog-${epickerId}` || - msg.to !== `epicker-${epickerId}` - ) { - return; - } + if ( msg.from !== `epickerDialog-${epickerId}` ) { return; } switch ( msg.what ) { - case 'connectionRequested': - epickerConnectionId = msg.id; - return true; - case 'connectionBroken': - stopPicker(); - break; - case 'connectionMessage': - onDialogMessage(msg.payload); - break; + case 'connectionRequested': + epickerConnectionId = msg.id; + return true; + case 'connectionBroken': + quitPicker(); + break; + case 'connectionMessage': + onDialogMessage(msg.payload); + break; } }; /******************************************************************************/ -pickerRoot = document.createElement('iframe'); -pickerRoot.setAttribute(vAPI.sessionId, ''); +// epicker-ui.html will be injected in the page through an iframe, and +// is a sandboxed so as to prevent the page from interfering with its +// content and behavior. +// +// The purpose of epicker.js is to: +// - Install the element picker UI, and wait for the component to establish +// a direct communication channel. +// - Lookup candidate filters from elements at a specific position. +// - Highlight element(s) at a specific position or according to whether +// they match candidate filters; +// - Preview the result of applying a candidate filter; +// +// When the element picker is installed on a page, the only change the page +// sees is an iframe with a random attribute. The page can't see the content +// of the iframe, and cannot interfere with its style properties. However the +// page can remove the iframe. + +// We need extra messaging capabilities + fetch/process picker arguments. +{ + const results = await Promise.all([ + vAPI.messaging.extend(), + vAPI.messaging.send('elementPicker', { what: 'elementPickerArguments' }), + ]); + if ( results[0] !== true ) { return; } + pickerBootArgs = results[1]; + if ( typeof pickerBootArgs !== 'object' || pickerBootArgs === null ) { + return; + } + // Restore net filter union data if origin is the same. + const eprom = pickerBootArgs.eprom || null; + if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) { + lastNetFilterHostname = eprom.lastNetFilterHostname || ''; + lastNetFilterUnion = eprom.lastNetFilterUnion || ''; + } +} + +// The DOM filterer will not be present when cosmetic filtering is disabled. +if ( + pickerBootArgs.zap !== true && + vAPI.domFilterer instanceof Object === false +) { + return; +} +// https://github.com/gorhill/uBlock/issues/1529 +// In addition to inline styles, harden the element picker styles by using +// dedicated CSS rules. const pickerCSSStyle = [ 'background: transparent', 'border: 0', 'border-radius: 0', 'box-shadow: none', 'display: block', - 'height: 100%', + 'height: 100vh', 'left: 0', 'margin: 0', 'max-height: none', @@ -1351,6 +1240,7 @@ const pickerCSSStyle = [ 'opacity: 1', 'outline: 0', 'padding: 0', + 'pointer-events: auto', 'position: fixed', 'top: 0', 'visibility: visible', @@ -1358,107 +1248,40 @@ const pickerCSSStyle = [ 'z-index: 2147483647', '' ].join(' !important;'); -pickerRoot.style.cssText = pickerCSSStyle; - -// https://github.com/uBlockOrigin/uBlock-issues/issues/393 -// This needs to be injected as an inline style, *never* as a user style, -// hence why it's not added above as part of the pickerCSSStyle -// properties. -pickerRoot.style.setProperty('pointer-events', 'auto', 'important'); const pickerCSS = ` -[${vAPI.sessionId}] { +:root [${vAPI.sessionId}] { ${pickerCSSStyle} } -[${vAPI.sessionId}-clickblind] { +:root [${vAPI.sessionId}-clickblind] { pointer-events: none !important; } `; -{ - const pickerRootLoaded = new Promise(resolve => { - pickerRoot.addEventListener('load', ( ) => { resolve(); }, { once: true }); - }); - document.documentElement.append(pickerRoot); - - const results = await Promise.all([ - vAPI.messaging.send('elementPicker', { what: 'elementPickerArguments' }), - pickerRootLoaded, - ]); - - pickerBootArgs = results[0]; - - // The DOM filterer will not be present when cosmetic filtering is - // disabled. - if ( - pickerBootArgs.zap !== true && - vAPI.domFilterer instanceof Object === false - ) { - pickerRoot.remove(); - return; - } - - // Restore net filter union data if origin is the same. - const eprom = pickerBootArgs.eprom || null; - if ( eprom !== null && eprom.lastNetFilterSession === lastNetFilterSession ) { - lastNetFilterHostname = eprom.lastNetFilterHostname || ''; - lastNetFilterUnion = eprom.lastNetFilterUnion || ''; - } - - const frameDoc = pickerRoot.contentDocument; - - // Provide an id users can use as anchor to personalize uBO's element - // picker style properties. - frameDoc.documentElement.id = 'ublock0-epicker'; - - // https://github.com/gorhill/uBlock/issues/2240 - // https://github.com/uBlockOrigin/uBlock-issues/issues/170 - // Remove the already declared inline style tag: we will create a new - // one based on the removed one, and replace the old one. - const style = frameDoc.createElement('style'); - style.textContent = pickerBootArgs.frameCSS; - frameDoc.head.appendChild(style); - - pickerBody = frameDoc.body; - pickerBody.setAttribute('lang', navigator.language); - pickerBody.classList.toggle('zap', pickerBootArgs.zap === true); - - svgRoot = frameDoc.createElementNS('http://www.w3.org/2000/svg', 'svg'); - svgOcean = frameDoc.createElementNS('http://www.w3.org/2000/svg', 'path'); - svgRoot.append(svgOcean); - svgIslands = frameDoc.createElementNS('http://www.w3.org/2000/svg', 'path'); - svgRoot.append(svgIslands); - pickerBody.append(svgRoot); - - dialog = frameDoc.createElement('iframe'); - pickerBody.append(dialog); -} - -highlightElements([], true); - -// https://github.com/gorhill/uBlock/issues/1529 -// In addition to inline styles, harden the element picker styles by using -// dedicated CSS rules. vAPI.userStylesheet.add(pickerCSS); vAPI.userStylesheet.apply(); -vAPI.shutdown.add(stopPicker); +pickerRoot = document.createElement('iframe'); +pickerRoot.setAttribute(vAPI.sessionId, ''); +document.documentElement.append(pickerRoot); -// https://github.com/gorhill/uBlock/issues/3497 -// https://github.com/uBlockOrigin/uBlock-issues/issues/1215 -// Instantiate isolated element picker dialog. -if ( pickerBootArgs.zap === true ) { - startPicker(); - return; +// https://github.com/gorhill/uBlock/issues/2060 +if ( vAPI.domFilterer instanceof Object ) { + vAPI.domFilterer.excludeNode(pickerRoot); } -// https://github.com/gorhill/uBlock/issues/2060 -vAPI.domFilterer.excludeNode(pickerRoot); +vAPI.shutdown.add(quitPicker); -if ( await vAPI.messaging.extend() !== true ) { return; } vAPI.MessagingConnection.addListener(onConnectionMessage); -dialog.contentWindow.location = `${pickerBootArgs.dialogURL}&epid=${epickerId}`; +{ + const url = new URL(pickerBootArgs.pickerURL); + url.searchParams.set('epid', epickerId); + if ( pickerBootArgs.zap ) { + url.searchParams.set('zap', '1'); + } + pickerRoot.contentWindow.location = url.href; +} /******************************************************************************/ diff --git a/src/web_accessible_resources/epicker-dialog.html b/src/web_accessible_resources/epicker-ui.html similarity index 90% rename from src/web_accessible_resources/epicker-dialog.html rename to src/web_accessible_resources/epicker-ui.html index d87b4656664f9..b1f4b486212ff 100644 --- a/src/web_accessible_resources/epicker-dialog.html +++ b/src/web_accessible_resources/epicker-ui.html @@ -4,7 +4,7 @@ uBlock Origin Element Picker - + @@ -35,13 +35,14 @@ + - + From 6c6a0d3613e35c1bb3da15182b96a709cb5e5ed6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Sep 2020 10:36:06 -0400 Subject: [PATCH 3556/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d8d1b156b141f..2835a0bf14832 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.29.3.8 +1.29.3.9 From 5856fab2d7e4149b9122fea383493264c762881d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Sep 2020 10:45:26 -0400 Subject: [PATCH 3557/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 52172b5a11789..a5364909c1e22 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.29.3.8", + "version": "1.29.3.9", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b8", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b8/uBlock0_1.29.3b8.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.29.3b9", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.29.3b9/uBlock0_1.29.3b9.firefox.signed.xpi" } ] } From 4ce3ff2e04edbc01ce6f0486552a9e7393cc68de Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Sep 2020 10:56:29 -0400 Subject: [PATCH 3558/4093] Remove code for unused message --- src/js/epicker-ui.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 3b01cd4c3ee9a..2e0014ea02c02 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -642,10 +642,6 @@ const onPickerMessage = function(msg) { } break; } - case 'svgListening': { - svgListening(msg.on); - break; - } case 'svgPaths': { let { ocean, islands } = msg; ocean += islands; From 3a51ca0002701bd44a5ce486a7a9f320bf1baa47 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Sep 2020 09:58:54 -0400 Subject: [PATCH 3559/4093] Add `:style`-based procedural operator --- docs/tests/procedural-cosmetic-filters.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/tests/procedural-cosmetic-filters.html b/docs/tests/procedural-cosmetic-filters.html index 714ea1acdd171..450cbd14d8ef9 100644 --- a/docs/tests/procedural-cosmetic-filters.html +++ b/docs/tests/procedural-cosmetic-filters.html @@ -182,6 +182,11 @@

        Tests

        #pcf #a20 b:upward(.fail) +
        +
        + #pcf #a21 b:upward(.fail):style(visibility: hidden !important) +
        + + diff --git a/src/js/dyna-rules.js b/src/js/dyna-rules.js index 581d48fc669d3..2c3655cb53072 100644 --- a/src/js/dyna-rules.js +++ b/src/js/dyna-rules.js @@ -29,6 +29,9 @@ /******************************************************************************/ +const psl = self.publicSuffixList; +const hostnameToDomainMap = new Map(); + const mergeView = new CodeMirror.MergeView( document.querySelector('.codeMirrorMergeContainer'), { @@ -387,8 +390,22 @@ const onPresentationChanged = (( ) => { const reRule = /^([^ ]+) ([^/ ]+) ([^ ]+ [^ ]+)/; const reUrlRule = /^([^ ]+) ([^ ]+) ([^ ]+ [^ ]+)/; - const reverseHn = function(hn) { - return hn.split('.').reverse().join('.'); + const sortNormalizeHn = function(hn) { + let domain = hostnameToDomainMap.get(hn); + if ( domain === undefined ) { + domain = psl.getDomain(hn); + hostnameToDomainMap.set(hn, domain); + } + let normalized = domain || hn; + if ( hn.length !== domain.length ) { + const subdomains = hn.slice(0, hn.length - domain.length - 1); + normalized += '.' + ( + subdomains.includes('.') + ? subdomains.split('.').reverse().join('.') + : subdomains + ); + } + return normalized; }; const slotFromRule = rule => { @@ -396,18 +413,18 @@ const onPresentationChanged = (( ) => { let match = reSwRule.exec(rule); if ( match !== null ) { type = ' ' + match[1]; - srcHn = reverseHn(match[2]); + srcHn = sortNormalizeHn(match[2]); desHn = srcHn; extra = match[3]; } else if ( (match = reRule.exec(rule)) !== null ) { type = '\x10FFFE'; - srcHn = reverseHn(match[1]); - desHn = reverseHn(match[2]); + srcHn = sortNormalizeHn(match[1]); + desHn = sortNormalizeHn(match[2]); extra = match[3]; } else if ( (match = reUrlRule.exec(rule)) !== null ) { type = '\x10FFFF'; - srcHn = reverseHn(match[1]); - desHn = reverseHn(vAPI.hostnameFromURI(match[2])); + srcHn = sortNormalizeHn(match[1]); + desHn = sortNormalizeHn(vAPI.hostnameFromURI(match[2])); extra = match[3]; } if ( sortType === 0 ) { @@ -614,6 +631,7 @@ vAPI.messaging.send('dashboard', { }).then(details => { thePanes.orig.original = details.permanentRules; thePanes.edit.original = details.sessionRules; + psl.fromSelfie(details.pslSelfie); onPresentationChanged(true); }); diff --git a/src/js/messaging.js b/src/js/messaging.js index 06cad9fdb2896..561c665613bfc 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1053,7 +1053,8 @@ const getRules = function() { µb.sessionFirewall.toArray().concat( µb.sessionSwitches.toArray(), µb.sessionURLFiltering.toArray() - ) + ), + pslSelfie: self.publicSuffixList.toSelfie(), }; }; From b12e0e05ea9d319ba3b6c2b97d9322c347230bd7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Nov 2020 10:02:22 -0500 Subject: [PATCH 3878/4093] Extract `Homepage` URL from a list when present Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1346 Additionally, fixed a case of filter list being compiled twice at subscription time. --- src/js/assets.js | 11 ++++++++--- src/js/storage.js | 39 ++++++++++++++++++++++++--------------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 3390351fac37c..b173ba151b0fc 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -538,10 +538,12 @@ const assetCacheRead = async function(assetKey, updateReadTime = false) { const assetCacheWrite = async function(assetKey, details) { let content = ''; + let options = {}; if ( typeof details === 'string' ) { content = details; } else if ( details instanceof Object ) { content = details.content || ''; + options = details; } if ( content === '' ) { @@ -555,8 +557,8 @@ const assetCacheWrite = async function(assetKey, details) { entry = cacheDict[assetKey] = {}; } entry.writeTime = entry.readTime = Date.now(); - if ( details instanceof Object && typeof details.url === 'string' ) { - entry.remoteURL = details.url; + if ( options.url === 'string' ) { + entry.remoteURL = options.url; } µBlock.cacheStorage.set({ assetCacheRegistry, @@ -565,7 +567,9 @@ const assetCacheWrite = async function(assetKey, details) { const result = { assetKey, content }; // https://github.com/uBlockOrigin/uBlock-issues/issues/248 - fireNotification('after-asset-updated', result); + if ( options.silent !== true ) { + fireNotification('after-asset-updated', result); + } return result; }; @@ -733,6 +737,7 @@ api.get = async function(assetKey, options = {}) { assetCacheWrite(assetKey, { content: details.content, url: contentURL, + silent: options.silent === true, }); } return reportBack(details.content, contentURL); diff --git a/src/js/storage.js b/src/js/storage.js index 1fcde7527e23d..96c190454fa55 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -749,7 +749,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { return { assetKey, content: '' }; } - const rawDetails = await this.assets.get(assetKey); + const rawDetails = await this.assets.get(assetKey, { silent: true }); // Compiling an empty string results in an empty string. if ( rawDetails.content === '' ) { rawDetails.assetKey = assetKey; @@ -782,6 +782,10 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { // https://github.com/gorhill/uBlock/issues/3406 // Lower minimum update period to 1 day. +// https://bugs.chromium.org/p/v8/issues/detail?id=2869 +// orphanizeString is to work around String.slice() potentially causing +// the whole raw filter list to be held in memory just because we cut out +// the title as a substring. µBlock.extractFilterListMetadata = function(assetKey, raw) { const listEntry = this.availableFilterLists[assetKey]; @@ -790,27 +794,32 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { const head = raw.slice(0, 1024); // https://github.com/gorhill/uBlock/issues/313 // Always try to fetch the name if this is an external filter list. - if ( listEntry.title === '' || listEntry.group === 'custom' ) { - const matches = head.match(/(?:^|\n)(?:!|# )[\t ]*Title[\t ]*:([^\n]+)/i); - if ( matches !== null ) { - // https://bugs.chromium.org/p/v8/issues/detail?id=2869 - // orphanizeString is to work around String.slice() - // potentially causing the whole raw filter list to be held in - // memory just because we cut out the title as a substring. - listEntry.title = this.orphanizeString(matches[1].trim()); + if ( listEntry.group === 'custom' ) { + let matches = head.match(/(?:^|\n)(?:!|# )[\t ]*Title[\t ]*:([^\n]+)/i); + const title = matches && matches[1].trim() || ''; + if ( title !== '' && title !== listEntry.title ) { + listEntry.title = this.orphanizeString(title); + this.assets.registerAssetSource(assetKey, { title }); + } + matches = head.match(/(?:^|\n)(?:!|# )[\t ]*Homepage[\t ]*:[\t ]*(https?:\/\/\S+)/i); + const supportURL = matches && matches[1] || ''; + if ( supportURL !== '' && supportURL !== listEntry.supportURL ) { + listEntry.supportURL = this.orphanizeString(supportURL); + this.assets.registerAssetSource(assetKey, { supportURL }); } } // Extract update frequency information const matches = head.match(/(?:^|\n)(?:!|# )[\t ]*Expires[\t ]*:[\t ]*(\d+)[\t ]*(h)?/i); if ( matches !== null ) { - let v = parseInt(matches[1], 10); - if ( isNaN(v) === false ) { + let updateAfter = parseInt(matches[1], 10); + if ( isNaN(updateAfter) === false ) { if ( matches[2] !== undefined ) { - v = Math.ceil(v / 24); + updateAfter = Math.ceil(updateAfter / 24); } - v = Math.max(v, 1); - if ( v !== listEntry.updateAfter ) { - this.assets.registerAssetSource(assetKey, { updateAfter: v }); + updateAfter = Math.max(updateAfter, 1); + if ( updateAfter !== listEntry.updateAfter ) { + listEntry.updateAfter = updateAfter; + this.assets.registerAssetSource(assetKey, { updateAfter }); } } } From 6b1a9e7aeed8768b4685ce8a79daa717d5e11918 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Nov 2020 11:25:56 -0500 Subject: [PATCH 3879/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 54b84a5826354..a6bfc31dce71a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.30.11.100", + "version": "1.31.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.30.11rc0", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.30.11rc0/uBlock0_1.30.11rc0.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b0", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b0/uBlock0_1.31.1b0.firefox.signed.xpi" } ] } From ee2fd45f00e680450f3400a4d0a63bc6d80d950a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Nov 2020 12:14:23 -0500 Subject: [PATCH 3880/4093] Ensure we do not extract truncated URL for Homepage directive Related feedback: - https://github.com/gorhill/uBlock/commit/b12e0e05ea9d319ba3b6c2b97d9322c347230bd7#commitcomment-44309540 --- src/js/storage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/storage.js b/src/js/storage.js index 96c190454fa55..56d529f7b0b78 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -801,7 +801,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { listEntry.title = this.orphanizeString(title); this.assets.registerAssetSource(assetKey, { title }); } - matches = head.match(/(?:^|\n)(?:!|# )[\t ]*Homepage[\t ]*:[\t ]*(https?:\/\/\S+)/i); + matches = head.match(/(?:^|\n)(?:!|# )[\t ]*Homepage[\t ]*:[\t ]*(https?:\/\/\S+)\s/i); const supportURL = matches && matches[1] || ''; if ( supportURL !== '' && supportURL !== listEntry.supportURL ) { listEntry.supportURL = this.orphanizeString(supportURL); From 38cecddcd18892fc48b407f1bf94d466305e1e35 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 18 Nov 2020 14:11:36 -0500 Subject: [PATCH 3881/4093] Improve zapper's detection of scroll-locked documents --- src/js/scriptlets/epicker.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/js/scriptlets/epicker.js b/src/js/scriptlets/epicker.js index f0b37e3e6bd5c..02f625225e7d7 100644 --- a/src/js/scriptlets/epicker.js +++ b/src/js/scriptlets/epicker.js @@ -921,12 +921,12 @@ const zapElementAtPoint = function(mx, my, options) { return; } - let elem = targetElements.length !== 0 && targetElements[0] || null; - if ( elem === null && mx !== undefined ) { - elem = elementFromPoint(mx, my); + let elemToRemove = targetElements.length !== 0 && targetElements[0] || null; + if ( elemToRemove === null && mx !== undefined ) { + elemToRemove = elementFromPoint(mx, my); } - if ( elem instanceof Element === false ) { return; } + if ( elemToRemove instanceof Element === false ) { return; } const getStyleValue = function(elem, prop) { const style = window.getComputedStyle(elem); @@ -934,23 +934,30 @@ const zapElementAtPoint = function(mx, my, options) { }; // Heuristic to detect scroll-locking: remove such lock when detected. - if ( - parseInt(getStyleValue(elem, 'zIndex'), 10) >= 1000 || - getStyleValue(elem, 'position') === 'fixed' - ) { + let maybeScrollLocked = false; + let elem = elemToRemove; + do { + maybeScrollLocked = + parseInt(getStyleValue(elem, 'zIndex'), 10) >= 1000 || + getStyleValue(elem, 'position') === 'fixed'; + elem = elem.parentElement; + } while ( elem !== null && maybeScrollLocked === false ); + if ( maybeScrollLocked ) { const doc = document; if ( getStyleValue(doc.body, 'overflowY') === 'hidden' ) { doc.body.style.setProperty('overflow', 'auto', 'important'); } if ( getStyleValue(doc.body, 'position') === 'fixed' ) { - doc.body.style.setProperty('position', 'static', 'important'); + doc.body.style.setProperty('position', 'initial', 'important'); + } + if ( getStyleValue(doc.documentElement, 'position') === 'fixed' ) { + doc.documentElement.style.setProperty('position', 'initial', 'important'); } if ( getStyleValue(doc.documentElement, 'overflowY') === 'hidden' ) { doc.documentElement.style.setProperty('overflow', 'auto', 'important'); } } - - elem.remove(); + elemToRemove.remove(); highlightElementAtPoint(mx, my); }; From b1c55b3de925fc6b4bc8442cd8c34c25e658157e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 19 Nov 2020 11:33:09 -0500 Subject: [PATCH 3882/4093] Emphasize entity portion of hostnames in _"My rules"_ --- src/js/codemirror/ubo-dynamic-filtering.js | 247 ++++++++++++--------- src/js/dyna-rules.js | 26 ++- 2 files changed, 160 insertions(+), 113 deletions(-) diff --git a/src/js/codemirror/ubo-dynamic-filtering.js b/src/js/codemirror/ubo-dynamic-filtering.js index 5c048a623543b..058858692c533 100644 --- a/src/js/codemirror/ubo-dynamic-filtering.js +++ b/src/js/codemirror/ubo-dynamic-filtering.js @@ -60,8 +60,9 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { const reBadHn = /[%]|^\.|\.$/; const slices = []; let sliceIndex = 0; - const tokens = []; - let tokenIndex = 0; + let sliceCount = 0; + let hostnameToDomainMap = new Map(); + let psl; const isValidHostname = hnin => { if ( hnin === '*' ) { return true; } @@ -75,125 +76,159 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { return hnout !== '_' && hnout !== '' && reBadHn.test(hnout) === false; }; - const isSwitchRule = ( ) => { - const token = tokens[0]; - return token.charCodeAt(token.length-1) === 0x3A /* ':' */; + const addSlice = (len, style = null) => { + let i = sliceCount; + if ( i === slices.length ) { + slices[i] = { len: 0, style: null }; + } + const entry = slices[i]; + entry.len = len; + entry.style = style; + sliceCount += 1; }; - const isURLRule = ( ) => { - return tokens[1].indexOf('://') > 0; + const addMatchSlice = (match, style = null) => { + const len = match !== null ? match[0].length : 0; + addSlice(len, style); + return match !== null ? match.input.slice(len) : ''; }; - const skipToEnd = (stream, style = null) => { - stream.skipToEnd(); - return style; + const addMatchHnSlices = (match, style = null) => { + const hn = match[0]; + if ( hn === '*' ) { + return addMatchSlice(match, style); + } + let dn = hostnameToDomainMap.get(hn) || ''; + if ( dn === '' && psl !== undefined ) { + dn = /(\d|\])$/.test(hn) ? hn : (psl.getDomain(hn) || hn); + } + const entityBeg = hn.length - dn.length; + if ( entityBeg !== 0 ) { + addSlice(entityBeg, style); + } + let entityEnd = dn.indexOf('.'); + if ( entityEnd === -1 ) { entityEnd = dn.length; } + addSlice(entityEnd, style !== null ? `${style} strong` : 'strong'); + if ( entityEnd < dn.length ) { + addSlice(dn.length - entityEnd, style); + } + return match.input.slice(hn.length); }; - const token = function(stream) { - if ( stream.sol() ) { - if ( stream.string === '...' ) { - return stream.skipToEnd(stream); + const makeSlices = (stream, opts) => { + sliceIndex = 0; + sliceCount = 0; + let { string } = stream; + if ( string === '...' ) { return; } + const { sortType } = opts; + const reNotToken = /^\s+/; + const reToken = /^\S+/; + // leading whitespaces + let match = reNotToken.exec(string); + if ( match !== null ) { + string = addMatchSlice(match); + } + // first token + match = reToken.exec(string); + if ( match === null ) { return; } + // hostname or switch + const isSwitchRule = validSwitches.has(match[0]); + if ( isSwitchRule ) { + string = addMatchSlice(match, sortType === 0 ? 'sortkey' : null); + } else if ( isValidHostname(match[0]) ) { + if ( sortType === 1 ) { + string = addMatchHnSlices(match, 'sortkey'); + } else { + string = addMatchHnSlices(match, null); } - slices.length = 0; - tokens.length = 0; - const reTokens = /\S+/g; - for (;;) { - const lastIndex = reTokens.lastIndex; - const match = reTokens.exec(stream.string); - if ( match === null ) { break; } - const l = match.index; - const r = reTokens.lastIndex; - if ( l !== lastIndex ) { - slices.push({ t: false, l: lastIndex, r: l }); - } - slices.push({ t: true, l, r }); - tokens.push(stream.string.slice(l, r)); + } else { + string = addMatchSlice(match, 'error'); + } + // whitespaces before second token + match = reNotToken.exec(string); + if ( match === null ) { return; } + string = addMatchSlice(match); + // second token + match = reToken.exec(string); + if ( match === null ) { return; } + // hostname or url + const isURLRule = isSwitchRule === false && match[0].includes('://'); + if ( isURLRule ) { + if ( isSwitchRule ) { + string = addMatchSlice(match, 'error'); + } else { + string = addMatchSlice(match, sortType === 2 ? 'sortkey' : null); } - sliceIndex = tokenIndex = 0; + } else if ( isValidHostname(match[0]) === false ) { + string = addMatchSlice(match, 'error'); + } else if ( sortType === 1 && isSwitchRule || sortType === 2 ) { + string = addMatchHnSlices(match, 'sortkey'); + } else { + string = addMatchHnSlices(match, null); } - if ( sliceIndex >= slices.length ) { - return stream.skipToEnd(stream); + // whitespaces before third token + match = reNotToken.exec(string); + if ( match === null ) { return; } + string = addMatchSlice(match); + // third token + match = reToken.exec(string); + if ( match === null ) { return; } + // rule type or switch state + if ( isSwitchRule ) { + string = validSwitcheStates.has(match[0]) + ? addMatchSlice(match) + : addMatchSlice(match, 'error'); + } else if ( isURLRule ) { + string = invalidURLRuleTypes.has(match[0]) + ? addMatchSlice(match, 'error') + : addMatchSlice(match); + } else { + string = validHnRuleTypes.has(match[0]) + ? addMatchSlice(match) + : addMatchSlice(match, 'error'); } - const slice = slices[sliceIndex++]; - stream.pos = slice.r; - if ( slice.t !== true ) { return null; } - const token = tokens[tokenIndex++]; - // Field 1: per-site switch or hostname - if ( tokenIndex === 1 ) { - if ( isSwitchRule(token) ) { - if ( validSwitches.has(token) === false ) { - return skipToEnd(stream, 'error'); - } - if ( this.sortType === 0 ) { return 'sortkey'; } - return null; - } - if ( isValidHostname(token) === false ) { - return skipToEnd(stream, 'error'); - } - if ( this.sortType === 1 ) { return 'sortkey'; } - return null; + // whitespaces before fourth token + match = reNotToken.exec(string); + if ( match === null ) { return; } + string = addMatchSlice(match); + // fourth token + match = reToken.exec(string); + if ( match === null ) { return; } + string = isSwitchRule || validActions.has(match[0]) === false + ? addMatchSlice(match, 'error') + : addMatchSlice(match, `${match[0]}rule`); + // whitespaces before end of line + match = reNotToken.exec(string); + if ( match === null ) { return; } + string = addMatchSlice(match); + // any token beyond fourth token is invalid + match = reToken.exec(string); + if ( match !== null ) { + string = addMatchSlice(null, 'error'); } - // Field 2: hostname or url - if ( tokenIndex === 2 ) { - if ( isSwitchRule(tokens[0]) ) { - if ( isValidHostname(token) === false ) { - return skipToEnd(stream, 'error'); - } - if ( this.sortType === 1 ) { return 'sortkey'; } - } - if ( - isValidHostname(token) === false && - isURLRule(token) === false - ) { - return skipToEnd(stream, 'error'); - } - if ( this.sortType === 2 ) { return 'sortkey'; } - return null; + }; + + const token = function(stream) { + if ( stream.sol() ) { + makeSlices(stream, this); } - // Field 3 - if ( tokenIndex === 3 ) { - // Switch rule - if ( isSwitchRule(tokens[0]) ) { - if ( validSwitcheStates.has(token) === false ) { - return skipToEnd(stream, 'error'); - } - if ( token === 'true' ) { return 'blockrule'; } - if ( token === 'false' ) { return 'allowrule'; } - return null; - } - // Hostname rule - if ( isURLRule(tokens[1]) === false ) { - if ( - tokens[1] !== '*' && token !== '*' || - tokens[1] === '*' && validHnRuleTypes.has(token) === false - ) { - return skipToEnd(stream, 'error'); - } - return null; - } - // URL rule - if ( - /[^a-z_-]/.test(token) && token !== '*' || - invalidURLRuleTypes.has(token) - ) { - return skipToEnd(stream, 'error'); - } + if ( sliceIndex >= sliceCount ) { + stream.skipToEnd(stream); return null; } - // Field 4 - if ( tokenIndex === 4 ) { - if ( - isSwitchRule(tokens[0]) || - validActions.has(token) === false - ) { - return skipToEnd(stream, 'error'); - } - if ( token === 'allow' ) { return 'allowrule'; } - if ( token === 'block' ) { return 'blockrule'; } - return 'nooprule'; + const { len, style } = slices[sliceIndex++]; + if ( len === 0 ) { + stream.skipToEnd(); + } else { + stream.pos += len; } - return skipToEnd(stream); + return style; }; - return { token, sortType: 1 }; + return { + token, + sortType: 1, + setHostnameToDomainMap: a => { hostnameToDomainMap = a; }, + setPSL: a => { psl = a; }, + }; }); diff --git a/src/js/dyna-rules.js b/src/js/dyna-rules.js index 2c3655cb53072..6fcf3c226e56f 100644 --- a/src/js/dyna-rules.js +++ b/src/js/dyna-rules.js @@ -393,7 +393,7 @@ const onPresentationChanged = (( ) => { const sortNormalizeHn = function(hn) { let domain = hostnameToDomainMap.get(hn); if ( domain === undefined ) { - domain = psl.getDomain(hn); + domain = /(\d|\])$/.test(hn) ? hn : psl.getDomain(hn); hostnameToDomainMap.set(hn, domain); } let normalized = domain || hn; @@ -488,15 +488,27 @@ const onPresentationChanged = (( ) => { }; return function(clearHistory) { - thePanes.orig.modified = thePanes.orig.original.slice(); - thePanes.edit.modified = thePanes.edit.original.slice(); + const origPane = thePanes.orig; + const editPane = thePanes.edit; + origPane.modified = origPane.original.slice(); + editPane.modified = editPane.original.slice(); const select = document.querySelector('#ruleFilter select'); sortType = parseInt(select.value, 10); if ( isNaN(sortType) ) { sortType = 1; } - thePanes.orig.doc.getMode().sortType = sortType; - thePanes.edit.doc.getMode().sortType = sortType; - sort(thePanes.orig.modified); - sort(thePanes.edit.modified); + { + const mode = origPane.doc.getMode(); + mode.sortType = sortType; + mode.setHostnameToDomainMap(hostnameToDomainMap); + mode.setPSL(psl); + } + { + const mode = editPane.doc.getMode(); + mode.sortType = sortType; + mode.setHostnameToDomainMap(hostnameToDomainMap); + mode.setPSL(psl); + } + sort(origPane.modified); + sort(editPane.modified); collapse(); rulesToDoc(clearHistory); onTextChanged(clearHistory); From 941e7e8e96467c73e2568d13f5b7d1bcf3a6a0df Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 19 Nov 2020 11:34:32 -0500 Subject: [PATCH 3883/4093] new revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 7a251efb7b000..87da5723b6f5e 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.0 +1.31.1.1 From ab98cd46b18a66add4c273063bc27e37e2513f6f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Nov 2020 05:34:56 -0500 Subject: [PATCH 3884/4093] Bring back action/state highlighting in _"My rules"_ --- src/js/codemirror/ubo-dynamic-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/codemirror/ubo-dynamic-filtering.js b/src/js/codemirror/ubo-dynamic-filtering.js index 058858692c533..d7f431238256e 100644 --- a/src/js/codemirror/ubo-dynamic-filtering.js +++ b/src/js/codemirror/ubo-dynamic-filtering.js @@ -176,7 +176,7 @@ CodeMirror.defineMode('ubo-dynamic-filtering', ( ) => { // rule type or switch state if ( isSwitchRule ) { string = validSwitcheStates.has(match[0]) - ? addMatchSlice(match) + ? addMatchSlice(match, match[0] === 'true' ? 'blockrule' : 'allowrule') : addMatchSlice(match, 'error'); } else if ( isURLRule ) { string = invalidURLRuleTypes.has(match[0]) From 13f6bdae37e4196cee37dcf6f322877d0f1f657f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Nov 2020 07:14:02 -0500 Subject: [PATCH 3885/4093] Improve representation of modifier filters in logger As per feedback from filter list maintainers. --- src/css/logger-ui.css | 16 ++++++++++++---- src/css/themes/default.css | 8 ++++++++ src/js/logger-ui.js | 7 ++++++- src/js/messaging.js | 3 +++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index b85edceba1b70..c0941bdaf8263 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -233,7 +233,11 @@ body[dir="rtl"] #netInspector #filterExprPicker { } #vwRenderer .logEntry > div[data-status="1"], #netFilteringDialog > .panes > .details > div[data-status="1"] { - background-color: rgba(192, 0, 0, 0.1); + background-color: var(--logger-blocked-surface); + } +#vwRenderer .logEntry > div[data-status="1"][data-modifier], +#netFilteringDialog > .panes > .details > div[data-status="1"][data-modifier] { + background-color: var(--logger-modified-surface); } body.colorBlind #vwRenderer .logEntry > div[data-status="1"], body.colorBlind #netFilteringDialog > .panes > .details > div[data-status="1"] { @@ -247,7 +251,7 @@ body.colorBlind #vwRenderer .logEntry > div[data-status="3"] { } #vwRenderer .logEntry > div[data-status="2"], #netFilteringDialog > .panes > .details > div[data-status="2"] { - background-color: rgba(0, 160, 0, 0.1); + background-color: var(--logger-allowed-surface); } body.colorBlind #vwRenderer .logEntry > div[data-status="2"], body.colorBlind #netFilteringDialog > .panes > .details > div[data-status="2"] { @@ -380,7 +384,11 @@ body[dir="rtl"] #vwRenderer .logEntry > div > span:first-child { } #vwRenderer .logEntry > div[data-status="1"] > span:nth-of-type(7) b, #netFilteringDialog > .panes > .details > div[data-status="1"] b { - background-color: rgba(192, 0, 0, 0.2); + background-color: var(--logger-blocked-em-surface); + } +#vwRenderer .logEntry > div[data-status="1"][data-modifier] > span:nth-of-type(7) b, +#netFilteringDialog > .panes > .details > div[data-status="1"][data-modifier] b { + background-color: var(--logger-modified-em-surface); } body.colorBlind #vwRenderer .logEntry > div[data-status="1"] > span:nth-of-type(7) b, body.colorBlind #netFilteringDialog > .panes > .details > div[data-status="1"] b { @@ -394,7 +402,7 @@ body.colorBlind #vwRenderer .logEntry > div[data-status="3"] > span:nth-of-type( } #vwRenderer .logEntry > div[data-status="2"] > span:nth-of-type(7) b, #netFilteringDialog > .panes > .details > div[data-status="2"] b { - background-color: rgba(0, 160, 0, 0.2); + background-color: var(--logger-allowed-em-surface); } body.colorBlind #vwRenderer .logEntry > div[data-status="2"] > span:nth-of-type(7) b, body.colorBlind #netFilteringDialog > .panes > .details > div[data-status="2"] b { diff --git a/src/css/themes/default.css b/src/css/themes/default.css index 0d2dbb5116ea3..2b4c08cf7f6af 100644 --- a/src/css/themes/default.css +++ b/src/css/themes/default.css @@ -202,6 +202,14 @@ --df-block-ink: #ff0000; --df-noop-ink: var(--dark-gray-10); --df-key-ink: var(--violet-70); + + /* logger */ + --logger-blocked-surface: #c0000014; + --logger-modified-surface: #0000c010; + --logger-allowed-surface: #00a00014; + --logger-blocked-em-surface: #c0000036; + --logger-modified-em-surface: #0000c028; + --logger-allowed-em-surface: #00a00036; } /** diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 4265c37fc3a6c..aae02f3e15099 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -646,7 +646,6 @@ const viewPort = (( ) => { const divcl = div.classList; let span; - // Realm if ( details.realm !== undefined ) { divcl.add(details.realm + 'Realm'); @@ -686,6 +685,9 @@ const viewPort = (( ) => { } if ( filteringType === 'static' ) { divcl.add('canLookup'); + if ( filter.modifier === true ) { + div.setAttribute('data-modifier', ''); + } } else if ( filteringType === 'cosmetic' ) { divcl.add('canLookup'); divcl.toggle('isException', filter.raw.startsWith('#@#')); @@ -1651,6 +1653,9 @@ const reloadTab = function(ev) { const attr = tr.getAttribute('data-status') || ''; if ( attr !== '' ) { rows[7].setAttribute('data-status', attr); + if ( tr.hasAttribute('data-modifier') ) { + rows[7].setAttribute('data-modifier', ''); + } } rows[7].children[1].appendChild(trch[6].cloneNode(true)); } else { diff --git a/src/js/messaging.js b/src/js/messaging.js index 561c665613bfc..3ba2d9dff8a7c 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1584,6 +1584,9 @@ const logCSPViolations = function(pageStore, request) { if ( type === 'script' ) { type = 'inline-script'; } else if ( type === 'font' ) { type = 'inline-font'; } } + // The resource was blocked as a result of applying a CSP directive + // elsewhere rather than to the resource itself. + logData.modifier = undefined; fctxt.setURL(violation.url) .setType(type) .setFilter(logData) From f903c956078bc595c9760679490d29acbe22b4d0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Nov 2020 07:36:06 -0500 Subject: [PATCH 3886/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index a6bfc31dce71a..2c69aa92a66cf 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.0", + "version": "1.31.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b0", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b0/uBlock0_1.31.1b0.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b1", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b1/uBlock0_1.31.1b1.firefox.signed.xpi" } ] } From efa8f92d215cee40aba1edf9603e90dd793f8eaa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Nov 2020 09:00:53 -0500 Subject: [PATCH 3887/4093] Use cogs icon for access to dashboard in popup panel Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1319 --- src/popup-fenix.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/popup-fenix.html b/src/popup-fenix.html index 4d0c495fd0025..73c08e045b41f 100644 --- a/src/popup-fenix.html +++ b/src/popup-fenix.html @@ -60,7 +60,7 @@ bolt eye-dropper list-alt - sliders + cogs
        From 4b943cf07fdcef38ef3fb3276120060c7e82b022 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 20 Nov 2020 11:37:00 -0500 Subject: [PATCH 3888/4093] Fix scrollbar not receiving mouse event in element picker Not sure why I set z-index to 0; removed due to mouse event not reaching the scrollbar. --- src/css/epicker-ui.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/css/epicker-ui.css b/src/css/epicker-ui.css index 53552d94aa513..c571462970a9b 100644 --- a/src/css/epicker-ui.css +++ b/src/css/epicker-ui.css @@ -96,9 +96,6 @@ html#ublock0-epicker, .CodeMirror pre { padding: 0; } -.CodeMirror-vscrollbar { - z-index: 0; - } #ublock0-epicker section .resultsetWidgets { display: flex; From 8d3c4916b0a0a32a91a0f5bfd596ee58062c6c2b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Nov 2020 09:51:14 -0500 Subject: [PATCH 3889/4093] Skip trying to find effective context for `about:srcdoc` frames `about:srcdoc` frames are their own origin, trying to use the origin of the parent context causes an exception to be thrown when accessing location.href. --- src/js/contentscript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 8b02bf0b1dd83..bd0768c1e6240 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -116,7 +116,7 @@ vAPI.contentScript = true; try { while ( context !== self.top && - context.location.protocol === 'about:' + context.location.href.startsWith('about:blank') ) { context = context.parent; } From daf464b3c30e9c0c5f5991ba1bde8f9dca1d7078 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Nov 2020 09:57:54 -0500 Subject: [PATCH 3890/4093] Add support to auto-complete values of domain lists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-complete feature in the _"My filters"_ pane will use hostname/domain from the set of opened tabs to assist in entering values for `domain=` option. This also works for the implict `domain=` option ṗrepending static extended filters. --- src/js/1p-filters.js | 1 + src/js/codemirror/ubo-static-filtering.js | 70 +++++++++++++++++------ src/js/messaging.js | 23 +++++++- 3 files changed, 75 insertions(+), 19 deletions(-) diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index adf179f8825ee..7d70b85ade01d 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -54,6 +54,7 @@ vAPI.messaging.send('dashboard', { }).then(response => { if ( response instanceof Object === false ) { return; } const mode = cmEditor.getMode(); + // TODO: listen to changes in currently opened set of tabs? if ( mode.setHints instanceof Function ) { mode.setHints(response); } diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index b4a74239caffd..c4b5d68e622a1 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -34,6 +34,7 @@ const redirectNames = new Map(); const scriptletNames = new Map(); const preparseDirectiveTokens = new Map(); const preparseDirectiveHints = []; +const originHints = []; /******************************************************************************/ @@ -362,6 +363,9 @@ CodeMirror.defineMode('ubo-static-filtering', function() { preparseDirectiveTokens.set(a, b); }); preparseDirectiveHints.push(...details.preparseDirectiveHints); + for ( const hint of details.originHints ) { + originHints.push(hint); + } initHints(); }, get parser() { @@ -418,7 +422,23 @@ const initHints = function() { }; }; - const getNetOptionHints = function(cursor, isNegated, seedLeft, seedRight) { + const getOriginHints = function(cursor, line) { + const beg = cursor.ch; + const matchLeft = /[^,|=~]*$/.exec(line.slice(0, beg)); + const matchRight = /^[^#,|]*/.exec(line.slice(beg)); + if ( matchLeft === null || matchRight === null ) { return; } + const hints = []; + for ( const text of originHints ) { + hints.push(text); + } + return pickBestHints(cursor, matchLeft[0], matchRight[0], hints); + }; + + const getNetOptionHints = function(cursor, seedLeft, seedRight) { + const isNegated = seedLeft.startsWith('~'); + if ( isNegated ) { + seedLeft = seedLeft.slice(1); + } const assignPos = seedRight.indexOf('='); if ( assignPos !== -1 ) { seedRight = seedRight.slice(0, assignPos); } const isException = parser.isException(); @@ -448,26 +468,32 @@ const initHints = function() { const getNetHints = function(cursor, line) { const beg = cursor.ch; - if ( beg < parser.optionsSpan ) { return; } + if ( parser.optionsSpan.len === 0 ) { + if ( /[^\w\x80-\xF4#,.-]/.test(line) === false ) { + return getOriginHints(cursor, line); + } + return; + } + if ( beg < parser.slices[parser.optionsSpan.i+1] ) { return; } const lineBefore = line.slice(0, beg); const lineAfter = line.slice(beg); - let matchLeft = /~?([^$,~]*)$/.exec(lineBefore); - let matchRight = /^([^,]*)/.exec(lineAfter); + let matchLeft = /[^$,]*$/.exec(lineBefore); + let matchRight = /^[^,]*/.exec(lineAfter); if ( matchLeft === null || matchRight === null ) { return; } - let pos = matchLeft[1].indexOf('='); - if ( pos === -1 ) { - return getNetOptionHints( + const assignPos = matchLeft[0].indexOf('='); + if ( assignPos === -1 ) { + return getNetOptionHints(cursor, matchLeft[0], matchRight[0]); + } + if ( /^redirect(-rule)?=/.test(matchLeft[0]) ) { + return getNetRedirectHints( cursor, - matchLeft[0].startsWith('~'), - matchLeft[1], - matchRight[1] + matchLeft[0].slice(assignPos + 1), + matchRight[0] ); } - return getNetRedirectHints( - cursor, - matchLeft[1].slice(pos + 1), - matchRight[1] - ); + if ( matchLeft[0].startsWith('domain=') ) { + return getOriginHints(cursor, line); + } }; const getExtSelectorHints = function(cursor, line) { @@ -527,10 +553,15 @@ const initHints = function() { const line = cm.getLine(cursor.line); parser.analyze(line); if ( parser.category === parser.CATStaticExtFilter ) { - if ( parser.hasFlavor(parser.BITFlavorExtScriptlet) ) { - return getExtScriptletHints(cursor, line); + let hints; + if ( cursor.ch <= parser.slices[parser.optionsAnchorSpan.i+1] ) { + hints = getOriginHints(cursor, line); + } else { + hints = parser.hasFlavor(parser.BITFlavorExtScriptlet) + ? getExtScriptletHints(cursor, line) + : getExtSelectorHints(cursor, line); } - return getExtSelectorHints(cursor, line); + return hints; } if ( parser.category === parser.CATStaticNetFilter ) { return getNetHints(cursor, line); @@ -538,6 +569,9 @@ const initHints = function() { if ( parser.category === parser.CATComment ) { return getCommentHints(cursor, line); } + if ( parser.category === parser.CATNone ) { + return getOriginHints(cursor, line); + } }); }; diff --git a/src/js/messaging.js b/src/js/messaging.js index 3ba2d9dff8a7c..5af3d539f354d 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1000,7 +1000,7 @@ const resetUserData = async function() { vAPI.app.restart(); }; -// 3rd-party filters +// Filter lists const prepListEntries = function(entries) { const µburi = µb.URI; for ( const k in entries ) { @@ -1041,6 +1041,26 @@ const getLists = async function(callback) { callback(r); }; +// My filters + +// TODO: also return origin of embedded frames? +const getOriginHints = function() { + const punycode = self.punycode; + const out = []; + for ( const tabId of µb.pageStores.keys() ) { + if ( tabId === -1 ) { continue; } + const tabContext = µb.tabContextManager.lookup(tabId); + if ( tabContext === null ) { continue; } + let { rootDomain, rootHostname } = tabContext; + if ( rootDomain.endsWith('-scheme') ) { continue; } + const isPunycode = rootHostname.includes('xn--'); + out.push(isPunycode ? punycode.toUnicode(rootDomain) : rootDomain); + if ( rootHostname === rootDomain ) { continue; } + out.push(isPunycode ? punycode.toUnicode(rootHostname) : rootHostname); + } + return out; +}; + // My rules const getRules = function() { return { @@ -1185,6 +1205,7 @@ const onMessage = function(request, sender, callback) { redirectResources: µb.redirectEngine.getResourceDetails(), preparseDirectiveTokens: µb.preparseDirectives.getTokens(), preparseDirectiveHints: µb.preparseDirectives.getHints(), + originHints: getOriginHints(), expertMode: µb.hiddenSettings.filterAuthorMode, }; break; From 5e70d6e3c17dddc4154a31467ff26dca57ec9742 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Nov 2020 10:01:50 -0500 Subject: [PATCH 3891/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 87da5723b6f5e..0e4b22a0b27fc 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.1 +1.31.1.2 From 50ad64d349a9dff959f1ae5109609c34f1a5823d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 21 Nov 2020 10:35:21 -0500 Subject: [PATCH 3892/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2c69aa92a66cf..e93f32acd0eeb 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.1", + "version": "1.31.1.2", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b1", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b1/uBlock0_1.31.1b1.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b2", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b2/uBlock0_1.31.1b2.firefox.signed.xpi" } ] } From bde3164eb445a4e74acca303ec9fa07f82ba1b1c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 08:22:43 -0500 Subject: [PATCH 3893/4093] Add support for `1P`, `3P`, `header=` filter options and other changes New filter options ================== Strict partyness: `1P`, `3P` ---------------------------- The current options 1p/3p are meant to "weakly" match partyness, i.e. a network request is considered 1st-party to its context as long as both the context and the request share the same base domain. The new partyness options are meant to check for strict partyness, i.e. a network request will be considered 1st-party if and only if both the context and the request share the same hostname. For examples: - context: `www.example.org` - request: `www.example.org` - `1p`: yes, `1P`: yes - `3p`: no, `3P`: no - context: `www.example.org` - request: `subdomain.example.org` - `1p`: yes, `1P`: no - `3p`: no, `3P`: yes - context: `www.example.org` - request: `www.example.com` - `1p`: no, `1P`: no - `3p`: yes, `3P`: yes The strict partyness options will be visually emphasized in the editor so as to prevent mistakenly using `1P` or `3P` where weak partyness is meant to be used. Filter on response headers: `header=` ------------------------------------- Currently experimental and under evaluation. Disabled by default, enable by toggling `filterOnHeaders` to `true` in advanced settings. Ability to filter network requests according to whether a specific response header is present and whether it matches or does not match a specific value. For example: *$1p,3P,script,header=via:1\.1\s+google The above filter is meant to block network requests which fullfill all the following conditions: - is weakly 1st-party to the context - is not strictly 1st-party to the context - is of type `script` - has a response HTTP header named `via`, which value matches the regular expression `1\.1\s+google`. The matches are always performed in a case-insensitive manner. The header value is assumed to be a literal regular expression, except for the following special characters: - to anchor to start of string, use leading `|`, not `^` - to anchor to end of string, use trailing `|`, not `$` - to invert the test, use a leading `!` To block a network request if it merely contains a specific HTTP header is just a matter of specifying the header name without a header value: *$1p,3P,script,header=via Generic exception filters can be used to disable specific block `header=` filters, i.e. `@@*$1p,3P,script,header` will override the block `header=` filters given as example above. Dynamic filtering's `allow` rules override block `headers=` filters. Important: It is key that filter authors use as many narrowing filter options as possible when using the `header=` option, and the `header=` option should be used ONLY when other filter options are not sufficient. More documentation justifying the purpose of `header=` option will be provided eventually if ever it is decided to move it from experimental to stable status. To be decided: to restrict usage of this filter option to only uBO's own filter lists or "My filters". Changes ======= Fine tuning `queryprune=` ------------------------- The following changes have been implemented: The special value `*` (i.e. `queryprune=*`) means "remove all query parameters". If the `queryprune=` value is made only of alphanumeric characters (including `_`), the value will be internally converted to regex equivalent `^value=`. This ensures a better future compatibility with AdGuard's `removeparam=`. If the `queryprune=` value starts with `!`, the test will be inverted. This can be used to remove all query parameters EXCEPT those who match the specified value. Other ----- The legacy code to test for spurious CSP reports has been removed. This is no longer an issue ever since uBO redirects to local resources through web accessible resources. Notes ===== The following new and recently added filter options are not compatible with Chromium's manifest v3 changes: - `queryprune=` - `1P` - `3P` - `header=` --- src/css/codemirror.css | 4 +- src/css/themes/default.css | 2 +- src/js/background.js | 3 +- src/js/codemirror/ubo-static-filtering.js | 42 +++- src/js/pagestore.js | 49 +++- src/js/static-filtering-parser.js | 98 +++++--- src/js/static-net-filtering.js | 285 +++++++++++++++++++--- src/js/traffic.js | 134 ++-------- 8 files changed, 409 insertions(+), 208 deletions(-) diff --git a/src/css/codemirror.css b/src/css/codemirror.css index 4bd080e99ae73..29f450368dc6e 100644 --- a/src/css/codemirror.css +++ b/src/css/codemirror.css @@ -74,9 +74,9 @@ .cm-s-default .cm-keyword { color: var(--sf-keyword-ink); } -.cm-s-default .cm-regex { +.cm-s-default .cm-notice { text-underline-position: under; - text-decoration-color: var(--sf-regex-ink); + text-decoration-color: var(--sf-notice-ink); text-decoration-style: solid; text-decoration-line: underline; } diff --git a/src/css/themes/default.css b/src/css/themes/default.css index 2b4c08cf7f6af..b9dbb7965fd54 100644 --- a/src/css/themes/default.css +++ b/src/css/themes/default.css @@ -191,7 +191,7 @@ --sf-error-ink: #ff0000; --sf-error-surface: #ff000016; --sf-keyword-ink: var(--purple-60); - --sf-regex-ink: var(--light-gray-60); + --sf-notice-ink: var(--light-gray-60); --sf-tag-ink: #117700; --sf-value-ink: var(--orange-80); --sf-variable-ink: var(--default-ink); diff --git a/src/js/background.js b/src/js/background.js index db905a34b1584..f4c6f475f3636 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -61,9 +61,10 @@ const µBlock = (( ) => { // jshint ignore:line debugScriptletInjector: false, disableWebAssembly: false, extensionUpdateForceReload: false, + filterAuthorMode: false, + filterOnHeaders: false, ignoreRedirectFilters: false, ignoreScriptInjectFilters: false, - filterAuthorMode: false, loggerPopupType: 'popup', manualUpdateAssetFetchPeriod: 500, popupFontSize: 'unset', diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index c4b5d68e622a1..aec5bb92fa92d 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -212,20 +212,42 @@ CodeMirror.defineMode('ubo-static-filtering', function() { const colorNetOptionSpan = function(stream) { const bits = parser.slices[parserSlot]; - let style; if ( (bits & parser.BITComma) !== 0 ) { - style = 'def strong'; netOptionValueMode = false; - } else if ( netOptionValueMode ) { + stream.pos += parser.slices[parserSlot+2]; + parserSlot += 3; + return 'def strong'; + } + if ( netOptionValueMode ) { return colorNetOptionValueSpan(stream, bits); - } else if ( (bits & parser.BITTilde) !== 0 ) { - style = 'keyword strong'; - } else if ( (bits & parser.BITEqual) !== 0 ) { + } + if ( (bits & parser.BITTilde) !== 0 ) { + stream.pos += parser.slices[parserSlot+2]; + parserSlot += 3; + return 'keyword strong'; + } + if ( (bits & parser.BITEqual) !== 0 ) { netOptionValueMode = true; + stream.pos += parser.slices[parserSlot+2]; + parserSlot += 3; + return 'def'; } - stream.pos += parser.slices[parserSlot+2]; - parserSlot += 3; - return style || 'def'; + const to = parser.skipUntil( + parserSlot, + parser.commentSpan.i, + parser.BITComma | parser.BITEqual + ); + if ( + to > parserSlot && + /^[13]P/.test(parser.strFromSlices(parserSlot, to - 3)) + ) { + parserSlot = to; + stream.pos = parser.slices[to+1]; + return 'def notice'; + } + parserSlot = to; + stream.pos = parser.slices[to+1]; + return 'def'; }; const colorNetSpan = function(stream) { @@ -259,7 +281,7 @@ CodeMirror.defineMode('ubo-static-filtering', function() { if ( parser.patternIsRegex() ) { stream.pos = parser.slices[parser.optionsAnchorSpan.i+1]; parserSlot = parser.optionsAnchorSpan.i; - return 'variable regex'; + return 'variable notice'; } if ( (parser.slices[parserSlot] & (parser.BITAsterisk | parser.BITCaret)) !== 0 ) { stream.pos += parser.slices[parserSlot+2]; diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 20198234ed9e6..505fe2a2a3700 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -272,7 +272,6 @@ const PageStore = class { this.popupBlockedCount = 0; this.largeMediaCount = 0; this.largeMediaTimer = null; - this.internalRedirectionCount = 0; this.allowLargeMediaElementsRegex = undefined; this.extraData.clear(); @@ -668,11 +667,57 @@ const PageStore = class { return result; } + filterOnHeaders(fctxt, headers) { + fctxt.filter = undefined; + + if ( this.getNetFilteringSwitch(fctxt) === false ) { return 0; } + + let result = µb.staticNetFilteringEngine.matchHeaders(fctxt, headers); + if ( result === 0 ) { return 0; } + + const loggerEnabled = µb.logger.enabled; + if ( loggerEnabled ) { + fctxt.filter = µb.staticNetFilteringEngine.toLogData(); + } + + // Dynamic filtering allow rules + // URL filtering + if ( + result === 1 && + µb.sessionURLFiltering.evaluateZ( + fctxt.getTabHostname(), + fctxt.url, + fctxt.type + ) === 2 + ) { + result = 2; + if ( loggerEnabled ) { + fctxt.filter = µb.sessionURLFiltering.toLogData(); + } + } + // Hostname filtering + if ( + result === 1 && + µb.userSettings.advancedUserEnabled && + µb.sessionFirewall.evaluateCellZY( + fctxt.getTabHostname(), + fctxt.getHostname(), + fctxt.type + ) === 2 + ) { + result = 2; + if ( loggerEnabled ) { + fctxt.filter = µb.sessionFirewall.toLogData(); + } + } + + return result; + } + redirectBlockedRequest(fctxt) { if ( µb.hiddenSettings.ignoreRedirectFilters === true ) { return; } const directive = µb.staticNetFilteringEngine.redirectRequest(fctxt); if ( directive === undefined ) { return; } - this.internalRedirectionCount += 1; if ( µb.logger.enabled !== true ) { return; } fctxt.pushFilter(directive.logData()); if ( fctxt.redirectURL === undefined ) { return; } diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 002178bdf91d8..866fc0ba98338 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -346,6 +346,7 @@ const Parser = class { // patternRightAnchorSpan: first slice to right-hand pattern anchor // optionsAnchorSpan: first slice to options anchor // optionsSpan: first slice to options + // commentSpan: first slice to trailing comment analyzeNet() { let islice = this.leftSpaceSpan.len; @@ -369,7 +370,7 @@ const Parser = class { } // Assume no options - this.optionsAnchorSpan.i = this.optionsSpan.i = this.commentSpan.i; + this.optionsAnchorSpan.i = this.optionsSpan.i = this.commentSpan.i; // Assume all is part of pattern this.patternSpan.i = islice; @@ -1900,41 +1901,44 @@ const BITFlavorNetAnchor = BITFlavorNetLeftAnchor | BITFlavorNetRightAnc const OPTTokenMask = 0x000000ff; const OPTTokenInvalid = 0; const OPTToken1p = 1; -const OPTToken3p = 2; -const OPTTokenAll = 3; -const OPTTokenBadfilter = 4; -const OPTTokenCname = 5; -const OPTTokenCsp = 6; -const OPTTokenCss = 7; -const OPTTokenDenyAllow = 8; -const OPTTokenDoc = 9; -const OPTTokenDomain = 10; -const OPTTokenEhide = 11; -const OPTTokenEmpty = 12; -const OPTTokenFont = 13; -const OPTTokenFrame = 14; -const OPTTokenGenericblock = 15; -const OPTTokenGhide = 16; -const OPTTokenImage = 17; -const OPTTokenImportant = 18; -const OPTTokenInlineFont = 19; -const OPTTokenInlineScript = 20; -const OPTTokenMedia = 21; -const OPTTokenMp4 = 22; -const OPTTokenObject = 23; -const OPTTokenOther = 24; -const OPTTokenPing = 25; -const OPTTokenPopunder = 26; -const OPTTokenPopup = 27; -const OPTTokenRedirect = 28; -const OPTTokenRedirectRule = 29; -const OPTTokenQueryprune = 30; -const OPTTokenScript = 31; -const OPTTokenShide = 32; -const OPTTokenXhr = 33; -const OPTTokenWebrtc = 34; -const OPTTokenWebsocket = 35; -const OPTTokenCount = 36; +const OPTToken1pStrict = 2; +const OPTToken3p = 3; +const OPTToken3pStrict = 4; +const OPTTokenAll = 5; +const OPTTokenBadfilter = 6; +const OPTTokenCname = 7; +const OPTTokenCsp = 8; +const OPTTokenCss = 9; +const OPTTokenDenyAllow = 10; +const OPTTokenDoc = 11; +const OPTTokenDomain = 12; +const OPTTokenEhide = 13; +const OPTTokenEmpty = 14; +const OPTTokenFont = 15; +const OPTTokenFrame = 16; +const OPTTokenGenericblock = 17; +const OPTTokenGhide = 18; +const OPTTokenHeader = 19; +const OPTTokenImage = 20; +const OPTTokenImportant = 21; +const OPTTokenInlineFont = 22; +const OPTTokenInlineScript = 23; +const OPTTokenMedia = 24; +const OPTTokenMp4 = 25; +const OPTTokenObject = 26; +const OPTTokenOther = 27; +const OPTTokenPing = 28; +const OPTTokenPopunder = 29; +const OPTTokenPopup = 30; +const OPTTokenRedirect = 31; +const OPTTokenRedirectRule = 32; +const OPTTokenQueryprune = 33; +const OPTTokenScript = 34; +const OPTTokenShide = 35; +const OPTTokenXhr = 36; +const OPTTokenWebrtc = 37; +const OPTTokenWebsocket = 38; +const OPTTokenCount = 39; //const OPTPerOptionMask = 0x0000ff00; const OPTCanNegate = 1 << 8; @@ -1974,9 +1978,11 @@ Parser.prototype.BITHostname = BITHostname; Parser.prototype.BITPeriod = BITPeriod; Parser.prototype.BITDash = BITDash; Parser.prototype.BITHash = BITHash; +Parser.prototype.BITNum = BITNum; Parser.prototype.BITEqual = BITEqual; Parser.prototype.BITQuestion = BITQuestion; Parser.prototype.BITPercent = BITPercent; +Parser.prototype.BITAlpha = BITAlpha; Parser.prototype.BITTilde = BITTilde; Parser.prototype.BITUnicode = BITUnicode; Parser.prototype.BITIgnore = BITIgnore; @@ -1993,7 +1999,10 @@ Parser.prototype.BITFlavorIgnore = BITFlavorIgnore; Parser.prototype.BITFlavorUnsupported = BITFlavorUnsupported; Parser.prototype.BITFlavorError = BITFlavorError; -Parser.prototype.OPTTokenInvalid = OPTTokenInvalid; +Parser.prototype.OPTToken1p = OPTToken1p; +Parser.prototype.OPTToken1pStrict = OPTToken1pStrict; +Parser.prototype.OPTToken3p = OPTToken3p; +Parser.prototype.OPTToken3pStrict = OPTToken3pStrict; Parser.prototype.OPTTokenAll = OPTTokenAll; Parser.prototype.OPTTokenBadfilter = OPTTokenBadfilter; Parser.prototype.OPTTokenCname = OPTTokenCname; @@ -2003,14 +2012,15 @@ Parser.prototype.OPTTokenDoc = OPTTokenDoc; Parser.prototype.OPTTokenDomain = OPTTokenDomain; Parser.prototype.OPTTokenEhide = OPTTokenEhide; Parser.prototype.OPTTokenEmpty = OPTTokenEmpty; -Parser.prototype.OPTToken1p = OPTToken1p; Parser.prototype.OPTTokenFont = OPTTokenFont; Parser.prototype.OPTTokenGenericblock = OPTTokenGenericblock; Parser.prototype.OPTTokenGhide = OPTTokenGhide; +Parser.prototype.OPTTokenHeader = OPTTokenHeader; Parser.prototype.OPTTokenImage = OPTTokenImage; Parser.prototype.OPTTokenImportant = OPTTokenImportant; Parser.prototype.OPTTokenInlineFont = OPTTokenInlineFont; Parser.prototype.OPTTokenInlineScript = OPTTokenInlineScript; +Parser.prototype.OPTTokenInvalid = OPTTokenInvalid; Parser.prototype.OPTTokenMedia = OPTTokenMedia; Parser.prototype.OPTTokenMp4 = OPTTokenMp4; Parser.prototype.OPTTokenObject = OPTTokenObject; @@ -2025,7 +2035,6 @@ Parser.prototype.OPTTokenScript = OPTTokenScript; Parser.prototype.OPTTokenShide = OPTTokenShide; Parser.prototype.OPTTokenCss = OPTTokenCss; Parser.prototype.OPTTokenFrame = OPTTokenFrame; -Parser.prototype.OPTToken3p = OPTToken3p; Parser.prototype.OPTTokenXhr = OPTTokenXhr; Parser.prototype.OPTTokenWebrtc = OPTTokenWebrtc; Parser.prototype.OPTTokenWebsocket = OPTTokenWebsocket; @@ -2045,8 +2054,10 @@ Parser.prototype.OPTNotSupported = OPTNotSupported; const netOptionTokenDescriptors = new Map([ [ '1p', OPTToken1p | OPTCanNegate ], [ 'first-party', OPTToken1p | OPTCanNegate ], + [ '1P', OPTToken1pStrict ], [ '3p', OPTToken3p | OPTCanNegate ], [ 'third-party', OPTToken3p | OPTCanNegate ], + [ '3P', OPTToken3pStrict ], [ 'all', OPTTokenAll | OPTNetworkType | OPTNonCspableType ], [ 'badfilter', OPTTokenBadfilter ], [ 'cname', OPTTokenCname | OPTAllowOnly | OPTModifierType ], @@ -2066,6 +2077,7 @@ const netOptionTokenDescriptors = new Map([ [ 'genericblock', OPTTokenGenericblock | OPTNotSupported ], [ 'ghide', OPTTokenGhide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ], [ 'generichide', OPTTokenGhide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ], + [ 'header', OPTTokenHeader | OPTMustAssign | OPTAllowMayAssign | OPTNonCspableType | OPTNonRedirectableType ], [ 'image', OPTTokenImage | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], [ 'important', OPTTokenImportant | OPTBlockOnly ], [ 'inline-font', OPTTokenInlineFont | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ], @@ -2097,8 +2109,10 @@ Parser.prototype.netOptionTokenDescriptors = Parser.netOptionTokenIds = new Map([ [ '1p', OPTToken1p ], [ 'first-party', OPTToken1p ], + [ '1P', OPTToken1pStrict ], [ '3p', OPTToken3p ], [ 'third-party', OPTToken3p ], + [ '3P', OPTToken3pStrict ], [ 'all', OPTTokenAll ], [ 'badfilter', OPTTokenBadfilter ], [ 'cname', OPTTokenCname ], @@ -2118,6 +2132,7 @@ Parser.netOptionTokenIds = new Map([ [ 'genericblock', OPTTokenGenericblock ], [ 'ghide', OPTTokenGhide ], [ 'generichide', OPTTokenGhide ], + [ 'header', OPTTokenHeader ], [ 'image', OPTTokenImage ], [ 'important', OPTTokenImportant ], [ 'inline-font', OPTTokenInlineFont ], @@ -2145,7 +2160,9 @@ Parser.netOptionTokenIds = new Map([ Parser.netOptionTokenNames = new Map([ [ OPTToken1p, '1p' ], + [ OPTToken1pStrict, '1P' ], [ OPTToken3p, '3p' ], + [ OPTToken3pStrict, '3P' ], [ OPTTokenAll, 'all' ], [ OPTTokenBadfilter, 'badfilter' ], [ OPTTokenCname, 'cname' ], @@ -2160,6 +2177,7 @@ Parser.netOptionTokenNames = new Map([ [ OPTTokenFont, 'font' ], [ OPTTokenGenericblock, 'genericblock' ], [ OPTTokenGhide, 'generichide' ], + [ OPTTokenHeader, 'header' ], [ OPTTokenImage, 'image' ], [ OPTTokenImportant, 'important' ], [ OPTTokenInlineFont, 'inline-font' ], @@ -2300,6 +2318,8 @@ const NetOptionsIterator = class { } // Keep track of which options are present: any given option can // appear only once. + // TODO: might need to make an exception for `header=` option so as + // to allow filters which need to match more than one header. const tokenId = descriptor & OPTTokenMask; if ( tokenId !== OPTTokenInvalid ) { if ( this.tokenPos[tokenId] !== -1 ) { diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index c5d480c3b4a09..3077d346ea59d 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -32,31 +32,34 @@ const µb = µBlock; // fedcba9876543210 -// | | || | -// | | || | -// | | || | -// | | || | -// | | || +---- bit 0- 1: block=0, allow=1, block important=2 -// | | |+------ bit 2: modifier -// | | +------- bit 3- 4: party [0-3] -// | +--------- bit 5- 9: type [0-31] -// +-------------- bit 10-15: unused -const CategoryCount = 1 << 0xa; // shift left to first unused bit - -const RealmBitsMask = 0b0000000111; -const ActionBitsMask = 0b0000000011; -const TypeBitsMask = 0b1111100000; +// || | || | +// || | || | +// || | || | +// || | || | +// || | || +---- bit 0- 1: block=0, allow=1, block important=2 +// || | |+------ bit 2: modifier +// || | +------- bit 3- 4: party [0-3] +// || +--------- bit 5- 9: type [0-31] +// |+-------------- bit 10-15: unused +// +--------------- bit 16: headers-based filters + +const CategoryCount = 1 << 0xb; // shift left to first unused bit + +const RealmBitsMask = 0b00000000111; +const ActionBitsMask = 0b00000000011; +const TypeBitsMask = 0b01111100000; const TypeBitsOffset = 5; -const BlockAction = 0b0000000000; -const AllowAction = 0b0000000001; -const Important = 0b0000000010; +const BlockAction = 0b00000000000; +const AllowAction = 0b00000000001; +const Important = 0b00000000010; const BlockImportant = BlockAction | Important; -const ModifyAction = 0b0000000100; -const AnyParty = 0b0000000000; -const FirstParty = 0b0000001000; -const ThirdParty = 0b0000010000; -const AllParties = 0b0000011000; +const ModifyAction = 0b00000000100; +const AnyParty = 0b00000000000; +const FirstParty = 0b00000001000; +const ThirdParty = 0b00000010000; +const AllParties = 0b00000011000; +const Headers = 0b10000000000; const typeNameToTypeValue = { 'no_type': 0 << TypeBitsOffset, @@ -166,6 +169,28 @@ const $docEntity = { }, }; +const $httpHeaders = { + init(headers) { + this.headers = headers; + this.parsed.clear(); + }, + reset() { + this.headers = []; + this.parsed.clear(); + }, + lookup(name) { + if ( this.parsed.size === 0 ) { + for ( let i = 0, n = this.headers.length; i < n; i++ ) { + const { name, value } = this.headers[i]; + this.parsed.set(name, value); + } + } + return this.parsed.get(name); + }, + headers: [], + parsed: new Map(), +}; + /******************************************************************************/ // Local helpers @@ -2310,6 +2335,105 @@ const FilterBucketOfOriginHits = class extends FilterBucket { registerFilterClass(FilterBucketOfOriginHits); +/******************************************************************************/ + +const FilterStrictParty = class { + constructor(not) { + this.not = not; + } + + // TODO: diregard `www.`? + match() { + return ($requestHostname === $docHostname) !== this.not; + } + + logData(details) { + details.options.push(this.not ? '3P' : '1P'); + } + + toSelfie() { + return [ this.fid, this.not ]; + } + + static compile(details) { + return [ FilterStrictParty.fid, details.strictParty < 0 ]; + } + + static fromCompiled(args) { + return new FilterStrictParty(args[1]); + } + + static fromSelfie(args) { + return new FilterStrictParty(args[1]); + } + + static keyFromArgs(args) { + return `${args[1]}`; + } +}; + +registerFilterClass(FilterStrictParty); + +/******************************************************************************/ + +const FilterOnHeaders = class { + constructor(headerOpt) { + this.headerOpt = headerOpt; + if ( headerOpt !== '' ) { + let pos = headerOpt.indexOf(':'); + if ( pos === -1 ) { pos = headerOpt.length; } + this.name = headerOpt.slice(0, pos); + this.value = headerOpt.slice(pos + 1); + this.not = this.value.charCodeAt(0) === 0x21 /* '!' */; + if ( this.not ) { this.value.slice(1); } + } else { + this.name = this.value = ''; + this.not = false; + } + this.reValue = null; + } + + match() { + if ( this.name === '' ) { return true; } + const value = $httpHeaders.lookup(this.name); + if ( value === undefined ) { return false; } + if ( this.value === '' ) { return true; } + if ( this.reValue === null ) { + let reText = this.value; + if ( reText.startsWith('|') ) { reText = '^' + reText.slice(1); } + if ( reText.endsWith('|') ) { reText = reText.slice(0, -1) + '$'; } + this.reValue = new RegExp(reText, 'i'); + } + return this.reValue.test(value) !== this.not; + } + + logData(details) { + let opt = 'header'; + if ( this.headerOpt !== '' ) { + opt += `=${this.headerOpt}`; + } + details.options.push(opt); + } + + toSelfie() { + return [ this.fid, this.headerOpt ]; + } + + static compile(details) { + return [ FilterOnHeaders.fid, details.headerOpt ]; + } + + static fromCompiled(args) { + return new FilterOnHeaders(args[1]); + } + + static fromSelfie(args) { + return new FilterOnHeaders(args[1]); + } +}; + +registerFilterClass(FilterOnHeaders); + /******************************************************************************/ /******************************************************************************/ @@ -2656,10 +2780,13 @@ const FilterParser = class { this.invalid = false; this.pattern = ''; this.party = AnyParty; + this.hasOptionUnits = false; this.domainOpt = ''; this.denyallowOpt = ''; + this.headerOpt = undefined; this.isPureHostname = false; this.isRegex = false; + this.strictParty = 0; this.token = '*'; this.tokenHash = this.noTokenHash; this.tokenBeg = 0; @@ -2731,6 +2858,7 @@ const FilterParser = class { } else if ( this.action === AllowAction ) { this.modifyValue = ''; } + this.hasOptionUnits = true; return true; } @@ -2740,9 +2868,17 @@ const FilterParser = class { case parser.OPTToken1p: this.parsePartyOption(true, not); break; + case parser.OPTToken1pStrict: + this.strictParty = this.strictParty === -1 ? 0 : 1; + this.hasOptionUnits = true; + break; case parser.OPTToken3p: this.parsePartyOption(false, not); break; + case parser.OPTToken3pStrict: + this.strictParty = this.strictParty === 1 ? 0 : -1; + this.hasOptionUnits = true; + break; case parser.OPTTokenAll: this.parseTypeOption(-1); break; @@ -2769,10 +2905,12 @@ const FilterParser = class { this.domainOptList ); if ( this.domainOpt === '' ) { return false; } + this.hasOptionUnits = true; break; case parser.OPTTokenDenyAllow: this.denyallowOpt = this.parseHostnameList(parser, val, 0b0000); if ( this.denyallowOpt === '' ) { return false; } + this.hasOptionUnits = true; break; // https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/ // Add support for `elemhide`. Rarely used but it happens. @@ -2780,6 +2918,10 @@ const FilterParser = class { this.parseTypeOption(parser.OPTTokenShide, not); this.parseTypeOption(parser.OPTTokenGhide, not); break; + case parser.OPTTokenHeader: + this.headerOpt = val !== undefined ? val : ''; + this.hasOptionUnits = true; + break; case parser.OPTTokenImportant: if ( this.action === AllowAction ) { return false; } this.action = BlockImportant; @@ -2790,11 +2932,13 @@ const FilterParser = class { if ( this.modifyType !== undefined ) { return false; } this.modifyType = parser.OPTTokenRedirect; this.modifyValue = 'empty'; + this.hasOptionUnits = true; break; case parser.OPTTokenMp4: if ( this.modifyType !== undefined ) { return false; } this.modifyType = parser.OPTTokenRedirect; this.modifyValue = 'noopmp4-1s'; + this.hasOptionUnits = true; break; case parser.OPTTokenQueryprune: case parser.OPTTokenRedirect: @@ -3018,9 +3162,16 @@ const FilterParser = class { } } + isJustPattern() { + return this.hasOptionUnits === false; + } + isJustOrigin() { - return this.isRegex === false && + return this.hasOptionUnits && + this.isRegex === false && this.modifyType === undefined && + this.strictParty === 0 && + this.headerOpt === undefined && this.denyallowOpt === '' && this.domainOpt !== '' && ( this.pattern === '*' || ( @@ -3410,12 +3561,7 @@ FilterContainer.prototype.compile = function(parser, writer) { // Pure hostnames, use more efficient dictionary lookup // https://github.com/chrisaljoudi/uBlock/issues/665 // Create a dict keyed on request type etc. - if ( - parsed.isPureHostname && - parsed.domainOpt === '' && - parsed.denyallowOpt === '' && - parsed.modifyType === undefined - ) { + if ( parsed.isPureHostname && parsed.isJustPattern() ) { parsed.tokenHash = this.dotTokenHash; this.compileToAtomicFilter(parsed, parsed.pattern, writer); return true; @@ -3483,6 +3629,11 @@ FilterContainer.prototype.compile = function(parser, writer) { units.push(FilterAnchorRight.compile()); } + // Strict partiness + if ( parsed.strictParty !== 0 ) { + units.push(FilterStrictParty.compile(parsed)); + } + // Origin if ( parsed.domainOpt !== '' ) { filterOrigin.compile( @@ -3497,6 +3648,12 @@ FilterContainer.prototype.compile = function(parser, writer) { units.push(FilterDenyAllow.compile(parsed)); } + // Header + if ( parsed.headerOpt !== undefined ) { + units.push(FilterOnHeaders.compile(parsed)); + parsed.action |= Headers; + } + // Modifier // // IMPORTANT: the modifier unit MUST always appear first in a sequence. @@ -3958,6 +4115,39 @@ FilterContainer.prototype.matchString = function(fctxt, modifiers = 0) { /******************************************************************************/ +FilterContainer.prototype.matchHeaders = function(fctxt, headers) { + const typeValue = typeNameToTypeValue[fctxt.type] || otherTypeBitValue; + const partyBits = fctxt.is3rdPartyToDoc() ? ThirdParty : FirstParty; + + // Prime tokenizer: we get a normalized URL in return. + $requestURL = urlTokenizer.setURL(fctxt.url); + this.$filterUnit = 0; + + // These registers will be used by various filters + $docHostname = fctxt.getDocHostname(); + $docDomain = fctxt.getDocDomain(); + $docEntity.reset(); + $requestHostname = fctxt.getHostname(); + $httpHeaders.init(headers); + + let r = 0; + if ( this.realmMatchString(Headers | BlockImportant, typeValue, partyBits) ) { + r = 1; + } + if ( r !== 1 && this.realmMatchString(Headers | BlockAction, typeValue, partyBits) ) { + r = 1; + if ( r === 1 && this.realmMatchString(Headers | AllowAction, typeValue, partyBits) ) { + r = 2; + } + } + + $httpHeaders.reset(); + + return r; +}; + +/******************************************************************************/ + FilterContainer.prototype.redirectRequest = function(fctxt) { const directives = this.matchAndFetchModifiers(fctxt, 'redirect-rule'); // No directive is the most common occurrence. @@ -4036,12 +4226,14 @@ FilterContainer.prototype.filterQuery = function(fctxt) { break; } if ( modifier.cache === undefined ) { - modifier.cache = this.parseFilterPruneValue(modifier.value); + this.parseFilterPruneValue(modifier); } - const re = modifier.cache; + const { all, not, re } = modifier.cache; let filtered = false; for ( const [ key, value ] of params ) { - if ( re.test(`${key}=${value}`) === false ) { continue; } + if ( all !== true && re.test(`${key}=${value}`) === not ) { + continue; + } if ( isException === false ) { params.delete(key); } @@ -4061,15 +4253,27 @@ FilterContainer.prototype.filterQuery = function(fctxt) { return out; }; -FilterContainer.prototype.parseFilterPruneValue = function(rawValue) { - let retext = rawValue; - if ( retext.startsWith('|') ) { retext = `^${retext.slice(1)}`; } - if ( retext.endsWith('|') ) { retext = `${retext.slice(0,-1)}$`; } - try { - return new RegExp(retext); - } catch(ex) { +FilterContainer.prototype.parseFilterPruneValue = function(modifier) { + const cache = {}; + let retext = modifier.value; + if ( retext === '*' ) { + cache.all = true; + } else { + cache.not = retext.charCodeAt(0) === 0x21 /* '!' */; + if ( cache.not ) { retext = retext.slice(1); } + if ( /^\w+$/.test(retext) ) { + retext = `^${retext}=`; + } else { + if ( retext.startsWith('|') ) { retext = `^${retext.slice(1)}`; } + if ( retext.endsWith('|') ) { retext = `${retext.slice(0,-1)}$`; } + } + try { + cache.re = new RegExp(retext, 'i'); + } catch(ex) { + cache.re = /.^/; + } } - return /.^/; + modifier.cache = cache; }; /******************************************************************************/ @@ -4190,6 +4394,7 @@ FilterContainer.prototype.benchmark = async function(action, target) { if ( fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) { this.matchAndFetchModifiers(fctxt, 'csp'); } + this.matchHeaders(fctxt, []); } else { this.redirectRequest(fctxt); } diff --git a/src/js/traffic.js b/src/js/traffic.js index 224b956cd694d..17cdb615ac41b 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -371,92 +371,6 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { /******************************************************************************/ -// https://github.com/gorhill/uBlock/issues/3140 - -const onBeforeMaybeSpuriousCSPReport = (function() { - let textDecoder; - - return function(details) { - const fctxt = µBlock.filteringContext.fromWebrequestDetails(details); - - // Ignore behind-the-scene requests. - if ( fctxt.tabId < 0 ) { return; } - - // Lookup the page store associated with this tab id. - const pageStore = µBlock.pageStoreFromTabId(fctxt.tabId); - if ( pageStore === null ) { return; } - - // If uBO is disabled for the page, it can't possibly causes CSP - // reports to be triggered. - if ( pageStore.getNetFilteringSwitch() === false ) { return; } - - // A resource was redirected to a neutered one? - // TODO: mind injected scripts/styles as well. - if ( pageStore.internalRedirectionCount === 0 ) { return; } - - if ( - textDecoder === undefined && - typeof self.TextDecoder === 'function' - ) { - textDecoder = new TextDecoder(); - } - - // Find out whether the CSP report is a potentially spurious CSP report. - // If from this point on we are unable to parse the CSP report data, - // the safest assumption to protect users is to assume the CSP report - // is spurious. - if ( - textDecoder !== undefined && - details.method === 'POST' - ) { - const raw = details.requestBody && details.requestBody.raw; - if ( - Array.isArray(raw) && - raw.length !== 0 && - raw[0] instanceof Object && - raw[0].bytes instanceof ArrayBuffer - ) { - let data; - try { - data = JSON.parse(textDecoder.decode(raw[0].bytes)); - } catch (ex) { - } - if ( data instanceof Object ) { - const report = data['csp-report']; - if ( report instanceof Object ) { - const blocked = - report['blocked-uri'] || report['blockedURI']; - const validBlocked = typeof blocked === 'string'; - const source = - report['source-file'] || report['sourceFile']; - const validSource = typeof source === 'string'; - if ( - (validBlocked || validSource) && - (!validBlocked || !blocked.startsWith('data')) && - (!validSource || !source.startsWith('data')) - ) { - return; - } - } - } - } - } - - // At this point, we have a potentially spurious CSP report. - - if ( µBlock.logger.enabled ) { - fctxt.setRealm('network') - .setType('csp_report') - .setFilter({ result: 1, source: 'global', raw: 'no-spurious-csp-report' }) - .toLogger(); - } - - return { cancel: true }; - }; -})(); - -/******************************************************************************/ - // To handle: // - Media elements larger than n kB // - Scriptlet injection (requires ability to modify response body) @@ -485,16 +399,30 @@ const onHeadersReceived = function(details) { if ( pageStore.getNetFilteringSwitch(fctxt) === false ) { return; } if ( fctxt.itype === fctxt.IMAGE || fctxt.itype === fctxt.MEDIA ) { - return foilLargeMediaElement(details, fctxt, pageStore); + const result = foilLargeMediaElement(details, fctxt, pageStore); + if ( result !== undefined ) { return result; } } - if ( isRootDoc === false && fctxt.itype !== fctxt.SUB_FRAME ) { return; } - // Keep in mind response headers will be modified in-place if needed, so // `details.responseHeaders` will always point to the modified response // headers. const responseHeaders = details.responseHeaders; + if ( isRootDoc === false && µb.hiddenSettings.filterOnHeaders === true ) { + const result = pageStore.filterOnHeaders(fctxt, responseHeaders); + if ( result !== 0 ) { + if ( µb.logger.enabled ) { + fctxt.setRealm('network').toLogger(); + } + if ( result === 1 ) { + pageStore.journalAddRequest(fctxt.getHostname(), 1); + return { cancel: true }; + } + } + } + + if ( isRootDoc === false && fctxt.itype !== fctxt.SUB_FRAME ) { return; } + // 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 @@ -1083,41 +1011,21 @@ return { vAPI.net = new vAPI.Net(); vAPI.net.suspend(); - return function() { + return ( ) => { vAPI.net.setSuspendableListener(onBeforeRequest); vAPI.net.addListener( 'onHeadersReceived', onHeadersReceived, - { - types: [ - 'main_frame', - 'sub_frame', - 'image', - 'media', - 'xmlhttprequest', - ], - urls: [ 'http://*/*', 'https://*/*' ], - }, + { urls: [ 'http://*/*', 'https://*/*' ] }, [ 'blocking', 'responseHeaders' ] ); - if ( vAPI.net.validTypes.has('csp_report') ) { - vAPI.net.addListener( - 'onBeforeRequest', - onBeforeMaybeSpuriousCSPReport, - { - types: [ 'csp_report' ], - urls: [ 'http://*/*', 'https://*/*' ] - }, - [ 'blocking', 'requestBody' ] - ); - } vAPI.net.unsuspend(true); }; })(), - strictBlockBypass: function(hostname) { + strictBlockBypass: hostname => { strictBlockBypasser.bypass(hostname); - } + }, }; /******************************************************************************/ From d97d7a848336faf0e7bd15565712d221c0cc350f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 08:31:22 -0500 Subject: [PATCH 3894/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0e4b22a0b27fc..c4c48e8a9b527 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.2 +1.31.1.3 From b48f9b8ba492bb9291717b7e684d7171a45dc897 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 08:33:57 -0500 Subject: [PATCH 3895/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/eu/messages.json | 14 +++++++------- src/_locales/fi/messages.json | 10 +++++----- src/_locales/fy/messages.json | 2 +- src/_locales/uk/messages.json | 2 +- src/_locales/zh_TW/messages.json | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index eb0de1c763352..dc73b3f680724 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -552,19 +552,19 @@ "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { - "message": "Sort:", + "message": "Ordena:", "description": "English: label for sort option." }, "rulesSortByType": { - "message": "Rule type", + "message": "Arau mota", "description": "English: a sort option for list of rules." }, "rulesSortBySource": { - "message": "Source", + "message": "Iturria", "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { - "message": "Destination", + "message": "Helburua", "description": "English: a sort option for list of rules." }, "whitelistPrompt": { @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "aldatua", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -912,7 +912,7 @@ "description": "No longer used" }, "subscribeButton": { - "message": "Subscribe", + "message": "Harpidetu", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { @@ -1068,7 +1068,7 @@ "description": "short for 'gigabytes'" }, "clickToLoad": { - "message": "Click to load", + "message": "Sakatu kargatzeko", "description": "Message used in frame placeholders" }, "dummy": { diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index a633c379fb159..c5802ae663b64 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -552,19 +552,19 @@ "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { - "message": "Sort:", + "message": "Järjestys:", "description": "English: label for sort option." }, "rulesSortByType": { - "message": "Rule type", + "message": "Sääntötyyppi", "description": "English: a sort option for list of rules." }, "rulesSortBySource": { - "message": "Source", + "message": "Lähde", "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { - "message": "Destination", + "message": "Kohde", "description": "English: a sort option for list of rules." }, "whitelistPrompt": { @@ -912,7 +912,7 @@ "description": "No longer used" }, "subscribeButton": { - "message": "Subscribe", + "message": "Tilaa", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index 0ace855c266a4..3abd56b440e4a 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "oanpast", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index e8aa6decfedf3..335fd5b262ce3 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "модифікований", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 14c9bd78ff880..5d18a5eaffc41 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -16,7 +16,7 @@ "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { - "message": "留下", + "message": "留在此頁面", "description": "Label for button to prevent navigating away from unsaved changes" }, "dashboardUnsavedWarningIgnore": { @@ -460,7 +460,7 @@ "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "每行一個網址。無效網址將被忽略。", + "message": "每行一個網址。無效的網址將被忽略。", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { From 43cb63f80a483bfdad013311dfd634c916595093 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 08:47:29 -0500 Subject: [PATCH 3896/4093] Fix parsing of `queryprune=*` in static filtering parser --- src/js/static-filtering-parser.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 866fc0ba98338..a0bc99d845dcc 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2484,6 +2484,10 @@ const NetOptionsIterator = class { this.optSlices[i+4], this.optSlices[i+5] - 3 ); + if ( val === '*' ) { return true; } + if ( val.charCodeAt(0) === 0x21 /* '!' */ ) { + val = val.slice(1); + } if ( val.startsWith('|') ) { val = `^${val.slice(1)}`; } if ( val.endsWith('|') ) { val = `${val.slice(0,-1)}$`; } try { From 13ddab5375a5d6e6c41ccd763faf45bb5097adde Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 09:46:20 -0500 Subject: [PATCH 3897/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index e93f32acd0eeb..5504ca4dbb812 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.2", + "version": "1.31.1.3", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b2", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b2/uBlock0_1.31.1b2.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b3", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b3/uBlock0_1.31.1b3.firefox.signed.xpi" } ] } From e45949417bf4d37220fe7a91ed8bc4a289259171 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 10:26:15 -0500 Subject: [PATCH 3898/4093] Magic compile/selfie numbers need to increased Related commit: - https://github.com/gorhill/uBlock/commit/bde3164eb445a4e74acca303ec9fa07f82ba1b1c --- src/js/background.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index f4c6f475f3636..7b21c2c701f4c 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -140,8 +140,8 @@ const µBlock = (( ) => { // jshint ignore:line // Read-only systemSettings: { - compiledMagic: 34, // Increase when compiled format changes - selfieMagic: 34, // Increase when selfie format changes + compiledMagic: 35, // Increase when compiled format changes + selfieMagic: 35, // Increase when selfie format changes }, // https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501 From 801c6a878542983a226f03ca46e6fa29120c15f8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 10:27:27 -0500 Subject: [PATCH 3899/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index c4c48e8a9b527..bd3b79ba6c080 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.3 +1.31.1.4 From 7d804169383103c3d526f3754883926d14f51c66 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 23 Nov 2020 13:25:23 -0500 Subject: [PATCH 3900/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 5504ca4dbb812..dfd6cec1cd4e3 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.3", + "version": "1.31.1.4", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b3", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b3/uBlock0_1.31.1b3.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b4", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b4/uBlock0_1.31.1b4.firefox.signed.xpi" } ] } From 57013c16e6bbfef7c975c2a0591781ecc4a08188 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 25 Nov 2020 09:36:12 -0500 Subject: [PATCH 3901/4093] Fix compilation of blocking counterpart of `redirect=` filters Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1358 --- src/js/static-filtering-parser.js | 10 ++ src/js/static-net-filtering.js | 215 +++++++++++++++++------------- 2 files changed, 134 insertions(+), 91 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index a0bc99d845dcc..feead031af3f1 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2455,6 +2455,16 @@ const NetOptionsIterator = class { } } } + // `header`: can't be used with any modifier type + { + const i = this.tokenPos[OPTTokenHeader]; + if ( i !== -1 && hasBits(allBits, OPTModifierType) ) { + optSlices[i] = OPTTokenInvalid; + if ( this.interactive ) { + this.parser.errorSlices(optSlices[i+1], optSlices[i+5]); + } + } + } return this; } next() { diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 3077d346ea59d..bc76e6f27d8a0 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2620,7 +2620,10 @@ const urlTokenizer = new (class { /******************************************************************************/ const FilterParser = class { - constructor(parser) { + constructor(parser, other = undefined) { + if ( other !== undefined ) { + return Object.assign(this, other); + } this.cantWebsocket = vAPI.cantWebsocket; this.noTokenHash = urlTokenizer.noTokenHash; this.reIsolateHostname = /^(\*?\.)?([^\x00-\x24\x26-\x2C\x2F\x3A-\x5E\x60\x7B-\x7F]+)(.*)/; @@ -2761,10 +2764,11 @@ const FilterParser = class { [ 'new',1412], ]); this.maxTokenLen = urlTokenizer.MAX_TOKEN_LENGTH; - this.reset(); + this.reset(parser); } - reset() { + reset(parser) { + this.parser = parser; this.action = BlockAction; // anchor: bit vector // 0000 (0x0): no anchoring @@ -2780,7 +2784,7 @@ const FilterParser = class { this.invalid = false; this.pattern = ''; this.party = AnyParty; - this.hasOptionUnits = false; + this.optionUnitBits = 0; this.domainOpt = ''; this.denyallowOpt = ''; this.headerOpt = undefined; @@ -2800,6 +2804,10 @@ const FilterParser = class { return this; } + clone() { + return new FilterParser(this.parser, this); + } + normalizeRegexSource(s) { try { const re = new RegExp(s); @@ -2830,14 +2838,14 @@ const FilterParser = class { this.party |= firstParty ? FirstParty : ThirdParty; } - parseHostnameList(parser, s, modeBits, out = []) { + parseHostnameList(s, modeBits, out = []) { let beg = 0; let slen = s.length; let i = 0; while ( beg < slen ) { let end = s.indexOf('|', beg); if ( end === -1 ) { end = slen; } - const hn = parser.normalizeHostnameValue( + const hn = this.parser.normalizeHostnameValue( s.slice(beg, end), modeBits ); @@ -2858,96 +2866,115 @@ const FilterParser = class { } else if ( this.action === AllowAction ) { this.modifyValue = ''; } - this.hasOptionUnits = true; return true; } - parseOptions(parser) { - for ( let { id, val, not } of parser.netOptions() ) { + parseOptions() { + for ( let { id, val, not } of this.parser.netOptions() ) { switch ( id ) { - case parser.OPTToken1p: + case this.parser.OPTToken1p: this.parsePartyOption(true, not); break; - case parser.OPTToken1pStrict: + case this.parser.OPTToken1pStrict: this.strictParty = this.strictParty === -1 ? 0 : 1; - this.hasOptionUnits = true; + this.optionUnitBits |= this.STRICT_PARTY_BIT; break; - case parser.OPTToken3p: + case this.parser.OPTToken3p: this.parsePartyOption(false, not); break; - case parser.OPTToken3pStrict: + case this.parser.OPTToken3pStrict: this.strictParty = this.strictParty === 1 ? 0 : -1; - this.hasOptionUnits = true; + this.optionUnitBits |= this.STRICT_PARTY_BIT; break; - case parser.OPTTokenAll: + case this.parser.OPTTokenAll: this.parseTypeOption(-1); break; // https://github.com/uBlockOrigin/uAssets/issues/192 - case parser.OPTTokenBadfilter: + case this.parser.OPTTokenBadfilter: this.badFilter = true; break; - case parser.OPTTokenCsp: + case this.parser.OPTTokenCsp: if ( this.parseModifierOption(id, val) === false ) { return false; } if ( val !== undefined && this.reBadCSP.test(val) ) { return false; } + this.optionUnitBits |= this.CSP_BIT; break; // https://github.com/gorhill/uBlock/issues/2294 // Detect and discard filter if domain option contains // nonsensical characters. - case parser.OPTTokenDomain: + case this.parser.OPTTokenDomain: this.domainOpt = this.parseHostnameList( - parser, val, 0b1010, this.domainOptList ); if ( this.domainOpt === '' ) { return false; } - this.hasOptionUnits = true; + this.optionUnitBits |= this.DOMAIN_BIT; break; - case parser.OPTTokenDenyAllow: - this.denyallowOpt = this.parseHostnameList(parser, val, 0b0000); + case this.parser.OPTTokenDenyAllow: + this.denyallowOpt = this.parseHostnameList(val, 0b0000); if ( this.denyallowOpt === '' ) { return false; } - this.hasOptionUnits = true; + this.optionUnitBits |= this.DENYALLOW_BIT; break; // https://www.reddit.com/r/uBlockOrigin/comments/d6vxzj/ // Add support for `elemhide`. Rarely used but it happens. - case parser.OPTTokenEhide: - this.parseTypeOption(parser.OPTTokenShide, not); - this.parseTypeOption(parser.OPTTokenGhide, not); + case this.parser.OPTTokenEhide: + this.parseTypeOption(this.parser.OPTTokenShide, not); + this.parseTypeOption(this.parser.OPTTokenGhide, not); break; - case parser.OPTTokenHeader: + case this.parser.OPTTokenHeader: this.headerOpt = val !== undefined ? val : ''; - this.hasOptionUnits = true; + this.optionUnitBits |= this.HEADER_BIT; break; - case parser.OPTTokenImportant: + case this.parser.OPTTokenImportant: if ( this.action === AllowAction ) { return false; } this.action = BlockImportant; break; // Used by Adguard: // https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters#empty-modifier - case parser.OPTTokenEmpty: - if ( this.modifyType !== undefined ) { return false; } - this.modifyType = parser.OPTTokenRedirect; - this.modifyValue = 'empty'; - this.hasOptionUnits = true; + case this.parser.OPTTokenEmpty: + id = this.action === AllowAction + ? this.parser.OPTTokenRedirectRule + : this.parser.OPTTokenRedirect; + if ( this.parseModifierOption(id, 'empty') === false ) { + return false; + } + this.optionUnitBits |= this.REDIRECT_BIT; break; - case parser.OPTTokenMp4: - if ( this.modifyType !== undefined ) { return false; } - this.modifyType = parser.OPTTokenRedirect; - this.modifyValue = 'noopmp4-1s'; - this.hasOptionUnits = true; + case this.parser.OPTTokenMp4: + id = this.action === AllowAction + ? this.parser.OPTTokenRedirectRule + : this.parser.OPTTokenRedirect; + if ( this.parseModifierOption(id, 'noopmp4-1s') === false ) { + return false; + } + this.optionUnitBits |= this.REDIRECT_BIT; break; - case parser.OPTTokenQueryprune: - case parser.OPTTokenRedirect: - case parser.OPTTokenRedirectRule: + case this.parser.OPTTokenQueryprune: if ( this.parseModifierOption(id, val) === false ) { return false; } + this.optionUnitBits |= this.QUERYPRUNE_BIT; break; - case parser.OPTTokenInvalid: + case this.parser.OPTTokenRedirect: + if ( this.action === AllowAction ) { + id = this.parser.OPTTokenRedirectRule; + } + if ( this.parseModifierOption(id, val) === false ) { + return false; + } + this.optionUnitBits |= this.REDIRECT_BIT; + break; + case this.parser.OPTTokenRedirectRule: + if ( this.parseModifierOption(id, val) === false ) { + return false; + } + this.optionUnitBits |= this.REDIRECT_BIT; + break; + case this.parser.OPTTokenInvalid: return false; default: if ( this.tokenIdToNormalizedType.has(id) === false ) { @@ -2971,10 +2998,10 @@ const FilterParser = class { if ( this.typeBits === 0 ) { return false; } } // CSP directives implicitly apply only to document/subdocument. - if ( this.modifyType === parser.OPTTokenCsp ) { + if ( this.modifyType === this.parser.OPTTokenCsp ) { if ( this.typeBits === 0 ) { - this.parseTypeOption(parser.OPTTokenDoc, false); - this.parseTypeOption(parser.OPTTokenFrame, false); + this.parseTypeOption(this.parser.OPTTokenDoc, false); + this.parseTypeOption(this.parser.OPTTokenFrame, false); } } // https://github.com/gorhill/uBlock/issues/2283 @@ -2989,7 +3016,7 @@ const FilterParser = class { parse(parser) { // important! - this.reset(); + this.reset(parser); if ( parser.hasError() ) { this.invalid = true; @@ -3019,7 +3046,7 @@ const FilterParser = class { } // options - if ( parser.hasOptions() && this.parseOptions(parser) === false ) { + if ( parser.hasOptions() && this.parseOptions() === false ) { this.unsupported = true; return this; } @@ -3084,11 +3111,12 @@ const FilterParser = class { // are not good. Avoid if possible. This has a significant positive // impact on performance. - makeToken(parser) { + makeToken() { + if ( this.pattern === '*' ) { return; } if ( this.isRegex ) { return this.extractTokenFromRegex(); } - const match = this.extractTokenFromPattern(parser); + const match = this.extractTokenFromPattern(); if ( match === null ) { return; } this.token = match.token; this.tokenHash = urlTokenizer.tokenHashFromString(this.token); @@ -3096,10 +3124,10 @@ const FilterParser = class { } // Note: a one-char token is better than a documented bad token. - extractTokenFromPattern(parser) { + extractTokenFromPattern() { let bestMatch = null; let bestBadness = 0x7FFFFFFF; - for ( const match of parser.patternTokens() ) { + for ( const match of this.parser.patternTokens() ) { const badness = match.token.length > 1 ? this.badTokens.get(match.token) || 0 : 1; @@ -3162,18 +3190,13 @@ const FilterParser = class { } } - isJustPattern() { - return this.hasOptionUnits === false; + hasNoOptionUnits() { + return this.optionUnitBits === 0; } isJustOrigin() { - return this.hasOptionUnits && - this.isRegex === false && - this.modifyType === undefined && - this.strictParty === 0 && - this.headerOpt === undefined && - this.denyallowOpt === '' && - this.domainOpt !== '' && ( + return this.optionUnitBits === this.DOMAIN_BIT && + this.isRegex === false && ( this.pattern === '*' || ( this.anchor === 0b010 && /^(?:http[s*]?:(?:\/\/)?)$/.test(this.pattern) @@ -3190,6 +3213,15 @@ const FilterParser = class { } }; +FilterParser.prototype.DOMAIN_BIT = 0b00000001; +FilterParser.prototype.DENYALLOW_BIT = 0b00000010; +FilterParser.prototype.HEADER_BIT = 0b00000100; +FilterParser.prototype.STRICT_PARTY_BIT = 0b00001000; + +FilterParser.prototype.CSP_BIT = 0b00010000; +FilterParser.prototype.QUERYPRUNE_BIT = 0b00100000; +FilterParser.prototype.REDIRECT_BIT = 0b01000000; + /******************************************************************************/ FilterParser.parse = (( ) => { @@ -3558,18 +3590,37 @@ FilterContainer.prototype.compile = function(parser, writer) { return false; } + // 0 = network filters + // 1 = network filters: bad filters + writer.select(parsed.badFilter ? 1 : 0); + + // Reminder: + // `redirect=` is a combination of a `redirect-rule` filter and a + // block filter. + if ( parsed.modifyType === parser.OPTTokenRedirect ) { + parsed.modifyType = parser.OPTTokenRedirectRule; + const parsedBlock = parsed.clone(); + parsedBlock.modifyType = undefined; + parsedBlock.optionUnitBits &= ~parsed.REDIRECT_BIT; + this.compileParsed(parsedBlock, writer); + } + + this.compileParsed(parsed, writer); + + return true; +}; + +/******************************************************************************/ + +FilterContainer.prototype.compileParsed = function(parsed, writer) { // Pure hostnames, use more efficient dictionary lookup - // https://github.com/chrisaljoudi/uBlock/issues/665 - // Create a dict keyed on request type etc. - if ( parsed.isPureHostname && parsed.isJustPattern() ) { + if ( parsed.isPureHostname && parsed.hasNoOptionUnits() ) { parsed.tokenHash = this.dotTokenHash; this.compileToAtomicFilter(parsed, parsed.pattern, writer); - return true; + return; } - if ( parser.patternIsMatchAll() === false ) { - parsed.makeToken(parser); - } + parsed.makeToken(); // Special pattern/option cases: // - `*$domain=...` @@ -3595,7 +3646,7 @@ FilterContainer.prototype.compile = function(parser, writer) { entities.push(hn); } } - if ( entities.length === 0 ) { return true; } + if ( entities.length === 0 ) { return; } parsed.tokenHash = tokenHash; const leftAnchored = (parsed.anchor & 0b010) !== 0; for ( const entity of entities ) { @@ -3607,7 +3658,7 @@ FilterContainer.prototype.compile = function(parser, writer) { parsed, FilterCompositeAll.compile(units), writer ); } - return true; + return; } const units = []; @@ -3656,30 +3707,16 @@ FilterContainer.prototype.compile = function(parser, writer) { // Modifier // - // IMPORTANT: the modifier unit MUST always appear first in a sequence. - // - // Reminder: A block filter is implicit with `redirect=` modifier. + // IMPORTANT: the modifier unit MUST always appear first in a sequence if ( parsed.modifyType !== undefined ) { - if ( - parsed.modifyType === parser.OPTTokenRedirect && - parsed.action !== AllowAction - ) { - const fdata = units.length === 1 - ? units[0] - : FilterCompositeAll.compile(units); - this.compileToAtomicFilter(parsed, fdata, writer); - parsed.modifyType = parser.OPTTokenRedirectRule; - } units.unshift(FilterModifier.compile(parsed)); - parsed.action = ModifyAction; + parsed.action = (parsed.action & ~ActionBitsMask) | ModifyAction; } const fdata = units.length === 1 ? units[0] : FilterCompositeAll.compile(units); this.compileToAtomicFilter(parsed, fdata, writer); - - return true; }; /******************************************************************************/ @@ -3689,10 +3726,6 @@ FilterContainer.prototype.compileToAtomicFilter = function( fdata, writer ) { - // 0 = network filters - // 1 = network filters: bad filters - writer.select(parsed.badFilter ? 1 : 0); - const catBits = parsed.action | parsed.party; let typeBits = parsed.typeBits; From 818417f6beb68d3f34809ab172b398c9ede5d756 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 25 Nov 2020 10:24:09 -0500 Subject: [PATCH 3902/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index bd3b79ba6c080..20b3bbd1fb7c2 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.4 +1.31.1.5 From b319dc98f80376b484617befbe536d22aae2a833 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 25 Nov 2020 13:45:45 -0500 Subject: [PATCH 3903/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index dfd6cec1cd4e3..2fa091e6a9c38 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.4", + "version": "1.31.1.5", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b4", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b4/uBlock0_1.31.1b4.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b5", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b5/uBlock0_1.31.1b5.firefox.signed.xpi" } ] } From 60d5b85e41c03133c01779304cb5fe90c294bc40 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Nov 2020 05:09:46 -0500 Subject: [PATCH 3904/4093] Rename `1P`/`3P` tp `strict1p`/`strict3p` as suggested Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1362 --- src/js/codemirror/ubo-static-filtering.js | 13 ++----------- src/js/static-filtering-parser.js | 12 ++++++------ 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index aec5bb92fa92d..79dc3618bd9ff 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -232,21 +232,12 @@ CodeMirror.defineMode('ubo-static-filtering', function() { parserSlot += 3; return 'def'; } - const to = parser.skipUntil( + parserSlot = parser.skipUntil( parserSlot, parser.commentSpan.i, parser.BITComma | parser.BITEqual ); - if ( - to > parserSlot && - /^[13]P/.test(parser.strFromSlices(parserSlot, to - 3)) - ) { - parserSlot = to; - stream.pos = parser.slices[to+1]; - return 'def notice'; - } - parserSlot = to; - stream.pos = parser.slices[to+1]; + stream.pos = parser.slices[parserSlot+1]; return 'def'; }; diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index feead031af3f1..24c41c5d11810 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2054,10 +2054,10 @@ Parser.prototype.OPTNotSupported = OPTNotSupported; const netOptionTokenDescriptors = new Map([ [ '1p', OPTToken1p | OPTCanNegate ], [ 'first-party', OPTToken1p | OPTCanNegate ], - [ '1P', OPTToken1pStrict ], + [ 'strict1p', OPTToken1pStrict ], [ '3p', OPTToken3p | OPTCanNegate ], [ 'third-party', OPTToken3p | OPTCanNegate ], - [ '3P', OPTToken3pStrict ], + [ 'strict3p', OPTToken3pStrict ], [ 'all', OPTTokenAll | OPTNetworkType | OPTNonCspableType ], [ 'badfilter', OPTTokenBadfilter ], [ 'cname', OPTTokenCname | OPTAllowOnly | OPTModifierType ], @@ -2109,10 +2109,10 @@ Parser.prototype.netOptionTokenDescriptors = Parser.netOptionTokenIds = new Map([ [ '1p', OPTToken1p ], [ 'first-party', OPTToken1p ], - [ '1P', OPTToken1pStrict ], + [ 'strict1p', OPTToken1pStrict ], [ '3p', OPTToken3p ], [ 'third-party', OPTToken3p ], - [ '3P', OPTToken3pStrict ], + [ 'strict3p', OPTToken3pStrict ], [ 'all', OPTTokenAll ], [ 'badfilter', OPTTokenBadfilter ], [ 'cname', OPTTokenCname ], @@ -2160,9 +2160,9 @@ Parser.netOptionTokenIds = new Map([ Parser.netOptionTokenNames = new Map([ [ OPTToken1p, '1p' ], - [ OPTToken1pStrict, '1P' ], + [ OPTToken1pStrict, 'strict1p' ], [ OPTToken3p, '3p' ], - [ OPTToken3pStrict, '3P' ], + [ OPTToken3pStrict, 'strict3p' ], [ OPTTokenAll, 'all' ], [ OPTTokenBadfilter, 'badfilter' ], [ OPTTokenCname, 'cname' ], From 80413dff830cd9e29accc5c5ee29a70a8046cf9c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Nov 2020 05:43:14 -0500 Subject: [PATCH 3905/4093] Fix forgotton instances of `1P`/`3P` Related commit: - https://github.com/gorhill/uBlock/commit/60d5b85e41c03133c01779304cb5fe90c294bc40 Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1362 --- 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 bc76e6f27d8a0..7ac40f0ef205b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2348,7 +2348,7 @@ const FilterStrictParty = class { } logData(details) { - details.options.push(this.not ? '3P' : '1P'); + details.options.push(this.not ? 'strict3p' : 'strict1p'); } toSelfie() { From 6ac09a28560ebe7de6c20841b5ffb024fe97442d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Nov 2020 09:34:12 -0500 Subject: [PATCH 3906/4093] Add ability to parse `removeparam=` as `queryprune=` Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1356 Related commit: - https://github.com/gorhill/uBlock/commit/bde3164eb445a4e74acca303ec9fa07f82ba1b1c It is not possible to achieve perfect compatiblity at this point, but reasonable compatibility should be achieved for a majority of instances of `removeparam=`. Notable differences: -------------------- uBO always matches in a case insensitive manner, there is no need to ask for case-insensitivity, and no need to use uppercase characters in `queryprune=` values. uBO does not escape special regex characters since the `queryprune=` values are always assumed to be literal regex expression (leaving out the documented special characters). This means `removeparam=` with characters which are special regex characters won't be properly translated and are unlikely to work properly in uBO. For example, the `queryprune` value of a filter such as `$removeparam=__xts__[0]` internally become the literal regex `/__xts__[0]/`, and consequently would not match a query parameter such as `...?__xts__[0]=...`. Notes: ------ Additionally, for performance reason, when uBO encounter a pattern-less `queryprune=` (or `removeparam=`) filter, it will try to extract a valid pattern from the `queryprune=` value. For instance, the following filter: $queryprune=utm_campaign Will be translated internally into: utm_campaign$queryprune=utm_campaign The logger will reflect this internal translation. --- src/js/static-filtering-parser.js | 2 + src/js/static-net-filtering.js | 92 ++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 24c41c5d11810..0c2d76577b547 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2092,6 +2092,7 @@ const netOptionTokenDescriptors = new Map([ [ 'popunder', OPTTokenPopunder | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ], [ 'popup', OPTTokenPopup | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ], [ 'queryprune', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], + [ 'removeparam', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], [ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ], [ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType ], [ 'script', OPTTokenScript | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], @@ -2147,6 +2148,7 @@ Parser.netOptionTokenIds = new Map([ [ 'popunder', OPTTokenPopunder ], [ 'popup', OPTTokenPopup ], [ 'queryprune', OPTTokenQueryprune ], + [ 'removeparam', OPTTokenQueryprune ], [ 'redirect', OPTTokenRedirect ], [ 'redirect-rule', OPTTokenRedirectRule ], [ 'script', OPTTokenScript ], diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 7ac40f0ef205b..07f4807b9761b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2628,7 +2628,7 @@ const FilterParser = class { this.noTokenHash = urlTokenizer.noTokenHash; this.reIsolateHostname = /^(\*?\.)?([^\x00-\x24\x26-\x2C\x2F\x3A-\x5E\x60\x7B-\x7F]+)(.*)/; this.reBadCSP = /(?:=|;)\s*report-(?:to|uri)\b/; - this.reRegexToken = /[%0-9A-Za-z]+/g; + this.reToken = /[%0-9A-Za-z]+/g; this.reRegexTokenAbort = /[\(\)\[\]]/; this.reRegexBadPrefix = /(^|[^\\]\.|\\[%SDWsdw]|[^\\][()*+?[\\\]{}])$/; this.reRegexBadSuffix = /^([^\\]\.|\\[%SDWsdw]|[()*+?[\]{}]|$)/; @@ -3110,34 +3110,48 @@ const FilterParser = class { // i.e. very common with a high probability of ending up as a miss, // are not good. Avoid if possible. This has a significant positive // impact on performance. + // + // For pattern-less queryprune filters, try to derive a pattern from + // the queryprune value. makeToken() { - if ( this.pattern === '*' ) { return; } + if ( this.pattern === '*' ) { + if ( + this.modifyType !== this.parser.OPTTokenQueryprune || + this.makePatternFromQuerypruneValue() === false + ) { + return; + } + } if ( this.isRegex ) { return this.extractTokenFromRegex(); } - const match = this.extractTokenFromPattern(); - if ( match === null ) { return; } - this.token = match.token; - this.tokenHash = urlTokenizer.tokenHashFromString(this.token); - this.tokenBeg = match.pos; + this.extractTokenFromPattern(); } // Note: a one-char token is better than a documented bad token. extractTokenFromPattern() { + this.reToken.lastIndex = 0; + const pattern = this.pattern; let bestMatch = null; let bestBadness = 0x7FFFFFFF; - for ( const match of this.parser.patternTokens() ) { - const badness = match.token.length > 1 - ? this.badTokens.get(match.token) || 0 + for (;;) { + const match = this.reToken.exec(pattern); + if ( match === null ) { break; } + const badness = match[0].length > 1 + ? this.badTokens.get(match[0]) || 0 : 1; - if ( badness === 0 ) { return match; } if ( badness < bestBadness ) { bestMatch = match; + if ( badness === 0 ) { break; } bestBadness = badness; } } - return bestMatch; + if ( bestMatch !== null ) { + this.token = bestMatch[0]; + this.tokenHash = urlTokenizer.tokenHashFromString(this.token); + this.tokenBeg = bestMatch.index; + } } // https://github.com/gorhill/uBlock/issues/2781 @@ -3147,15 +3161,16 @@ const FilterParser = class { // Mind `\b` directives: `/\bads\b/` should result in token being `ads`, // not `bads`. extractTokenFromRegex() { - this.reRegexToken.lastIndex = 0; - const s = this.pattern; + this.reToken.lastIndex = 0; + const pattern = this.pattern; + let bestToken; let bestBadness = 0x7FFFFFFF; for (;;) { - const matches = this.reRegexToken.exec(s); + const matches = this.reToken.exec(pattern); if ( matches === null ) { break; } let token = matches[0]; - let prefix = s.slice(0, matches.index); - let suffix = s.slice(this.reRegexToken.lastIndex); + let prefix = pattern.slice(0, matches.index); + let suffix = pattern.slice(this.reToken.lastIndex); if ( this.reRegexTokenAbort.test(prefix) && this.reRegexTokenAbort.test(suffix) @@ -3181,13 +3196,47 @@ const FilterParser = class { ? this.badTokens.get(token) || 0 : 1; if ( badness < bestBadness ) { - this.token = token.toLowerCase(); - this.tokenHash = urlTokenizer.tokenHashFromString(this.token); - this.tokenBeg = matches.index; + bestToken = token; if ( badness === 0 ) { break; } bestBadness = badness; } } + if ( bestToken !== undefined ) { + this.token = bestToken.toLowerCase(); + this.tokenHash = urlTokenizer.tokenHashFromString(this.token); + } + } + + makePatternFromQuerypruneValue() { + let pattern = this.modifyValue; + if ( pattern === '*' || pattern.charCodeAt(0) === 0x21 /* '!' */ ) { + return false; + } + if ( /^\w+$/.test(pattern) ) { + this.pattern = `${pattern}=`; + return true; + } + const reRegex = /^\/(.+)\/i?$/; + if ( reRegex.test(pattern) ) { + pattern = reRegex.exec(pattern)[1]; + } else { + let prefix = '', suffix = ''; + if ( pattern.startsWith('|') ) { + pattern = pattern.slice(1); + prefix = '\\b'; + } + if ( pattern.endsWith('|') ) { + pattern = pattern.slice(0, -1); + suffix = '\\b'; + } + if ( pattern.indexOf('|') !== -1 ) { + pattern = `(?:${pattern})`; + } + pattern = prefix + pattern + suffix; + } + this.pattern = pattern; + this.isRegex = true; + return true; } hasNoOptionUnits() { @@ -4288,6 +4337,7 @@ FilterContainer.prototype.filterQuery = function(fctxt) { FilterContainer.prototype.parseFilterPruneValue = function(modifier) { const cache = {}; + const reRegex = /^\/(.+)\/i?$/; let retext = modifier.value; if ( retext === '*' ) { cache.all = true; @@ -4296,6 +4346,8 @@ FilterContainer.prototype.parseFilterPruneValue = function(modifier) { if ( cache.not ) { retext = retext.slice(1); } if ( /^\w+$/.test(retext) ) { retext = `^${retext}=`; + } else if ( reRegex.test(retext) ) { + retext = reRegex.exec(retext)[1]; } else { if ( retext.startsWith('|') ) { retext = `^${retext.slice(1)}`; } if ( retext.endsWith('|') ) { retext = `${retext.slice(0,-1)}$`; } From 1b5841cdea9509b1e02acbf38655b318720129d1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Nov 2020 09:59:48 -0500 Subject: [PATCH 3907/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 20b3bbd1fb7c2..cf6617728287c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.5 +1.31.1.6 From 123147480159a4a8d7091e89742e9f7bf1bb5283 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 26 Nov 2020 13:26:07 -0500 Subject: [PATCH 3908/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2fa091e6a9c38..8dedebabb1570 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.5", + "version": "1.31.1.6", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b5", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b5/uBlock0_1.31.1b5.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b6", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b6/uBlock0_1.31.1b6.firefox.signed.xpi" } ] } From bf7ce857eec2bd7a85550b73d1e0dc74921f6af4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 27 Nov 2020 11:36:50 -0500 Subject: [PATCH 3909/4093] Update URL of HUN filter list Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1364 --- assets/assets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/assets.json b/assets/assets.json index bd9c328ff96a5..41af26ee772ef 100644 --- a/assets/assets.json +++ b/assets/assets.json @@ -363,7 +363,7 @@ "off": true, "title": "HUN: hufilter", "lang": "hu", - "contentURL": "https://raw.githubusercontent.com/hufilter/hufilter/master/hufilter.txt", + "contentURL": "https://raw.githubusercontent.com/hufilter/hufilter/master/hufilter-ublock.txt", "supportURL": "https://github.com/hufilter/hufilter" }, "IDN-0": { From c959fd6cd94afc6eddbbc7ed1a7a969adbca0d0b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 27 Nov 2020 16:01:34 -0500 Subject: [PATCH 3910/4093] Fix comment --- src/js/static-net-filtering.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 07f4807b9761b..20895709e9ac4 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -40,8 +40,8 @@ const µb = µBlock; // || | |+------ bit 2: modifier // || | +------- bit 3- 4: party [0-3] // || +--------- bit 5- 9: type [0-31] -// |+-------------- bit 10-15: unused -// +--------------- bit 16: headers-based filters +// |+-------------- bit 10: headers-based filters +// +--------------- bit 11-15: unused const CategoryCount = 1 << 0xb; // shift left to first unused bit From ab5ab8575cb9164937c39d53fb90df3e6ab4433d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 08:28:20 -0500 Subject: [PATCH 3911/4093] Avoid re-assigning asset cache registry at launch Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1365 --- src/js/assets.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/js/assets.js b/src/js/assets.js index b173ba151b0fc..09acd95f0183b 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -477,7 +477,21 @@ const getAssetCacheRegistry = function() { bin instanceof Object && bin.assetCacheRegistry instanceof Object ) { - assetCacheRegistry = bin.assetCacheRegistry; + if ( Object.keys(assetCacheRegistry).length === 0 ) { + assetCacheRegistry = bin.assetCacheRegistry; + } else { + console.error( + 'getAssetCacheRegistry(): assetCacheRegistry reassigned!' + ); + if ( + Object.keys(bin.assetCacheRegistry).sort().join() !== + Object.keys(assetCacheRegistry).sort().join() + ) { + console.error( + 'getAssetCacheRegistry(): assetCacheRegistry changes overwritten!' + ); + } + } } return assetCacheRegistry; }); From a8379785fc4e243f487a87faad810206f54d79d0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 08:29:40 -0500 Subject: [PATCH 3912/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index cf6617728287c..65ac858e51680 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.6 +1.31.1.7 From 5d153826732c752cffca877c4207bc711ff35936 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 08:32:16 -0500 Subject: [PATCH 3913/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/fil/messages.json | 26 +++++++++++++------------- src/_locales/hy/messages.json | 24 ++++++++++++------------ src/_locales/sq/messages.json | 2 +- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index e9ca2931acd1f..1232407e4d623 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -32,7 +32,7 @@ "description": "appears as tab name in dashboard" }, "1pPageName": { - "message": "Ang iyong (mga) filters", + "message": "Mga filter ko", "description": "appears as tab name in dashboard" }, "rulesPageName": { @@ -508,7 +508,7 @@ "description": "header" }, "rulesTemporaryHeader": { - "message": "Temporary rules", + "message": "Pansamantalang mga batas", "description": "header" }, "rulesRevert": { @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "binago", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -700,7 +700,7 @@ "description": "Label to identify a rule field" }, "loggerEntryDetailsContext": { - "message": "Context", + "message": "Konteksto", "description": "Label to identify a context field (typically a hostname)" }, "loggerEntryDetailsRootContext": { @@ -760,11 +760,11 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyOrigin": { - "message": "from anywhere", + "message": "mula kahit saan", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartNotImportant": { - "message": "except when", + "message": "maliban kung", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartImportant": { @@ -860,11 +860,11 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "Pinagmulang kodigo", "description": "Link text to source code repo" }, "aboutTranslations": { - "message": "Translations", + "message": "Mga Pagsasalin", "description": "Link text to translations repo" }, "aboutFilterLists": { @@ -876,7 +876,7 @@ "description": "Shown in the About pane" }, "aboutBackupDataButton": { - "message": "Back up to file", + "message": "I-backup sa talaksan", "description": "Text for button to create a backup of all settings" }, "aboutBackupFilename": { @@ -884,7 +884,7 @@ "description": "English: my-ublock-backup_{{datetime}}.txt" }, "aboutRestoreDataButton": { - "message": "Restore from file...", + "message": "Ibalik mula sa talaksan...", "description": "English: Restore from file..." }, "aboutResetDataButton": { @@ -980,11 +980,11 @@ "description": "English: Disable strict blocking for {{hostname}} ..." }, "docblockedDisableTemporary": { - "message": "Temporarily", + "message": "Pansamantalang", "description": "English: Temporarily" }, "docblockedDisablePermanent": { - "message": "Permanently", + "message": "Permanenteng", "description": "English: Permanently" }, "cloudPush": { @@ -1012,7 +1012,7 @@ "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { - "message": "Submit", + "message": "Ipasa", "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index 350aec59aef38..f7c7ce6a8b34f 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -8,7 +8,7 @@ "description": "this will be in the Chrome web store: must be 132 characters or less" }, "dashboardName": { - "message": "uBlock₀ — Dashboard", + "message": "uBlock₀ — Վահանակ", "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { @@ -272,11 +272,11 @@ "description": "English: Create" }, "pickerPick": { - "message": "Pick", + "message": "Ընտրել", "description": "English: Pick" }, "pickerQuit": { - "message": "Quit", + "message": "Դուրս գալ", "description": "English: Quit" }, "pickerPreview": { @@ -296,7 +296,7 @@ "description": "English: Click, Ctrl-click" }, "pickerContextMenuEntry": { - "message": "Block element...", + "message": "Արգելափակել տարրը...", "description": "An entry in the browser's contextual menu" }, "settingsCollapseBlockedPrompt": { @@ -464,7 +464,7 @@ "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { - "message": "Out of date.", + "message": "Ժամկետանց", "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pViewContent": { @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "փոփոխված", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -712,7 +712,7 @@ "description": "Label to identify a field providing partyness information" }, "loggerEntryDetailsType": { - "message": "Type", + "message": "Տիպ", "description": "Label to identify the type of an entry" }, "loggerEntryDetailsURL": { @@ -728,7 +728,7 @@ "description": "Label for the context selector" }, "loggerURLFilteringTypeLabel": { - "message": "Type:", + "message": "Տիպ.", "description": "Label for the type selector" }, "loggerStaticFilteringHeader": { @@ -748,11 +748,11 @@ "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartType": { - "message": "type “{{type}}”", + "message": "տիպ «{{type}}»", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartAnyType": { - "message": "any type", + "message": "կամայական տիպ", "description": "Used in the static filtering wizard" }, "loggerStaticFilteringSentencePartOrigin": { @@ -912,7 +912,7 @@ "description": "No longer used" }, "subscribeButton": { - "message": "Subscribe", + "message": "Բաժանորդագրվել", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { @@ -940,7 +940,7 @@ "description": "English: {{value}} days ago" }, "showDashboardButton": { - "message": "Show Dashboard", + "message": "Տեսնել վահանակը", "description": "Firefox/Fennec-specific: Show Dashboard" }, "showNetworkLogButton": { diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 9c25751798cbe..9cafef79f9159 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "e modifikuar", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { From f75040afb8a7810607680d7bb9c1711493a6e6ff Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 08:35:39 -0500 Subject: [PATCH 3914/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 8dedebabb1570..a63e9247d3f8c 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.6", + "version": "1.31.1.7", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b6", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b6/uBlock0_1.31.1b6.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b7", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b7/uBlock0_1.31.1b7.firefox.signed.xpi" } ] } From c6d0204b236449a8fe055c17c0b10af0446cb9ec Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 08:52:18 -0500 Subject: [PATCH 3915/4093] Remove requirement for presence of type with `redirect=` option Related issue: - https://github.com/gorhill/uBlock/issues/3590 Since the `redirect=` option was refactored into a modifier filter, presence of a type (`script`, `xhr`, etc.) is no longer a requirement. --- docs/tests/static-filtering-parser-checklist.txt | 4 +--- src/js/static-filtering-parser.js | 9 ++------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/docs/tests/static-filtering-parser-checklist.txt b/docs/tests/static-filtering-parser-checklist.txt index c88599983d854..e7b5b5496b346 100644 --- a/docs/tests/static-filtering-parser-checklist.txt +++ b/docs/tests/static-filtering-parser-checklist.txt @@ -42,6 +42,7 @@ a* ! valid options $script,redirect=noop.js +*$redirect=noop.js *$empty *$xhr,empty *$xhr,redirect=empty @@ -91,9 +92,6 @@ $ ! bad regex /(abc|def/$xhr -! can't redirect without type (except to `empty`) -*$redirect=noop.js - ! non-redirectable types *$beacon,redirect-rule=empty *$ping,redirect-rule=empty diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 0c2d76577b547..5b9fdcf216a24 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2383,18 +2383,13 @@ const NetOptionsIterator = class { } } } - // `redirect=`: requires at least one single redirectable type + // `redirect=`: can't redirect non-redirectable types { let i = this.tokenPos[OPTTokenRedirect]; if ( i === -1 ) { i = this.tokenPos[OPTTokenRedirectRule]; } - if ( - i !== -1 && ( - hasNoBits(allBits, OPTRedirectableType) || - hasBits(allBits, OPTNonRedirectableType) - ) - ) { + if ( i !== -1 && hasBits(allBits, OPTNonRedirectableType) ) { optSlices[i] = OPTTokenInvalid; if ( this.interactive ) { this.parser.errorSlices(optSlices[i+1], optSlices[i+5]); From eae7cd58fe679d6765d62bb6c01e296d5301433a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 11:26:28 -0500 Subject: [PATCH 3916/4093] Add support for `match-case` option; fine-tune behavior of `redirect=` `match-case` ------------ Related issue: - https://github.com/uBlockOrigin/uAssets/issues/8280#issuecomment-735245452 The new filter option `match-case` can be used only for regex-based filters. Using `match-case` with any other sort of filters will cause uBO to discard the filter. `redirect=` ----------- Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1366 `redirect=` filters with unresolvable resource token at runtime will be discarded. Additionally, the implicit priority is now set to 1 (was 0). The idea is to allow custom `redirect=` filters to be used strictly as fallback `redirect=` filters in case another `redirect=` filter is not picked up. For example, one might create a `redirect=click2load.html:0` filter, to be taken if and only if the blocked resource is not already being redirected by another "official" filter in one of the enabled filter lists. --- src/js/background.js | 4 +- src/js/redirect-engine.js | 10 ++++ src/js/static-filtering-parser.js | 47 ++++++++++------ src/js/static-net-filtering.js | 91 ++++++++++++++++++------------- 4 files changed, 95 insertions(+), 57 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index 7b21c2c701f4c..e913b9ff11723 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -140,8 +140,8 @@ const µBlock = (( ) => { // jshint ignore:line // Read-only systemSettings: { - compiledMagic: 35, // Increase when compiled format changes - selfieMagic: 35, // Increase when selfie format changes + compiledMagic: 36, // Increase when compiled format changes + selfieMagic: 36, // Increase when selfie format changes }, // https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501 diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 95db7673dbd0a..57a79a1733af2 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -304,6 +304,16 @@ RedirectEngine.prototype.tokenToURL = function(fctxt, token) { /******************************************************************************/ +RedirectEngine.prototype.hasToken = function(token) { + const asDataURI = token.charCodeAt(0) === 0x25 /* '%' */; + if ( asDataURI ) { + token = token.slice(1); + } + return this.resources.get(this.aliases.get(token) || token) !== undefined; +}; + +/******************************************************************************/ + RedirectEngine.prototype.toSelfie = async function() { }; diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 5b9fdcf216a24..8504abf577cee 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1923,22 +1923,23 @@ const OPTTokenImage = 20; const OPTTokenImportant = 21; const OPTTokenInlineFont = 22; const OPTTokenInlineScript = 23; -const OPTTokenMedia = 24; -const OPTTokenMp4 = 25; -const OPTTokenObject = 26; -const OPTTokenOther = 27; -const OPTTokenPing = 28; -const OPTTokenPopunder = 29; -const OPTTokenPopup = 30; -const OPTTokenRedirect = 31; -const OPTTokenRedirectRule = 32; -const OPTTokenQueryprune = 33; -const OPTTokenScript = 34; -const OPTTokenShide = 35; -const OPTTokenXhr = 36; -const OPTTokenWebrtc = 37; -const OPTTokenWebsocket = 38; -const OPTTokenCount = 39; +const OPTTokenMatchCase = 24; +const OPTTokenMedia = 25; +const OPTTokenMp4 = 26; +const OPTTokenObject = 27; +const OPTTokenOther = 28; +const OPTTokenPing = 29; +const OPTTokenPopunder = 30; +const OPTTokenPopup = 31; +const OPTTokenRedirect = 32; +const OPTTokenRedirectRule = 33; +const OPTTokenQueryprune = 34; +const OPTTokenScript = 35; +const OPTTokenShide = 36; +const OPTTokenXhr = 37; +const OPTTokenWebrtc = 38; +const OPTTokenWebsocket = 39; +const OPTTokenCount = 40; //const OPTPerOptionMask = 0x0000ff00; const OPTCanNegate = 1 << 8; @@ -2021,6 +2022,7 @@ Parser.prototype.OPTTokenImportant = OPTTokenImportant; Parser.prototype.OPTTokenInlineFont = OPTTokenInlineFont; Parser.prototype.OPTTokenInlineScript = OPTTokenInlineScript; Parser.prototype.OPTTokenInvalid = OPTTokenInvalid; +Parser.prototype.OPTTokenMatchCase = OPTTokenMatchCase; Parser.prototype.OPTTokenMedia = OPTTokenMedia; Parser.prototype.OPTTokenMp4 = OPTTokenMp4; Parser.prototype.OPTTokenObject = OPTTokenObject; @@ -2082,6 +2084,7 @@ const netOptionTokenDescriptors = new Map([ [ 'important', OPTTokenImportant | OPTBlockOnly ], [ 'inline-font', OPTTokenInlineFont | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ], [ 'inline-script', OPTTokenInlineScript | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ], + [ 'match-case', OPTTokenMatchCase ], [ 'media', OPTTokenMedia | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], [ 'mp4', OPTTokenMp4 | OPTNetworkType | OPTBlockOnly | OPTModifierType ], [ 'object', OPTTokenObject | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], @@ -2138,6 +2141,7 @@ Parser.netOptionTokenIds = new Map([ [ 'important', OPTTokenImportant ], [ 'inline-font', OPTTokenInlineFont ], [ 'inline-script', OPTTokenInlineScript ], + [ 'match-case', OPTTokenMatchCase ], [ 'media', OPTTokenMedia ], [ 'mp4', OPTTokenMp4 ], [ 'object', OPTTokenObject ], @@ -2184,6 +2188,7 @@ Parser.netOptionTokenNames = new Map([ [ OPTTokenImportant, 'important' ], [ OPTTokenInlineFont, 'inline-font' ], [ OPTTokenInlineScript, 'inline-script' ], + [ OPTTokenMatchCase, 'match-case' ], [ OPTTokenMedia, 'media' ], [ OPTTokenMp4, 'mp4' ], [ OPTTokenObject, 'object' ], @@ -2462,6 +2467,16 @@ const NetOptionsIterator = class { } } } + // `match-case`: valid only for regex-based filters + { + const i = this.tokenPos[OPTTokenMatchCase]; + if ( i !== -1 && this.parser.patternIsRegex() === false ) { + optSlices[i] = OPTTokenInvalid; + if ( this.interactive ) { + this.parser.errorSlices(optSlices[i+1], optSlices[i+5]); + } + } + } return this; } next() { diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 20895709e9ac4..a58e1aa7c6bdb 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -146,6 +146,7 @@ const typeValueToTypeName = [ // valid until the next evaluation. let $requestURL = ''; +let $requestURLRaw = ''; let $requestHostname = ''; let $docHostname = ''; let $docDomain = ''; @@ -867,9 +868,13 @@ const FilterPatternGeneric = class { } static compile(details) { - const anchor = details.anchor; + const out = [ + FilterPatternGeneric.fid, + details.pattern, + details.anchor, + ]; details.anchor = 0; - return [ FilterPatternGeneric.fid, details.pattern, anchor ]; + return out; } static fromCompiled(args) { @@ -1107,20 +1112,22 @@ registerFilterClass(FilterTrailingSeparator); /******************************************************************************/ const FilterRegex = class { - constructor(s) { + constructor(s, matchCase = false) { this.s = s; + if ( matchCase ) { + this.matchCase = true; + } } match() { if ( this.re === null ) { - this.re = FilterRegex.dict.get(this.s); - if ( this.re === undefined ) { - this.re = new RegExp(this.s, 'i'); - FilterRegex.dict.set(this.s, this.re); - } + this.re = new RegExp( + this.s, + this.matchCase ? '' : 'i' + ); } - if ( this.re.test($requestURL) === false ) { return false; } - $patternMatchLeft = $requestURL.search(this.re); + if ( this.re.test($requestURLRaw) === false ) { return false; } + $patternMatchLeft = $requestURLRaw.search(this.re); return true; } @@ -1128,33 +1135,36 @@ const FilterRegex = class { details.pattern.push('/', this.s, '/'); details.regex.push(this.s); details.isRegex = true; + if ( this.matchCase ) { + details.options.push('match-case'); + } } toSelfie() { - return [ this.fid, this.s ]; + return [ this.fid, this.s, this.matchCase ]; } static compile(details) { - return [ FilterRegex.fid, details.pattern ]; + return [ FilterRegex.fid, details.pattern, details.patternMatchCase ]; } static fromCompiled(args) { - return new FilterRegex(args[1]); + return new FilterRegex(args[1], args[2]); } static fromSelfie(args) { - return new FilterRegex(args[1]); + return new FilterRegex(args[1], args[2]); } static keyFromArgs(args) { - return args[1]; + return `${args[1]}\t${args[2]}`; } }; FilterRegex.prototype.re = null; +FilterRegex.prototype.matchCase = false; FilterRegex.isSlow = true; -FilterRegex.dict = new Map(); registerFilterClass(FilterRegex); @@ -2783,6 +2793,7 @@ const FilterParser = class { this.modifyValue = undefined; this.invalid = false; this.pattern = ''; + this.patternMatchCase = false; this.party = AnyParty; this.optionUnitBits = 0; this.domainOpt = ''; @@ -2944,6 +2955,9 @@ const FilterParser = class { } this.optionUnitBits |= this.REDIRECT_BIT; break; + case this.parser.OPTTokenMatchCase: + this.patternMatchCase = true; + break; case this.parser.OPTTokenMp4: id = this.action === AllowAction ? this.parser.OPTTokenRedirectRule @@ -3833,6 +3847,7 @@ FilterContainer.prototype.matchAndFetchModifiers = function( modifierType ) { $requestURL = urlTokenizer.setURL(fctxt.url); + $requestURLRaw = fctxt.url; $docHostname = fctxt.getDocHostname(); $docDomain = fctxt.getDocDomain(); $docEntity.reset(); @@ -4126,6 +4141,7 @@ FilterContainer.prototype.matchStringReverse = function(type, url) { // Prime tokenizer: we get a normalized URL in return. $requestURL = urlTokenizer.setURL(url); + $requestURLRaw = url; this.$filterUnit = 0; // These registers will be used by various filters @@ -4172,6 +4188,7 @@ FilterContainer.prototype.matchString = function(fctxt, modifiers = 0) { // Prime tokenizer: we get a normalized URL in return. $requestURL = urlTokenizer.setURL(fctxt.url); + $requestURLRaw = fctxt.url; this.$filterUnit = 0; // These registers will be used by various filters @@ -4203,6 +4220,7 @@ FilterContainer.prototype.matchHeaders = function(fctxt, headers) { // Prime tokenizer: we get a normalized URL in return. $requestURL = urlTokenizer.setURL(fctxt.url); + $requestURLRaw = fctxt.url; this.$filterUnit = 0; // These registers will be used by various filters @@ -4239,13 +4257,9 @@ FilterContainer.prototype.redirectRequest = function(fctxt) { const directive = directives[0]; if ( (directive.bits & AllowAction) !== 0 ) { return directive; } const modifier = directive.modifier; - if ( modifier.cache === undefined ) { - modifier.cache = this.parseRedirectRequestValue(modifier.value); - } - fctxt.redirectURL = µb.redirectEngine.tokenToURL( - fctxt, - modifier.cache.token - ); + const { token } = this.parseRedirectRequestValue(modifier); + fctxt.redirectURL = µb.redirectEngine.tokenToURL(fctxt, token); + if ( fctxt.redirectURL === undefined ) { return; } return directive; } // Multiple directives mean more work to do. @@ -4258,15 +4272,11 @@ FilterContainer.prototype.redirectRequest = function(fctxt) { winningDirective = directive; break; } - if ( modifier.cache === undefined ) { - modifier.cache = this.parseRedirectRequestValue(modifier.value); - } - if ( - winningDirective === undefined || - modifier.cache.priority > winningPriority - ) { + const { token, priority } = this.parseRedirectRequestValue(modifier); + if ( µb.redirectEngine.hasToken(token) === false ) { continue; } + if ( winningDirective === undefined || priority > winningPriority ) { winningDirective = directive; - winningPriority = modifier.cache.priority; + winningPriority = priority; } } if ( winningDirective === undefined ) { return; } @@ -4279,15 +4289,18 @@ FilterContainer.prototype.redirectRequest = function(fctxt) { return winningDirective; }; -FilterContainer.prototype.parseRedirectRequestValue = function(rawValue) { - let token = rawValue; - let priority = 0; - const match = /:(\d+)$/.exec(rawValue); - if ( match !== null ) { - token = rawValue.slice(0, match.index); - priority = parseInt(match[1], 10); +FilterContainer.prototype.parseRedirectRequestValue = function(modifier) { + if ( modifier.cache === undefined ) { + let token = modifier.value; + let priority = 1; + const match = /:(\d+)$/.exec(token); + if ( match !== null ) { + token = token.slice(0, match.index); + priority = parseInt(match[1], 10); + } + modifier.cache = { token, priority }; } - return { token, priority }; + return modifier.cache; }; /******************************************************************************/ From 8d7e7cae6955152b7de3d68cec901923cceadeaa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 11:38:37 -0500 Subject: [PATCH 3917/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 65ac858e51680..ab8b9e16bf724 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.7 +1.31.1.8 From da01ea467138b49268bbcbce8ce120abb72a8701 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 28 Nov 2020 12:26:00 -0500 Subject: [PATCH 3918/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index a63e9247d3f8c..0ff61338fefa8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.7", + "version": "1.31.1.8", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b7", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b7/uBlock0_1.31.1b7.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b8", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b8/uBlock0_1.31.1b8.firefox.signed.xpi" } ] } From dac8d6becb2c522f9c0278bb0619c53c3066ece7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 07:38:15 -0500 Subject: [PATCH 3919/4093] Fix broken token extraction Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1367 Regression from: - https://github.com/gorhill/uBlock/commit/6ac09a28560ebe7de6c20841b5ffb024fe97442d Need to mind wildcards adjacent to extracted token. --- src/js/static-net-filtering.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index a58e1aa7c6bdb..7a58c37755409 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3152,14 +3152,23 @@ const FilterParser = class { for (;;) { const match = this.reToken.exec(pattern); if ( match === null ) { break; } - const badness = match[0].length > 1 - ? this.badTokens.get(match[0]) || 0 - : 1; - if ( badness < bestBadness ) { - bestMatch = match; - if ( badness === 0 ) { break; } - bestBadness = badness; + const token = match[0]; + const badness = token.length > 1 ? this.badTokens.get(token) || 0 : 1; + if ( badness >= bestBadness ) { continue; } + if ( match.index > 0 ) { + const c = pattern.charCodeAt(match.index - 1); + if ( c === 0x2A /* '*' */ ) { continue; } + } + if ( token.length < this.maxTokenLen ) { + const lastIndex = this.reToken.lastIndex; + if ( lastIndex < pattern.length ) { + const c = pattern.charCodeAt(lastIndex); + if ( c === 0x2A /* '*' */ ) { continue; } + } } + bestMatch = match; + if ( badness === 0 ) { break; } + bestBadness = badness; } if ( bestMatch !== null ) { this.token = bestMatch[0]; From 74730b4e690be9b1d2ac093df1df994db5bcf869 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 07:40:22 -0500 Subject: [PATCH 3920/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ab8b9e16bf724..3b706951e0a51 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.8 +1.31.1.9 From 6261b2ab6364bca1f93f74eec051a247ef7a6a45 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 07:55:44 -0500 Subject: [PATCH 3921/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 0ff61338fefa8..ccd62d0616d08 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.8", + "version": "1.31.1.9", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b8", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b8/uBlock0_1.31.1b8.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b9", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b9/uBlock0_1.31.1b9.firefox.signed.xpi" } ] } From d1895d4749b76de5c06ed6545ac2a4a3cd1724a4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 11:02:40 -0500 Subject: [PATCH 3922/4093] Another round of fine-tuning `queryprune=` syntax Related discussions: - https://github.com/uBlockOrigin/uBlock-issues/issues/1356#issuecomment-732411286 - https://github.com/AdguardTeam/CoreLibs/issues/1384 Changes: Negation character is `~` (instead of `!`). Drop special anchor character `|` -- leading `|` will be supported until no such filter is present in uBO's own filter lists. For example, instance of `queryprune=|ad` will have to be replaced with `queryprune=/^ad/` (or `queryprune=ad` if the name of the parameter to remove is exactly `ad`). Align semantic with that of AdGuard's `removeparam=`, except that specifying multiple `|`-separated names is not supported. --- src/js/background.js | 4 +- src/js/static-filtering-parser.js | 118 ++++++++++++++++++++---------- src/js/static-net-filtering.js | 93 +++++++++++------------ 3 files changed, 123 insertions(+), 92 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index e913b9ff11723..af21783a24c77 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -140,8 +140,8 @@ const µBlock = (( ) => { // jshint ignore:line // Read-only systemSettings: { - compiledMagic: 36, // Increase when compiled format changes - selfieMagic: 36, // Increase when selfie format changes + compiledMagic: 37, // Increase when compiled format changes + selfieMagic: 37, // Increase when selfie format changes }, // https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501 diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 8504abf577cee..9337a627a7081 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -389,14 +389,24 @@ const Parser = class { } // If the pattern is not a regex, there might be options. + // + // The character `$` is deemed to be an option anchor if and only if + // all the following conditions are fulfilled: + // - `$` is not the last character in the filter + // - The character following `$` is either comma, alphanumeric, or `~`. if ( patternIsRegex === false ) { let optionsBits = 0; - let i = this.optionsAnchorSpan.i; + let i = this.optionsAnchorSpan.i - 3; for (;;) { i -= 3; if ( i < islice ) { break; } const bits = this.slices[i]; - if ( hasBits(bits, BITDollar) ) { break; } + if ( + hasBits(bits, BITDollar) && + hasBits(this.slices[i+3], BITAlphaNum | BITComma | BITTilde) + ) { + break; + } optionsBits |= bits; } if ( i >= islice ) { @@ -1151,6 +1161,41 @@ const Parser = class { BITFlavorError | BITFlavorUnsupported | BITFlavorIgnore ); } + static parseQueryPruneValue(arg) { + let s = arg; + if ( s === '*' ) { return { all: true }; } + const out = { }; + out.not = s.charCodeAt(0) === 0x7E /* '~' */; + if ( out.not ) { + s = s.slice(1); + } + const match = /^\/(.+)\/(i)?$/.exec(s); + if ( match !== null ) { + try { + out.re = new RegExp(match[1], match[2] || ''); + } + catch(ex) { + out.bad = true; + } + return out; + } + // TODO: remove once no longer used in filter lists + if ( s.startsWith('|') ) { + try { + out.re = new RegExp('^' + s.slice(1), 'i'); + } catch(ex) { + out.bad = true; + } + return out; + } + // Multiple values not supported (because very inefficient) + if ( s.includes('|') ) { + out.bad = true; + return out; + } + out.name = s; + return out; + } }; /******************************************************************************/ @@ -1926,20 +1971,21 @@ const OPTTokenInlineScript = 23; const OPTTokenMatchCase = 24; const OPTTokenMedia = 25; const OPTTokenMp4 = 26; -const OPTTokenObject = 27; -const OPTTokenOther = 28; -const OPTTokenPing = 29; -const OPTTokenPopunder = 30; -const OPTTokenPopup = 31; -const OPTTokenRedirect = 32; -const OPTTokenRedirectRule = 33; -const OPTTokenQueryprune = 34; -const OPTTokenScript = 35; -const OPTTokenShide = 36; -const OPTTokenXhr = 37; -const OPTTokenWebrtc = 38; -const OPTTokenWebsocket = 39; -const OPTTokenCount = 40; +const OPTTokenNoop = 27; +const OPTTokenObject = 28; +const OPTTokenOther = 29; +const OPTTokenPing = 30; +const OPTTokenPopunder = 31; +const OPTTokenPopup = 32; +const OPTTokenRedirect = 33; +const OPTTokenRedirectRule = 34; +const OPTTokenQueryprune = 35; +const OPTTokenScript = 36; +const OPTTokenShide = 37; +const OPTTokenXhr = 38; +const OPTTokenWebrtc = 39; +const OPTTokenWebsocket = 40; +const OPTTokenCount = 41; //const OPTPerOptionMask = 0x0000ff00; const OPTCanNegate = 1 << 8; @@ -2025,6 +2071,7 @@ Parser.prototype.OPTTokenInvalid = OPTTokenInvalid; Parser.prototype.OPTTokenMatchCase = OPTTokenMatchCase; Parser.prototype.OPTTokenMedia = OPTTokenMedia; Parser.prototype.OPTTokenMp4 = OPTTokenMp4; +Parser.prototype.OPTTokenNoop = OPTTokenNoop; Parser.prototype.OPTTokenObject = OPTTokenObject; Parser.prototype.OPTTokenOther = OPTTokenOther; Parser.prototype.OPTTokenPing = OPTTokenPing; @@ -2087,6 +2134,7 @@ const netOptionTokenDescriptors = new Map([ [ 'match-case', OPTTokenMatchCase ], [ 'media', OPTTokenMedia | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], [ 'mp4', OPTTokenMp4 | OPTNetworkType | OPTBlockOnly | OPTModifierType ], + [ '_', OPTTokenNoop ], [ 'object', OPTTokenObject | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], [ 'object-subrequest', OPTTokenObject | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], [ 'other', OPTTokenOther | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], @@ -2144,6 +2192,7 @@ Parser.netOptionTokenIds = new Map([ [ 'match-case', OPTTokenMatchCase ], [ 'media', OPTTokenMedia ], [ 'mp4', OPTTokenMp4 ], + [ '_', OPTTokenNoop ], [ 'object', OPTTokenObject ], [ 'object-subrequest', OPTTokenObject ], [ 'other', OPTTokenOther ], @@ -2191,6 +2240,7 @@ Parser.netOptionTokenNames = new Map([ [ OPTTokenMatchCase, 'match-case' ], [ OPTTokenMedia, 'media' ], [ OPTTokenMp4, 'mp4' ], + [ OPTTokenNoop, '_' ], [ OPTTokenObject, 'object' ], [ OPTTokenOther, 'other' ], [ OPTTokenPing, 'ping' ], @@ -2434,10 +2484,20 @@ const NetOptionsIterator = class { if ( this.interactive ) { this.parser.errorSlices(optSlices[i+1], optSlices[i+5]); } - } else if ( this.validateQueryPruneArg(i) === false ) { - optSlices[i] = OPTTokenInvalid; - if ( this.interactive ) { - this.parser.errorSlices(optSlices[i+4], optSlices[i+5]); + } else { + const val = this.parser.strFromSlices( + optSlices[i+4], + optSlices[i+5] - 3 + ); + const r = Parser.parseQueryPruneValue(val); + if ( r.bad ) { + optSlices[i] = OPTTokenInvalid; + if ( this.interactive ) { + this.parser.errorSlices( + optSlices[i+4], + optSlices[i+5] + ); + } } } } @@ -2501,24 +2561,6 @@ const NetOptionsIterator = class { this.readPtr = i + 6; return this; } - validateQueryPruneArg(i) { - let val = this.parser.strFromSlices( - this.optSlices[i+4], - this.optSlices[i+5] - 3 - ); - if ( val === '*' ) { return true; } - if ( val.charCodeAt(0) === 0x21 /* '!' */ ) { - val = val.slice(1); - } - if ( val.startsWith('|') ) { val = `^${val.slice(1)}`; } - if ( val.endsWith('|') ) { val = `${val.slice(0,-1)}$`; } - try { - void new RegExp(val); - } catch(ex) { - return false; - } - return true; - } }; /******************************************************************************/ diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 7a58c37755409..d1145d10691e4 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2967,6 +2967,8 @@ const FilterParser = class { } this.optionUnitBits |= this.REDIRECT_BIT; break; + case this.parser.OPTTokenNoop: + break; case this.parser.OPTTokenQueryprune: if ( this.parseModifierOption(id, val) === false ) { return false; @@ -3232,33 +3234,20 @@ const FilterParser = class { makePatternFromQuerypruneValue() { let pattern = this.modifyValue; - if ( pattern === '*' || pattern.charCodeAt(0) === 0x21 /* '!' */ ) { + if ( pattern === '*' || pattern.charCodeAt(0) === 0x7E /* '~' */ ) { return false; } - if ( /^\w+$/.test(pattern) ) { - this.pattern = `${pattern}=`; - return true; - } - const reRegex = /^\/(.+)\/i?$/; - if ( reRegex.test(pattern) ) { - pattern = reRegex.exec(pattern)[1]; + const match = /^\/(.+)\/i?$/.exec(pattern); + if ( match !== null ) { + pattern = match[1]; + this.isRegex = true; + } else if ( pattern.startsWith('|') ) { + pattern = '\\b' + pattern.slice(1); + this.isRegex = true; } else { - let prefix = '', suffix = ''; - if ( pattern.startsWith('|') ) { - pattern = pattern.slice(1); - prefix = '\\b'; - } - if ( pattern.endsWith('|') ) { - pattern = pattern.slice(0, -1); - suffix = '\\b'; - } - if ( pattern.indexOf('|') !== -1 ) { - pattern = `(?:${pattern})`; - } - pattern = prefix + pattern + suffix; + pattern = encodeURIComponent(pattern).toLowerCase() + '='; } this.pattern = pattern; - this.isRegex = true; return true; } @@ -4323,24 +4312,42 @@ FilterContainer.prototype.filterQuery = function(fctxt) { const params = new Map(new self.URLSearchParams(url.slice(qpos + 1))); const out = []; for ( const directive of directives ) { + if ( params.size === 0 ) { break; } const modifier = directive.modifier; const isException = (directive.bits & AllowAction) !== 0; if ( isException && modifier.value === '' ) { out.push(directive); break; } - if ( modifier.cache === undefined ) { - this.parseFilterPruneValue(modifier); + const { all, bad, name, not, re } = this.parseFilterPruneValue(modifier); + if ( bad ) { continue; } + if ( all ) { + if ( isException === false ) { params.clear(); } + out.push(directive); + break; } - const { all, not, re } = modifier.cache; - let filtered = false; - for ( const [ key, value ] of params ) { - if ( all !== true && re.test(`${key}=${value}`) === not ) { + if ( name !== undefined ) { + const value = params.get(name); + if ( not === false ) { + if ( value !== undefined ) { + if ( isException === false ) { params.delete(name); } + out.push(directive); + } continue; } - if ( isException === false ) { - params.delete(key); + if ( value !== undefined ) { params.delete(name); } + if ( params.size !== 0 ) { + if ( isException === false ) { params.clear(); } + out.push(directive); } + if ( value !== undefined ) { params.set(name, value); } + continue; + } + if ( re === undefined ) { continue; } + let filtered = false; + for ( const [ key, value ] of params ) { + if ( re.test(`${key}=${value}`) === not ) { continue; } + if ( isException === false ) { params.delete(key); } filtered = true; } if ( filtered ) { @@ -4358,29 +4365,11 @@ FilterContainer.prototype.filterQuery = function(fctxt) { }; FilterContainer.prototype.parseFilterPruneValue = function(modifier) { - const cache = {}; - const reRegex = /^\/(.+)\/i?$/; - let retext = modifier.value; - if ( retext === '*' ) { - cache.all = true; - } else { - cache.not = retext.charCodeAt(0) === 0x21 /* '!' */; - if ( cache.not ) { retext = retext.slice(1); } - if ( /^\w+$/.test(retext) ) { - retext = `^${retext}=`; - } else if ( reRegex.test(retext) ) { - retext = reRegex.exec(retext)[1]; - } else { - if ( retext.startsWith('|') ) { retext = `^${retext.slice(1)}`; } - if ( retext.endsWith('|') ) { retext = `${retext.slice(0,-1)}$`; } - } - try { - cache.re = new RegExp(retext, 'i'); - } catch(ex) { - cache.re = /.^/; - } + if ( modifier.cache === undefined ) { + modifier.cache = + vAPI.StaticFilteringParser.parseQueryPruneValue(modifier.value); } - modifier.cache = cache; + return modifier.cache; }; /******************************************************************************/ From 40a7c47bfcedcb54fa6d0eeac2e32ecb4117baec Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 11:31:20 -0500 Subject: [PATCH 3923/4093] Properly handle instances of `#?#` or `#$#` in picker Related feedback: - https://github.com/uBlockOrigin/uBlock-issues/issues/1363#issuecomment-734957406 --- src/js/epicker-ui.js | 10 ++++++---- src/js/scriptlets/epicker.js | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 84a9a963a2c90..7c8df09749caa 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -45,6 +45,8 @@ const svgOcean = svgRoot.children[0]; const svgIslands = svgRoot.children[1]; const NoPaths = 'M0 0'; +const reCosmeticAnchor = /^#[$?]?#/; + const epickerId = (( ) => { const url = new URL(self.location.href); if ( url.searchParams.has('zap') ) { @@ -144,7 +146,7 @@ const userFilterFromCandidate = function(filter) { const hn = vAPI.hostnameFromURI(docURL.href); // Cosmetic filter? - if ( filter.startsWith('##') ) { + if ( reCosmeticAnchor.test(filter) ) { return hn + filter; } @@ -460,7 +462,7 @@ const onCandidateChanged = function() { vAPI.MessagingConnection.sendTo(epickerConnectionId, { what: 'dialogSetFilter', filter, - compiled: filter.startsWith('##') + compiled: reCosmeticAnchor.test(filter) ? staticFilteringParser.result.compiled : undefined, }); @@ -487,13 +489,13 @@ const onCreateClicked = function() { autoComment: true, filters: filter, docURL: docURL.href, - killCache: /^#[$?]?#/.test(candidate) === false, + killCache: reCosmeticAnchor.test(candidate) === false, }); } vAPI.MessagingConnection.sendTo(epickerConnectionId, { what: 'dialogCreate', filter: candidate, - compiled: candidate.startsWith('##') + compiled: reCosmeticAnchor.test(candidate) ? staticFilteringParser.result.compiled : undefined, }); diff --git a/src/js/scriptlets/epicker.js b/src/js/scriptlets/epicker.js index 02f625225e7d7..ae1e70e2a8df9 100644 --- a/src/js/scriptlets/epicker.js +++ b/src/js/scriptlets/epicker.js @@ -38,6 +38,8 @@ if ( pickerRoot !== null ) { return; } let pickerBootArgs; +const reCosmeticAnchor = /^#[$?]?#/; + const netFilterCandidates = []; const cosmeticFilterCandidates = []; @@ -739,7 +741,7 @@ const filterToDOMInterface = (( ) => { return; } lastFilter = filter; - if ( filter.startsWith('##') === false ) { + if ( reCosmeticAnchor.test(filter) === false ) { lastResultset = fromNetworkFilter(filter); if ( previewing ) { apply(); } return lastResultset; @@ -789,7 +791,7 @@ const filterToDOMInterface = (( ) => { return unapply(); } if ( Array.isArray(lastResultset) === false ) { return; } - if ( permanent === false || lastFilter.startsWith('##') === false ) { + if ( permanent === false || reCosmeticAnchor.test(lastFilter) === false ) { return apply(); } if ( vAPI.domFilterer instanceof Object === false ) { return; } From a92efecd2baada85714a274c6e8ff8fbf020502e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 11:32:40 -0500 Subject: [PATCH 3924/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 3b706951e0a51..d2c39bc1fbefc 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.9 +1.31.1.10 From f9a84c82de70355fa642262ee75a58a421b2d466 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 12:20:59 -0500 Subject: [PATCH 3925/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index ccd62d0616d08..20caa7f3942c0 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.9", + "version": "1.31.1.10", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b9", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b9/uBlock0_1.31.1b9.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b10", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b10/uBlock0_1.31.1b10.firefox.signed.xpi" } ] } From ed64039912759157900ee134efa02be682ee03f9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 29 Nov 2020 14:03:33 -0500 Subject: [PATCH 3926/4093] Rename method --- src/js/static-net-filtering.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index d1145d10691e4..a4a825f6a75ef 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4319,7 +4319,7 @@ FilterContainer.prototype.filterQuery = function(fctxt) { out.push(directive); break; } - const { all, bad, name, not, re } = this.parseFilterPruneValue(modifier); + const { all, bad, name, not, re } = this.parseQueryPruneValue(modifier); if ( bad ) { continue; } if ( all ) { if ( isException === false ) { params.clear(); } @@ -4364,7 +4364,7 @@ FilterContainer.prototype.filterQuery = function(fctxt) { return out; }; -FilterContainer.prototype.parseFilterPruneValue = function(modifier) { +FilterContainer.prototype.parseQueryPruneValue = function(modifier) { if ( modifier.cache === undefined ) { modifier.cache = vAPI.StaticFilteringParser.parseQueryPruneValue(modifier.value); From 5db8d05975f3c1038fdc568df35cce3d569234c5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 30 Nov 2020 09:09:37 -0500 Subject: [PATCH 3927/4093] Better align syntax of `header=` option to that of `queryprune=` The header value is no longer implicitly a regex-based literal, but a plain string against which the header name is compared. The value can be set to a regex literal by bracing the header value with the usual forward slashes, `/.../`. Examples: *$1p,strict3p,script,header=via:1.1 google *$1p,strict3p,script,header=via:/1\.1\s+google/ The first form will cause a strict comparison with the value of the header named `via` against the string `1.1 google`. The second form will cause a regex-based test with the value of the header named `via` against the regex `/1\.1\s+google/`. The header value can be prepended with `~` to reverse the comparison: *$1p,strict3p,script,header=via:~1.1 google The header value is optional and may be ommitted to test only for the presence of a specific header: *$1p,strict3p,script,header=via --- src/js/static-filtering-parser.js | 51 ++++++++++++++++++++++++++++--- src/js/static-net-filtering.js | 46 ++++++++++------------------ 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 9337a627a7081..912e2f975bc90 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1161,8 +1161,9 @@ const Parser = class { BITFlavorError | BITFlavorUnsupported | BITFlavorIgnore ); } + static parseQueryPruneValue(arg) { - let s = arg; + let s = arg.trim(); if ( s === '*' ) { return { all: true }; } const out = { }; out.not = s.charCodeAt(0) === 0x7E /* '~' */; @@ -1196,6 +1197,29 @@ const Parser = class { out.name = s; return out; } + + static parseHeaderValue(arg) { + let s = arg.trim(); + const out = { }; + let pos = s.indexOf(':'); + if ( pos === -1 ) { pos = s.length; } + out.name = s.slice(0, pos); + out.bad = out.name === ''; + s = s.slice(pos + 1); + out.not = s.charCodeAt(0) === 0x7E /* '~' */; + if ( out.not ) { s = s.slice(1); } + out.value = s; + const match = /^\/(.+)\/(i)?$/.exec(s); + if ( match !== null ) { + try { + out.re = new RegExp(match[1], match[2] || ''); + } + catch(ex) { + out.bad = true; + } + } + return out; + } }; /******************************************************************************/ @@ -2520,10 +2544,27 @@ const NetOptionsIterator = class { // `header`: can't be used with any modifier type { const i = this.tokenPos[OPTTokenHeader]; - if ( i !== -1 && hasBits(allBits, OPTModifierType) ) { - optSlices[i] = OPTTokenInvalid; - if ( this.interactive ) { - this.parser.errorSlices(optSlices[i+1], optSlices[i+5]); + if ( i !== -1 ) { + if ( hasBits(allBits, OPTModifierType) ) { + optSlices[i] = OPTTokenInvalid; + if ( this.interactive ) { + this.parser.errorSlices(optSlices[i+1], optSlices[i+5]); + } + } else { + const val = this.parser.strFromSlices( + optSlices[i+4], + optSlices[i+5] - 3 + ); + const r = Parser.parseHeaderValue(val); + if ( r.bad ) { + optSlices[i] = OPTTokenInvalid; + if ( this.interactive ) { + this.parser.errorSlices( + optSlices[i+4], + optSlices[i+5] + ); + } + } } } } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index a4a825f6a75ef..463e342dbd9b3 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -19,8 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint bitwise: false */ - 'use strict'; /******************************************************************************/ @@ -2389,32 +2387,22 @@ registerFilterClass(FilterStrictParty); const FilterOnHeaders = class { constructor(headerOpt) { this.headerOpt = headerOpt; - if ( headerOpt !== '' ) { - let pos = headerOpt.indexOf(':'); - if ( pos === -1 ) { pos = headerOpt.length; } - this.name = headerOpt.slice(0, pos); - this.value = headerOpt.slice(pos + 1); - this.not = this.value.charCodeAt(0) === 0x21 /* '!' */; - if ( this.not ) { this.value.slice(1); } - } else { - this.name = this.value = ''; - this.not = false; - } - this.reValue = null; + this.parsed = undefined; } match() { - if ( this.name === '' ) { return true; } - const value = $httpHeaders.lookup(this.name); - if ( value === undefined ) { return false; } - if ( this.value === '' ) { return true; } - if ( this.reValue === null ) { - let reText = this.value; - if ( reText.startsWith('|') ) { reText = '^' + reText.slice(1); } - if ( reText.endsWith('|') ) { reText = reText.slice(0, -1) + '$'; } - this.reValue = new RegExp(reText, 'i'); + if ( this.parsed === undefined ) { + this.parsed = + vAPI.StaticFilteringParser.parseHeaderValue(this.headerOpt); } - return this.reValue.test(value) !== this.not; + const { bad, name, not, re, value } = this.parsed; + if ( bad ) { return false; } + const headerValue = $httpHeaders.lookup(name); + if ( headerValue === undefined ) { return false; } + if ( value === '' ) { return true; } + return re === undefined + ? (headerValue === value) !== not + : re.test(headerValue) !== not; } logData(details) { @@ -4231,12 +4219,10 @@ FilterContainer.prototype.matchHeaders = function(fctxt, headers) { let r = 0; if ( this.realmMatchString(Headers | BlockImportant, typeValue, partyBits) ) { r = 1; - } - if ( r !== 1 && this.realmMatchString(Headers | BlockAction, typeValue, partyBits) ) { - r = 1; - if ( r === 1 && this.realmMatchString(Headers | AllowAction, typeValue, partyBits) ) { - r = 2; - } + } else if ( this.realmMatchString(Headers | BlockAction, typeValue, partyBits) ) { + r = this.realmMatchString(Headers | AllowAction, typeValue, partyBits) + ? 2 + : 1; } $httpHeaders.reset(); From 5f1048490be6416f856f1d2c9050a4dbd98dc58f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 30 Nov 2020 09:20:44 -0500 Subject: [PATCH 3928/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d2c39bc1fbefc..dab6ac05586dd 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.10 +1.31.1.11 From d7e8e16fc5a6d71b1738773a80e8b2167d425c84 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 30 Nov 2020 10:51:12 -0500 Subject: [PATCH 3929/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 20caa7f3942c0..2478506ec4ead 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.10", + "version": "1.31.1.11", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b10", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b10/uBlock0_1.31.1b10.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b11", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b11/uBlock0_1.31.1b11.firefox.signed.xpi" } ] } From 391a5c99c7eb32e9c988929ffb74e28ab713ef69 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 30 Nov 2020 12:02:36 -0500 Subject: [PATCH 3930/4093] Fix the parsing of unsupported static network filter types Related issue: - https://github.com/gorhill/uBlock/issues/2283 This is a regression causing the referenced issue to no longer be fixed. The regression was introduced when the new static filtering parser code was introduced in version 1.28.0: https://github.com/gorhill/uBlock/releases/tag/1.28.0 --- src/js/static-filtering-parser.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 912e2f975bc90..d19004bbdc0a0 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2380,7 +2380,6 @@ const NetOptionsIterator = class { // Validate option according to context if ( descriptor === undefined || - hasBits(descriptor, OPTNotSupported) || ltok !== lopt && hasNoBits(descriptor, OPTCanNegate) || this.exception && @@ -2419,7 +2418,12 @@ const NetOptionsIterator = class { // Accumulate description bits allBits |= descriptor; // Mark slices in case of invalid filter option - if ( this.interactive && descriptor === OPTTokenInvalid ) { + if ( + this.interactive && ( + descriptor === OPTTokenInvalid || + hasBits(descriptor, OPTNotSupported) + ) + ) { this.parser.errorSlices(lopt, i); } // Store indices to raw slices, this will be used during iteration From e08f8cb00126175a44d939bfdf48c98d27e2ccf2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 30 Nov 2020 12:52:37 -0500 Subject: [PATCH 3931/4093] Make `queryprune` an exact alias of `removeparam` As per agreed upon discussion, `queryprune` now follows exactly the syntax of AdGuard's `removeparam`. Related discussion: - https://github.com/uBlockOrigin/uBlock-issues/issues/1356 Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/760 For the short term, `queryprune` will still interpret a leading `|` to mean "anchor to start of name", until no such filters are present in uBO's own filter lists. --- src/js/static-filtering-parser.js | 11 ++++++----- src/js/static-net-filtering.js | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index d19004bbdc0a0..dbe64875bf707 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1164,7 +1164,7 @@ const Parser = class { static parseQueryPruneValue(arg) { let s = arg.trim(); - if ( s === '*' ) { return { all: true }; } + if ( s === '' ) { return { all: true }; } const out = { }; out.not = s.charCodeAt(0) === 0x7E /* '~' */; if ( out.not ) { @@ -2017,7 +2017,8 @@ const OPTBlockOnly = 1 << 9; const OPTAllowOnly = 1 << 10; const OPTMustAssign = 1 << 11; const OPTAllowMayAssign = 1 << 12; -const OPTDomainList = 1 << 13; +const OPTMayAssign = 1 << 13; +const OPTDomainList = 1 << 14; //const OPTGlobalMask = 0x0fff0000; const OPTNetworkType = 1 << 16; @@ -2166,8 +2167,8 @@ const netOptionTokenDescriptors = new Map([ [ 'beacon', OPTTokenPing | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTNonCspableType | OPTNonRedirectableType ], [ 'popunder', OPTTokenPopunder | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ], [ 'popup', OPTTokenPopup | OPTNonNetworkType | OPTCanNegate | OPTNonCspableType | OPTNonRedirectableType ], - [ 'queryprune', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], - [ 'removeparam', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], + [ 'queryprune', OPTTokenQueryprune | OPTMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], + [ 'removeparam', OPTTokenQueryprune | OPTMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], [ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ], [ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType ], [ 'script', OPTTokenScript | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], @@ -2387,7 +2388,7 @@ const NetOptionsIterator = class { this.exception === false && hasBits(descriptor, OPTAllowOnly) || assigned && - hasNoBits(descriptor, OPTMustAssign) || + hasNoBits(descriptor, OPTMayAssign | OPTMustAssign) || assigned === false && hasBits(descriptor, OPTMustAssign) && ( this.exception === false || diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 463e342dbd9b3..8245b6d0e5454 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1638,7 +1638,7 @@ const FilterModifier = class { FilterModifier.fid, details.action, details.modifyType, - details.modifyValue + details.modifyValue || '', ]; } From cf2c638d8e953585651d8a31f3a67f19fbc62842 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Dec 2020 09:29:40 -0500 Subject: [PATCH 3932/4093] Improve reporting of matching `redirect=` rules in logger All matching `redirect-rule` directives will now be reported in the logger, instead of just the effective one. The highest-ranked redirect directive will be the one effectively used for redirection. This way filter list authors can see whether a lower priority redirect is being overriden by a higher priority one. The default priority has been changed to 10, so as to allow more leeway to create lower ranked redirect directives. Additonally, rendering of redirect directives with explicit priority has been fixed in the logger, they will no longer be rendered as unknown redirect tokens. --- src/js/codemirror/ubo-static-filtering.js | 5 +- src/js/pagestore.js | 6 +-- src/js/static-filtering-parser.js | 11 ++++ src/js/static-net-filtering.js | 66 +++++++++-------------- 4 files changed, 42 insertions(+), 46 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 79dc3618bd9ff..976554a206666 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -192,11 +192,12 @@ CodeMirror.defineMode('ubo-static-filtering', function() { parser.commentSpan.i, parser.BITComma ); - const token = parser.strFromSlices(parserSlot, end - 3); + const raw = parser.strFromSlices(parserSlot, end - 3); + const { token } = StaticFilteringParser.parseRedirectValue(raw); if ( redirectNames.has(token) === false ) { style += ' warning'; } - stream.pos += token.length; + stream.pos += raw.length; parserSlot = end; return style; } diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 505fe2a2a3700..037c84875969f 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -716,10 +716,10 @@ const PageStore = class { redirectBlockedRequest(fctxt) { if ( µb.hiddenSettings.ignoreRedirectFilters === true ) { return; } - const directive = µb.staticNetFilteringEngine.redirectRequest(fctxt); - if ( directive === undefined ) { return; } + const directives = µb.staticNetFilteringEngine.redirectRequest(fctxt); + if ( directives === undefined ) { return; } if ( µb.logger.enabled !== true ) { return; } - fctxt.pushFilter(directive.logData()); + fctxt.pushFilters(directives.map(a => a.logData())); if ( fctxt.redirectURL === undefined ) { return; } fctxt.pushFilter({ source: 'redirect', diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index dbe64875bf707..cd2732628640e 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1162,6 +1162,17 @@ const Parser = class { ); } + static parseRedirectValue(arg) { + let token = arg.trim(); + let priority = 10; + const match = /:\d+$/.exec(token); + if ( match !== null ) { + token = token.slice(0, match.index); + priority = parseInt(token.slice(match.index + 1), 10); + } + return { token, priority }; + } + static parseQueryPruneValue(arg) { let s = arg.trim(); if ( s === '' ) { return { all: true }; } diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 8245b6d0e5454..b5f83382bf847 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4236,57 +4236,41 @@ FilterContainer.prototype.redirectRequest = function(fctxt) { const directives = this.matchAndFetchModifiers(fctxt, 'redirect-rule'); // No directive is the most common occurrence. if ( directives === undefined ) { return; } - // A single directive should be the next most common occurrence. - if ( directives.length === 1 ) { - const directive = directives[0]; - if ( (directive.bits & AllowAction) !== 0 ) { return directive; } - const modifier = directive.modifier; - const { token } = this.parseRedirectRequestValue(modifier); + // More than a single directive means more work. + if ( directives.length !== 1 ) { + directives.sort(FilterContainer.compareRedirectRequests); + } + // Redirect to highest-ranked directive + const directive = directives[directives.length - 1]; + if ( (directive.bits & AllowAction) === 0 ) { + const { token } = + FilterContainer.parseRedirectRequestValue(directive.modifier); fctxt.redirectURL = µb.redirectEngine.tokenToURL(fctxt, token); if ( fctxt.redirectURL === undefined ) { return; } - return directive; - } - // Multiple directives mean more work to do. - let winningDirective; - let winningPriority = 0; - for ( const directive of directives ) { - const modifier = directive.modifier; - const isException = (directive.bits & AllowAction) !== 0; - if ( isException && modifier.value === '' ) { - winningDirective = directive; - break; - } - const { token, priority } = this.parseRedirectRequestValue(modifier); - if ( µb.redirectEngine.hasToken(token) === false ) { continue; } - if ( winningDirective === undefined || priority > winningPriority ) { - winningDirective = directive; - winningPriority = priority; - } - } - if ( winningDirective === undefined ) { return; } - if ( (winningDirective.bits & AllowAction) === 0 ) { - fctxt.redirectURL = µb.redirectEngine.tokenToURL( - fctxt, - winningDirective.modifier.cache.token - ); } - return winningDirective; + return directives; }; -FilterContainer.prototype.parseRedirectRequestValue = function(modifier) { +FilterContainer.parseRedirectRequestValue = function(modifier) { if ( modifier.cache === undefined ) { - let token = modifier.value; - let priority = 1; - const match = /:(\d+)$/.exec(token); - if ( match !== null ) { - token = token.slice(0, match.index); - priority = parseInt(match[1], 10); - } - modifier.cache = { token, priority }; + modifier.cache = + vAPI.StaticFilteringParser.parseRedirectValue(modifier.value); } return modifier.cache; }; +FilterContainer.compareRedirectRequests = function(a, b) { + if ( (a.bits & AllowAction) !== 0 ) { return -1; } + if ( (b.bits & AllowAction) !== 0 ) { return 1; } + const { token: atok, priority: aint } = + FilterContainer.parseRedirectRequestValue(a.modifier); + if ( µb.redirectEngine.hasToken(atok) === false ) { return -1; } + const { token: btok, priority: bint } = + FilterContainer.parseRedirectRequestValue(b.modifier); + if ( µb.redirectEngine.hasToken(btok) === false ) { return 1; } + return aint - bint; +}; + /******************************************************************************/ FilterContainer.prototype.filterQuery = function(fctxt) { From 59c0762eb6775d546eab3dad00b8ed124ddaceb9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Dec 2020 09:36:37 -0500 Subject: [PATCH 3933/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index dab6ac05586dd..6ff7968613c83 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.11 +1.31.1.12 From a48e986546673f2907cdbb5613082068bcd53176 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 1 Dec 2020 13:06:12 -0500 Subject: [PATCH 3934/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2478506ec4ead..8ae31f2d08d4b 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.11", + "version": "1.31.1.12", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b11", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b11/uBlock0_1.31.1b11.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b12", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b12/uBlock0_1.31.1b12.firefox.signed.xpi" } ] } From 26dc7a1490ce1ae422b0e1a7e9a29eb16057a20c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Dec 2020 08:18:55 -0500 Subject: [PATCH 3935/4093] Minor review of redirect-related code Notably, I finally settled for implicit priority of 0, but now negative priority values are allowed. --- src/js/codemirror/ubo-static-filtering.js | 2 +- src/js/redirect-engine.js | 10 +++++----- src/js/static-filtering-parser.js | 8 +++++--- src/js/static-net-filtering.js | 5 +++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 976554a206666..cc55c754896b2 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -187,7 +187,7 @@ CodeMirror.defineMode('ubo-static-filtering', function() { /[$,]redirect(-rule)?=$/.test(string.slice(0, pos)) ) { style = 'value'; - let end = parser.skipUntil( + const end = parser.skipUntil( parserSlot, parser.commentSpan.i, parser.BITComma diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 57a79a1733af2..1b8780996f611 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -291,11 +291,11 @@ RedirectEngine.prototype.freeze = function() { /******************************************************************************/ -RedirectEngine.prototype.tokenToURL = function(fctxt, token) { - const asDataURI = token.charCodeAt(0) === 0x25 /* '%' */; - if ( asDataURI ) { - token = token.slice(1); - } +RedirectEngine.prototype.tokenToURL = function( + fctxt, + token, + asDataURI = false +) { const entry = this.resources.get(this.aliases.get(token) || token); if ( entry === undefined ) { return; } this.resourceNameRegister = token; diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index cd2732628640e..ccdfa506cefb7 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1164,13 +1164,15 @@ const Parser = class { static parseRedirectValue(arg) { let token = arg.trim(); - let priority = 10; - const match = /:\d+$/.exec(token); + let priority = 0; + const asDataURI = token.charCodeAt(0) === 0x25 /* '%' */; + if ( asDataURI ) { token = token.slice(1); } + const match = /:-?\d+$/.exec(token); if ( match !== null ) { token = token.slice(0, match.index); priority = parseInt(token.slice(match.index + 1), 10); } - return { token, priority }; + return { token, priority, asDataURI }; } static parseQueryPruneValue(arg) { diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index b5f83382bf847..75fb0d6635c5b 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4236,12 +4236,13 @@ FilterContainer.prototype.redirectRequest = function(fctxt) { const directives = this.matchAndFetchModifiers(fctxt, 'redirect-rule'); // No directive is the most common occurrence. if ( directives === undefined ) { return; } + const highest = directives.length - 1; // More than a single directive means more work. - if ( directives.length !== 1 ) { + if ( highest !== 0 ) { directives.sort(FilterContainer.compareRedirectRequests); } // Redirect to highest-ranked directive - const directive = directives[directives.length - 1]; + const directive = directives[highest]; if ( (directive.bits & AllowAction) === 0 ) { const { token } = FilterContainer.parseRedirectRequestValue(directive.modifier); From cb7ec8ac1cf26a045778c87774c9a411b1f144bc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Dec 2020 08:23:51 -0500 Subject: [PATCH 3936/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 6ff7968613c83..1504c5bcae59d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.12 +1.31.1.13 From 262a1a044f706d8937ba430911359d7c306329e4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Dec 2020 09:09:28 -0500 Subject: [PATCH 3937/4093] Improve auto-complete of hostname values in "My filters" Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1134 Related commit: - https://github.com/gorhill/uBlock/commit/daf464b3c30e9c0c5f5991ba1bde8f9dca1d7078 --- src/js/1p-filters.js | 45 +++++++++++++++++------ src/js/codemirror/ubo-static-filtering.js | 43 ++++++++++++++-------- src/js/messaging.js | 18 +++++---- 3 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 7d70b85ade01d..647d48cdf62a9 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -49,22 +49,43 @@ const cmEditor = new CodeMirror(document.getElementById('userFilters'), { uBlockDashboard.patchCodeMirrorEditor(cmEditor); -vAPI.messaging.send('dashboard', { - what: 'getAutoCompleteDetails' -}).then(response => { - if ( response instanceof Object === false ) { return; } - const mode = cmEditor.getMode(); - // TODO: listen to changes in currently opened set of tabs? - if ( mode.setHints instanceof Function ) { - mode.setHints(response); - } - mode.parser.expertMode = response.expertMode !== false; -}); - let cachedUserFilters = ''; /******************************************************************************/ +// Add auto-complete ability to the editor. + +{ + let hintUpdateToken = 0; + + const responseHandler = function(response) { + if ( response instanceof Object === false ) { return; } + if ( response.hintUpdateToken !== undefined ) { + const firstVisit = hintUpdateToken === 0; + const mode = cmEditor.getMode(); + if ( mode.setHints instanceof Function ) { + mode.setHints(response, firstVisit); + } + if ( firstVisit ) { + mode.parser.expertMode = response.expertMode !== false; + } + hintUpdateToken = response.hintUpdateToken; + } + vAPI.setTimeout(getHints, 2503); + }; + + const getHints = function() { + vAPI.messaging.send('dashboard', { + what: 'getAutoCompleteDetails', + hintUpdateToken + }).then(responseHandler); + }; + + getHints(); +} + +/******************************************************************************/ + const getEditorText = function() { const text = cmEditor.getValue().replace(/\s+$/, ''); return text === '' ? text : text + '\n'; diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index cc55c754896b2..78a8710c54adb 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -361,26 +361,37 @@ CodeMirror.defineMode('ubo-static-filtering', function() { style = style.trim(); return style !== '' ? style : null; }, - setHints: function(details) { - for ( const [ name, desc ] of details.redirectResources ) { - const displayText = desc.aliasOf !== '' - ? `${name} (${desc.aliasOf})` - : ''; - if ( desc.canRedirect ) { - redirectNames.set(name, displayText); + setHints: function(details, firstVisit = false) { + if ( Array.isArray(details.redirectResources) ) { + for ( const [ name, desc ] of details.redirectResources ) { + const displayText = desc.aliasOf !== '' + ? `${name} (${desc.aliasOf})` + : ''; + if ( desc.canRedirect ) { + redirectNames.set(name, displayText); + } + if ( desc.canInject && name.endsWith('.js') ) { + scriptletNames.set(name.slice(0, -3), displayText); + } } - if ( desc.canInject && name.endsWith('.js') ) { - scriptletNames.set(name.slice(0, -3), displayText); + } + if ( Array.isArray(details.preparseDirectiveTokens)) { + details.preparseDirectiveTokens.forEach(([ a, b ]) => { + preparseDirectiveTokens.set(a, b); + }); + } + if ( Array.isArray(details.preparseDirectiveHints)) { + preparseDirectiveHints.push(...details.preparseDirectiveHints); + } + if ( Array.isArray(details.originHints) ) { + originHints.length = 0; + for ( const hint of details.originHints ) { + originHints.push(hint); } } - details.preparseDirectiveTokens.forEach(([ a, b ]) => { - preparseDirectiveTokens.set(a, b); - }); - preparseDirectiveHints.push(...details.preparseDirectiveHints); - for ( const hint of details.originHints ) { - originHints.push(hint); + if ( firstVisit ) { + initHints(); } - initHints(); }, get parser() { return parser; diff --git a/src/js/messaging.js b/src/js/messaging.js index 5af3d539f354d..478f2c215c5d5 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1201,13 +1201,17 @@ const onMessage = function(request, sender, callback) { break; case 'getAutoCompleteDetails': - response = { - redirectResources: µb.redirectEngine.getResourceDetails(), - preparseDirectiveTokens: µb.preparseDirectives.getTokens(), - preparseDirectiveHints: µb.preparseDirectives.getHints(), - originHints: getOriginHints(), - expertMode: µb.hiddenSettings.filterAuthorMode, - }; + response = {}; + if ( request.hintUpdateToken === 0 ) { + response.redirectResources = µb.redirectEngine.getResourceDetails(); + response.preparseDirectiveTokens = µb.preparseDirectives.getTokens(); + response.preparseDirectiveHints = µb.preparseDirectives.getHints(); + response.expertMode = µb.hiddenSettings.filterAuthorMode; + } + if ( request.hintUpdateToken !== µb.pageStoresToken ) { + response.originHints = getOriginHints(); + response.hintUpdateToken = µb.pageStoresToken; + } break; case 'getRules': From 4d68d7f586bab3c692f6648c4af7fd49e8dff395 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Dec 2020 10:07:14 -0500 Subject: [PATCH 3938/4093] Fix handling of no-longer-existing port condition The condition has been spotted occurring when bringing up the DOM inspector for a page on which cosmetic filters are being applied. Not clear why this happens, but uBO must be ready to graciously handle such condition. --- platform/chromium/vapi-background.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index d480711847883..36fdf742b905d 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -959,7 +959,7 @@ vAPI.messaging = { try { port.postMessage(messageWrapper); } catch(ex) { - this.ports.delete(port.name); + this.onPortDisconnect(port); } } }, @@ -985,7 +985,11 @@ vAPI.messaging = { msg.tabId = tabId; for ( const { port: toPort } of this.ports.values() ) { if ( toPort === port ) { continue; } - toPort.postMessage(request); + try { + toPort.postMessage(request); + } catch (ex) { + this.onPortDisconnect(toPort); + } } break; case 'connectionBroken': From f8b15ed6ccfbbfb34a22a19a2799b5dc56ed207c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Dec 2020 10:46:59 -0500 Subject: [PATCH 3939/4093] Fix calls to tab.removeCSS() Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1375 --- platform/chromium/vapi-background.js | 35 +++++++++++++++++----------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 36fdf742b905d..07721183117c4 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -1024,22 +1024,29 @@ vAPI.messaging = { } case 'userCSS': if ( tabId === undefined ) { break; } - const details = { - code: undefined, - frameId: portDetails.frameId, - matchAboutBlank: true - }; - if ( msg.add ) { - details.runAt = 'document_start'; - } const promises = []; - for ( const cssText of msg.add ) { - details.code = cssText; - promises.push(vAPI.tabs.insertCSS(tabId, details)); + if ( msg.add ) { + const details = { + code: undefined, + frameId: portDetails.frameId, + matchAboutBlank: true, + runAt: 'document_start', + }; + for ( const cssText of msg.add ) { + details.code = cssText; + promises.push(vAPI.tabs.insertCSS(tabId, details)); + } } - for ( const cssText of msg.remove ) { - details.code = cssText; - promises.push(vAPI.tabs.removeCSS(tabId, details)); + if ( msg.remove ) { + const details = { + code: undefined, + frameId: portDetails.frameId, + matchAboutBlank: true, + }; + for ( const cssText of msg.remove ) { + details.code = cssText; + promises.push(vAPI.tabs.removeCSS(tabId, details)); + } } Promise.all(promises).then(( ) => { callback(); From ee87bda326f90e76c0c4d84dda9fde3d4a33e24e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Dec 2020 13:07:29 -0500 Subject: [PATCH 3940/4093] Fix regression in syntax rendering of redirect values in asset viewer Related commit: - https://github.com/gorhill/uBlock/commit/262a1a044f706d8937ba430911359d7c306329e4 --- src/js/asset-viewer.js | 2 +- src/js/messaging.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/asset-viewer.js b/src/js/asset-viewer.js index ecdef01c24c29..54fef45991208 100644 --- a/src/js/asset-viewer.js +++ b/src/js/asset-viewer.js @@ -66,7 +66,7 @@ if ( hints instanceof Object ) { const mode = cmEditor.getMode(); if ( mode.setHints instanceof Function ) { - mode.setHints(hints); + mode.setHints(hints, true); } } diff --git a/src/js/messaging.js b/src/js/messaging.js index 478f2c215c5d5..c8c1751658061 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1202,7 +1202,7 @@ const onMessage = function(request, sender, callback) { case 'getAutoCompleteDetails': response = {}; - if ( request.hintUpdateToken === 0 ) { + if ( (request.hintUpdateToken || 0) === 0 ) { response.redirectResources = µb.redirectEngine.getResourceDetails(); response.preparseDirectiveTokens = µb.preparseDirectives.getTokens(); response.preparseDirectiveHints = µb.preparseDirectives.getHints(); From 210ec6b1aa06f108ff639584f10f0a71d9d0a9d3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 2 Dec 2020 16:16:15 -0500 Subject: [PATCH 3941/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 8ae31f2d08d4b..4ea00009459a8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.12", + "version": "1.31.1.13", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b12", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b12/uBlock0_1.31.1b12.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b13", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b13/uBlock0_1.31.1b13.firefox.signed.xpi" } ] } From 1de8349045f03b69907a1d7f3b2dda347d4a9733 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Dec 2020 07:34:50 -0500 Subject: [PATCH 3942/4093] Fix hint helper in element picker's text editor Regression from: - https://github.com/gorhill/uBlock/commit/262a1a044f706d8937ba430911359d7c306329e4 --- src/js/1p-filters.js | 5 ++--- src/js/asset-viewer.js | 2 +- src/js/codemirror/ubo-static-filtering.js | 6 ++++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 647d48cdf62a9..e02de5df253de 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -61,12 +61,11 @@ let cachedUserFilters = ''; const responseHandler = function(response) { if ( response instanceof Object === false ) { return; } if ( response.hintUpdateToken !== undefined ) { - const firstVisit = hintUpdateToken === 0; const mode = cmEditor.getMode(); if ( mode.setHints instanceof Function ) { - mode.setHints(response, firstVisit); + mode.setHints(response); } - if ( firstVisit ) { + if ( hintUpdateToken === 0 ) { mode.parser.expertMode = response.expertMode !== false; } hintUpdateToken = response.hintUpdateToken; diff --git a/src/js/asset-viewer.js b/src/js/asset-viewer.js index 54fef45991208..ecdef01c24c29 100644 --- a/src/js/asset-viewer.js +++ b/src/js/asset-viewer.js @@ -66,7 +66,7 @@ if ( hints instanceof Object ) { const mode = cmEditor.getMode(); if ( mode.setHints instanceof Function ) { - mode.setHints(hints, true); + mode.setHints(hints); } } diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 78a8710c54adb..183af09099d78 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -35,6 +35,7 @@ const scriptletNames = new Map(); const preparseDirectiveTokens = new Map(); const preparseDirectiveHints = []; const originHints = []; +let hintHelperRegistered = false; /******************************************************************************/ @@ -361,7 +362,7 @@ CodeMirror.defineMode('ubo-static-filtering', function() { style = style.trim(); return style !== '' ? style : null; }, - setHints: function(details, firstVisit = false) { + setHints: function(details) { if ( Array.isArray(details.redirectResources) ) { for ( const [ name, desc ] of details.redirectResources ) { const displayText = desc.aliasOf !== '' @@ -389,7 +390,8 @@ CodeMirror.defineMode('ubo-static-filtering', function() { originHints.push(hint); } } - if ( firstVisit ) { + if ( hintHelperRegistered === false ) { + hintHelperRegistered = true; initHints(); } }, From 48dd54208fdde48e0a320ae4f628fd5ebfdd42ae Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Dec 2020 08:13:01 -0500 Subject: [PATCH 3943/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 1504c5bcae59d..f6db943d67628 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.1.13 +1.31.3.0 From d44988cb4684cc4478efc6bc94056324c54822ad Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Dec 2020 10:15:34 -0500 Subject: [PATCH 3944/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4ea00009459a8..75b166e67bd2a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.1.13", + "version": "1.31.3.0", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.1b13", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.1b13/uBlock0_1.31.1b13.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b0", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b0/uBlock0_1.31.3b0.firefox.signed.xpi" } ] } From da9d068243e9707e39de09421742020c2ea19285 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 3 Dec 2020 11:52:49 -0500 Subject: [PATCH 3945/4093] Fix improper typeof test for string type Regression from: - https://github.com/gorhill/uBlock/commit/b12e0e05ea9d319ba3b6c2b97d9322c347230bd7 This broke the ability to provide a link to the actual asset on the remote server in the asset viewer. --- 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 09acd95f0183b..94fc4ce35db91 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -571,7 +571,7 @@ const assetCacheWrite = async function(assetKey, details) { entry = cacheDict[assetKey] = {}; } entry.writeTime = entry.readTime = Date.now(); - if ( options.url === 'string' ) { + if ( typeof options.url === 'string' ) { entry.remoteURL = options.url; } µBlock.cacheStorage.set({ From e8e4a1ac74e8425d576063c5bf254396c7ed007b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Dec 2020 06:17:18 -0500 Subject: [PATCH 3946/4093] Wait for removal of storage entries to be completed Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1365 When compiled data format changes, do not rely on order of operations at launch to assume deletion of storage occurs before attempts to access it. It's unclear this commit will fix the reported issue, as I could not reproduce it except when outright commenting out the code to prevent the storage deletion from occurring. --- src/js/assets.js | 13 +++++++------ src/js/start.js | 15 ++++++++++----- src/js/storage.js | 18 +++++------------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/js/assets.js b/src/js/assets.js index 94fc4ce35db91..310c6ef77e62c 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -603,14 +603,15 @@ const assetCacheRemove = async function(pattern) { delete cacheDict[assetKey]; } if ( removedContent.length !== 0 ) { - µBlock.cacheStorage.remove(removedContent); - µBlock.cacheStorage.set({ assetCacheRegistry }); + await Promise.all([ + µBlock.cacheStorage.remove(removedContent), + µBlock.cacheStorage.set({ assetCacheRegistry }), + ]); } for ( let i = 0; i < removedEntries.length; i++ ) { - fireNotification( - 'after-asset-updated', - { assetKey: removedEntries[i] } - ); + fireNotification('after-asset-updated', { + assetKey: removedEntries[i] + }); } }; diff --git a/src/js/start.js b/src/js/start.js index b4ee201dbbfa4..a1298677ab6dd 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -185,10 +185,12 @@ const onUserSettingsReady = function(fetched) { // https://bugzilla.mozilla.org/show_bug.cgi?id=1588916 // Save magic format numbers into the cache storage itself. +// https://github.com/uBlockOrigin/uBlock-issues/issues/1365 +// Wait for removal of invalid cached data to be completed. -const onCacheSettingsReady = function(fetched) { +const onCacheSettingsReady = async function(fetched) { if ( fetched.compiledMagic !== µb.systemSettings.compiledMagic ) { - µb.assets.remove(/^compiled\//); + await µb.assets.remove(/^compiled\//); µb.compiledFormatChanged = true; µb.selfieIsInvalid = true; } @@ -292,15 +294,18 @@ try { ); log.info(`Backend storage for cache will be ${cacheBackend}`); + // https://github.com/uBlockOrigin/uBlock-issues/issues/1365 + // Wait for onCacheSettingsReady() to be fully ready. await Promise.all([ µb.loadSelectedFilterLists().then(( ) => { log.info(`List selection ready ${Date.now()-vAPI.T0} ms after launch`); }), µb.cacheStorage.get( { compiledMagic: 0, selfieMagic: 0 } - ).then(fetched => { - log.info(`Cache magic numbers ready ${Date.now()-vAPI.T0} ms after launch`); - onCacheSettingsReady(fetched); + ).then(fetched => + onCacheSettingsReady(fetched) + ).then(( ) => { + log.info(`Integrity of cached data processed ${Date.now()-vAPI.T0} ms after launch`); }), vAPI.storage.get(createDefaultProps()).then(fetched => { log.info(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`); diff --git a/src/js/storage.js b/src/js/storage.js index 56d529f7b0b78..3ff2359bbf579 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -737,7 +737,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { this.compiledFormatChanged === false && this.badLists.has(assetKey) === false ) { - let compiledDetails = await this.assets.get(compiledPath); + const compiledDetails = await this.assets.get(compiledPath); if ( compiledDetails.content !== '' ) { compiledDetails.assetKey = assetKey; return compiledDetails; @@ -763,19 +763,11 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { return { assetKey, content: '' }; } - // Fetching the raw content may cause the compiled content to be - // generated somewhere else in uBO, hence we try one last time to - // fetch the compiled content in case it has become available. - const compiledDetails = await this.assets.get(compiledPath); - if ( compiledDetails.content === '' ) { - compiledDetails.content = this.compileFilters(rawDetails.content, { - assetKey - }); - this.assets.put(compiledPath, compiledDetails.content); - } + const compiledContent = + this.compileFilters(rawDetails.content, { assetKey }); + this.assets.put(compiledPath, compiledContent); - compiledDetails.assetKey = assetKey; - return compiledDetails; + return { assetKey, content: compiledContent }; }; /******************************************************************************/ From c77f697b4b7b1060a5e3217116c2920d81bfb77b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Dec 2020 07:53:01 -0500 Subject: [PATCH 3947/4093] Reuse duplicate strings stored in tries This is particularly helpful for static network filters used with filter options causing the same pattern to be reused across multiple filter instances, i.e. `all` or `~css`, etc. --- src/js/hntrie.js | 13 ++++++++++++- src/js/strie.js | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/js/hntrie.js b/src/js/hntrie.js index 76cd1900df7f0..dd9c82e67aa22 100644 --- a/src/js/hntrie.js +++ b/src/js/hntrie.js @@ -141,6 +141,9 @@ const HNTrieContainer = class { this.buf32[CHAR0_SLOT] = len >>> 1; this.buf32[CHAR1_SLOT] = this.buf32[CHAR0_SLOT]; this.wasmMemory = null; + + this.lastStored = ''; + this.lastStoredLen = this.lastStoredIndex = 0; } //-------------------------------------------------------------------------- @@ -160,6 +163,9 @@ const HNTrieContainer = class { } this.buf32[TRIE1_SLOT] = this.buf32[TRIE0_SLOT]; this.buf32[CHAR1_SLOT] = this.buf32[CHAR0_SLOT]; + + this.lastStored = ''; + this.lastStoredLen = this.lastStoredIndex = 0; } setNeedle(needle) { @@ -419,6 +425,11 @@ const HNTrieContainer = class { hn = hn.slice(-255); n = 255; } + if ( n === this.lastStoredLen && hn === this.lastStored ) { + return this.lastStoredIndex; + } + this.lastStored = hn; + this.lastStoredLen = n; if ( (this.buf.length - this.buf32[CHAR1_SLOT]) < n ) { this.growBuf(0, n); } @@ -428,7 +439,7 @@ const HNTrieContainer = class { for ( let i = 0; i < n; i++ ) { buf8[offset+i] = hn.charCodeAt(i); } - return offset - this.buf32[CHAR0_SLOT]; + return (this.lastStoredIndex = offset - this.buf32[CHAR0_SLOT]); } extractHostname(i, n) { diff --git a/src/js/strie.js b/src/js/strie.js index 6a3096fa320e0..9c26af22455cc 100644 --- a/src/js/strie.js +++ b/src/js/strie.js @@ -147,6 +147,9 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1); this.extraHandler = extraHandler; this.textDecoder = null; this.wasmMemory = null; + + this.lastStored = ''; + this.lastStoredLen = this.lastStoredIndex = 0; } //-------------------------------------------------------------------------- @@ -174,6 +177,9 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1); } this.buf32[TRIE1_SLOT] = this.buf32[TRIE0_SLOT]; this.buf32[CHAR1_SLOT] = this.buf32[CHAR0_SLOT]; + + this.lastStored = ''; + this.lastStoredLen = this.lastStoredIndex = 0; } matches(icell, ai) { @@ -602,6 +608,11 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1); storeString(s) { const n = s.length; + if ( n === this.lastStoredLen && s === this.lastStored ) { + return this.lastStoredIndex; + } + this.lastStored = s; + this.lastStoredLen = n; if ( (this.buf8.length - this.buf32[CHAR1_SLOT]) < n ) { this.growBuf(0, n); } @@ -611,7 +622,7 @@ const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1); for ( let i = 0; i < n; i++ ) { buf8[offset+i] = s.charCodeAt(i); } - return offset - this.buf32[CHAR0_SLOT]; + return (this.lastStoredIndex = offset - this.buf32[CHAR0_SLOT]); } extractString(i, n) { From 244e8fb87d441f4d185fa6902631d0d049fb8f3f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Dec 2020 07:57:02 -0500 Subject: [PATCH 3948/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index f6db943d67628..0fd61539f0124 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.0 +1.31.3.1 From 5b92f93e7c165aeca3d694d35c7ee3d2b74027af Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 4 Dec 2020 09:31:36 -0500 Subject: [PATCH 3949/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 75b166e67bd2a..7ce5a92cfd144 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.0", + "version": "1.31.3.1", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b0", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b0/uBlock0_1.31.3b0.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b1", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b1/uBlock0_1.31.3b1.firefox.signed.xpi" } ] } From 4b921f10e8eb55dd014d83fca029968403e0e307 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Dec 2020 15:21:21 -0500 Subject: [PATCH 3950/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/cs/messages.json | 12 ++++++------ src/_locales/it/messages.json | 32 ++++++++++++++++---------------- src/_locales/sv/messages.json | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index a842a07c15984..f38534d6b8ab7 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -552,19 +552,19 @@ "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { - "message": "Sort:", + "message": "Třídit:", "description": "English: label for sort option." }, "rulesSortByType": { - "message": "Rule type", + "message": "Typ pravidla", "description": "English: a sort option for list of rules." }, "rulesSortBySource": { - "message": "Source", + "message": "Zdroj", "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { - "message": "Destination", + "message": "Cíl", "description": "English: a sort option for list of rules." }, "whitelistPrompt": { @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "upraveno", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -1068,7 +1068,7 @@ "description": "short for 'gigabytes'" }, "clickToLoad": { - "message": "Click to load", + "message": "Klik pro načtení", "description": "Message used in frame placeholders" }, "dummy": { diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 684adca4888bf..e007be9ce27ec 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -72,7 +72,7 @@ "description": "Message to be read by screen readers" }, "popupPowerSwitchInfo2": { - "message": "Fare click per attivare uBlock₀ in questo sito.", + "message": "Clicca per attivare uBlock₀ in questo sito.", "description": "Message to be read by screen readers" }, "popupBlockedRequestPrompt": { @@ -104,7 +104,7 @@ "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { - "message": "Domini contattati", + "message": "Domini connessi", "description": "For the new mobile-friendly popup design" }, "popupTipDashboard": { @@ -172,7 +172,7 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { - "message": "Clicca per disabilitare completamente JavaScript su questo sito", + "message": "Clicca per disattivare JavaScript su questo sito", "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { @@ -192,7 +192,7 @@ "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { - "message": "Tipi di carattere remoti", + "message": "Caratteri remoti", "description": "Caption for the no-remote-fonts per-site switch" }, "popupNoScripting_v2": { @@ -256,7 +256,7 @@ "description": "" }, "popupHitDomainCountPrompt": { - "message": "domini contattati", + "message": "domini connessi", "description": "appears in popup" }, "popupHitDomainCount": { @@ -356,7 +356,7 @@ "description": "" }, "settingsNoLargeMediaPrompt": { - "message": "Blocca elementi multimediali di dimensioni maggiori di {{input:number}} kB", + "message": "Blocca elementi multimediali di dimensioni maggiori di {{input}} KB", "description": "" }, "settingsNoRemoteFontsPrompt": { @@ -404,7 +404,7 @@ "description": "English: Parse and enforce Adblock+ element hiding filters." }, "3pParseAllABPHideFiltersInfo": { - "message": "

        Questa opzione abilita i filtri di Adblock Plus-compatible “element hiding” filters. Questi filtri sono essenzialmente estetici, servono a nascondere elementi fastidiosi in una pagina web, e che non possono essere bloccati normalmente.

        Questa funzione aumenta l'uso della memoria da parte di uBlock.

        ", + "message": "\nI filtri cosmetici servono a nascondere gli elementi in una pagina web che sono considerati un fastidio visivo, e che non possono essere bloccati dai motori di filtraggio basati sulla richiesta di rete.", "description": "Describes the purpose of the 'Parse and enforce cosmetic filters' feature." }, "3pIgnoreGenericCosmeticFilters": { @@ -460,7 +460,7 @@ "description": "The label for the checkbox used to import external filter lists" }, "3pExternalListsHint": { - "message": "Un URL per ogni riga. Ogni riga che comincia con ‘!’ verrà ignorata. URL non validi verranno silenziosamente ignorati.", + "message": "Un URL per riga. URL non validi verranno silenziosamente ignorati.", "description": "Short information about how to use the textarea to import external filter lists by URL" }, "3pExternalListObsolete": { @@ -580,7 +580,7 @@ "description": "English: Export" }, "whitelistExportFilename": { - "message": "ublock-lista-bianca_{{datetime}}.txt", + "message": "ublock-whitelist_{{datetime}}.txt", "description": "The default filename to use for import/export purpose" }, "whitelistApply": { @@ -668,7 +668,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinAllowed": { - "message": "permesso", + "message": "Consentito", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { @@ -676,7 +676,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { - "message": "Dominio corrente", + "message": "Dominio attuale", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin3p": { @@ -720,7 +720,7 @@ "description": "Label to identify the URL of an entry" }, "loggerURLFilteringHeader": { - "message": "Filtraggio dinamico dell'URL", + "message": "Regola dell'URL", "description": "Small header to identify the dynamic URL filtering section" }, "loggerURLFilteringContextLabel": { @@ -776,7 +776,7 @@ "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "Non è stato possibile trovare il filtro statico {{filter}} in nessun filtro di terze parti attualmente abilitato", + "message": "Non è stato possibile trovare il filtro statico in nessuno degli elenchi di filtri attualmente abilitati", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { @@ -796,7 +796,7 @@ "description": "A logger setting" }, "loggerSettingPerEntryLineCount": { - "message": "Utilizza {{input}} righe per ogni voce con la modalità espansione verticale", + "message": "Usa {{input}} righe per voce in modalità espansa verticale", "description": "A logger setting" }, "loggerSettingHideColumnsPrompt": { @@ -836,7 +836,7 @@ "description": "Label for radio-button to pick export text format" }, "aboutChangelog": { - "message": "Change log", + "message": "Changelog", "description": "" }, "aboutWiki": { @@ -912,7 +912,7 @@ "description": "No longer used" }, "subscribeButton": { - "message": "Abbonati", + "message": "Sottoscrivi", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 887e773239f15..8ca0e3a6fb3ee 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -100,7 +100,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "Blockerat sen installationen", + "message": "Blockerad sedan installation", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { From db7f54dbf6c39cf2e6f35359248bf9f408e2d134 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Dec 2020 15:26:29 -0500 Subject: [PATCH 3951/4093] Add support to launch element picker in embedded frames Related issue: - https://github.com/gorhill/uBlock/issues/1744 A new context menu entry, "Block element in frame...", will be present when right-clicking on a frame element. When this entry is clicked, uBO's element picker will be launched from within the embedded frame and function the same way as when launched from within the page. --- platform/chromium/vapi-background.js | 1 + src/_locales/en/messages.json | 4 ++++ src/css/epicker-ui.css | 10 ++++++--- src/js/commands.js | 1 + src/js/contextmenu.js | 32 ++++++++++++++++++++++------ src/js/epicker-ui.js | 8 +++---- src/js/messaging.js | 2 +- src/js/ublock.js | 18 +++++++++++----- 8 files changed, 56 insertions(+), 20 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 07721183117c4..9a5f4f0b3913d 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -1010,6 +1010,7 @@ vAPI.messaging = { case 'extendClient': vAPI.tabs.executeScript(tabId, { file: '/js/vapi-client-extra.js', + frameId: portDetails.frameId, }).then(( ) => { callback(); }); diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 84dee24e960b5..5179f0af5e787 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Temporarily allow large media elements", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/css/epicker-ui.css b/src/css/epicker-ui.css index c571462970a9b..c8759f2acb3ae 100644 --- a/src/css/epicker-ui.css +++ b/src/css/epicker-ui.css @@ -15,16 +15,18 @@ html#ublock0-epicker, #ublock0-epicker aside { background-color: var(--default-surface); border: 1px solid var(--default-surface-border); - bottom: 4px; + bottom: 2px; box-sizing: border-box; cursor: default; display: none; + max-height: calc(100vh - 4px); max-width: 36rem; min-width: 24rem; + overflow-y: auto; padding: 4px; position: fixed; - right: 4px; - width: calc(40% - 4px); + right: 2px; + width: calc(40% - 2px); } #ublock0-epicker.paused:not(.zap) aside { display: block; @@ -89,6 +91,8 @@ html#ublock0-epicker, box-sizing: border-box; font: 11px monospace; height: 8em; + max-height: 50vh; + min-height: 1em; padding: 2px; width: 100%; } diff --git a/src/js/commands.js b/src/js/commands.js index f99c0bf8cb6be..c30b8627b64d1 100644 --- a/src/js/commands.js +++ b/src/js/commands.js @@ -165,6 +165,7 @@ vAPI.commands.onCommand.addListener(async command => { µb.epickerArgs.mouse = false; µb.elementPickerExec( tab.id, + 0, undefined, command === 'launch-element-zapper' ); diff --git a/src/js/contextmenu.js b/src/js/contextmenu.js index 7ac5b2d4bc123..80b2ed7a7569c 100644 --- a/src/js/contextmenu.js +++ b/src/js/contextmenu.js @@ -58,7 +58,16 @@ const onBlockElement = function(details, tab) { } µBlock.epickerArgs.mouse = true; - µBlock.elementPickerExec(tab.id, tagName + '\t' + src); + µBlock.elementPickerExec(tab.id, 0, `${tagName}\t${src}`); +}; + +/******************************************************************************/ + +const onBlockElementInFrame = function(details, tab) { + if ( tab === undefined ) { return; } + if ( /^https?:\/\//.test(details.frameUrl) === false ) { return; } + µBlock.epickerArgs.mouse = false; + µBlock.elementPickerExec(tab.id, details.frameId); }; /******************************************************************************/ @@ -76,6 +85,9 @@ const onEntryClicked = function(details, tab) { if ( details.menuItemId === 'uBlock0-blockElement' ) { return onBlockElement(details, tab); } + if ( details.menuItemId === 'uBlock0-blockElementInFrame' ) { + return onBlockElementInFrame(details, tab); + } if ( details.menuItemId === 'uBlock0-temporarilyAllowLargeMediaElements' ) { return onTemporarilyAllowLargeMediaElements(details, tab); } @@ -83,18 +95,23 @@ const onEntryClicked = function(details, tab) { /******************************************************************************/ -const menuEntries = [ - { +const menuEntries = { + blockElement: { id: 'uBlock0-blockElement', title: vAPI.i18n('pickerContextMenuEntry'), contexts: ['all'], }, - { + blockElementInFrame: { + id: 'uBlock0-blockElementInFrame', + title: vAPI.i18n('contextMenuBlockElementInFrame'), + contexts: ['frame'], + }, + temporarilyAllowLargeMediaElements: { id: 'uBlock0-temporarilyAllowLargeMediaElements', title: vAPI.i18n('contextMenuTemporarilyAllowLargeMediaElements'), contexts: ['all'], } -]; +}; /******************************************************************************/ @@ -115,10 +132,11 @@ const update = function(tabId = undefined) { currentBits = newBits; let usedEntries = []; if ( newBits & 0x01 ) { - usedEntries.push(menuEntries[0]); + usedEntries.push(menuEntries.blockElement); + usedEntries.push(menuEntries.blockElementInFrame); } if ( newBits & 0x02 ) { - usedEntries.push(menuEntries[1]); + usedEntries.push(menuEntries.temporarilyAllowLargeMediaElements); } vAPI.contextMenu.setEntries(usedEntries, onEntryClicked); }; diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 7c8df09749caa..5b6f73a4a86ff 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -599,8 +599,8 @@ const onStartMoving = (( ) => { const move = ( ) => { timer = undefined; - const r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax); - const b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax); + const r1 = Math.min(Math.max(r0 - mx1 + mx0, 2), rMax); + const b1 = Math.min(Math.max(b0 - my1 + my0, 2), bMax); dialog.style.setProperty('right', `${r1}px`); dialog.style.setProperty('bottom', `${b1}px`); }; @@ -646,8 +646,8 @@ const onStartMoving = (( ) => { r0 = parseInt(style.right, 10); b0 = parseInt(style.bottom, 10); const rect = dialog.getBoundingClientRect(); - rMax = pickerRoot.clientWidth - 4 - rect.width ; - bMax = pickerRoot.clientHeight - 4 - rect.height; + rMax = pickerRoot.clientWidth - 2 - rect.width ; + bMax = pickerRoot.clientHeight - 2 - rect.height; dialog.classList.add('moving'); if ( isTouch ) { self.addEventListener('touchmove', moveAsync, { capture: true }); diff --git a/src/js/messaging.js b/src/js/messaging.js index c8c1751658061..b8e8cdf3264cf 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -154,7 +154,7 @@ const onMessage = function(request, sender, callback) { case 'launchElementPicker': // Launched from some auxiliary pages, clear context menu coords. µb.epickerArgs.mouse = false; - µb.elementPickerExec(request.tabId, request.targetURL, request.zap); + µb.elementPickerExec(request.tabId, 0, request.targetURL, request.zap); break; case 'gotoURL': diff --git a/src/js/ublock.js b/src/js/ublock.js index 1a0c0ac8281c2..bc765036ff47e 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -414,7 +414,12 @@ const matchBucket = function(url, hostname, bucket, start) { /******************************************************************************/ -µBlock.elementPickerExec = async function(tabId, targetElement, zap = false) { +µBlock.elementPickerExec = async function( + tabId, + frameId, + targetElement, + zap = false, +) { if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; } this.epickerArgs.target = targetElement || ''; @@ -422,13 +427,16 @@ const matchBucket = function(url, hostname, bucket, start) { // https://github.com/uBlockOrigin/uBlock-issues/issues/40 // The element picker needs this library - vAPI.tabs.executeScript(tabId, { - file: '/lib/diff/swatinem_diff.js', - runAt: 'document_end', - }); + if ( zap !== true ) { + vAPI.tabs.executeScript(tabId, { + file: '/lib/diff/swatinem_diff.js', + runAt: 'document_end', + }); + } await vAPI.tabs.executeScript(tabId, { file: '/js/scriptlets/epicker.js', + frameId, runAt: 'document_end', }); From 7b16c11b87b5db69f8ef64b6cf6b0d6e9f76f347 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Dec 2020 15:31:28 -0500 Subject: [PATCH 3952/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/ar/messages.json | 4 ++++ src/_locales/az/messages.json | 4 ++++ src/_locales/bg/messages.json | 4 ++++ src/_locales/bn/messages.json | 4 ++++ src/_locales/bs/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/en_GB/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 | 4 ++++ 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/hy/messages.json | 4 ++++ src/_locales/id/messages.json | 4 ++++ src/_locales/it/messages.json | 4 ++++ src/_locales/ja/messages.json | 4 ++++ src/_locales/ka/messages.json | 4 ++++ src/_locales/kk/messages.json | 4 ++++ src/_locales/kn/messages.json | 4 ++++ src/_locales/ko/messages.json | 4 ++++ 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/oc/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/th/messages.json | 4 ++++ src/_locales/tr/messages.json | 4 ++++ src/_locales/uk/messages.json | 4 ++++ src/_locales/ur/messages.json | 4 ++++ src/_locales/vi/messages.json | 4 ++++ src/_locales/zh_CN/messages.json | 4 ++++ src/_locales/zh_TW/messages.json | 4 ++++ 61 files changed, 244 insertions(+) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index cbb35bcc4d592..e4339041c6900 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -1027,6 +1027,10 @@ "message": "بايت", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "السماح مؤقتا لعناصر الوسائط كبيرة", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/az/messages.json b/src/_locales/az/messages.json index f080fba822619..f67cc49a7a9ac 100644 --- a/src/_locales/az/messages.json +++ b/src/_locales/az/messages.json @@ -1027,6 +1027,10 @@ "message": "bayt", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Böyük media elementlərinin yüklənməsinə müvəqqəti icazə ver", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 886854721de65..61ffd0b64531e 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1027,6 +1027,10 @@ "message": "байта", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Временно разрешаване на големи мултимедийни елементи", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/bn/messages.json b/src/_locales/bn/messages.json index 31bbf754729f1..6e84e255dbd1c 100644 --- a/src/_locales/bn/messages.json +++ b/src/_locales/bn/messages.json @@ -1027,6 +1027,10 @@ "message": "বাইট", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "সাময়িকভাবে বৃহৎ মিডিয়া উপাদান মঞ্জুরি দিন", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/bs/messages.json b/src/_locales/bs/messages.json index 358a482fbae52..c246c975177f4 100644 --- a/src/_locales/bs/messages.json +++ b/src/_locales/bs/messages.json @@ -1027,6 +1027,10 @@ "message": "bajta", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Privremeno omogući velike medijske elemente", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index c9d38b343aee9..b0f4d3370baed 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permet temporalment els fitxers multimèdia grans", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json index f38534d6b8ab7..fcbf4499d7275 100644 --- a/src/_locales/cs/messages.json +++ b/src/_locales/cs/messages.json @@ -1027,6 +1027,10 @@ "message": "bajtů", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Dočasně povolit velké multimediální prvky", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/cv/messages.json b/src/_locales/cv/messages.json index a22e4c78ff6d2..5fe827ef66bab 100644 --- a/src/_locales/cv/messages.json +++ b/src/_locales/cv/messages.json @@ -1027,6 +1027,10 @@ "message": "байт", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Пысӑк медиа-элементсене вӑхӑтлӑха уҫ", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index f77dd3c3bc5f0..ba928e046637e 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Tillad midlertidigt store medieelementer", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 761cd8d36346d..8c3df4ac0f000 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1027,6 +1027,10 @@ "message": "Bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Vorübergehend große Medienelemente erlauben", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json index e9579cc64e7f6..bd167f1b38c01 100644 --- a/src/_locales/el/messages.json +++ b/src/_locales/el/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Να επιτρέπονται προσωρινά μεγάλα στοιχεία πολυμέσων", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/en_GB/messages.json b/src/_locales/en_GB/messages.json index 293cc0662b52d..4b325e13ceaff 100644 --- a/src/_locales/en_GB/messages.json +++ b/src/_locales/en_GB/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Temporarily allow large media elements", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/eo/messages.json b/src/_locales/eo/messages.json index df8d433a9976d..9f9b0e50e69f3 100644 --- a/src/_locales/eo/messages.json +++ b/src/_locales/eo/messages.json @@ -1027,6 +1027,10 @@ "message": "bajtoj", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permesi grandajn aŭdvideajn elementojn nedaŭre", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index e93054f3af3bc..c2cfcbcbb2c9b 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permitir temporalmente elementos multimedia grandes", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index abd7273f5abca..1ba0e01ea14c6 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -1027,6 +1027,10 @@ "message": "baiti", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Luba ajutiselt mahukad meediaelemendid", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/eu/messages.json b/src/_locales/eu/messages.json index dc73b3f680724..fd4a9af7f2e9f 100644 --- a/src/_locales/eu/messages.json +++ b/src/_locales/eu/messages.json @@ -1027,6 +1027,10 @@ "message": "byte", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Une batez baimendu multimedia elementu handiak", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/fa/messages.json b/src/_locales/fa/messages.json index 404bdc7f619c5..9dd10585c2ff9 100644 --- a/src/_locales/fa/messages.json +++ b/src/_locales/fa/messages.json @@ -1027,6 +1027,10 @@ "message": "بایت", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "اجازه موقت عناصر رسانه‌ای حجیم", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/fi/messages.json b/src/_locales/fi/messages.json index c5802ae663b64..e183f71c8e959 100644 --- a/src/_locales/fi/messages.json +++ b/src/_locales/fi/messages.json @@ -1027,6 +1027,10 @@ "message": "tavua", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Salli suuret mediaelementit tilapäisesti", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/fil/messages.json b/src/_locales/fil/messages.json index 1232407e4d623..d93d4b98ee7d2 100644 --- a/src/_locales/fil/messages.json +++ b/src/_locales/fil/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Temporarily allow large media elements", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index c7d852b7c99c7..c41c083a93ab9 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1027,6 +1027,10 @@ "message": "octets", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Autoriser temporairement le chargement d'éléments médias à taille plus importante", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json index 3abd56b440e4a..4da73f86f6591 100644 --- a/src/_locales/fy/messages.json +++ b/src/_locales/fy/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Grutte media-eleminten tydlik tastean", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/gl/messages.json b/src/_locales/gl/messages.json index ba3adebebcb20..1fbd4af606614 100644 --- a/src/_locales/gl/messages.json +++ b/src/_locales/gl/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permitir temporalmente os elementos multimedia grandes", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index 0d16cbff3db71..33ecb87e6e6c8 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -1027,6 +1027,10 @@ "message": "בתים", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "זמנית אפשר אלמנטי מדיה גדולים", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index da3e1dc66e1b5..6844f29ecc897 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -1027,6 +1027,10 @@ "message": "बाइट्स", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "कुछ समय के लिए विशाल तत्वोंको चलने की अनुमति दे", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index 455818d374d51..75a174f88f5b9 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -1027,6 +1027,10 @@ "message": "bajtovi", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Privremeno dopusti velike medijske elemente", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index 42ea760157ffd..e2ddb6e5cef0e 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -1027,6 +1027,10 @@ "message": "bájt", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Nagyméretű média elemek ideiglenes engedélyezése", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/hy/messages.json b/src/_locales/hy/messages.json index f7c7ce6a8b34f..24a59de4c1a3c 100644 --- a/src/_locales/hy/messages.json +++ b/src/_locales/hy/messages.json @@ -1027,6 +1027,10 @@ "message": "բայտ", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Ժամանակավորապես թույլ տալ մեծ մեդիա էլեմենտները", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index d8675eedb3c64..ac9dd894d3c0a 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -1027,6 +1027,10 @@ "message": "byte", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Sementara izinkan elemen media besar", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index e007be9ce27ec..7bfd5de0905db 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -1027,6 +1027,10 @@ "message": "byte", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Consenti temporaneamente elementi multimediali di grandi dimensioni", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index c31e15c903017..774ccc7e4070d 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -1027,6 +1027,10 @@ "message": "バイト", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "一時的に大きなメディア要素を許可", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 96bf7d83f7e41..49eb1a2a115de 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -1027,6 +1027,10 @@ "message": "ბაიტი", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "დიდი მედია-ელემენტების დროებით დაშვება", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/kk/messages.json b/src/_locales/kk/messages.json index dd6c0883284d2..fc7e4c19a31b5 100644 --- a/src/_locales/kk/messages.json +++ b/src/_locales/kk/messages.json @@ -1027,6 +1027,10 @@ "message": "байт", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Үлкен медиа элементтеріне уақытша рұқсат беру", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/kn/messages.json b/src/_locales/kn/messages.json index efe96934bfbad..aa5d536a539b8 100644 --- a/src/_locales/kn/messages.json +++ b/src/_locales/kn/messages.json @@ -1027,6 +1027,10 @@ "message": "ಬೈಟ್ಗಳು", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Temporarily allow large media elements", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index a29173fe06b1e..c75245d548cbd 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -1027,6 +1027,10 @@ "message": "바이트", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "이번만 대형 미디어 구성요소 허용", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index cca81cf2ef3db..b0ba79c68fec0 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -1027,6 +1027,10 @@ "message": "baitai", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Laikinai leisti didelius medijos elementus", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/lv/messages.json b/src/_locales/lv/messages.json index a541731ffa5e2..3a77bf83ee7fc 100644 --- a/src/_locales/lv/messages.json +++ b/src/_locales/lv/messages.json @@ -1027,6 +1027,10 @@ "message": "baiti", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Īslaicīgi atļaut lielos multivides elementus", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ml/messages.json b/src/_locales/ml/messages.json index a0b3253a91a5b..e6325ddfcece0 100644 --- a/src/_locales/ml/messages.json +++ b/src/_locales/ml/messages.json @@ -1027,6 +1027,10 @@ "message": "ബൈറ്റുകള്‍", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "താൽക്കാലികമായി വലിയ മീഡിയ അനുവദിക്കുക", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/mr/messages.json b/src/_locales/mr/messages.json index 81beab697e1ab..c70eacca9cfdd 100644 --- a/src/_locales/mr/messages.json +++ b/src/_locales/mr/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Temporarily allow large media elements", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ms/messages.json b/src/_locales/ms/messages.json index 99aefed5be701..238b214544427 100644 --- a/src/_locales/ms/messages.json +++ b/src/_locales/ms/messages.json @@ -1027,6 +1027,10 @@ "message": "byte", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Biarkan sementara elemen media besar", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index 99fb5430a947c..a51b38abfa16e 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Tillat store mediaelementer midlertidig", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index f97574f00ee90..e3e569666a60f 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Grote media-elementen tijdelijk toestaan", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/oc/messages.json b/src/_locales/oc/messages.json index 221e5e05b484f..00eaf919afdc8 100644 --- a/src/_locales/oc/messages.json +++ b/src/_locales/oc/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permetre temporàriament los elements mèdias larges", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index 51041edd82cf6..c6aede6357217 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -1027,6 +1027,10 @@ "message": "bajty", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Tymczasowo zezwalaj na wyświetlanie dużych elementów multimedialnych", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index f7e6c29f7e095..8dce0c5ea98a5 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permitir temporariamente grandes elementos de mídia", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 26708e2d9506b..6051d9029208a 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permitir temporariamente elementos multimédia grandes", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index c42d656cef68f..d39d9ca23da18 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -1027,6 +1027,10 @@ "message": "octeți", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Permite temporar elementele media mari", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index 475b149b8e5e0..6dba7374749da 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -1027,6 +1027,10 @@ "message": "байт", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Временно разрешить большие медиа-элементы", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 27a03df1aabe0..6b6437fb6733a 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1027,6 +1027,10 @@ "message": "bajtov", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Dočasne povoliť veľké multimediálne prvky", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index 1ba40e23781a2..9cb02a0b7e497 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -1027,6 +1027,10 @@ "message": "biti", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Začasno dovoli velike medijske elemente", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json index 9cafef79f9159..e61dc9df6f9ce 100644 --- a/src/_locales/sq/messages.json +++ b/src/_locales/sq/messages.json @@ -1027,6 +1027,10 @@ "message": "bajt", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Autorizoj përkohësisht elementet e mëdha multimediale", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index b74cd994a6612..dae1c84edd2e6 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -1027,6 +1027,10 @@ "message": "бајтови", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Привремено дозволи велике мултимедијалне елементе", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 8ca0e3a6fb3ee..f97b48ffbc717 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -1027,6 +1027,10 @@ "message": "byte", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Tillåt tillfälligt stora medieelement", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ta/messages.json b/src/_locales/ta/messages.json index 53819f715ee00..81fd6e72b191e 100644 --- a/src/_locales/ta/messages.json +++ b/src/_locales/ta/messages.json @@ -1027,6 +1027,10 @@ "message": "பைட்டுகள்", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "தற்காலிகமாகப் பெரிய ஒலி‍-ஒளி-படத்தொகுப்புகளின் கூறுகளை அனுமதி", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index 2693566d9bc1c..79bc7948d62ec 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -1027,6 +1027,10 @@ "message": "బైట్లు", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "భారి దృశ్య/శ్రవణ అంశాలను తాత్కాలికంగా అనుమతించు", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index f650e2e78803f..4ee525268da64 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "อนุญาตองค์ประกอบชนิด Media ขนาดใหญ่ชั่วคราว", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 70473965d5e2b..2356200e09cc1 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1027,6 +1027,10 @@ "message": "byte", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Geçici olarak büyük medya ögelerine izin ver", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index 335fd5b262ce3..f1d8a0ec06393 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -1027,6 +1027,10 @@ "message": "байтів", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Тимчасово дозволити великі медіа елементи", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/ur/messages.json b/src/_locales/ur/messages.json index c07c6e839c277..a3f402a942ce6 100644 --- a/src/_locales/ur/messages.json +++ b/src/_locales/ur/messages.json @@ -1027,6 +1027,10 @@ "message": "bytes", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Temporarily allow large media elements", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index 4b0873ad0bb23..c84fa8f759240 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -1027,6 +1027,10 @@ "message": "byte", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "Tạm thời cho phép yếu tố đa phương tiện lớn", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index 30aed228f69f2..f8a31b0e6b22e 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -1027,6 +1027,10 @@ "message": "字节", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "临时允许较大媒体元素", "description": "A context menu entry, present when large media elements have been blocked on the current site" diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index 5d18a5eaffc41..f9b0025d0c84d 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -1027,6 +1027,10 @@ "message": "位元組", "description": "" }, + "contextMenuBlockElementInFrame": { + "message": "Block element in frame...", + "description": "An entry in the browser's contextual menu" + }, "contextMenuTemporarilyAllowLargeMediaElements": { "message": "暫時允許大型媒體元素", "description": "A context menu entry, present when large media elements have been blocked on the current site" From b3433057a5b0f6b9b9618a3bd8b23022e8e352fd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Dec 2020 15:32:05 -0500 Subject: [PATCH 3953/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0fd61539f0124..dd1b369fb6355 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.1 +1.31.3.2 From add3fddc8557e44ab30d9f2a4c5dfaa39f3d0e00 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Dec 2020 15:55:44 -0500 Subject: [PATCH 3954/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 7ce5a92cfd144..3714cd34ac385 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.1", + "version": "1.31.3.2", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b1", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b1/uBlock0_1.31.3b1.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b2", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b2/uBlock0_1.31.3b2.firefox.signed.xpi" } ] } From e559cb73b9ae28152db45f8059a5fae3429d726c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Dec 2020 11:24:04 -0500 Subject: [PATCH 3955/4093] Complete removal of font-based Fontawesome icons Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/249 --- src/1p-filters.html | 12 +-- src/advanced-settings.html | 12 +-- src/css/3p-filters.css | 2 - src/css/common.css | 21 ----- src/css/dashboard-common.css | 6 +- src/css/dyna-rules.css | 3 - src/css/fa-icons.css | 16 +++- src/css/fonts/OFL.txt | 97 ----------------------- src/css/fonts/fontawesome-webfont.ttf | Bin 112160 -> 0 bytes src/css/popup-fenix.css | 3 - src/dyna-rules.html | 30 +++---- src/img/fontawesome/fontawesome-defs.svg | 5 ++ src/js/fa-icons.js | 5 ++ src/logger-ui.html | 10 +-- src/whitelist.html | 12 +-- 15 files changed, 64 insertions(+), 170 deletions(-) delete mode 100644 src/css/fonts/OFL.txt delete mode 100644 src/css/fonts/fontawesome-webfont.ttf diff --git a/src/1p-filters.html b/src/1p-filters.html index c1d97da1ba9d6..e17733d53c70f 100644 --- a/src/1p-filters.html +++ b/src/1p-filters.html @@ -10,7 +10,7 @@ - + @@ -24,13 +24,13 @@
        -

        +

        question-circle

        - - + +   - - + +

        diff --git a/src/advanced-settings.html b/src/advanced-settings.html index 5afb9a0d93d7f..718731ad318ba 100644 --- a/src/advanced-settings.html +++ b/src/advanced-settings.html @@ -7,17 +7,18 @@ - - - - + + + + +
        -

        +

        info-circle

        @@ -27,6 +28,7 @@ + diff --git a/src/css/3p-filters.css b/src/css/3p-filters.css index f8d369b4597a4..111d4e8f710ca 100644 --- a/src/css/3p-filters.css +++ b/src/css/3p-filters.css @@ -74,7 +74,6 @@ body.hideUnused #listsOfBlockedHostsPrompt::before, .listEntry.toRemove .listname { text-decoration: line-through; } -.listEntry .fa, .listEntry .fa-icon, .listEntry .counts { color: var(--fg-0-50); @@ -83,7 +82,6 @@ body.hideUnused #listsOfBlockedHostsPrompt::before, font-size: 120%; margin: 0 0.2em 0 0; } -.listEntry .fa:hover, .listEntry .fa-icon:hover { color: inherit; fill: inherit; diff --git a/src/css/common.css b/src/css/common.css index a2af2f934cf47..c15a14f32dc77 100644 --- a/src/css/common.css +++ b/src/css/common.css @@ -25,14 +25,6 @@ src: url('fonts/Metropolis/Metropolis-SemiBold.woff2') format('woff2'); } -/* Usage of FontAwesome is deprecated and will be removed eventually */ -@font-face { - font-family: FontAwesome; - font-weight: normal; - font-style: normal; - src: url('fonts/fontawesome-webfont.ttf') format('truetype'); - } - /** Common uBO spacing. Ref: https://github.com/uBlockOrigin/uBlock-issues/issues/1005 @@ -59,16 +51,6 @@ body { margin: 0; padding: 0; } -.fa { - display: inline-block; - font-family: FontAwesome; - font-style: normal; - font-weight: normal; - line-height: 1; - user-select: none; - -moz-user-select: none; - -webkit-user-select: none; - } a { color: var(--link-ink); } @@ -134,7 +116,6 @@ button.preferred { background-color: var(--button-preferred-surface); color: var(--button-preferred-ink); } -button.iconifiable > .fa, button.iconifiable > .fa-icon { padding-left: 0; padding-right: 0.4em; @@ -142,7 +123,6 @@ button.iconifiable > .fa-icon { button.iconifiable > .fa-icon { font-size: 120%; } -body[dir="rtl"] button.iconifiable > .fa, body[dir="rtl"] button.iconifiable > .fa-icon { padding-left: 0.5em; } @@ -233,7 +213,6 @@ select { -webkit-margin-start: 2em; } @media (max-width: 640px) { - button.iconifiable > .fa, button.iconifiable > .fa-icon { font-size: 1.2rem; padding: 0; diff --git a/src/css/dashboard-common.css b/src/css/dashboard-common.css index 56292344353a1..628aa3b982c3c 100644 --- a/src/css/dashboard-common.css +++ b/src/css/dashboard-common.css @@ -18,24 +18,22 @@ h3 { a { text-decoration: none; } -.fa.info, .fa-icon.info { color: var(--fg-icon-info-lvl-0-dimmed); fill: var(--fg-icon-info-lvl-0-dimmed); font-size: 115%; } -.fa.info:hover, .fa-icon.info:hover { color: inherit; fill: inherit; } -.fa.info.important, .fa-icon.info.important { color: var(--fg-icon-info-lvl-3-dimmed); + fill: var(--fg-icon-info-lvl-3-dimmed); } -.fa.info.important:hover, .fa-icon.info.important:hover { color: var(--fg-icon-info-lvl-3); + fill: var(--fg-icon-info-lvl-3); } input[type="number"] { width: 5em; diff --git a/src/css/dyna-rules.css b/src/css/dyna-rules.css index 1245cbb2d1f09..c709bedd47c18 100644 --- a/src/css/dyna-rules.css +++ b/src/css/dyna-rules.css @@ -64,9 +64,6 @@ body:not(.editing) #diff.dirty #commitButton:hover { display: flex; justify-content: center; } -#ruleFilter .fa { - color: var(--fg-0-60); - } #ruleFilter #diffCollapse { padding: 0 0.5em; font-size: 150%; diff --git a/src/css/fa-icons.css b/src/css/fa-icons.css index ba98eb96e095c..5c8bf7b128fe7 100644 --- a/src/css/fa-icons.css +++ b/src/css/fa-icons.css @@ -34,7 +34,10 @@ position: absolute; visibility: visible; } -.fa-icon.fa-icon-vflipped { +.fa-icon.fa-icon-hflipped > svg { + transform: scale(-1, 1); + } +.fa-icon.fa-icon-vflipped > svg { transform: scale(1, -1); } @@ -69,10 +72,12 @@ .fa-icon > .fa-icon_list-alt { width: calc(1em * 1792 / 1792); } +.fa-icon > .fa-icon_download-alt, .fa-icon > .fa-icon_font, .fa-icon > .fa-icon_search, .fa-icon > .fa-icon_spinner, -.fa-icon > .fa-icon_unlink { +.fa-icon > .fa-icon_unlink, +.fa-icon > .fa-icon_upload-alt { width: calc(1em * 1664 / 1792); } .fa-icon > .fa-icon_home { @@ -90,9 +95,14 @@ .fa-icon > .fa-icon_power-off, .fa-icon > .fa-icon_question-circle, .fa-icon > .fa-icon_refresh, -.fa-icon > .fa-icon_sliders { +.fa-icon > .fa-icon_save, +.fa-icon > .fa-icon_sliders, +.fa-icon > .fa-icon_undo { width: calc(1em * 1536 / 1792); } +.fa-icon > .fa-icon_arrow-right { + width: calc(1em * 1472 / 1792); + } .fa-icon > .fa-icon_filter { width: calc(1em * 1410 / 1792); } diff --git a/src/css/fonts/OFL.txt b/src/css/fonts/OFL.txt deleted file mode 100644 index f1a20ac1a8ade..0000000000000 --- a/src/css/fonts/OFL.txt +++ /dev/null @@ -1,97 +0,0 @@ -Copyright (c) , (), -with Reserved Font Name . -Copyright (c) , (), -with Reserved Font Name . -Copyright (c) , (). - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/src/css/fonts/fontawesome-webfont.ttf b/src/css/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 96a3639cdde5e8ab459c6380e3b9524ee81641dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112160 zcmd4434B%6xi`Gm+S8fmAvrlo&PmRY0RtpCNq`UzVTOQAPJkFt6hRae1aUelRlymQ zQd>1@rP6DAZLNJ>jTzMP+(K$0`&E{uGiX<@$^0Bj* zjc>h+@9aaq0r~!mH?7(H>b_@IA%CYN@h@Js=9BfD_WmjBx>B6P4J;=|L z*gaogzi!PXmP@^_OKdN0OC9TR!Og9|M7|68#QIHJcSI9`oyen3edvm-E?&cKe&o2s z9zGv+@J(xWZ06_ksKg${eJOV3noaBa>b7N(zd@4ZuFY3nvvrH}S6d|Z_?ILpuy*^p zwU<8k`DH^A`*H=!Yxt+$N|`HdFIzhD?}cbPXDv{x~s2|vQq5-paCaQM3Y!OPNF5nCt@Opaig)5 z&_BA)o4HFf>Tp`)&&HAj1n zE;_pU=#@urI(qNXM~{B~=ogP3Ir^)k?;bUdxsKHwYdO|)Y|*jR$F4kf)3JMxJ$mf( z$6h>kj(U#9k7kc9KH7hD^U>VV`;QJBefDVn z=qpDDj~+cH9rGNE9h-10du;Ks{$rbu<&NEdY~a|l$MVNsIW~Cg=z9{q;pA^lUUKrn zlNX#^esadi)Z$TndMZ3&PskJW1U!C^&*Swd9@)b^ z%p1J>)*&KJNa&{Wtet-S4~qkNYp~KfB*^A9Ejd(476h{=)!ErPnZm4*DWq8ivN!G>WO*aInGbAM zW5+jZ(sA*Q(y)olL>k5mPfFU8YEG&~CZIEKyfqZi>f?2(_Kvo=m!&f8J*+L>TEny_ zn+tccY$TP64CUy^vV}XF6AfWC7j8(Xv+HrYAf?(<_>(2Rqq#m@WwBI=slq!XyrUTz zZ@|UtT6lX8Z)**E)zR7Zj!xFm)*8~Jnd>iGaoPHrIGuI*d4|O7qHh3RB82$ls}LvjK^85rm)(IkZ8S;^@3biqStqSL@OYheV2dd>x6H z67mHx3?U_Fd|=#be86;ewXFBGcO;BM&%JSQ(-7IY6 z+WS)M+#5zpTy@wuao-!y8HbVrBv0maAQ34dO_df(QqrsGitggg7!a0DB~xi{AcV2* z@OJYS8FQco1L07(Mw!A}d*sfJ&K}n3H76(IrRl*yM-Y+`j!K}loSkUi;_VLTWff@N5+KGn92{g`wI8l>ifFK8-qQ!T(vlnSbWtjJ%h$u zg$HszzQU5Y=#qP9yz#f@dD%oFJFod~Z~Vtwg{RHBKZm&+l z2~0ba{*KnLU&WY2jEBx;!GJ$#Of#loLWBHV$N@+k< z5klH~R2u(QT4*(@Ix~bOQWgol!W6OH2Q`gPzhy`^c z|EBTHH{WDEx9zy=t{s_m+b+3iMniL^8Gj8kF1lpfI{EkJ{Wm4aPHRf1_qy@s@zONu zZ0REDD(PnFKIt*(UnNP+w5OU`omR~Pp(zYt{SkTQZBGfPFD?T%ru-@Sk0}39?;E?A zSS}S2nC%P)MM^~q5}`gB$06iO1=X@A4Wvg(eN>%Th98K9q+uatOZBDL!>3CYA{;MH zMGQJBBSlV(B<1oV#>n;4SNOtl@orTtVzChk99f!A!q#FhD50B5LYUYaO8JkvFH3#x zhSc8I*UrUpBrWI8bcaiXM*G?s9r+K+GDGE=QFkPZ!~`n%*(_ zvG@O{^JCw~rLG1e-_X_7z_N54N%LHJt}rS$`rhc=hm|a^k;TMo>A-$IoGgqa<&k9B z)w1O23zSu6Qu^3t$KZwk@mcu$M^(jm4~dbM(dQGRMt}6Z@^b&=SdAJAiAmQcP4N+)S%WTX7hVsynTt>kkEVD^q=mBAHyLZ;cOFw6P>;Di1AzFe;dC&vh(r1&6n54+)ZmYF4=SVmBV|MY+T#q zj@52x+WUAR*SEe8e?0doD!KCri+<|Mtanq))!cM>Z2oK4tw(V@wf?%-=Ep8?YIemo z887nr1%byo9f_6#;VbCha(Y2Z3YaNDN^2;I)`4aaI}8EM*gUnq{QfC<$>++ueB!`z z|5&=e^q}u*LnK)iHN965X-;W&^$?w0GF@Wt9TypuGDTVu^8vi4OIIS_o~qLVp;lTD zSf4s(B!C&I#~Rgi{8BHlT+=!&gjAX+SkU*l)WQhZfFL?cSKELkIza!6WmL;T;ZBg& z;0%bYb}>Cv3wA`2_P@G+|Eqkz$MIEvpnk5+T6KTO;o389yvM0m|H>6)(TR=s*xWAr zO=;cYp6jb}{V%7-V}HR_*)YRqjXV%?I!712*XnjUZb^v35jP6+5WQhP+w?0(h(|k; zt>-%;w&cCmE5hzOTccj*S3JRuR{PZ*HmAcLTv^#Vv5E(sqHIgcq$LiA&6&8*wz0gh zZF`%=Wfq z)lU$@GPB)_Xn$Yip3O2YpByU#Bi9+yg&O%wLw$gGZ&I1R&C0p;Av9#DZ`pO*mdRfc zP5Vr;y*>FE0ypp`5e(R+sx0}%`WIb8$BXn?#>zsS05m`sc7`;;8gbVEr6N8Kdc)vi zL9H6Olc2dGDaNPqY3x6HEKb>JDfAWk91f?Y$HHy=hq3cxe-Vr6mp0C0Mht~>MCh_X zrZD!pk>b$Irc3;ZE$!# zOwuf@d*i7zOF<4nI3Vs-zaDMqYB(-v6*9Ujm|Xgtah+Tj^jQBJ3Si^f)9GPxi$mXf5w>*Rl@62z<7wIC3#v{%*8x4EY=}; zIIt;%0+0#FKqMwc7!;Gh2KF8|etvxK-s7y{IJ^3Y@tCpNcOR4sQ00&GoruIj7O#am5JJ~A@UB=hEwMN$0;WM(eUT+hV0GZ&CnACJo$fHcD z6pM{e+IMz!-Py&xjnzih?`Qey#x%?o zcK8&~IZa!E7cscz7HLXHh|*+dZtLo@7TVY}G@E7JKmO3BJ{T|tsDZ5C=W;mMG^^Ff zd)Nmb(p1PO2)P5sonqz3A@GvpGB&SxI8J-KiIgGAF|l#jACgb9ZYHx=3*E2c#JVqH zS>B(D90#JReAkwV$k|B7_HHH5$~KuDH9XwG^G_HxG>PojJyUr@WnEom;pbD!#>g#I zk%WZkaIxuvjqU8f*qmY6D+95@pxf*5#A5MU9{bQm&!3v_GxAo8Kgn}Rzt3;vzyD#Y zo(k=SXMg#!hJh07*#tIBtTG-%k(3N32XDaha zanbhHkotR;HP##N?lt~<<1KzH&j_tN|L!?oT66m!X4{(pj!u6i^$%Ckz2e31IQ`Sv z!_2>z1vcJ_$Jn6CjlUSrU3uv(ezS^HyMK4@+*_~qUJ~}petH~N_Utwjtoqr*Q*T^#*Sx%O)a!|)YJ-#C{_4gTZc4Rw+4p z9hr6x3WEm&wX~fNlV&CgpGrIeN3V*i2`$$h_-bhP`6E>7oNMc5RzC}I@fVGsJzG7q z?%Fvc_s-uP`f8y2_CeOp`dItm?R?L{2PejtZHy7_7W|AWHmBQh(b@-@_Nh-9#~)mK zk)wN#xN8!qv5m{(6CXVIaaQs2&YdqCe=z$MlO<&kG@QU&*shE8W?LK^O-ROG?Khq? zjte}jv4vQw%D@R);cOw+X%4&cLURogyu_58sOzlL*9Iv8O(X`OM{aMCF*?NeobDYg zcg}2^JCdrXtE-^@RK#tYeVP{=z5};K)nrw$I#}5q>8fN5H<)mswR@7Z&Gq6JBD^Cy4*D0CV}jKUN(6-fuG-5pPU<;f0r zbs!DspYmm+-MD!r?j*vBQ>l!sWFFSaJS!uW$c7UrvQl!;APPMM=^^c){rr%jR6#dT z5A8skSgXPMj357T{4;PW^h;-k1S?(#@0O|e)_dc@whUdTUzWp zsgP50xR66eoC~=ER$W0{k|kWr4Ka2z6VEVQFXVX65Z6i0jHft?$P!(qf9isV4nlr; zYCqDDbeVmb0)2y0-Qa{PpzQR9ibu{5>*l8vbq)f2*fWJG^=| z6`M9q%^kl*z4@Q|CtPIi=?|%YLRu${@34%bND+a9C~ZR^i&!4Walr=V+N2Row`Y=t zOezDp{6Hp`;@?jycDlL1$Yzp8AerPpNaiwZpuI1XDs&K$B@xf{kiN0_E=Z_8{B5e) z25^7CiBKT2dcxNq)e4pqjZ3uDu-B5*!dzzX?`R)-gGNVd@ep3dzn99G&6Xt__{8hb z=H=2Q(pF#q@Fc+9z;WqRC)Cp&sm>lwf*MMYL~V2ex3sVh_NBG-oUUQd0s98lI~`Jq zb!#QrP6|~PS-G;jc3DHnc*lRu^r3YN?~7K1G=@EqJAztxoJCf-9F>Dj3ey!Oq4>uu z%)+@Vq*=U9e;}TQ)Y!>Cn7=q=yqlPF;m{|m>~>ql4*8SS9TqlD=cyC#C=M6zcUCGv zBnksatUu+7Qa5St(6!m~HZGdct+co-Rhm6eWlL>L*%~bNIxVre&f20n>($7%l%?Kk z2}CT8WISCNVw!B-Jb&og?X%pTs@b&>`In)3cMa{Af?6<$S}>CsQozN>RbUFz6|+_d zAxH`!#9$CqKwM!0A@*zK?r<=kPRIR~6Y7mQ#+<}>GarP_fz{bncl@t)T~14kJ#CyH zr@U%KUZ{cym*>R(D+4bDq;3dFO=KeEKJgMLk_u3WtWAoIwi>ZL7r9TOzXhkqfPIGW zKLC+KPRW^!C_05@ZzMjMXZ&ao)bKC9P(UAA~OsaVKC^<(MD>X*|K4Am1N4%J@UMF4;^~< zkUU5v)A1Y~2iyGXGF-~6^S2c)8w}00>CTKwoicw(jW3+=Eyt&2aq8Zb=PP zO^w_}QcAk1)oc8xpN;=;l0S9c(D!(_cS2jr@eZq4kg>=w$M-h6&#ex){d?RRn`UJD zj6bH8+gR8Vv^v$ErOfDwtcy-b^~sD+{;$cFq`X-Ekvo$zUCY<=S6#Xh zTV#CVqPqW>e3rvqt)={mPw}`|bA43B{%mttJdb}<=97(gDnqqCaBFF+FJN(*xC$5& zFc}1fUjr?As4eDgPq%>g($TqqR>NdLJEChKEA@crb3kB#9;KUQJSaP!btHhapyrT+ z0hg=;cyIzxVPtso{9d-Bv1(TDMe`=li!#nETGNcBJJ+^NzGQ1}>tYKl{Fb}#PUv<` zg#ag!X=ziHwd}XIg;$1Vf9!@;UGcM)_hcS^dG@x)o?bQX*>M|;E8Q`6_SL=Py5nBO zmU*?^vVH!A{53r?ZR_&cmrsd0Tff&zQh{-uX5dF;|zQ7t6aXHKE@IZ2X&0>yQ9L|8i0!qc6^ngZ#OZb3&6 zHI5@mq%|G$i;mJfd$o@zqE5DR1FM+2$nTGT{>I4@*4-0TT{ZV5Ee_4ftFH6%5X1+} z`?Tz|H`}YXM)%BY`^rt{@U*YKSLf~AUSH|7tMX;ss;X9=ZnY)d{_*k2&Ib!`F1M~- zdXC$tRE_JD100f26IPF-y;ahUn7P&vsl!Oz326=5M5;D4kpv?ERWPeGML^I!5OyL( z;Hl{#$9TF$ralnc8VPry(LJI`s-{EcNB%vo5r|!an2akKTSK_|FO@Yby z_r(`4F3)`MqYlS+FlUMT5-h3J*n=)hlM+z4ny#*_mOW0UIsAGx_g>t(C}w4fs@fW! zPN;HSpYhx2m_^xp!4(yLjd4Y`e>}b;;ID~Cnq0YL!MlAVwE{#in640b>T~od#;)r4>o%mY%VwB0bd)lR>dN&CU(v`_Taj0 zyeb?GD2@u3bNgjH;$vWnX^dr|+gKw#1OaYw91}`7G-ePp*eHvG2uU-9@Mj#y9^MZ6 zmuP!z_T?kV$ZUv|C0IHw80btq5DH)u21A#IdXo%_YG8;EjJK!o>=JWqXG8cZZI6e` z2i9fts#9xjT6{&5m0`i1c3gF<42vF&m}38U<6k`H*s3*-?#`?di7465ZimyY%0rT@ zLLD;ZszO)Qn=$4ba`0H$kT0CgoEqnfx}@_!d*@3}%su^(d$#`T9nZ*mwMCylcS(op zsIoh@uNPx}{A7AuhaBt*${pjLT;At-k-ertDLul5_UCk7&kCjt=R9=US z=>xE9sR#_JQY7p@AyH1nkp!&AMNY#}+{@8D1;@Nd(Scq15y}6L+HIOE%4m#ew`i1# zqp;KwIgaE1bi2peCwx?X^mvz#cKKN2x@hq~Jko#HSbtO-$KD^?<`H-)hn@2DKQzi8 zDyJK(Ii|Le*xR%@Xbp|cpAO#3%a6T3wy$IJOoHNr$l5a;G~7Qf?x|U)|9DyH(Ra#A zm8S=X>t)xRE;;n);j79>fwHToe@y7%$KZ;yLE#aRNxB!Pm1u+fM@Qq7(aHIpE~_yJ zg+|N@!I_Hu2N(yxQxnZTA&!c;Ql1_uBM*`p1w9_6ga0FYR@Pq$iiT7BSd{w;H8h`>BIMD(FHJ)kFVi7x|GW)nJ;6AZ1v^sL-LTGpA2t%8GrIAYq~T6C6~jPbD_K zn$dKIL%NiP+{kBaI<&oz-G1oMcAnpUi0$)LIh<({5H)#KKihY(bm!3ar`TS<3N3&s z7Xxns`bvkdN{!TlYl1iFXa!4^VHim8vfxq#Z;KbF!etx_QCd8=d0_MA0cG>?9Lo-H zP!k`Bj%r!-bYHmzq~f81n+q^q&x@ig=69Z;Von8*#7>Z5(9@GM}v(LOI^unfF9SyF`9#+83snd8@nYI*z{DwX;pBprhO6!fwV zdDkc@hYR=!Yf1>cWz#@|?T;G|dZx{t<~H`l**Nwz8z&d-Dx^)bhmOZnskp4o-t;OP zXS{0GU9>5I#5L)y6YA+v%4z9A(k{ynj!{GRD_K(^$B&(=H$+HSC?p8F1Rvk zZEbI}M6bMHi?)R25^>fX?+kl9;m&w7izgs8fBsbi{d)C*Tdhyt^@|H@;5T#OFYbEM zdb7D+wZ8$zG{D#-sYjZNR++OYr7)MFPUZ)KFY&>EDzbk8VGhEv4ElilLGFiSG37cY zoaQ?q@7Q`^Yd@D_UgHUG%*$3UIkbHU@PBB#oSoJIV-CkemoFS5KY4jGS2g1IFQNwx1=3EsDox z3r%XO*Ms#_7G1UH`3(a=84*9r`FXujDD~6ttWqO&N~xEx`EAY$kHyN~Fmk{bP5Ik) z8_$OA-07;jtbbS6#O3{qmrb9X4haNhxraC(1pZFsYe_^s!8L@{~tm-v>N91@m z;_&mAthT}m!8r)ZwXni&G3ysHc6e2cuKx_L5rsNBwc)p&`cD3mKXS^OC!e7SDC~$7 zCX2T0EXoSuq;*PLXmUh9wPj{M;m(EL`q3|cM750Rr};L_#z^&|uQ#YStGmc!0uoL^ ze~2}@{`f25cs#652=g_C8fPG)<|6?oQVD`7v9Ac+PquKh!OJ)<`-NdmhP46Mt1t!9Jbf5YbvNRYeKdPRQXEi*Fu?r7(Ee!c7^$>^~ zz18%yXz2J$G;|mk8a@miK?pkRK-OaCFNp+34mTYU{*ui)Tz?5pPN|<>L#kAgkeU`R z+G*ctf#OQ^90%2M=C`962Wgnh4)cRHYk6bDIF;7K=(db)#BhJh-#fa$V_t;LlGm%G z!D|a}0)?dCL<(ZgSyB8;#1wVbg;6ZR7_Bk&rI9I0@v}-p94Y(`8dr&WbP`8%JRd&! zuyRoS9VjNr%0s5*xJmVkty0-nc!&G_{)03V5kUFxkT~d9eo}a+@Qz5DmvEiRn02l| zotGBtG(~S^M(6+oWf`iXYW&=fT14fjfbXL>(3?1Z%>qM|!C=`jgc8r@NHSm!)97bd^BB^pd`)7G z%yyMpb7~vP{D4mTRueoJhLx(~TZwr$*8dvEl`yH^KyBo;zM(NKlIx;AG~KxT*XWHe3Pxr>fT`9ue@q)l z=UBpJlcm|9m;pHiG$kK22B|HW0}W&$T4Nf8U{8iPyHo=EFSHzqvR0D$XI_{%l2!0k z2haO+&K=&RJ3Q7*ysmx1f`$pxE*B-5TG&jJ!Dc&&ZO`90lYl||tKU@~ifl4yvI?z1~m&J3aL;2h$TDqHJk6$5{(-n`$ z#$I68q$2kv|Ma-H|M;Jh_t67mE^re=oaX7_>ex6SiZeW3tdH>F$b1p*nt~A!PCw#6 zjz5rLn<|MScjCs%4RoBz265hATg0||Hx7GkbjE2^{^c^O%TtU>*>_L>&~PP{A7-RD zsxL*mX>u|mV%F?|saXk}(SUNFv4WQO>wf>GIKvJR$4mV?Kdj08CwK-9y`rRegq|fs z>kl!Z9v<_L!4uFY{DfgbfEC`uRbf*JpaNbr{bP!L-fHZ;f@}A{Ro~rv?ocKF^Bqrt zjaFkYbNUVZVSYmfPe2J>tomhs+vB$v+!vg;_xoSx@2%WB^xzXvP`+gRS~$Ygu*s~N zQkZ7grDZ@zEs$c!0D9}=*!zI{gj|j6wL66P0aOvTaZQ@uUdXa!Dz$)25DMF1LU9-A zLl&e`#xHrkeL5^tG7F5?6IUeqaPMwmsIVuMnxEQ$0%TSOT$fSv#rF}dMZP7(O@LaU z)dGtwF;RjeRP)Kgwsd=28uhbeA=^HEdOOb>zr_1f?U@w6E6KARD3VMrzzbM%K?ZMU zDZCvI6t>mV`!c|-3)C!m(33nxbZnUPGB^HWH-YT61*nPqv|blgiH@Kueph{G2fCW% znGb0TwUyQqz4LjzGgtEcE)6E&kGeHX02apR%IJTiV`f<*A5RPmZI@nkmPyX z+e+g}GM)v=r13h&8t$f;ixm2fx6-)gKy&8FPoT)lWq@E^@E{2by)W4)@H8B)I(_jr zG{NN83}VOz*M9O7Th{i}tE$)Sap(@Wd~@ar{@p=vWn6*>ydR~A9C6fkoU?6UUFS@# z-s%o`tr6^$)d#lX?sePEoqCFY`uUL=6z&gA_ zh5-m8rovvs=b<=7q+ZSBHokuC-UH{f%An6h7-fhR5jCW=PYPQr-5_|tHbS0cEDu`K7OkDy_Tv- zHgZ{u@xFj`xDvNNVZ1E7t=m3q^i67wJ zEc^>X;FjkTmE?t;A@mX-Rk0y++Z`~AW#!T{`cQrIeZv18gdlm#$SHlTRY`>tUzH;Ghw_Uh#YA!c* zBc<3^T)r=Lu~+kXV_a8dRh7K%@!GD%UHGeg9JPX?>Ng<`<`7wz@3t3iTlmyd3vu!h z|6kN$1QA(*-f=cFU3jUxp z=kTP7JY&4^o1Iwn6~U_2f!$31a)hS>EykaI`P$%vd)#}&p7G5+)iq54FSp2Y&-|V! zx1RU$7dLf&>A5dHl(wY{x(7p)yMzPag&@#_3+ zUp5q}R$Q7>uV2_P*{{sBwPmjP@nhQ)KDTU5Cv9nO*t%-hRw3iSx`Eux4GU3;eDr8K z%-suGsDMDa>97!Rs=(mkbd5r~q!G>9NonHQ{rzW8oT0E4ckf=&Y36!mGdCb~2Xs*U zi*{YOZ0_8ZZT&gM8kcXq<(ajmE30oUUZEie{YK-iUvE8=^bU4aipn z?l#he_l)%2fxzAD7qAci#oavn_O|uceU*aFeD%8Z+unZp&wu8V8lunL7>Gs#=k7Fq zJhT3H#-CW|t@@euZ?TZ^$G1psesTb99R%G|2~VpT(m8j!$!w9ww+08r@3*1 z)Ic$_#So?ww3CeA4_*l7M<_>rCjc=xp>~4M=FN-FTZ_JYhVLHf1-pY?Zmilc(dKjP z^o+aj*!h9LC)i8OdBMsKn@^1-YT~jd`RJ{z!ou=_^z8k{wqMPEm0f<_HJ_Pw(Z5dm z?mg4;8>yd$!LJjlT*3p}$??Skn)-(A~R`zPk{uJJhFSHo?_guC8qW$&N0 zYj$0B$ulqR^1b`@=dRhD{UTTmnmZ5h=}`esae^r9`X7OlWSDpkTX+J;f}@Z|l)Au5 zPWu~nXAvtoWvM>toln@|y=5)%>9?wmi zR$W(DO{TlGi3IRHe$*?}D%%(UWP*VwoMl&Ome{u%Gl+-df^NVy?#gbS1 z$7TB-A5gtH-J!^C&G;{)kWroeRu^|$4-eTnvmveVZ!+0XTr#)kTps?3fxf)j-=6P# zyfD}A>era;WJ5;bn_gGHmD`67>mH|Ljg@8KWfiu-BRJ<&9~|RprRv~A!eWST7h`$zjH^7xVx+A!25}tvoG5~Z#!zDT^1>4mRjuOKPdb@?^Vlbu z`zzM7ItVVN6Lz5ze8pQ7?4d>WmoN>{-N-@{*rKI7I%||R8X2O7eZx27*b1V zA0^W@m?saH<_~u-4Ar!?Ef_aQJJ;ZGRf8WN>9b=Sx>mIJwf448u9{LTLf+6NS3fFp zQkt-+yQw19Qr$RX>UkILm}%BA=3?n7rFPZxXLZhPtQKODAs5u%d8obfjLEtyT-P!+ zec_kHeQbzuos_qi3e1uvlb@M{&z8ZpnnZTIM!fz_k6hzVpnwe=+9`D@Dyg^3^81 zc!L2!6_s`}NIGg{MDZ%+KU$jqZR2rcuJQP{L7qeGFur?fOH<3z?(t@pf)A0)wwa^A zL?bz#&wbZ;@%iUj?{`HBKy50dC?R5m@C3hfq-gnLG;kQl6;e<;sKiJGIJ1GB2$ehdM2gBMsjRe7_yqPK= zmIm{mqYkPo<45hLU>dcfPLnpuDLH8U!3vu(uUh18giauhn&3jQAjn9UbZR8prifia zb|KIR{L8^B)4D-yJ2?tgpLBI9F#k~2V%HU(kEGlzi+Ex1hD}BCJnOLz=sf2(@-Xp) zV=t~1@^sDbl=G!0u*MY|>|X`c135(7b2;Q@aquIERgetRFRZ- z>eUrC&jd1MkGR@qDsm^1PG4;(si$b|f%eV;_5m|v;TkGVic+_0)rst?UAtB>9QnYi zUGhLd@L3Cg>3Py;oi2C*OYK>=` zKiPXCUze$6i;+^Ybs6K(P=581sm8ymtoY&>UOue&+f*VO&+*tuCY~9 zyh>SPNR}h%j%MxH{V6?0D6xDbVq550js8*LFk1~Tj7Y-x9s&G^^1+ey8u)ta~26> zOnbT$6mF2_4E8bfAB4i%Od-c}7y(?|Su?U!PsQa(w2JdDS6jB)Dj_PCW~dj{aN}$%Mc5$t3u@A#?fLK5{8!h^UH!}N{Pf^pVNlo+pcw<(5ApuN z`#L7GA6g%O;NW0k00t+xerP+!9`6x)O^P#AgBgnAkJW{$xx^-X$M!QAJs-IL3m5D%zy6!Se- z+lToMl8-oAFJ_whU@}KExfC>xY`1mcD1r$W6bzhN$yowOjCGb=J8Kj<3-d33W7A?X z1EaJ2t+ifjx~^I7e{0M%+$vthhHMSu*Vbw z`~ZmoL;oY;eMD_$a38z_HB$W;$y6GMf!-rx27x;OO##Y|Ha&{<7zzVVz{L!vGANH$ zK?L&8KP=}26v_J${s~)xc{Fk^>nH8Ox-MN0Z};16*CZS44n6#W-N(Xpjo0c_D&A;o)RY}co7ef!KU%&R!sw(RzyZLpn*t?{gmM2@ZGKi!-#B50&F0W+w(BeW zjw{AjxNV=X1uxJoAFHz3T#G{EQWeZ=A1-RQIxIEU>MMM%D_TYs_4I`%)P=dXFnG7e zT~)cIQjzDZ4ssq`Jx5lMt#W&CqdH7C;QxIgZp~@rv*}*A+ASabXPzSX75G=s!AT)A z@=)-IG=U?*4csNbMJhr(K(TJIF!dTGT%!@(lEZRZtB=u&O#oJbkSRRS*Nw0J+qo-l zcsS82+x>7Mk+~|vNFm{=4%%+G_v>sHyNS)>-S^&L3s!p)DjWgfr-)(!M{DBY8&;fa z9Q*F%n#Wng)*EjR-?Cr6%lPBlyFKSOSiyC|eMnPu85>?Im~5z+`{V6*y}f&PVfT(7 z&8=ui22&ctO-0jm+2vunwc&ivE@j2?RYz}MxM0p}!!$RRtPcOaO(RieuuALWa2vsC zmPy5dG?by(8U5q7zGmmI?i92*is)7%{4WdYHUD!CR3V3n?sNM*teAT{*a@ z)fni{_D3p`jiF8@RXHxvm`0osXR>;Hc!K(q+pf#2HTAwsz#VJOO|+&!nLcw*;==x~ zUB5MC3=+a+zQnr86Dz{0=5*Wg+h#WMDUbZT6!Tfk);f!Et-NL&bKdZT6L5Alt3o33~kg2?G zS5tEOo^2Oid;oAkG$oK5@U#vo(dJPY4WmGtFNTB01XxRVse<0AQOUiJhe^nl%8(B$ zZHP2f0{f7~D1PH5!70fkNr|fmhevdHxSC_`K*m>Jqpm$KciT^3@HD5RoZ>Bhvk z%9PR>YD`u{FrKWxby4oX`e!H9*WbRpEnU}OukcTpvMyn~E5qJFNM#_-tS26F@%2}; zVy0${=iqteMg%D$d?=b!F-wvU76S_MYBoh4@D~Qj+%YTIkvyr(V*N@i7;&1W>ahQ& z%pHvQ{4j|T4I+yg0BbLWpG=L_|w5m2^r{yrW&la|t`bU2EvzS6MSmgaCgviBD^^Dy#2vRGJ2_&e&@nczDtWO&$muq6vy8Crruf+SEfkZ(&-phSRD;)dDx=AV=f zE8jXP&A;bxZrMFAZ)wV;s;ACau+8Th!jx=VFk@pm&iz}@Ry!K&7PfWFUpb4W!Iho0a(+kK!n(!|_3W+p&&fgS zB_xacqj9i;_=8Y9ojzV@rG>e zlUA;o-gtKMtmuYx>cW>U^klBC9+y13F}r5vqy}qnLhtmje@Y+_^k@!U4>j9t&Yrn5 zD0oFEG+5#WzhZURE%?tkbSiwTOy})fwpl7sA@>=($NXn0@D^B)|OJVvZB@c znWFRkOYq{UOqzOeko}7Y(APu;nPiQ5Qlh|RERS$~EMIGG;pP!ic<51!VX^1Vg_^a$ zp|m3)Y#GbL0x(+xP@{E^IH4zjLnk6m2li9)-^L;Ulo0O;Vi(F#*j>Rl8>H?Q53BV*n>cIw=Ptfn3p?u(Zk=|+5P*;{=UGH z`8KX7Rs@ygFO9paswR3?1m68gAG1yfSA;qy&ik+bzNKNHF?`;*>QHUste>&KT~8Tb zJJC6=y85bl73YT=9&fzrr$@d#eah5D6Kw02hgXDcUau{rH9SIN!ssAk7(iPL9EILv zAWSL^s!7Br0Eb8)ksvP$qU%V4NaI6E1`i)IG!`Y{ejSE6M8F0N$N_!0X z{0x*lg0Nr(e3>yyG-1mM;aF#w`9CyRNe-%@&s=Z;`;6m^QA?x~DYpNdbBqn@iVu%p zBH&xlFtbRbOa58Fa1?ohNN);NFrwwBqzYn2M0*C0BZX`5a$&;vT^i9w{ zZG5Mj`*f$O&TPrZlgg zJ0N51(3a1*i1mH)HRH$67{}hMZ+`RH%MaGZqs>j5_sv|?yJ*~XY~@Rq!?)kvzo|cY z`Gv~*wX8r2^D!Zsx(kGpr-`3oL;&X!8te)!Vhq-&IO#e>=)(KqHNI-GtDmM2dC2RQaKDaTOn>fRBT zR9qe$box&~iNyO6V9AfrVmXquQ$wf?^zEUk$dqKdpoWM*!8Bq$3n?BV>tF@@)Zsf^ zN{rldz(T;sOlMlYnfra!cT^^L$oSe@m9TV*r~@pqNuk((pw-|3cQ56W(SN@FM#;U*Q zWXa0=z-%~Q``QaeoW_y_q&N}nP>U!<;1)`KDe0!*k^{negj>KWX)(hVmtmu_D6fiV zeDC=2y$t{Od#v2q_e87msYjFw*U)>e3Pt&XInthQdslVJuFh57Z+qApdZzeyv=pcq zYIgPx`?b^SbrxX{b!IaSFv?@sZ~ zLG~PjX*dmgMfo;Gq7GA@dPX`c@d2Wf`p()Flhu=a7jpIh+OuO zL>LhnNwS4tHZ`(*zh}xhvCHNau2loZ`x91t;)PGFn4sj*kt`ONk%h*8>G@OBe|*sb z>om)Ye@st3f9bQabEbGa^Dbi(*f<_&yJGFMX=|@&E4*#I+TKU2uCKjm)xOWZch>=? zM*RVz-4GDkIC0>v_ddIC71|F^M9^u5dZXZP;D!zYo{r;*HUo7+X9`VDN3x7JkDU-- z6T?78c;+z-V@F~j=xIE!_V1~&IU2s6anx2fzA(Yo=+J8ecia(eYP3ywp|QHwk@E*L)*|{1mV7j+M3S4*NEOn^LcS(ZbHN+D0-B1!z89~c%ns}@?Y^y|#l9HF;J5Cf$7^FM#df5D7 zyFr@;1SLftMUe1_Gz_{nMJ^(=5y!<**s?*eO-!-cAB)vb?{28(5KYf*a8)qBFBG)Q zxd0Ab>K6|4x`SS+(3$8!~}O>tS)_>yc0RChcTo;ss>S!PmTA?#>}#gi4W zbCzbaCci^5Co>DC%=+ZrYTu=y;G~`dmtS_Ed*;sD>$5#egPrqb45HU>g@FT&9dNIZ zbqm;1N+Us`4j|dm!SHB0Az#A17*#Qrv{>jD#0r_dK)^_1oYF4aq87OVkT2v)DTEAA zA0gKPQwVbuMoo2l+rlx>zyS?8ns(~RX{P+E7=`j7>Ps5W(#84t?KC}y=9UqlBPL_*bCBqmMYG5$8?(Oj``Q!F=noXD0<2) zo&_Y%Eds7ZIRn_%lT2M%BTp4WTbOBrYK{KkpjrfM44cVE3wpFxP)0-q#XCESu6w!$ z4?{-L`RNLfQ@L*;*%BMJ!+!YfA@2Tuc<-%b8<0feFngaoDu>Oy5t<8T-<H{g-CZP!s{y^1=Mgc>R<6B!?G%*Cf!p?G!JyjKTn~gDSLZYMtHMgyVBUK&@Rz18mwWjRPkYhQSDMr?fLM_ zm}_jSE`@|-0}U+3>D0ayKB`@i%c5Dp2_Q1D?oCI`Kp0yn8p%e@CHyeOGz>R}d@;oo zu??rT>k_juG|Q)f0qNwJh85RmPQaO+{hU|eO1a+vBsCONkkoA*VSJ^e2L>HlDjk5G zk4Bz0g4rd`H-*)V!Vm=N9jSDixTQnv7Yxx3LAMaI51I)83GFB;o&KpbR9vW**N0Gd zX9t8@Aw**pCA4tL1qPa>>!`{Oq)-hBKq#!A7Sf6DB-tWrLgSFb-YhB!cZR|#;1v|% zco+%DO*%t*2O(TMhKDOankggwU?e z_Ecx6Q@k8lkJ{M-V`J8y!2>irXi;k?90=+==ux~)oH|H70u+G3>qyfW(K#h|5KE36 zO#UL=%Jf4SynX*J|L=LbCvC~+hfzLvaT|BK(@5wtTSg+kt4FI>zrvS!X)|? z-5S=^L}gslbO%JKR_4&Ni-hA$n<8-t*abHfR(C@o~br&x9AqcKV;0U!ynA$Rf6~`EyHkIA)!{SkXEa; zvd(2C#J#fYbJ{$z!zz2ZJLEll?3zwf#aYm;I;;p}%CVSK*==QVW%SN{wfaHI!p`3pgZH+%*$*Jrdu@4;^!d-um~}a6ClMg^wtVlwNn&V)n%{z7)^mquBKQmT(v5i)h}xo&W5PcD2q=wv;s>SL=)Ki8JH)&y-ShquQ zs}&ea8#yQV@B%AFC=9r(WNwR#IoudC-HJ%d%%&hVBuBVTwNgQ>NQLVb3@C=%9YGVU z%%!Uyt0HTfLz7(?$;J2TjCs%nJBxZ1%$W<*$YN=QInI*h2E=o=TQ#*_)1vrbl8c_< zfu>4D4JtC;rUyMCu2ltWmV~A|HGFN!D=X-0o#MAJr_U~HK21?A6*`3g5SNUWZpI~NHmko*o z?zQU{Xhviog086+#qY7=O?G_w8@{Rn@}m3N#dWE#`pRGL7I#gU|DfZ1r%3mSh;p?mGL2Q%!#elS?jHIhZMca0*Y3af+vI8O+r2rBu~N; zl`o<}V-o{;548^LK}q(B@a&*dDLkke3=4ZFW|CI?vxRfX$8!TroDZcx&ff@+|I zKYc(+m70`a;M+(D0U`p!N&X1?9eW4gkik$W=6HyiBilvH*yu4JB_?T&5TYuG_;3)Y z5nm>lv!cN+Yyu=hQXoB}Z%~sen?cOi54E`T0fh1l9(DB557ytiT9sg5YQ#*D$^dnG z07EcHUjcy3o+J(ftErzQ-6O0Jt=Pz5{ASJxNfgMl2D~CkM(9f*sn#H?C33|8c7jOt4haAS;3kmroNQ0J1 zE75gf+m-Qe%TXC)ZQ6Wb}Z0tFbxPf zpm50|wx+2$oUFd9;5x(SrPWqpcWTrYzcO8TY|)bI)opiGC&SH6Y=gK-;75L5_iLMB zrx}O0#pM_UVp+fn*MQ5z)V9cEYAk|$fO09`1XWnP)>$&Kk;5I5>B(;5nKYh7iozQR zUwz0~h##(H>a)>TU_x3W$LxN+tHE6van#E3=#i?%hUmU%VS4mPv>{!+FB*NNs&Q;7 z`Q~%>E!%P3vLnmRKmXjFJC?t)d`upn2}JENxz-V>bT@SAeml~zb^T#gWN(!J0f}hU z-e?+ys%l3UD!h4g+1_R6{BYTh>(4#^eAGNTOX~u-D+k#H{S9z%RTlc91?f^vLot7@V;m7?b*L!!L*tm zfp@$H`hF+s4r3M&F%PT_z-3!dbvkaDRkj@aSQlLXbjcFo#wBDY~y7yB#Lk7@S- z0l)FKag_gW<7gmv{slMRe1Tla?lW<;v1O*QjD4;)$?h|@Bt=&wCS+`ckQYg-qz%#z z>2~RE+@iO^QUp>1)}fh<(e zxhWFXVW)v^2edThT)-nRXGXLVR6;f54^O3`r6d9$)(5PU-YOpy{5ZRUorub6P0s1@ zx(bV~v?!p7*Dl-jz@6u=u3+ zxs-_9pDXs8pq2@CJZEMK(z`o4QJ%WIw1dGoB!+U1#h z`=(rxK6`oly$dHyWJ)i)&7x;L^@+fqrd@4Q5_Bj`Y1`G55C=Xm*`5ek#z$li$RhS% zF`msDOSbe|pz8K05hI^v2lmL=G_VN)e@Vb!wTR}Bgk=c6%D@D^E#hVqLE}>y&`}FS z+|h1zs%KBqw5`ZK$8#!p!@wpbkhopl>I^3>;2 zgZy(dso;X?lFwqr?>69J)M0$3;itw=`M(%HH9n2+&kc}!Hohh!HS`btP05)#KpR7( z^>J6j=A@3uAn<;oSosLA_6v0s#5<;@#gJ_Uv3a6w|<<%P=-FC+%Lx0`!#$%6O z!!NW=^*C*XC(gcf!`?pGGHq#g`Lx2jnz zLbUVuXCPsM{jV7AP8u zE=_$iwLfMw=?}|~j+0jkA*bdD%^ept6jUEW)~_K49%Dq#J+^#Hta(*G#*fhV&r=$%yy}6!s&3kOcYU7DR{_ zatN_eLArsDLXGJ>+?FzJ?L=*AdK#9VWAC3b2sdt8vY~g<#7Wi7mq#oU6MoNh&jz;e zqPA{s?AONk_KvTvY^gt|;-bm(E}6M>7Q0#fqd5*f7sVhxo-@9%k#S4YoI5wDZ9Wme^f8_}aQ-!p`8@kr!q>LEy?I=?vTE{_wn@w8v@UDutn4j4mi^iHJ*e0=uk;#u4E0^3s z+%O_3Zfw9r*xT?c$B6n=h;Ghwk|2zJL0Dp|1QttagJcKzfv^T---?DO z-2O49v~KIY%4T<|j^(b_%=tU7o;jnp_ouVgPfou5|M2!6fNhm$+pwN9wD-2;Az7B> zc*aAv;}s=whBKX=kdT;6XFxUqG7w2vDTNRqP)1`Y6ey%nHgD6`ZGqCVDRk1-w3Lt1 zGCC+Uu};40evV|zP6E8||NbAuXX%V*-p@U+o86`xev(bibGIce5== z>O?M5#A8su#Xv1GI_lbn(NVo<3AWZBC|)pUdtp-{6Izq4$OFWz+R8}VqQyN6o61K! zN*o@Y4KlZ@xO|mWnD^53iy-S)#yhn(QE%0Hklk+Tv<>GUzIVsY);6!*ktZ*3T8C1Q z%V9xS#1Kyb8Q+>T81k$aTH@M2EAQ=|*%GeKcZN&yo0>aspS9wK1uYXi5hwx{7@@_8 zS#*9gGihxBU8%{XT>0bkr&o<@9uo>zRZp9~v+E8v<9J@liGA6=fh#=u!)Ul4he|66 z1z@>`a%WzrISR@-qVA3n=Of$ZfBSso_lEm3A}SV<>}oP+?pd63Jp31B*nPu)8-DhA zcjkVJ#N9p;WaT78*FKs@v|-l{9x6kJ;vnRpGv{i~;hAs9c^R9To1K&BaPZV^89WCU zf9T3hia{yuXh{q@X&_+9?&n+^0V9&Mm!ozGp*pDSFU4Djb#pGhyvToDR0 z2N-rzCif@t|8|XEGh;|w#0X27L_8jZNWppl5|UyOS~B5LOG*mHTIPeIlkg76J4{QK zxYssqXmJ@T-Rs*f{(jHSKVG};iA$H1cg-l&1NT7dsC(`HoA1ARL)%oVK8pCk_62z> z9n#B6Hlz7$ZqW&yJGuBf@iA9_d}QnMdz-uWTrr{N>mhSUHyV2VwsUU&_1*iw_2I&{ z$d1KDwd1$W@2pXlP1>-8?fwh*0n4o$kS+%K{%q}>YGSQS<>)GG2%l3qZkk2iCGKFI zE}!o+RCw04KK|!PyPjCz^Z1@~%4f~6cqF5&b=1Cc?@jk!xxSSu=S|eK&G)bHJDw!| zkH;#26TD8fC?*TUG86y+m?Nircn)kZR^~TF7N>SmD9KASBaQs1vD!$Si~2D#XkJKnM5~ zT7#&w$Y???I^=>p zspDG`U6EvKVs>QxBIVQhx2(Nvnb%_}eP~Ygm}u+F8L`%j*N-o4ZZ0jVs3@weWf!JW zN&I7}T<(~)Pw#ZaIx4Cv+5MM2BeVhVFa@+X+mhPnP7ECL+0}jW0|YJLBh@*J_}kxZ{58pFTz8{E2E%;##*(zm zQ=>v9MFCAEaNfoc!wAEOVh9r=Dn}tgNQ~7ma@C^<{nXYQXOvk;_gXe%?~%PT%G8}u zw*JV;6wxLrb>w}hp+U=H0Ufq1)y?{@?uxpV{&%lAw0q{v-G|hjQij~kctGJ>F?ljY zk5En`5HZj&mPBT(6rx(-AE?H(skjtCR#KAi0Kg^|Ktd+*9DeMAXMa7BKmIH#E)tF# zp5;PL24#UjP6qG=els?V`;*WaUZ*~r)TD%z#J@|^g=BL6Fpw}1bcBzpACi)}@8QXa zQD!`wRG%G;BI1Y(LXwvm&Kr1|LVdD@2TEg7ga0@mJ{ZRXynNtNhv5Sd#THudkv)O= zkVdM6^O0`08!n=`Jb{!t*$ea?srzKgCA~D{Sh|e!uzkQDr*?rRZ+NRhDkRZ#u$_2$ zhl)9(*?yDL5@%>b$e*xIXui1bSni9c9nglz46T;&3;GWIuC`~k?>LVR8BwDN5W?{g zvGe*6pDeTp+&>`NK=5Q5xbh%U7b@Nu`Nk4Sh4MiMy8#&!D#oz&SB{x{VI5<27fv4Y zEjDFL`HD{Es-?zpatzGkFy1{4%I0qle+4H5~s7Ipjwywz+ZO5*qJ@cc%MHEn!gc8HtF+v0=#~`Oy zaLpr4703}$C`Z_7hx?2tLYeEl>|Esuww$ey#&FFBm)DV^W@kXv8{U z4V=7o>;tcg*A0ZlKd{=)6)QTYo_F5B@6yi;&UHH{))m&Jf61<6ACDe=C^WjM=uerp zÄXa(OuVc#WCZ;~FHG?TQj@WhocSr0db5Qw1U)oLzzS$XI72bG_luVebFjW)Zk z^NpQ7-#a*a_QCJG%VIvDa^HFRlIsr`^YjM|f^m5dZhsX| zO&)(R$GUOZ>P-O1g%S;RzQ4-9B3!F*7C#o`oph!E0|63!H;H#z}z7LzM0eCzaEQK~cCy7!c(9Ce8krwjgq&kfQEQFd6e{=g|P z%jjnJ%+*i@YY^f`$tMPjWGrh*&EApq8f12~AH{GvvYF+XiWS669QTKPx>_5ot7kFZy@5(= zFre&{XSB{ZSlTtCb*q*CB)q_PJJkF7l#{;jym$5Az5vqUb0!QHtbk$rvHH_<&K&g!S*SM^zXKivBJnud6jK45Ci(kxc%m|3DQk;n_S zp;pzzl4!}Dx721w%a1taiy7y~0dh*K203;y58`pL1Op^Db<3-_z-~8l)y#0a78dSpI+3_yr{+u1Tbl`i z2L<8v6@svWm{PKLfQ~@s&_inwq?{TuxHIasFgS=|$~v+*Wkv!#h;#duTR23G$n8Mz zKtP~RI!StP0XkX?-*Q-v(A!yq6!4zWPaYes1z=3kJ-sZ%@25@reB3`jjXs78gKEkk z^OMDf^`IL>Lgg#LPo<#gD23LXWJ>C~82UgJBYm0Z4>z}9`szqdg5Zp0R2V`vA=Lnn zk)~%kN)YYgwTB&v4ua6{3b;1bQ$1=|PV1ex>B@swZkpI(9A!*d-m#>x??|n!Y-yFM z^YSV!W2@X<%evfEV=a|=dDT*DOXb?d*FX9FC$C>Dq7ht{s#?4)G`)Vx?pc+UvvyBe zJBdT5X6kR3XzWCwg5L zvsw8e(orUPI?8UOmQ=wmPxMl;M8 zMdWf+CQfb<^a6ucFSYGxxQdNXsdL2%nN+dT*Ef1YjTiu=YA4QsTUt3e8g?Fw*OQ-W zp)~0HqME~{*x`!@j$C}$6m9P5@HS6^X>9VCyaQ~~fxPucLI{HjL50Wn6I-C~GwM5F z(=aK08CMqo`+-dDx%lA0i#zrn*|x-1-|>QbRU5F&y4qH`UuZAt=_zVY9$CM*pp0gD zS;1mL=omWd*ja2GS5#l-vMt$mWG`&fKYIIZpsk@Ti0?^d+5$SxEdK@o9-YGt0O~f_ zXu0!Jtq-drk60Tg&faD zM{9)Q+QLQ0nf`cDn2sZ@4x=^@d+TnxG-fhdhfu%qFWJ7rqwF~P_S;7fxPNts!*>*x zfbVlE7jO;dVJA*X3I#Y$X%79$eSly5if2VTnugQj6!@VOdYq)$DCQ0P=wzsGGixYh zr@D+-SHLnj?Wm9HHKz1(;crKR0?#On%9Lxi1wU$H%-b3I3LN`(obHJTi=-I3(0# zz?NqXni+33ZEAB@GTHT?k9E+#oYbs8qD#JgG$l4to8(T(qK=V38F= z2ad;R@y^6Rxu7LbadzjT4$unbFmA*m`gD#kmz%bMXQAqnu39Fw|n4 zmgaXTR~4Aq81o6I1U`ZFp3sP(~@2oxqYwstKwrL39z$e(w3m`)R~|-tQytA9?=&`uQ*V-pKkg@P2CC zK1Ri9xKGG0vF*=R%=OQ~qrnR1TuTrA{P{=!TQ@3a`pi(tPTWA?ru`}dm*YN7+RM+GGf!%M ztNG;r{Ve&Pj8futLBzn-4vp75&SnzJ17zA5<|zer60{+FVCt~c(@`#lKJ?Kl{evbF z`bUg_(>r~!WP1}#IbWVt-h^*e?hZYw+OIQRo5A{4UV#1Ds{b(} zg*0HnrmcSg+&XtN=%;mN@DP#XfxfIwJ4Iw5;CjxL4D_m29RBDuGGz<8ADfNoV_Zjv z%tcn`@b}Owg(@=t5Q|5DSpKn;C-FA!(+{2l%uPneLiigs@R%g5voBNiFU1vd>FEqr zgndP$Xp|J^ex$yWeZ526Vh9%*d0?EOHXnX26A2ED;ZLJWNhxlr&{~)-qO#!SVghD4 zT_jFc$3#5QNY>i~+=g&90TTv1l*<{b^T~kt(50C2w$j_5RDL^=n!md@ne6TB4uw*E zeW_5WyN}Mh>6eKtn(SxYOh&j-GKBvjhgl6F*4rQI3+eqSzaIO3)*HfA@W!ELWF;Y9 zH{+wDg}wuPUKkXjjy&ZE(jwuAH-;O-V3UN@Db2J5>`q{vkG`D@vHpXKfGi@5@k_KHSz(Wd3eDD@YyrOe@b=W;zp4~i|IdTmPB}hTW4U> znJx<3jJ1GBRH_h@_c{)0jYefByP6$5Mc8!o$7O^UB>VgutLrdf1WLu zYER_;Kgc)3lRNrQE;8MYxG2n}GO3@t8eibwVy~lIXSyuRP^&;yLE$NjB~^r8Ks6hA znaVXo^Hr%%nmeq$hUcJgs_ixWqEz=qwayfp8k4<_WOpbC%c%hsi(Poe%e=j2XpW&= z+thLm*o`>=^Kx+vhlb!kPy%a&R;=*%-HhXHbiNlpujvD3tCeBeNDZY9S=zXQUdTTg4gVrWc*vW+9?u zZS9IJL;4Ebib`pQd_YL{O$O{K%P_C^9QFhm{UivhD z>-dwsKqTd#KZ(!F-MuQjRj;_&Ztq20F6`(63Zx?KirqsBZr8xvZsK#gu}V?du*{%< zDXaxLL;%51nYA|3s&IO%4HY{Ri^9H{X#oqh1{@)VaQfD8EmOa$Q68YeiZ2awX5{T6 z5^F)<<{tZJ`?|oJpoIqY*7C!MtMTDe}v(!OHL*KS+UPmWj`Bz4kIvRvV(cO_WwH ziUS6R+h&MpI~rH_?wH?DWTv2Iej9BFIaWFU3ZjSL^HP}iG|y@@i%>7X{KB&mlo*-& za*lmuC?m%b>|h!w6fq~-MHh@?@D-?%$o$2vVXB^-)aVok0exm(+q||s+6Z48Jbe1# zg`;kr{NUtU$}c>aTygk{Irq)E;_!-Oe_QOz8-93X>CDu<2d`QmZoev6xAE=`H{5mO zfpvFps0&`jdb;Lybj%yR*?rM{9+Sy)-$je|PphIX;XEZV+i*1Sk)&dfF27tZdb{u`P{K0?aOP+6KrpG$4IbxaGaHQBeOJdny=ddn(qL`pNN4`Pm~^Oug6V`5G-AYi{}N(DHt5BWvtH# z-_MZ)c)7TR9C**4Bu@5~E(s{VaVB6hU7E*Y&XZpesnEPgWYGpZ=plJbmGbNI!xK*S z4JMOr5@*2 zxgh#8R>Rp$l#daA3^_}{BrU0$_4TP?l5IuBJ94FA)*nc&?(s0^^`qZ%~G zxW4PlS1A<>q#@HGA~_XMV*kCGs765c_J8R++B5X{T3)G) zN7oz5BIONWFI2Gm80Zh|RrrtVL5LPdz%RETR+0SQH)wWh_VZ|*6ua%|!Qc69L$?n*&0bbC>e~RirT(s=*KVfw|0kt`2IfCN z&qER}Y}sah$HzI_bnc0ItmIzGoMd)P{mIT>U{`vn79ZOwCU+o3fAk@dw$y!uFNy+y zo_mpVZvpy>%*UV!SUMfBAr}f9Ljj!SFf(Ds8kmh3B(y>9k%>i>l4+2eYc^&O#65NY z)pN$Kx^LOBcRxAac;3p!#{7yg7o9vmf^48ktFs`2K`Hk|jJn_4yl7H>a?W8iBvjLQY5M*xwrF0^>J_&{njI&tG~T6u zIGV|by(2BhowBq&VhtDOFKRaET~XoPh}%=%7He;GZ8pnxCqzc=VBKYK6J^NAJ4v&Z z=Al;SX>jo^j^RxhuQH%H$QulykREScEq+8J0T28COS6c{$6t8q(Ffo7rTCY>-sE=4 zO_o|$RiGkL;q?VvYaZX=a+lRybnO1CE5kRQeDHtNR)W9JzWV8I_VBa%3%|EXX?kjV zWj}zk^0j`QOKXxO@%POMgZ8*X(0y--{+TlN;s2~5NtdM2rntVKgyP9gQyO{Qn2H&h zRJBA1om?w2QU@bdB1Hwpgwra5fC-~W=P^=AWDF>k{1)1%W4Q9v4Z69~2hanQP<9=j zw{$R;jqBLFZU8kAf;s>i+F>Ov1m4RTiYct4ubrl85hf~Mk$mQMi$!8P)C1wGXRN^0 zR3lZzl+n0w9g7q`@d+MwNIr{fQV-HSXRcgEmc*R=E--sqIQ1l6JHuNOmM4G)eaMWC z^jWwZYjk3|f=mv($%9XUmF1{DD!UCB8)cizrL`27C-Sv=_>1NVQZOmxCdC#6EvxDga?9e@vXIV~;xKBBe|HEU{CjxMPj{(!E zAJL+vs6!>%UUc|m5&2|Y9M?8VUY&62WZ4Y#U6Cpbka9YY9fLh@e0XcMJb%LbS^6tyWorAn~(w>6~Irz@e=kr;8xJE z=k6O=Z^(v6IuO(v%UlDGJR~t4d~hRlh~&vmIYxy_VJ=J;bJNG9RMucK&^ydhA1jDq9apC2R@6h1 zt*^-J8df!qn_d=o@KZm3N_vX#rtocd{o*|3?Mq|jrR@^~d5h~wP{$>)e&|@S1%M$I zEo+^XxtNvLVFf_;nE>)YkJFqBWS|}3M2IHQR8d0-ylx)}t6bku>jixGAj2q=VvXQ>BzZ+KwxOF0I@yi6kVubRiHKPN(17F1v$DP+!e%KBY1F2S3ORr!;&lAV3vEqAn*0x}T?%>b;1tgxD-k#HoB3WGdtk zbA9B&rxpmyoXnYlAyPj4*n=W1xR5`fe8;m+O-ZH6dF4IBKBm%yZcLN`%sU&8W#e-r zI~kylBZ@}8eWb+VQv`AeiINcFiMDa#?L@X_LFn^?qw(_%Yb}aTu85Cn#F@>rZ)QvF zxozXhBU3C+v*m7!tcNbI>#lusm_Pe~UzpOctfe*R_07w36h&Q?b8mWr~Y2&b5*u zZRqud`7BPSahA`bWQ~ooP(Qt!Hj*~2p<|J@oN8%+)4oAdOn4(vPlQkpA_S!ba1ECj zNrX8NL|wyJ0f9`S3#LTwKn$RHwTI#mmC+0c(3F7DAzt>`Q9tkp4My8-ijsQv>8p{; zM)2T@sL#8Gu{}?{D7>FmM5%t}IWy~9M%7hWz3T$ex$7>ts%F}v>5bxh_ue~DW-xo) z{uB4I2(#b!juZoCr@8E%`;>rcUzN>m+{3I{huJNaFB1b#1)hs);LCO_jc&O22+NSjkSW(fD-} znmgiDApqb&-nta?M+D{8M9ELxOR5(>0r@krKtz@&_~(ql&SYu%~rVbLuUQ572`X3^a}+4qpVF2hdkw@yP>sFuPPW6YZ$%95rk4k~!sFHDkP$6%oH60W*|Inh}p? zN-`z^(lYF8oCcgqNwlWK$=;3mr_oVlhdK?3mrcYpL=m|9T@%V2(<%_+t3b#L)Tm$o zn*1NLItHfsweo9nli*oQaBxa0!c`Phod)bEt1{ReOn{|@-srEG9M_@Ia|(G{1>(?>4q-od-BGx( zQ};33Y6`=U)+sk1KhW6Fecnc-Rl$YR>a*tpU~C)bAUzhbzH^MqCFvWEA6RpbFl+VN zO=<-aLZNbV>cDYVcOAgw)N8p_wR9*(JQ<)@&>nA~8eXW9uK+prCjC?Q$c0( z(4tsOPGI^CId_Vhp<_z^aUw-lC)mPZ0A%V8S5lIukA+AqQo!;#tvSatPjWMqjBPg= z?Yh-1Oj4j1BHAql9$W|1r9mHZl#|a}3a}4*hC9!~V+8^9nQ2X#f=R)~5I#j+ zL8?%_$Hi}&frBe5Nt5-IX4CcRVz*~ysAcoyHn-#`wOf1+v+Qabx2`DTH||o+dw~!bTPF4{=!YwEmOn#h|XN=H-@H-o9Ha7pt^;N zOirO2V8c|ml2akhZ|h(IAFLaokijg7S{(@&7}5|g29K!xjSVH3ymBvRPMQDaM`mwD z2&j_MAunIjBF|U;kMcKBYc(Vt=6<7{?dtA2&gL=M>XuY4m8Jfp-1KNyw{p4N*e@B9 z;J@80Z$2|5U2c{_Xy?}1-@Vp_@_?2?CVowoF&Ltu0A^86`!N1QlmRk^_O-i}M;@`{ z2b=DHQF-J=<&U)enl!NbJ1wnc!pXEOCYwUxfyv_2^v5R8?(F;ly%u~)#@EFSf}@E7 zt{+lW7PFsZLvL-ac}M)}8iZND#OhqGH6+C~BMkmISG{n>2z@hdLx_7F?yJX*bRWN2 z_~i(t^2qPw(_n`QdWEvs5<36z?+Y*CbL#8xT2`mL#0w%$8u@)H6%|b_=1aJb3i3tY zN5m8VJ{Cg$=|-%I!|E^b`e$mx->p`Xjcfp>w!p~3vXKpNhCawPKfGtuh8R%>vGTNf zshu!V>Hh(51hmtz4ik2sp%0QgKEy#%ENjHbBFLVIORh^qUEw(LF3C}8y?x-CYGIZ4 z*=H;ddD(i2t*uS(wkb_=DwY0z`bXje52fFKCy}^Dd4CmKDTE$pZ=P6j*IlR|)0j^s zwf_RmB`m$LL2!k2GT!Tg+Zc1nZ!7;Ecq=_=G8ETpUw*%2`(0{00Pah{L;u^PJvKY_Zsccc|l`T8Z1@ySy4T{0Q3`4)iL$UcF#A_qu!Uz3yCqYx5u7F8it_d)&g6 zoLm1!@s3I4@i=Km@i+K|^u_KyOIF!kZl^l`Io}XL`;myCatu^K1YOl*;${RL@XzF5 zB8A9a#jS3op$umbNb=NYLuN3JiJauQ&7P)e(ASkdG%0irS(>2A^_*MD+CMb*SV(L4 zhF~Me{GH8gr9$~KZzjHpou_c6KUeubIAmu!qq$0WUxn^H4-riCyfBaK1*)|mz4r?( zRa}PxDFO{Fjt@(smdp6OT&Wv>qXo^wQP30)4po#JDk zdzOqW2LTFZWmGEH$n)HC{o-u$vMpEX}C>N2g_E1EUj5RO%&PUV%*7t zqCN{L<$6OjCR8!tJ?PZyUdgHcaC#0%L3Ime-?AuAy=QehEVsU8VopoS;s(y)n(zEY zdHYtY!RWNS$d9=ml;QDt?bmu`o9tbTZRhw^|%-%dM>FFW*@sGi1M| ztGd^eyI-_8jRx_hkv@^xv1&ryG{Z81a8eFIfwJpBmJmi}i+F_GsEWeK9B+5nPRk&W zzS%j|$&xOoE1FJ4U3vrhvf)%h`-1#49J$D&%ODS}7PL^RYTyP;LS05xQ-pN{31y&= zgP_owenxqQtrOORAX5&O^bxFJ$Z{ioWnf2iLv(M`=H8|~(Wv+poa~{Ky-}%Ec_vMm zv-A|!Gh~&)Q&>umIECv5wny<$?`GV$Au1k>;vt;uiEcnU46UoGtWT0PZ0qFC1G(-D z**vpOvE(Rw1`kzLr7+whm5*({Zm6+Dr)w0xz;}z3l9WUm8hUU)!<@DVL#mIXssd3< z=*Q10Z>zv8N$eYU?-KV7-E%*t8O=8FgnTJ1??5u=ZX~EQflq?0V*vntCl5>J6;C)z z`zXlDqt}~z4R)67D|I@c)o`|>%Y))QQPPsaH?$8}$I)mJOL@I;{-&u+d@#PDq0#07 z@5S{sU>8WI-bmy)%z4Fz5V?5um6imRKD-o;#twWEDlJp5#Q;D!mv!LIsUZdLWvQZA zR7jcntZp!SL;Xhf2gv1FR%|fgj+e0LxR{<5RfJ;#)_Bg2RsNi_IWC4XaZT<_`vCW- ztQhW5Z@$$fUXeSShUmT))ZL?c!ZDwY9M3s~0&hR0>mV)(3^ACKTsejG1?YKXR z>sE*IJBP*U0QRqPQV1#i>3%V_G(Z2A{I2|^LT_%t*n_v!cQ>*Bvd|+|3q6uf3L%EM zsq_ooOYy`l`T0w`b4!}rPI=@Dja87ww@wSx>!RUggCf<`hB$_1n(hd z&}@m181~()ADH{23J&2u-g3APp!z~tZb^pvD@rlj#5!Xj5a}$oVo6bz7;ypGM|e`w z*~rclKVaRU2faYJ+4-aW=QV|m_Zn@03KuKZSKW6_so5M5V#Av2QQQwo&`qY4-uT$% z-IuIxef$q*q%>hGcGg$-!ipmF#QZyG5j+6w_?DLARMntno zmMkuR5FOxpU%6}Sa_Zahf;fQ+wPFH0uYb)_WQq~XMXyDYZ0@{Zk#+C$wd@VM!6^FW zpyEfGm=|o|5d6>qD0@b~aH+GTDBpuLGZu^a&qvK3N>_svOvt~(z;NS^2faqkJB_GZL&AHKt|isDrN-K4x(_tq*I9!)11@(|y>6 zyjP+#Qs7(A5vYg5~wzx;y$PKKHnSPx|fw$je5_I?FQxLK0teHK5(a3nNNMg?ilm)>#1nO z*Ep?zsdhX7X|QaK)p_VK_an-!cBj+KHoa)DTxxMGnB%nKhb=D4<#aC&+vbwY2hE{) z3grd29wv1;g`ZOyp(P$P9H}e^tleH8#8(&T1`!QL0c7ehQ*nd%fOBhwB@bdy^wVGh z5D?%0LivGSZ*>01W&EWpY8<8ef!^~2htZ%{e)3B`=6=tL)jg`hraG-_Ew1@aYmdbx zjJMnEPGwBuI!koc2rJq+GWdEUdQgklMy;-w#KV9iZynOI^aqaWF zl_a}U+54{xM>?&8Lo&6CS5>YBBCu^7mv^d z0OYC{R2fm^BSwtyeJm~xmUf69ikuZhzd%<z*Y4kaCq1Y!2kX~5~*9#P&3 zu{*yKnZ%CHylXbDYziyCEEd2Yzj?RLf7Gx0=4a3 zd=6WCp3cA5uUo;+KUWT1Z8sX_C7bA$>x&-+&6p2(pf?z(o6H_WbY2>wG_qO9uwSra zsZY#on{Kh z74lL77})JRkwkIa69JTHIctRY<)}kSbQ~vqwT+27PeUCx$Rk}}B>|})K%=$oS~|hf zfRlEube;329osFsx|!QAopWnf*{#kguIGz3)gn2b(K3D08_Dpkb4qWWbxBX#YlRh) zTNl;N((9XJ9W~>sY6@MG^GaH4JIlxE-%Jqu7+{vk_P^kll`P< zAXEoT7qS;*-&=}#GXkoT1LUkzSH&?7130FSyTt1F(mU^unxkYJu{!DNa zxzH1IER|TjROIwCL#3reDQOx!s%*vvVJ4h8hopZfMxFMct&EUq#%t!FMs<)M5)mC1 zBcx)>_(^c_Ni}eAsR}041VdyprEiJEzU2?Nx^U1<2&=WLqayQlVM6dJmznmjDoCe@{yx#Rx@90py$%&oxlo_!xr`{Ahq!c z+lJ~tvX*CW4{l`5X%E+k_8ECDp*BMmP(o*J4WV~Lorkr?kOn3+Si!AlY6`Y>@b|Me z03Y-6%bB@8fxLjDpiz_#8{FmD$9xnHJEWkA!$FGfY>Z$bASZzaVz_8RK-rC~EaXH& zd0FJ~i(2a2J3DG8rN4fbN`Dw=>e?}}y~^*5+w9TUyw!HWGrMB_6^G8>b$6jselJ7v zO=tU@zFmJ9yMF4{=?x3cROiO_o#)S~vFmkPbdqJqLSO!MtJfX=o>0AYD|=Yym+fYY zvw6YO>8*qFeX#D0+yi>3?w?QRMpV!BdCl=9>i%kO{eJv84IyPJfAU*rs{O#oYRYwI zY!BiCNWM>k4wnp_xmwnoe16|HWUr>M5Hwa_1%UQw*|yRCd2P+Mrw z7UW04+k*SQWXAGH2|nueaA_DRo8jKVA&aX7$cwx^vQ0wm(IR4IATKnvoBM1Hv96JA znW_9$(pyESFPXs>uI{V~xZL?Boxu=rhC6C{{COp@KxEg9g}0A)OfR`S*=&g09F8hc z%(g`O&nlD_Z;yxC7R}shb^Eo^(it&-VQXn^k;mn3t%#RJTb<#B$*qDA%@ZzzHyA4q z1dD{}6E{c4py8&62x&g6^D%J$&~i;1M#d`ScDY9Lbd6}(GrkcZZN(n= ziXpjQBmw-kM8=3$mr>t4Fc7$554RBeNLmKEq8j@kFL1|K0G}XuthTYfp`LO(Q4mNi zt0$-CSU3caK<+n0Sfe36&cNR5;*>!f@2aDOuOL<2?x8B~2yBDLFhKl57BhY^EAVHv zuj0)G4j2#$o*F+s{cP9Nne00g;?b}{J01yn++H?TXC4&^PnZxY8D-X;6hw0{QD5M3 z7pw_-E-&_LnQ~b&DR^AQ@#R+`b>RnBRg5#b-GCRrT8Lc@XmNMia?Z56#7uoi7cos` zVNXF#UC`qR*3}ev9-lCQLsn1Fn(%h^X|9^^FL%@;D&&FUy1Mr!DT~>?llCgtmsaN6 zW2{*DhhMN2G5@B+^`5d(CG3McOUpb@7z(UjXK5_ha#>3-7Rzs*KCUjn%pQ~2bbDRh z?e%H#J98^qWSdQHsaSaI;d$k)blh4#50Q|iKmM_Asc&uLPcPcnTo8*DH1l1sm2Fl2 zTx1vg4C!*CPB^6LbG1r*b^urD&sZyl#>Wz1-0aa@t+`F}5SP=jCQ#^z4Cb%CHd;rR zxsJN<8M-Cgc?pb;1dXSLXd=P~3_{mW>saW8G29@C)$&ZhI&Fv#5kzqk^$C$N%**OT zbUIQ<#Oqwyu}6#wQ6(P$`A;9A;tO$~*XxV3Ip>@+(7Zu;e%&e-TD-Ur$&uM&y}4?1 z13P8_MsE4y#g(HQ;L|;43CLR2qrv!uj(C1SeBu-cDnhz7TF2F0S!M+m=1s8E9(wb$Z?C#>U`WOP#S~;=;AFqIGrA zS;w$T1cL_gN3Tzu`1+*u!uPkgbZI>vZCA_Y59wIvcI$8~Sz#FeJF`taxOSfMpGgnR z#?!H`hq~w`a}-Nsd(>aY4l37&1#daqLppmkfAGIyJ&U7vk;j=dERC*OxSSGCPo^0i z^JJAWtbx%*VZQOxVC*B0+n8qTPWU|gJ}M1}KQo!qAG0o#(dhlC%C3M?=FcdxkG zsZ6aZrj!ooLYu9Ut+IOt&SB}VxgEAj;ewEOExGt))+>_#sVwm12a$kq$}I>Uq`UFr z!;Oqzfxk9CYlt(5BjoN)9BX#^3&-|)ik@;J@A;l*knr06bdgJJ)H%Le=u%cg+;)ea zav~G9GQhs3|84FB1-JaWVw2hNE2ezYYPFPzv(1roTu{Oh2-xf`Cj8uf)$r+}>QkVz zAfIPgA2q6_A#2`5-X&TmLE-pVrd%ErjF}nDh(gd5Dw?9=aM*4`NIVqwg3V@MKl%3q zdw)N9gWYNqGUwvH%=wb34wiH~ow0N(=0tA$H{cIuoi}G#7DYhxED1TyOm3KBOzr~GMh&Oh#eE41p$~)4pls_r2GOO|r z;U3Y)FtB&u3$(! z9(5t>d~doKPbo=(4`9hH%=vOw}52Y^aiIep#P*W+XBbeQ~`{CWY z9~K_wJ9$`spn?17r_8_Hc`0C3@ZdYHuv}+gb&cU+ZfKgHDi;V1%anwYSk@yL*~t<9 zU*ciq<$mGO^o(AH)KRC$F?Y$A$=`rJf+7_sXx8F8UZ}T86%Nv0Me_)20H%)%oLGqr z?vosn!G*ct(Z~aykuW4amVu3c@10A_F$|C*5ejwa&ne$TV+mr73Yl1~-;szHTQPQ;DBZAh$tCZ&r&QD^zf(RauSz-#mo~P(^VZnJ(gk{(rJ`iPE6=X2nmi}z z&I@Use-Ik`JzD$Yf%$Spd3Zp%^|Qk^k44rAhKMI%5DSW%N(%QJXS>*_+gj~RxM%G2 zkYmmqhtu5R*s!%C|Kf>DQhNO@!X?3oL0?^?GZqK(BL-bTzFr?0a0XUS=yZ>+79Dzb zaU#p~INC6WQ0r!ibzb4totd3@ef{h|ZwMWL~B(sfU`C&VjmyT2kf!DFc^E`09w za7k^GNw(do^xS2Z1Gefr{_|*Yq3ue8qkQwPl)oQX7Avol^xhIJ(`%iUb&oRfaeq;f zG@6y>(rDq<@+z-;ofBDJ#$RAwI-zEfyJ!w;_5`%D8=9*;x67}CflJoqrA1vlPg=iT zYreHL(|K^1&N%Bw^$p1=^sNF>(+4>W*j&B+jNPZ5UcwA@GU%=m*4!@Cs>W|qOUaq9INDU$q*nDoUyd^&G zvQ*8I1>@Rg&#t@WrW>|wesIdVp5n?CYbNhpR$o6WGVY-Ac0u9ThKA?_aoW^}8IlvS zaeMYW6AFckaU%cYox_I;3yX`#l_V$BE!(pDIq8zNY176wI8EA{Hf|Ut+Tq5n`lxBR z54MQ4+r&LZ9Z|R_P&B=|7rvEVK!4iQzz%Ym5}fHB%MjuCf70g*iS*8a5BCT+i5CpK zE8Kzl6Kw)_C-24EZ14wa1Qy&9T(2eXEUjD0?19}(-jpgkhfsbnr07o4M?#E5OT`jo z)JZrfXpy|u;T+IVL_S2IVi=?}Gt_6HrDDGe`FtTSJ09|SL%xBNWvwj>T3e-A$;xT1 z3tA7hmY21%sZ~kg+Z$2?D^nXM>&zD2l;v+MpQ5vvb?gZJ-da}PDi!$XJ?g(#TFaC< z<*lvd?Av9nuJoF!9^fYS?7<5e76E4=sj6txp@%p;9bHbVmmc7)l4R6}Z>+@pd4! zgLXrR2Cb$aaip$vF_3XOp@kE_c;Oh7zygRIjuB)Jic{iy+>VtLzv~cM7HjY!TnDef zM`(!!mB&|TNq~J>{ct>{t_WB@DJa8AnvfWcPOHF4B0fV`8XI7e#$)O~E!JmG0~Q$2 zE4&h4Qwz%Nq7AeJ)wP3|!LdEH?{$NQ-Xa4Vt=c>(dZLJ{T-yphUC+AMl2)dXd4$2n@< zh;u4h1Kq^Gk9)Cb@;BqPXd!CU%!@PaTqp}Sn+!dWYmWgg-)kT+A_)KO2pVWFppCC8{udReln3=v)G-(Y24E>@>WZw`B z4y|mEwSs6Za~e#K8O@?qhXja{zDc%-Hu&0!0y7E{RAPE&w+fAJub}h$qJfw`wmjdl zCg$`Riwi3jxTd=+CYRLZ7u=n6B}>Zvvay)K`;-~23mk=hPa;%TY_K>5GrT~GMX}g@ zS;W6;oUgoGbh?cfkM5{6Ng#aALLIV##@rWJ&5}^x6(5&aUovJQ@T!VeHZHb-)i4=@ z!G>aI&}py=-k8(wb{U(_DQ#)%OpG?gL*cM!Wma3j9+Xxy7t^9D%qE&FT4fH?1NKU9 z6qwzJ3}EPPLAllGx8()x1;%1sxjgy;w|nee+e-zh@{+1}YZ3el+UFFOcs=8a^&&Dl z*48s|e4Yz2=SjJ+)MF6!du;|$(v5+dYD|%>qDT-;23Fwm7P7Ju$!0bCm^C2leKt6i zIGEYsFj%!HiKs1-ToSlXxZoiDo!RcP86<-M-x#e3Os3X=+0 zqxXA?#^&cEc4pjab4c=CX|Pq5inf-TDu0LGt`}s3uHJ5&64Ps|@+SBSm}`5;vu<&~ z84a1lGDkpwOAE8Pf22n$YS9R5p91sk(iw30=JQo$@T z>BRHqJfGJKPhzL!ni{n0oQ47~hA}!RKa|H@fKjn(U*aB?hx(bQTwPhTXDg zv6X54X0OTCVRaa^d3tTgDrzh0$Hg7rub*0M@Y}rwlqq~oLx=mi`pwUsv#Z?03W8-v zJC^U3~LdK}s;F&3A?v_kvTRKuVoAQK@u28A#pxjIYSWDaf5(C@%zFB>>9h84n|R6OR@*z`VPEWu>#$mw{EIj58TXHyKWZ)d z=-p|57SCtw`nz4l^4-Gg41vV39KhFt;zuD^BYPisS;P`i#&s;&Rj@TtYf}8Eny?BNODM%L4^jh1 z1g=Q(I-y_oN;k!u7tN^YDal$KNks>f`8u)8C*X+mu3g4V<5ctc&|>SouJ z)fUv&{pjX18{R&;uV+T*b;`-_ZL)V|PMAz5?ANt(8!G%JzfNbj2OKl};bMQX zvT<=$(b!F$ZA)6C))KGPT^g?oRaS;tE0^w%PTFzk=-XZPP2Xekb)7SM_NgbzNjgCr zh?)w>4KHAQkH90X1Fe8;eb7;n=Q|;kaHRp(8M>CWv^F$qjaX+ST+(U50}O`Cz(u7Y zz{K~Wa=s_sr6)4nFLrz70$&oNCn&qI(P;H z(uow=eq?O>Bn|QU1GHt=3Mo3_Hd4_#bW@DVM0_%%P06772sr2*G zh)GIa0zCchfz7-muPUQyFCJ2Q`So7FY_OMx%8}x8)C1g0__VhJ4gkyzx7<_-V5z*m zk{lW(%4``7D%GV6+WaN0EhYZ81*589WRVt)ATaN}8xrU-eM@e8^Zhq(TcYmRCdsb6WwBY6w;nTwjE^aAS#1{OEx4Z=9(&9n zOLkU*A6dy`hGN5Ga2&K*SV`tb!8G(5ye(mqyOo#W!KGdHnZ@$iGA&%ZSZ%j#bC^H- z%wor{tXBQiY*v3&UdFF>%V(dNd7r1`?;{4ni4m%a5?v#*rsWh687`wdn=8-e-cZ8X zWS%V?K7%*`X3mEVO;0F4d#vZDrx2pG?+_Nu*fQnv{@W=v>$Yc^^J^6jXL!Mq!zXUM z@PbiAR^4Avn}#R)?rBxN{mXp-5Zv|S7yfz4%Pjx)uQA_?d$hu+QAIOf*>>ZJ!*$Lg zYboZmsv}nI#O2f*dXeK~|*#Od&10J;d%4VBg!@lh zdl8z*V(NqHYn0yzn#;fYT}<#(@Y&bxktS=dzzHM=RgUx36$#)51PFSvHip#^8cfOO zh9deCS0H3@1R8KHv`W&pP^?AJHY6N)YVoOn(GQshifT|gXhRDbq!NCJP-?Jn#ZGtMs>{Vp4HRgyEZZSs*V=lb)E zk&QiHZPkjBt&BO%URk#5-SswmK|^_IzD3YF4Mth8>a=;S7N^6L_^&w$cM$wNczUhF zs&^KbwMSd4C2-|})@@{=c&%3aEctFIv8rfPsizHv*nf^}ixuWcvfFI-ESOjgeU(2l zvD}uYm0hAVYTN|B-&lHWFVlY2?v=GJ@SBoo^3-m~FKAs3EB|*dTaa zrhvfAvZE;6T)#MGYA>!XG6+(jd`WxH#YP)UI`}8ZHUqhqYEFGi`>8w)I%cAJ)reMI z2g|o6Iw%v3HF^O`g71 zifjeY1bJNY7c@Y=#7psN^dzp~o%l!o+Zjl-R4BI{XLaw^l1O8Sve_>tRP}>mD=a;m%Ke#Y| zw7DpM*FOe|C)uolaPh=Y@HR_O29Q~iRW>bK(_K>h^zw6;1`8fzLKRa~jGr3I(4k+iX{3Y{$ zmreXdx=eZRmn+%P0ruy@UnrQO&>s^2a0z%dMCmcBNbIUs1JwvtU(jQ#0ObPEVFh0U z9m{kqL*bODlA(~3tPpcRqS~k#5?Gw08n-r{ihYPJ4pT|2j5%f8dKy)7hK3-gS|ca;CIKrD~FdEDyM zTPmjRom=gW%$#1azn6)E=qPBKx@}Uv!!@d9?ARKc{gO_td*am1TfW+n*V%Q>qPdeA z;6Jb=p!1DQG#3fJnU~IKD|BN1h&NoN^R-mPgc{h&Jn$|4E9{-*q3z~zOPtGsR*)E3 zsN@{<7lnJ6%DhN_-8OrSGLZgg_BQDkC(E*b+h&V1XK!P{@$+{o|74b(^T)GEr{DlQ z1)FOoOqjQGXAZBK2W|-~Cy%=U#UHMSg=E0IX9=2;Qkf*6*#wnp643iUFMvw8_6)f| zANagLs+@64c|v(vRj)xV*+3J`c}?;%2RG+DYgsAZClzogjVbH4xN&PH^C_SUmO4ICO8rr>ThLnl)?(-CF&D(md*C#8;e*#D*N zy#wQ@uJ+-*Gqb(-U2W59t9n_jR$Z%NNtPvB#Z|U!x%b}t8ryVJj2mFE0Mkn-rWgzn zHH4l3N#I9u5+H#<=*6~n_?|l}*|JGu-akH<*_k_c`n~6#d(Ly7)APzhA6!r52OlO` z)!R!x+zCRU3*Jv#kwEUD_q{e&sY{F0OsyL+UCMu$Ncecnb5eSxpu<-P%s}wgQ7Z#A z`qICGO%&q{EhSPA!C*|IItNq+;V%ZHSjjIudE6(uK=DQTg8J$*U3`fxsg;fGFcT*A9B( zAfw@sNQe`{T-wBNsVSW>U7_=5Akv4gr;yt&Ob=*ehg57HTG5x#6up>zTe!rN{ITEm zX$*g6B?`IP`svWGL4!iFR-0x;UX|3(F~SL@O#g5BV^0FJJhP5S6uN{}*3@%)?IfL{ zKDJp3!GW<+dD*%|_=-J&!kPY8G5+Ku#y+_V&1LxWU!a zn>P{QQ%;j#G}2FA9FVUfeerm{*Jfw*Ha%mvdGq6OsfE=>a{M_FEo+eu_?P+J1$zqk zKLxW25KM!q0C|HPCvQ+FE2s9_&F%5Qeg=t&XaQiS(RR$>ksLHzVZ;}oS*2}|K7S1y zlBZWOeZ^2%WWj9p%qsQqQQ@H_MgZRetXTYIbyv?lrP8q#`EA-5|58jgwlcp}8@twJ zuIh;89GrhJ%~IJJ%ef(%+5sR|iEJFL9KG3WsT^0CbHn_@wt)dsGM|5m`KhC7y0_wX zb6UmtlH6Mt9JX2M$}LfOdlgO^C1oYD4to0NA)B>wTuE-<{61PGmUB}~GNvMTq_%{A zu2jaKoKGq!b-}Q)m}2NLW2bL{4jX8+0_+OB(p1byd}RpTgV4dhLDbBUfe40D+8!iD z)#6y7nhXb{u%LX%cs@F#u5L!&Z}U}IiqbF}50}O=2l~UMRe}76L#$KdG}_E2v(1P# zmMDESXJb}Q9VbV8Cd(H8h!N@Q(`7*!-wLA#Gdr`qG#nUXPhXM77-2D2h{X#07@7O5 zW9W0?qYlPKh|!vxL>;2(qUB%_zbhUS6x5z&~WM zaJ|^g^)ko!=SHjg>$8I?Vrke@}T) zc0iX3n42gOdsu@Hq(#US=o)+8~vUE!3d^ zb;L|#N{+9KNjaUy#|DKpbUOBJjW%Q|)77&&Z*=a`u9EywGiOK27fz0?&Zu4x&+16a zGi6szDh_nmqsz!mm+TnTTG%+EFy1{mUf9I{t8d50<^D-6+lfBiW6rbedAYf!^{waa z1^#?%o~i&&P=9GpMd_4^OnqAMRQ5o{&dr@6Z^i7qxpO;L# z0-r%lm;~c(OJFZ9#v6nXgVcv)x1iNhHf8KX1UEIp4YpNWUI6a0H65j8on6a1$lhfg zbd{~CE*4+1Z8QJd-`vmtcGI>?#0BL$rgqi-L?&LyIkaT5rKhxQ@#41D#e{!;6>0i3 zK4Iz({)_H-ygPoPH&VFWpI1FW{KsW$*DhPdzYQ_<_9|f=T17MdUs*Pxx-hUk`Jpo1 zqMZ32^WIFQC0*Hej5)?smbSO!2Joj$SnH{t=k_|+|G%-F6DD+yeRqQ^;F(=9bw}(* z3AtUPWjl+i7hktzQCkbYTXUd%2eTbF5bsV-tIyd!&pshJY2@QC9UVEUqhr*_qc1&9 zSD2c-rs@gK`MgqT@hWG|RC+DSHhe35q``TY1@q=CWEWi|T7~a4__i4IZ1igSx|pKV zX{3ZNm{JwkbBEj^`s859h@lmpH36Rro+F7A6p8dRQST&OaIiAt>!2M_KSMG5h}5i+ z)?P`-m2sI&YL*smBxJ)!#Vy6fEligyE6e51%5qW`(g9F<9^1iw>dR@4R0j7S?|O|i z6&5u&7x^o-f0ygoX~%EymqnUGUg;ju&-?d@e%`~crDrK7mq;}hDOIxIZb^^u3X)O70!xodnY229R+}Mslt$WXPe9-ak7UU1^K?}eLgx)uJ)3kG9_@Q?u z=u`BjrD7Baomg)L!kF&jf|X+{2OfCv6lumv@;CPnJWH-5&8HrGU|{>RC}B(2P{>m9 z;BS69^&nC3CjmCfW)|K3&3E@)Tz(V(!-J7?6mS{_Q<{dNRJ9bDcGHqcTdACKGX= zz)2^^I7f4>xnL#9#PieP)@w(6Ik@rltT_@jVmpezKw#@JB%fJtekJ)iY2HY#ef8B> zI~jBGU!<9Tj22wSn6Rgb2ZQED?vsH`<|y_p=dVPaCgvz{zXImXfzDex52p%Gui|co z`XjY9`tUvCxKsMVh4_|XYdR{{ATp);SQO2Q5w?A)jb9i?EUnROhche6e?PdwY`K54 z$!LvD*z{(kZu9LAY;LK4{LNU^X4X3V4KfXhZp2aRNk?Kb{Y@4U)l=-~@@bOfj?CAL z%zSM62Oh&J`RVNUs}N=WESJ6t@p6IanCKw*Dz90 zzfg3qTMCB)HiPt0sVY$oUjyVgobVJ6MF&SZG(x?=5H5@c!XQ9rD~v?wRv2P&SO_8| zgyF$0w#GCd56P1P?UjYozyum|Gd0AF(V|*b1DhyR7+jDJ!Yn-@?ucHS#H>=PDMLd5 z3ORzVNp~6}D2f*olUPHpU9MEqXT)FCE7IUEpokGuYH7&TP^ul z<;U_B4cX$(>YP}X$*i!cir8?jk5q~EQjJ6*m2*;Unjv4aWwI{ZP~&QnsnXLeD$9?X zoH?2H42@5jEt4{tV+M|BN^|sV_K%^XC31($YG>AOtcvp|3KowfH?h95NGZq{#?(6b z5xo*cuFCkPN0G^{C%}afW*VE{xORGT>4I35J659$9K83~-suc{l;VKYrE=Q?7H?Wj zW-Ho+Lg#6*sLQI%Oj@*O%e5vhZJ9-N|wGi!70;C^p1YRop%u*r{UGpyHsjMfgg9 zAAvrHLx8-d?T8`_sh%ew6{)i;W*VGbfxcWE6Pj#naIVQ+DK@%Sv}}uuWlF7-$TAkr zD9W6WEmh?hP1b0>%~hDDk?XCj7M#F3jZx|FDP;<=!b-Xo)?BwYae?14a?HeKv6Y7z zrqxy7ShjD?hV-=2wM`~pe!9~Y-Sh_kFa8bwleZJ0iq27;`9@8PugdMuk!>r>xhLD~ zA6MTM3l$kPmW)Eo)=Y|YC(CkPhg7vAU!zs1a%?7<)WoPc1+ZF-R-@HRI2Fma1*5IzN;Du^)w?dbKPr)`G5R&(aPTuXWyjTH!U9(cPV56Q`qL5 z)Ny^#HQJ%Jjc8u8q^zwyV<$x#aYx=qbI4&JM@Y;p;iYALbz~H3|c3L!i>fyp%1b|rd1?sD#?Ock6j(;#y z;b0%F6@!}*^@_xZXAJ1Y#L9*scCAFL$0rP-7BwUe+L(l6Y1BSC7vS1-$`dNaz(%hV z(~FC8(22}?<_aLnO*z@p2Clxo!^U}7NvnCAM&H25=Ey>DV5o>j@~x-hq>vWS&$Ff`1~`F34u` z7#IyIK>P6$i-EA=_Ptb!s>KB#s_F3 zz>sF9s7zec;gl3JKvy5vs;ycTYt^Qq8**?~?*4mL^4foLvQLvG9_DIK@}Hh1wQR*> zWYbB#y05Owt{R;ul|ytGm_VV+FV({+kvR4HA0*!*aRFBXZc#d*CSF*w(9BO2Vyod~ zMmx|7@rzBO31|sxMHh+oi*6S^D(XjjNU88CdoOwxG9sO2MT3$>b61(EUWiJkUZ{|GU01Mb!-7UOHv^Owfh+I7pTk4D{7a1&vN$xEGX=;bgkN@AO|6MD$;G2|LcW zzZXcRWP$@N>6vWNw`8mtkrXZ1ht%7maA_E~(HlOMNKjiiT@Yb;?kfKuONZ4xZv}D% z0bHz)hsFp!5*8fcyHiYDjc5#Hz)~O!t`r?Y%=B+XuZuo}CiXMY!g`ob5MTHU>nWxr z6cPwehVY%iIQ)OwX3x_;&ewj<-A~&SMe)ITBB1!r-T!~x{=c@*^POKDr^dBYBDy5~ zDXOD0Oh^B1E%9qBo~g&6!46A$^xw{W<^W-hHsd&Lfd7Yu1Wwfxg3VBZC4c<%q5L=J zTYd0!g<%{|=UqKTDVS2+In0?GJ?~)y|A)H6P6l0s0nSXv^^1Fj*&nR0nB3CIdIa&M9q5HZgfG=`ggFTUDxl&FsyqnJF5&<-)ovMv}BtQ*ogQ^sCGgWY6RqLioEZa6#@^_7GYu(-`EXbv6h~cq}n!4^snm0!;tZcb{C6*%(uAH~Fz2)H2HSH}oEQMV*ju^Xs$Rir73*8Jx zWjf--jHyS3V$Jlgn3l`r{d{2HW!k0KXyEy)6W`u&!?*Zs zf~`e#It~nec`?lNpau zeqc!YEjbpZKbY4;dYDb0F6VikNs4@xdPLG8s83(%V@2UQ4H3y?AW^EL*B9c(WmLWn z#i7yIaqJR92f}@bsV+o+Lqps2zQmw^2559}W$*?89mTvBcPR|KSb$X*?Iuq4@Qe6G z;cyJYDls@tx{`XrE4cPC?CJ*|vdizQF;br&U zdv9{r(Av6NiQ@3GC!c&WS;hDIt98dUn&aRmW9YB0+E4m|aoywODlGdIihf-@$S-?b z7f;y>d6`IzJTI`Dc;K_hL(V%92uHjuWpE9$(C#9PHv@BV;1lTNTIw}f0^TApxWI5i zk@h|>HicA9bT{~%ywXx0L81fQ%OvE0;kKGJ`uAt?NB@*0;@2*HbvBb+vhq|33BUR~ z{*S~ydh%2J0RJzhbHc@|YwlUGs<3NCqA_^`ckd?tkMp~qO+FfrfqqZ+=QoJ);twv- zyO*vny8XygBipX}v$KB7*T_9pUI4}7t5`Hfk{%gV-N z>G@|K>z>L#@Xqpi>8&FarX3I5bHPQ2f142|OE#3&5e2pF3iB+1yOQ$xhoA$TMz090 z0aTZ#`acXTboPp2e&`uWVkVJ~M*L-9s-PERwq+FvdqtAGD_^?u%9oP6cF%J-=C##& zJO^6Mou>3PP4n0{9@?_?p@+6^d1xR1{V{%&>X{wuAGd!(c8-~Z?xNSVd%F4u*R0vQ*v!7=E5@`h=U=>SWqEn@)=@aEoqZ~kEq{}c(VC2s*%!uQSEwd=(zc8S2M{_}Xrm%yQ`VUf+n9C;KxC?dG; z;TOW!!sN-~z-*ZXjcp!H7#Rxziw8vxvoqF6-vB660wE*jyKXVfd@4mqVh|-UHV~sg zLU9Q+dJEg2W%w!R`%0-+p23XHIdV@tx|8O**re^8Go(IhbS}gVX~AgxL0Sf zun*Somp`E*vpi0YF7}#dA=-Ds2_{&V=CtcT5k6=aCq19HU z+DIJoDFF#hZMyY?Z3KpDq(RD~i3=stAr1xC(i!uY5OLIAtq{n6%OrBD!Z z9O&-J*(Ttm|^PN50$rgIt zRKPc8%Zx@@(w^FcD;7`~nqoAOS^^`JK=rB^|}#C<4D)YAHSrI7|^y`0aeZ-LD{gQCiSQc7H4^pQpfjJ&^U}n$wE}xb<;BkY6k;hRGVUC>!`LiYXdo{YpuBDia~?OJXRc zu~9>%=|ZUyrGCMdI8+Wm2C7$+Veu>6T=&!b&g-%q7IFHHrGL8{7z<~w?+gC-*X}Fu z*`@9c+lciKHjUl4D7=M#@cvi&te#Ad(zWxxLnL>u+33oC^&B4%X-qe+%#dfBTr$U8 zrQ`Fkc~_P?V)x0so76s{&$o^ol`jprJz26qLzOCX@;Q#6Grk9k!7LYzrkRrlTb=M> zsKERM4%0Z4+o1}GA#|A%4ni2#p-@mbGzeN0Z1}8jRN!zUg`ERQu)4gXqx_VGF2#9a z=P3(~%;7$Bh6j?z7_(A($|6-Vzk7?*ad#2rZ%Q4-@&4&cnQEzW++6-${w9g4_S11Y zW+VY*}LGZl!k7nif*X(!F%}289Zh z1VdX0^|TnJg~C3@7{zEw8!}RRqwfg{DJ>9L=}BO-(h;>nuF+_ST5cg(N|hR+xX4wD zz-kRr{GR&UgiLmfUe9PIrlm15xz#F{k+frWyHdfJ&5S}h)oNu_YO`6b>czH3A~%`j z5)IkLe`q!*Njr3(I}GNf2~j# zzsa=dWQdN|Ns>>Je-VXLDVM6rqQn-td`m*!`1;Fo#Y?ZtAyoeL{TE8*7vHPI1K+9D z-wmiepZ$QOfj@jEk@FU2F~8#nsnYNR*2FKhy?;dc|r6jZH2U%M8gqt8ZltYIZw< z%=r`jmfO(uQe%K%!&O7yp)9!~0JUNelN63qg&4vAxy4bK>0s6362?g0B?s5OhD7DP z{Ee@zB?r&5eU$W(8Lti1e~lH5AA45{lXKVDfxCunkgQ=FTo&piQuXj7U_mg7LCzbI zAKQo6+nJ)(qJ-#TNES$Z48W%)ixt2OM>h=jJFQx=Pl zIbotZ2~-~tehJtNcaU`o75_UGnMs2elOm9GV z@~PuAa;7-e;J2yON{^XXRR%fbR#3%wNAbAGNU{wPe3+3^x)T-IbkSbMB5sX1O5My_ z+p5+A4ae;eY=iXbl-WD%Y~U|;sYsdXqye#&VbXU}#B`*&rG*yE3<(K_y|xPeq*O&X zMOt`nt{jAHf;g(rM%EM?y7G{JICcU29ErcC2$47bf2(HlRbjos&FZOZeq8Wq~i@S3MI%PZZuOj!p@I zOgir)aESp?KQ-92_btN|;8)x?L3*!#dPoBGm-SIr)1mi2WJ~e^i4_yI2n_fD2>~eN z0-T-xn$Q1Te3Sqm5LJq(gA|4MGa`io#&c#+^=A?ZU_|MEw(@_9z626GF}oJZuKwU^ znR#Ynj3wikkcW>$YKYT+$ob?~A^{2Z2mTg^y=(E}F1w?Kv;k+zry)Q!SWLea28XlS zUl}q7Q;vpTA%g(a7|Q60!2zBMgi*jd4^>MC5rkf7wde%uo)C&Cy)P|6%Y=%0-Y-j_ z-N-nV@;0Q-L86@7bmWM~xNV!R#AFuhXUzi7u;EFEX~G0UNf11B#YV9M?GQO|$Sl$8qvnnLGaJoOopz6@XQ0Q(_@kz>J!Ph-f$E~?_ETyx z{&jEZ9D9~{=&cD%rJy)E?+7Slh~|YQyNJFPjhz3H$dTyu*E}+EOs9?|I0Mp}Cj060 z6Gb;spzZ(S`^RAKnEWfBteQq3L)KcUuOD*@gg|*gO(Eozf@uUHuCR|ly@i5+`8=&l zcZSaU#H3f2ri>_A*&~n0SgfSU{-(jhYBYa4x13+2)-sne7In?w@2`3zICBtZ`u1C# zIfyHeT!eBP`8UrkPfBoRmY!OHm4TvA7@BE^fgpc-r z|7QQ8t%OsB(&u(e=$<+G@jnk@5Cq>di*KyJEXn}uznyYS7~%aF$B;ofFk~c`BlWI0 z0L=vbIh7?5R+yCW-tre_GXEg|@Y7GT5v+a7KiEce7`(o^jEqj+%DwtD|1eP}Z)GDH z1FxEM%mc4xWUvvepa9mVC1mc0{%zX^-Xpt@e0bp_k37=zA(_iB;lJEQ82=Hno4+N`GH!^WLPs9NEE1i+{#sFqYk6=E*n zn~_lOWD!*|X*J;^xWyFpNiC0*9W?b-urrnOOt$or&u{0n?5QS1gx~e~k}0agtEaV% zBB6(FBeq+}$ye^!bje&@jjFya*47ry>8Pz8*|EHK{q1*bymE%d6I9f-7Pq&QWsj+? z8`-(EX2V^~K;G{*9R8Fj{&DM)$4f%lD{n5p?$}NI=eI~~{8t;Um}wfRsjV-GHe@w) zb~a>Pxpw^(({=tFRlF`zHX>EFi$1a-lLv7Fl*g4uR>e?$PT+_?9r05|))>GefZj=v z>le$6kkpV~BIN%SgH$LawV0Tfei{D3^z%FJex~!T&Sy@2{fyK3OgB?UHl+$)BB^w~ z?5tCj&=zQ7LtqsWUdcm|kd z@W=ELq(pWz>DAO-5u(xC(qY$niA?+R`~3SLxDYZ4^Y6d^XEN<2Ch^E%{7UO1ACPS) zJp4c|-}eb6wV+fOpOD^M!g)^cTj_g57%IlLf8%w|M5`|`#EJ^hBRK&GBTynhGErg$ z%>8K?4>euW;7%>D?0`Vg70P-74h4ZeA&)(Ri-M>yte{ka9Ck zF|iOgv zp4X9pKs7$+j{G21+;!5Y-#mi@cJS8{ivo9+a#UH(XaK^(%|zf}q@Xs6 z9L6G4VvJBbehi%1dXpH(AjJd5!${Oe%UqbPQ9&Fr1A_sQq8 zmvfbV!s;-SGk8jaasI`EW<(JbGP8!`t3Rr%iIctK#&$;nn_aFIf;)*$Ce}0E*WD30l;)ejBL-dS_}AfMe_CL&c8CNJ54rE{%Wv^yb~y?2-=u; z!POJ+M@za=uBOwR!4hx=izLS&hv@sIcFaXUfgw`KmqGJjuyk~yE3{|Oi379-ycn@r z=LNeB-f5IhB%;EIhrzCh_-I5xC_-Z!0%p8iN2qTpRL=yDICge8b7`%m)|>L!;;!Z>T8;(J#~3+=M3`52OReS z$MiJKt?n*z$w0>_F$a4kf0x{?Ez^vfP?h{@bXj@(n2K`Cta-E9DOH_UUqoJgNu|in z-1?AJ77Tfi1=5|{RmQ(zFI(7hYbBRCZn2ZI-Pv*3(fom@awjpS-p?cU&#D!_?KsVOl#=SjLRwtW-M>IG%fiM-^PA@&NpL3 zW#F~=9ln`M;G?372ep4uj~+FJ1pzBg=^sTL+zQwUEf-Ed=pWS#9MuAy9pwo{RSFbA zP$=87VoYVEI{ITSahSyz`84KWV?(&ANw>U@{QDsP?TztzGkEm;=1AG}2NSKWi3gv- zPq9KB%v8jC4*q4$jYQ3v`j-3Z$MCy&o5jmGOk2MF?ZX#Tc8~I9wJ*;@NB{1iMjSxL zVyRt53E-4?~IJ3Q6+*PkBRuQq7 ztoZ$+>=jy5y4eE*&UGV9fxIlvCYf%q7{v_Ca=9S6Oe+b5LoUVwQdYPmo~&j~ne`k} zMCTEjmQ~Qjs-c5EBk<6Bp+AolIErbXP5GUMyY89)Tue}z1GyKCamZss(wLvJ)=>6B zipH^0ZPg#t30ka$X(-CfuB*$=WbKi#BRAI(j(lF2Dq-#^4$+cOG5>=nbSMAOEmog5 zt)SY`DNi=@A3RIip1+@zy~!-SWOeL!`xCqXBim1>se%j;Nq&YNnI=j<>#9P6K6=%` zYl4(j3?S~X>n6YE|737!ZJHHJKq3 z+iyOp5oZrPe+jd7;O~R?kQyh81(`tg5q!DSJU2o$#lg-`VGh(BK4@MS=%|IyjR}@e zm@<|Ko^DVri$Kcx(ZPH8mlh);;Sz;bCms3L+Idf2+R<_8lk;XAX}pA{5$Az$42Rqo zEF{Kj4ie{U$&*7s#Nz_2kahAeQvSEAcPQ+#OXZAW+B_Wo2F}t{cPSE=Q(Pp?sJ?CX z(haX2NM+ZHgV&-L29~p)O$!}RBudvXIzcxFIn7y-aTo9dDP>zw%jeupu0F>RDi%Q# zA6|)n^c-I&5miH;KO;_vc0#`#MAHdU5)y>E?(p8=yo2w~jR0LVsvusdFrfqb0x|~g z4H7922sU9@gUCfggUq4`dL+Jr4E9o41V1nxKIy)5YY69+?9O>0H|PEwTUtg=xz0<7 zI*{xMs*$@y7cUCiZTUy@vhT{W+C7;iTI_|4l4<1H$~?c#mUlES>&`5@JtMnR>%)O* z%oAYsAU;D!#BRqav+v2a+kLs^*qNcL%=g<8Qfa2$4Dhk zgfql?=|IO?xb+y9J1qy_kBDrDi{|l;v6YhI5a2>MB!&K^K$fXBbX6hf3*LlGI4C(j zU@PL%B&^@Q$nL+=m$oR)cg>6~b@7Q4*DobSf~M z`AU^vzJB!;x2;=~8So493ff;NPH!l?3q?cM1L=hvFWx9cOAa5t3CfJHpwi!81h<}3 zmu8!y=|xE|-^cV*km4YBVBbLB@#7LvGX40OLKXuB^<0K$iS2=2;lt|S#*+gw8j|aa)czuI2xdhGacoSiDJx*#3fum z7y$Vno?!R`Q?_7r=awmC9z!Vw=_-E!PKJ3?7!j@V#7>pv$auPI{1J;Pbr{xcC_JmL z21HSj2-#eq`GsI&jnRglQl>FYL#GkUAwt0KX++kLYAqIRo;bGZYliu{YV5?#oA2Mk zd|lmzm5E)|Un4+~Y#y#LCGX!-zD}pntt&_9;^v7`-MX^P_irv+r;|?H%pM=EItkcJ zVJ@kM)uI~K2SDE3*t4+s4}2$MU{w zFdE~NmOja!;{Qgee+A0kM{bH6qsE3)3YA(hSuR(kDY_N!DQ(Jbg+lI-PnM?xuR~4I zy_)+BP6Ph!pG>PNP%RDl?5`^_DRORGWG_&N!(+E)D9OEf-!|Zc@tYnI=!NMuVE+WS z@T9oW*g$dy55$=rU&`rHE|feWoV#!EQU=3_q3h$0Qn*{;-ExRAz?X*wkM%O=n1u*} z2BZi84~DGbKujV9Q~|HZ8WS6(ppXa|1I%<7J3Nc|8^ph~3vrA0&iSh5!hK&x`M>gi zjefcBqUx{a>~)jI%T}%aVfCuZNF(#c8*lLUbBX^j;XT#-@+o%GaZ;~(t##9(Lz`M( zQ}It8pTwSec}JN4(}+-L1j!1cB_NdqoeDuVQLGD<2s8uje8J*yGja|dqtYSug;N71 z%`STOHkD{pdi}Tk0lLeJO1|^eJpX=gv{=l6sSRp82fKrtLomi!7pL2Fs0Z6!e+oY@ zBr`s<%EZsC537-U#u;Ropo97OKkoi7N0CI5=P%$dNb>qf`>uz8x~?XwBfHuo`ZH$< zI{1VmNRyeQ%7$fy<%cDRJ+rzy=-9T+5lsFc4k4GS74sM}TcOq$w~lHn4+P5FM#0%I z;mlRX;*>Zs{oI28L}#1lYa7U%IdF z7QW&rzwcqPU{n4reft36UV!ptpOLGBTyM();J8sGf0Iz-D0!Y%xjN9Y5Qlz7t_t88 z>_4j{|G@QVR;_Zxicz$_pyeReUQmQm>dYAqFt-@G4}ci>i>w`P2Jx;Esez94(7##O z3_>(okPh&moDY^ztiYgY#jKB&SlIbnAKZ$6(qLCRtTA5 zrq*+x)=xEuvRG%=+O=I{*Q^;k_{;yqTt8uC!<6JSYla2Uw;XXwSbN%Jnw5c-D0Nnk zZSP$E??;yV((@zBNh7SDguib^QGU9A#S!9|yEjnmU=%F#Nb{UI&B+$610GCHGz+@q zLA*2SztzISfmY>1GxF(;G5mPV2zDgkdx2Zl$R@64JXc?xJT;y)z5|7MH2*l5gH|l& zM)RY|gY7K0d@!0W~6 z31M6iAU3E5s%^0LXUn8_ zMgnP?yYe;2&ssp%ygXXwOm>Sa%1ikRWsXeJRvwnKLFRharR86!w;_?5#_c98n~UVm zK*2uAJ6l1Joi3A4&C;4x8b!-PjYg$h5&S5o4NYV+>_x2)H!y831AvbFv64TTG-d@c zx0#E~*?JPHb4V>r#~hP>A~W9S$nMc9e1_!HFNREtR;>)&zn1(knSFPi#HhEvPw`YV z2NLz~B!q8A^9iN2L?3k4QhY~zJwd~xLV;>}!~fGDAp{*$ehLIR45y~>MmZpSq0c1~ zH0newf**a@e<*lxeoNpNSBeqal33P$0w`dDhQud+hVsXXgyXO_=%*Kc2jXo1K%7bn zE`F-t>j`r2o)U1kTs(n8vqWm?pYR+sDx-`>68Q&vt=SZVu_Qx4^9$Bd=qS{>0@fyq zSVa5YYk7?a{!PZf%VZUPZ=bwB&TCrdBvr={O zKM#z%d+V%nM!!!1{1i!$bvqRMz&7&`zm+fLw?3p)>i2`Vnq$%!?g_&|$oY6Q-qnPAS{h|WoMQGBMMe1k*S?_c{%@vgA42w!^Wm~%0(y1{Fl z%Y#S~qbOd2ye$0isUH?4_&2!q9}C%0t@B#(j~_aID6CM7fkHU?<<{bpf;V1_WmEuV z2<4;5%fbeq`Wf8%kA+FJ&*IiW&ph+9a2T?o3PX`F*Whmz%2?4!5v?boOZ1Xf$hsqV z=XxO1JJCamp#w>zEHy+SS`>LQ0J!i{>jO*46on>)83FaaSCDiOjK&t}FKa-5z=YW? z<|cm8m>!eXFd4S!h_wrlGb9HU$+3nNTW9rD2e`UJ*&hCLvC`&AD_uB-|M8Zau>G7r680!! z`Cd}#Eg*3s-ZpwlIsen)n{qt-^ZrOEU8WM7{SlcZSTk+|mG5iu%)5kV&V%io#$vb` ziBvEEK)PB2U|be#lITznnR#F?fq=!FA6BVgh_Xn~!O>!Lv*5&qVNx(rf#zI@-eynu((-ZdJ@iP6wq~bCUzCjX?ccugz9$|$+`T@K{SfoC zzV@!i;dcL)fB43Nn9g%){T3qq%bWYQMkTeoGE5OFLg}02 z#P4uwiV<|f{CG$~gZWLt;dGvp#K2^F_ZQ;=pb5ZetFNXy14cb^fmfRJCu%J}+~<2sti294?w^EaF2fR8d9IKnIYVq6a1-h=Q}~ui zjcZ*z!)!}#VJ^@))=Zt#Z1tPn>0aek8D!n81r7ELv&Bp7vg=EdM|v$S>@%l?lZk~s zqdWa>knj(-LqB+<$H4z`foL!I7>mM@YA4& z342&yOzI0sK~ZWAP_hQ!5K$batq2+wGNnVDV~fte(JiS|4}oZbPR#|J9`&bLBT^qt zcY}$rFk!_Jv53_Krhn8Dic)$Wbh#kC2KGwv8HFi*DyCs@fS?yT_cnlbz;{dC#F^tk zNKRrA+}5WD3Dm~v`RkcmOG@*H|Z_p z@@kmHSczQfWK608S`v2~ZBCQ@SMm{kGt*+vHjhqm_%PkGM zS`NxAMu%J}~lbMa#jEuF!o|i6V)9h}i-0hea%kpJj z20Xk$R|>^8!fLFq$ek8X*kLz26i!QSw5c@hc}~sc5mU(OjO0V_z{O-i*T`KOsa3Bp zWsQnrq{X_SG&{;#U7kQJ;IVAH`qZ9>ui2VYl(S+57F(}*c+aV;g|c9v4=mbl29BcxKFHc9>nZjLfo}N`GEJW^`H#tXVltkvOpgG7D>J z^0I^BaLe2|Em_=;wTIwQyOTHZyu_Op9JqJEz6A^R5$39NC?ZO4t&jmEit2(=@lBl9mF-jn+l~OGCI=3@1cO13MhXd7P217EvNgHzc_aVit8N z5?XMt31#pYutFhHTMGMzZWHqel4`&>45~WXV+ATu(Ou#uF|$Ny+}MXCENAv1q+LJs zI)ISC5g9=Z=xL#a#e}yLT{|h4scmVz<%%mv)yyZuW4khmH>+1}t?` z%ckzIUu17w)w^WDxHjg1Qtz~dY?<;c?On(c!?kz5zLWim z@L5R_e+!uqD}K{l;ki#H;~0IJ=Z?x`uFYaM)Y>ve)LvIm&i~79PSe+du}ft&G{&zj z#Ju7!f7!oh5C26S^W&T?TQY!Y$tVtAu-5M@EcAV8i*MfSwFj~T_Goz98h`niJySO9 zNW0KJYTM2lX_nRl+G2;_HD&tZnJd`wi;@?P8B-W58NKA4O7DoUtBQQ%sthj5=f8dn ze<~}97P$(@V~-`@GPzBl5F?YjyNPzvq=8bREyHoiKYSb;GbYB|R#lakm!ChAXvSL+ zlEhS1m6wwZIrwA2pXt+cavmZV(VEF_T0sAlm-81^R7_IOnaRl}*lee)VYxiRRg&v9 z&m>wmtVY=Ox}$QR)}oNk0Qk$5T!pKa;;PJ@{MSUATs6Mju2V>Xhsr9m>)>MyXlDD$ z?P|E1l>s*`G=ajoj{oN6mn$oGURuErR-tzpgW+GA86-OeUpDd!A(N<= zbvs)WGB^x^(MnHo(3Wj=Ak?sws8}gWayhcK#iAD%=5S&M5lbaXiCU~h(33bUW~#zf z+V2&gZ9~>$bWycfjlEKim>IqD^wrV|f(j`olaVmJ3T_4KlgLt;R4(Or%caT@ zBeWS!h5jO|tXG1lCgk&!$iyzBP?GtTG$aL(Uq>Vm%vP)QQkhH%iaoJJ{ES-PA+|~< zjv`#!Bs?I8dI(;4E>|Zrj?<~_U>==zl2fEid64Myyvi$OgBIsjD@Xmg^bF`57=D5wc=6UBT{EilEYFwUri zg2}{!!hpd7B%wHqQP4O-^aLmpC^=)N6^K;mFivc>prwXzJm!Rvl5^Xiq{?jcS`98| z8F^%hq$qOY^STCqda%6CP~X{>S5R9Y@)Wo_J%;Aqj)DjY8GE-G^7Pd?!IA0t>8dPp ziB_GSuTX5?msYCF-?xuhk{fP{M`b(q`O~{1ReVlfU0z-tdw)UE)ZV2vu?4d$bY)H1 zCad@-=Iq(e`Vj%2{J4Akj87|S?P?3sFD*+Ch8oLjZ5pf2V>c|%3}h1D(u>S1WOM)D zSif7jMq2c|{W3P)UCP6I>*0Sx{`|p)vf|SGL8c%2;@= z$7sygFb@p>Y_Kh8fYbd3^K2!!R45~r0qMtlUTS|1iHk6$fT~7EMPxY#-~&)uitZ00 z?LAG2Le)47*Cq_Wu!e(T*i!WctQ+xtZ|y~pn@(3TE`2T+krBmD_bVK-u~>QBSkyVO zD)iY?GNdh(ZF(w7ZpI$w9{%8q#jOkW?OpJj^l=qB-N?C;xWXYnahHry^rFH|=^0s5 zuDR=*%MK8+(`cfBdnTh{TMt=?3RJ!#N#yD0ut4vDQpBCP`G_2lUkFadtb=8J@abY8 zPKg<46vKHRj7vSr$mEag;;e^v_FUUt!1WJ3=w9ag+p3mUk$U=k|NBAjAAC6SFXpF- zt7~Q~itq_Oo_g?YPY~U7{vdY;p7+;1IDKyFUr7kLL{dJr7)2?8Wdo`Zly6wjsN_B0 zHu0isc)^f^5rCox@rI}dhi^~)Y!NT)D-@OKfyQN_L|Ad^E5Twoz18sbHz5n@wtVXF^&SswvF*6(ksliMPmOnfLH6h?3s)?9F zUnoQdpO0F&&>amBixw*#u<_x6MG|a;5%gA_$cqDk?V-aqJ|%n(f>kV)jKUvD7qPD_ zoLaMCM%BXUy?x`D;+Bn&+KjW}e4Mg#03&7%ldK@5zIA!3#^9Gm*rc?!iJ z;mV(%yfqMg`Dal)5nv|IPnFI4uxH?TCf=Xymxzw>KlXe$4;BBY5bA;|O7wD6s4JAs z`|H$`aiMO1>V70VWU5Z!wiYC$Xvnrtkgpz&c#8;_Kqg9Y&`9Md8PhmFmp`&|`uZ&o zPhqxH3_KpXsEcs?_kZ5_)XH*cLus`(Q)90MfL|i&X{?!;ylms-qgxYWnfj7bKeR5g zG`-D#*K_kLYs5vNj6hvag`Wmwp7FhAVVuS%03o!3Zb)IObR$)s zS~p^9100p0Z3^6H|9OK>yD)R29=E~2sp*%{7}4y`I52;?Ar+kv<+cZ%?(D|QbeF$9 zFSp(AHd{kBU$)yBZ0{C!`7(r!T%S-SH?Q3f8%dZ}`Q;J9UU#++}LM!MuNJJoDQ4AVsY5hoG!cFsMA=m?Hnw`8j1G{JDq8%o#)g`vpX#P za4Yrm@uC0ASY2D!sHiK)mhLGJ?rHt68$!ED2!1g!oiBKiJ}&}Hr5FEYqMt+%aYS?? zLHe0ER!=54(LjPhn@jeKL>R|04oJ{Yaik8uN}#0$kRme6_#=SJA_on=J7-`;OvVEK z;~S8r<+azy^gleoiq|bVoD}_mOn;5JF!{lvbtok_V=F1Tf&X{`b2BRf(C@5!1M^$z z-sn(4dl>CzA)#l{;6FN42=^-$g>>ta7opR9%J=p&Bk2lxW4%sqCJ%w^MtFwfe4AM> z)EcUksuO}igW$PfiXKdr8O2U`^+Qi7ll{_BTsMk1HT5i<{e) z=CrmHHnMSv&z0!_lIZK*PX|h-wQn7Bp|fND#PHGwd;7keRuest;U@=fgl&BOOZ%q; zt7pu*aOLij7pJ#pRi=BaxfSypb^0ZTfpE@JI&#G`3t>&E!z*BfZ!5z1MtNi@Cl0(F z$eoTSgZ}KZK!p~(id5IdlhOgtLI(vJ?1tD|b4upNhK2}Xgm8mb`xm;f_`qjAe^|~j zh5izlM~poog?B`xeG{XbKFbv@a*(cy>5bO1(1L&$L%^YL)hnb7V9Uoz#| z^}stOIxB;;pHhZI)#xlf@a5dSp#(*~`Gde6{3ptz&; z>uBEyMWEgTA7Qa_LJ|WS-$2`ppf99Dgrw8_cpy2$@JUq*l+d{v#5z?7&0d)9gf&W1 zheQY``4_@I+p*eank8iA{kJ@BC?m^BI-fpszF90jwxhD@KCQx{HTw+r^&BHIQpum- zui#INX{_ZB8NAP12ktC zXK~QUF9S4I7#jtS6p9}40NXK&ww<&6)Q!;-H%gx`Y34nvw~V(`jN7CUOsT zIwwU~B~w~m$;ruE6VXwlqKVX! znY?T%d13UL%E~pP`SLl!xNtGXl%FszhoO@k#<+CEL!<~&l~rB)zcPymUCAjEvk2X zDQ*frQ{kqMT54)qYA(8HuKSb<_YFIC_q_E;7H-}B53%YL_k|bU*Ym~)D~0o2cZE!e z>JL`-eD$uI-`#NG!LTne7joYYf&FLX9_;3U#e9!UzNNI?`swz>^b( zoL7*9ALWUq2woNsX6P3vhFR*|V8B_fTsmX!8G!2+xQB+<-FQ|)qtxM6hm^xY?I&JT z#=L~G`jrfvg4dEkZRQ8jiO1EL(PVx~&D=Y>p=bRt^Qe)zm8bOl^3LMn1(Q0?sp{AN zyw+7C^9Ppajc%Aaw13T(K|lKE9Ut9x3)cVjJ+Guk<>sE+eDS!a z^YNvoYjPYT==|C__mA*6&aKZKx_juUwd#cn%Q`0y9e4MfSt}3V-Svs%rcF6-)LC=x zoP6Hs{Dlv6-;zw-^qyr+&yxeh3)AYmQ?nhFgUD_-uMYIg$Mz_`_fP5mvSR!C!TF`L z%4Y`}YkTe(cgBtPJaE6DQ>$hcS9@L7VIw_d{jgh1zkU^EgG)*$u03;jdRQ)Yih7;w z`Q90~pFeU$V{W7)544RJSBriWxY$}+WSux{ z|JNoe-17LxFCX~puC0wN9hs`>(<-k0E@I{rZ@fI&ky}h>oM9=*b4+^aSBGAj?8wiz zjwo-!P6#=ZUNpb<4J@30SQo&NEyB8BDE3K{PgTl?KjeoNu{1LhJks$TS`l{i;*rk} zg5%r}H(B7(vI+Bt^1G&6Q$3$a04M5)u0FC_bge#ebx#$ap>M_MeqjnvR{}6^=qZ#Z z^Pi=*{;P{2E6&YV9}zRUH-M`+-@IR*)SI@Z%qc)nQ}&@eM=!ur3K#I3*=T>MV)k6z zDsSM7w2$UX7dU5!lG&{9ON|0Kdt+SWkd*RD$9J#pS%(iPeYLc#42K~-B~9Md&1GfH zE4)nuu$$+gg{5T!YD>yW{aEqW4WM(UdV9Y1P6aspjOV;lm#57B>eFc-g zG`aBb27ZS|hVTS}9v?q`9J99UT8G}Z$N(R{A@~8$=g2>fccNHQpP%S4ci~HK_z~|M zxL*$}{rdt=6HGQp$i{3!qDvPl1@8yUt0*}7&*HN&^I5tieqvJ{S?8Sqg%VwTzEOlo*g473j2Ch@q$Dr+-Z^I5E&}B2if^1#>i?~tJbeX)6 z<&|aVvh%ncSyq>+Gb@Ml8ON~^3JscUTGj!13uFK->nQa^jJ9lKJ_kZynNk+=InLtE z*)(FtSrGT;1D13~oYhtKg$a4MPKWmNWofu?q@Ku=WkC<*kpcIXDe0NNZ|E`&U^?(y zv*jCoU1-E<;DteB>C4MFgaVEwzDw#h1Zgh+L^)lia+bw5z=66>HO zPG^I;OV>fRHSk$_mdhdAMh1Oj7RP$@=Am4f4|>Sy)e*8LAmmxPOy_cdZW9oC)7dhR z$9=5V3oz?qE7#L3SEhlJ^hiq_LwWCK$W~J&9#--Hdn<^e`a=Aj8T5 z^g`wV5Bj|9_ylYQzT&%Of=AXL_*~Ajbm{tVn+OAD8sybxX;HqJ1E>E}U_FiCF|Pn@ zHd$C7E(dXaFK-vVdWitM48V_+p-Zo)K{o_CaUCT;Xd78aBTvTJG|Fsdycz!-m{yi) z$TR3%SzhQeo?+IF^<^0J634vIt=!&q{5Z>ybX}5mK$gEZ2A*LHVlKmh0N$)TsW*>( zV|%DL%1he!>-o%wzLT_B|6u>hG_F@R=Ob_$e5@1KPu7d&_3{`rpeG0K*5 zvbg^ckKr;|2FFI|$1(FDmhB9E8UPpfrOV0$ehTtSvuT4bE30oj2(%&O&o}h0M4Izw zA}nFOzb}9`pF_6qzbikhQ#R&&hB;*0f???B;+XTZG63?g$z zCYoffFt4yox4dro#yZKm-P&!NYddHU+q-esZlmMFoas3`a(bL|oEx0)xyHLT=Qigq z&3!emHt*8Bd-9v}cNCNq%q-YmIInPB;U9}Ci?$VyE$-^)?oa&}_TP(-btmu&x$dR&=vc|H-WlxkH z8`?Z{&Ct(=O&|91@QK4$3_m!$yWCTrDBn^3$%siK){i(a;_%4Ykt;@ia>~L}cAU~v zv8LkfQR7D)9lc`o0o)LoJ*IQa$737EhQ>ZH_QP={<66dTANOA6l*;YnZR3|sD4wu$ z!kZH-C$63N&S~YREkEu3s^;pF>Q1-Cz101H`&dn=W>3xAp1GduJ%_v=?=9X>YiHFS zteac+dHsrpVGXf{Cr`JWK4x|=tgS>MvXrM>0oS#!_YKYiZxPi9P?5uUMX#Z#w_*oRT@(oX_U2yYR(}W?%H= z#m+=TB3(wm#uV?<)`E%wko4R57!xr7{Z}fNhKMtH7xFv8PSQ1qIF=lrY)|e_Ia3=`$1aaueo!5)YU$GSru2TTQrn&>&unckZ{M<{Y{|BzqdRIl zCw6L`uU|3jiqI7gFUwulxJHv%l9rna@C}(LRTGI@#M;RE8kny zvTDPsqpK@dFJArOnyNM0n!{J$v$lQh!`HN2v+SBT*Nt7Lt=n^L?zJnfJ+i)K{r>Bw zUbpM|#P#=F|LF}gZ&k&2Rnm(5<0cw{0%n z+_<@GbN6k5+upqW^xJpcG4qaxx0G*5Z8>(Qx^>T8{qH)uExhgM-LvoBe$VK8_TD@C z-hJDPw`IGr{IUrR+~J~wj57W#qd{dI>D8eDFyE! zE5I^$2$U_5o`B3I?8L))NmCs09E4U}C5l11YLuSFvyFyt(DF2Ski%^1! z@}jc*a;dc&`c(Bws`&v)v!Rs&y|^A+KgAT5vdU45BrqDU>P69o#zaotds<}I28nS+GtZ18199>t@?ev#{H?Gg-^$u zpr@fGdinm7_$JDd{(H*P&_cR43E4`g;Xa81owL%*VI|zsb5RR!sV2m&h~2oF#CdLL zQ;qkxPRF~|brP@J6^|tRj(74dg#Z4N*#hGYJ3*PQ$8%2Wusi(*(~a168ZYeOsXpxL zfhTUA!i7z!^Kcct0C!+Fnr;w=VQyTOT?w( zGO=BQO$He2}34CMVN%54}E1nk5h-bw<@f=>OdR`n5FNlNU=i){2l6YCXB3>1*iC>6c zir2*(;*j{2cvJjZyd{1k-WI9_3R36;cruQ$OlY1E_=snnqG74Z@pxL#T{~5;nJ{avDJ+v1wHW zjiS*shQ`u3s-#nCJWZg9bQ)DrH9inuLmu){E!9yyHPGqwBbr2$X$qY|jnqU_X&N=t znbbmO(R7+YXVW<}lg_1CbRNy7^JxxUKy&Frx`-~Oc{HCE&?R&!T}BJZM~lc$0n?il zq!1|-rdEnjlonGP#VAe*N>Yk0CzaBqQHI)S2`!}#>ZB`Z8C^-s=_=}?AJYn2Nvmiz zt)Z)FEnS0cTd$?{bRAt!H_!&Ukv7s#XcOH;H`7n)7P^%-(`|G+-9cOEPTER$(Kfo9 z?xA~WJKaY+=ze;D9;BW05bdJf^e{a_d+1Smj2@>a=x6jKJwF4w!y+kk5EA%S8M!%q6((CjF9im^+oAhgXi+)3I({Je=dY9g#-_iT@0Uf5_ z(}(m2`XhZrN9a#ST~(_iQq{gpnUztN|rP4-M8T2<9l#j(4pDjQcDX}1yA7_rBQ zy+&MX#C1koZ^R8o+@#0u7CrXrvA1QKwKe8Xr>*f!IvTX46~7vcIFv-Y5=*8OYXoV{ zlGgmHlMg;6p3*ujnY5x>!qHgVp+$T#zuKyh7O^uNO>2~Fv#Clv*{;|-lgYR*nsCTC znbFM2aM+fPwkG^Bb1>Oz)l`2vVu>Wingg*}^S4?M(w0Cn+2-Iw+^@D-Q))D!*@FJK zqWUf2WI{uJEM$vn{#Z2V(v+o|FQP9YLLRv{UhgGqG5%0jJ~sSgcT48jShl{$8~#tZbf@06i3h>QxYM+YE%7*P%>^0CgXARw=M2(O(>c( z+g_PeZ#%MnFn4WtgBY;6VOXJ}>V>(C1glVBDBiB9S`;M~8RK5-q;cC*{rgT^^n$r$L<#e7F$;1O`Una#3 zS74-AT~6mnM-uVJ!Y=7ubf0494uy-zi$xP{FiyRP?Ws&Uf@yt|}{>jmX!2d|!VN&?AjH!AGN*43sbu{Nx`io+N?0hOvn~c{O}OwU`9h%raGJ{e@fa*nrWm{p~z_TaPmUL2uso@~m>=MG@ z$Qf zTM!eKqF{ze!YlJkDW?;zLLd{3VYIY5z?|ZFC&wR0>Hb7evBi~8TU2v}StXRRSb^#a z=7ET8cT2b`tQ3Wk8FZ8ndg929S$q;kx4)B6u)mYi+$+u#{4O1oj1C=Uk1FLesXe5m z+c0g|V*V6I(onSAcrw8ClA|%#uy<*1&dW1NO;^pOgL*%swuuBPqtjY3`^P$*hATkB z6!vw2+=c~x+#si&%F+}MQGn=ObYLni7a-Pj9Ew=Om?0A8xDv6qVs=mYLk_q(X%`M& zOE6o$1f*+$U56ZKW6WOu7)DS?$&m_yELPC#?+gb7XQEFQa?o3X@M1a4;=^>=#?A&- zY4N%18eDy57FRlh5sBd&O~I@)0UZKaeNApE)7i;w7gd4^CQug0tDO83ATM-m=}1(G zh4Ql#jjl}*Pf^+)FN7KF&6H-wxE<0&id^J@ySTbPg$4c2S zlR;n9HoJ0QnTE@kNJmV;a+ZCD4oHiIia~ug%aLxKML}}4+o@0aoaRXw!&!|>MC>JoE63-U5q$>|-lh0+fNI-p`I;tya% z`fA(_#l2V!?lh3mlyu3zqqtgmS+w-QMJJ^=AL42}eDLOWU^dMJ6n$zl5|*Xt<{Umq zbT17zrac6^!J-;29Sgv$^THYn=~mSrw}r8$ZBxzuP{InTt<>ITU7|z- zNt`$&@DGAIcfPDUhJ)_88Rr?GS0FnF$MhvQXVvD1l2{MO(+{KZ>*{mcu@uLuRO$q( z`l>vAW|IhCl2L9x)bN4(s@}_oT0YeAp`H)&w5_GOsS0iFuLh=pnHp+1$xIE*)WA#) z%+$b44Gk8br%G}J7y^f<3dMM;bRIXE~c)QiGvJrF?GyQ&m8s4!FJ(cyYR4Gc-SsHY!@E33lH0cr=B?)n4^I? V*eEM;|ho{trTA6=?tf diff --git a/src/css/popup-fenix.css b/src/css/popup-fenix.css index 7af6d9420c715..3c849872fcea9 100644 --- a/src/css/popup-fenix.css +++ b/src/css/popup-fenix.css @@ -53,9 +53,6 @@ hr { margin: 0; padding: 0; } -.fa { - font-size: 120%; - } #sticky { background-color: var(--default-surface); diff --git a/src/dyna-rules.html b/src/dyna-rules.html index 704b3976e73c4..0feb56565e671 100644 --- a/src/dyna-rules.html +++ b/src/dyna-rules.html @@ -4,35 +4,35 @@ uBlock — Dynamic filtering rules - - + + - - - - - - - + + + + + + +
        -

        +

        info-circle

        - - + +
        - - - + + +
        filter  double-angle-up diff --git a/src/img/fontawesome/fontawesome-defs.svg b/src/img/fontawesome/fontawesome-defs.svg index 9ceadfa008d1a..d559817f80a90 100644 --- a/src/img/fontawesome/fontawesome-defs.svg +++ b/src/img/fontawesome/fontawesome-defs.svg @@ -27,6 +27,7 @@ License - https://github.com/FortAwesome/Font-Awesome/tree/a8386aae19e200ddb0f68 + @@ -39,6 +40,7 @@ License - https://github.com/FortAwesome/Font-Awesome/tree/a8386aae19e200ddb0f68 + @@ -60,12 +62,15 @@ License - https://github.com/FortAwesome/Font-Awesome/tree/a8386aae19e200ddb0f68 + + + diff --git a/src/js/fa-icons.js b/src/js/fa-icons.js index 7ed55eb6d2c9e..6b64e21bf0a95 100644 --- a/src/js/fa-icons.js +++ b/src/js/fa-icons.js @@ -29,6 +29,7 @@ const faIconsInit = (( ) => { const svgIcons = new Map([ // See /img/fontawesome/fontawesome-defs.svg [ 'angle-up', { viewBox: '0 0 998 582', path: 'm 998,499 q 0,13 -10,23 l -50,50 q -10,10 -23,10 -13,0 -23,-10 L 499,179 106,572 Q 96,582 83,582 70,582 60,572 L 10,522 Q 0,512 0,499 0,486 10,476 L 476,10 q 10,-10 23,-10 13,0 23,10 l 466,466 q 10,10 10,23 z' } ], + [ 'arrow-right', { viewBox: '0 0 1472 1558', path: 'm 1472,779 q 0,54 -37,91 l -651,651 q -39,37 -91,37 -51,0 -90,-37 l -75,-75 q -38,-38 -38,-91 0,-53 38,-91 L 821,971 H 117 Q 65,971 32.5,933.5 0,896 0,843 V 715 Q 0,662 32.5,624.5 65,587 117,587 H 821 L 528,293 q -38,-36 -38,-90 0,-54 38,-90 l 75,-75 q 38,-38 90,-38 53,0 91,38 l 651,651 q 37,35 37,90 z' } ], [ 'bar-chart', { viewBox: '0 0 2048 1536', path: 'm 640,768 0,512 -256,0 0,-512 256,0 z m 384,-512 0,1024 -256,0 0,-1024 256,0 z m 1024,1152 0,128 L 0,1536 0,0 l 128,0 0,1408 1920,0 z m -640,-896 0,768 -256,0 0,-768 256,0 z m 384,-384 0,1152 -256,0 0,-1152 256,0 z' } ], [ 'bolt', { viewBox: '0 0 896 1664', path: 'm 885.08696,438 q 18,20 7,44 l -540,1157 q -13,25 -42,25 -4,0 -14,-2 -17,-5 -25.5,-19 -8.5,-14 -4.5,-30 l 197,-808 -406,101 q -4,1 -12,1 -18,0 -31,-11 Q -3.9130435,881 1.0869565,857 L 202.08696,32 q 4,-14 16,-23 12,-9 28,-9 l 328,0 q 19,0 32,12.5 13,12.5 13,29.5 0,8 -5,18 l -171,463 396,-98 q 8,-2 12,-2 19,0 34,15 z' } ], [ 'clipboard', { viewBox: '0 0 1792 1792', path: 'm 768,1664 896,0 0,-640 -416,0 q -40,0 -68,-28 -28,-28 -28,-68 l 0,-416 -384,0 0,1152 z m 256,-1440 0,-64 q 0,-13 -9.5,-22.5 Q 1005,128 992,128 l -704,0 q -13,0 -22.5,9.5 Q 256,147 256,160 l 0,64 q 0,13 9.5,22.5 9.5,9.5 22.5,9.5 l 704,0 q 13,0 22.5,-9.5 9.5,-9.5 9.5,-22.5 z m 256,672 299,0 -299,-299 0,299 z m 512,128 0,672 q 0,40 -28,68 -28,28 -68,28 l -960,0 q -40,0 -68,-28 -28,-28 -28,-68 l 0,-160 -544,0 Q 56,1536 28,1508 0,1480 0,1440 L 0,96 Q 0,56 28,28 56,0 96,0 l 1088,0 q 40,0 68,28 28,28 28,68 l 0,328 q 21,13 36,28 l 408,408 q 28,28 48,76 20,48 20,88 z' } ], @@ -41,6 +42,7 @@ const faIconsInit = (( ) => { [ 'cogs', { viewBox: '0 0 1920 1761', path: 'm 896,880 q 0,-106 -75,-181 -75,-75 -181,-75 -106,0 -181,75 -75,75 -75,181 0,106 75,181 75,75 181,75 106,0 181,-75 75,-75 75,-181 z m 768,512 q 0,-52 -38,-90 -38,-38 -90,-38 -52,0 -90,38 -38,38 -38,90 0,53 37.5,90.5 37.5,37.5 90.5,37.5 53,0 90.5,-37.5 37.5,-37.5 37.5,-90.5 z m 0,-1024 q 0,-52 -38,-90 -38,-38 -90,-38 -52,0 -90,38 -38,38 -38,90 0,53 37.5,90.5 37.5,37.5 90.5,37.5 53,0 90.5,-37.5 Q 1664,421 1664,368 Z m -384,421 v 185 q 0,10 -7,19.5 -7,9.5 -16,10.5 l -155,24 q -11,35 -32,76 34,48 90,115 7,11 7,20 0,12 -7,19 -23,30 -82.5,89.5 -59.5,59.5 -78.5,59.5 -11,0 -21,-7 l -115,-90 q -37,19 -77,31 -11,108 -23,155 -7,24 -30,24 H 547 q -11,0 -20,-7.5 -9,-7.5 -10,-17.5 l -23,-153 q -34,-10 -75,-31 l -118,89 q -7,7 -20,7 -11,0 -21,-8 -144,-133 -144,-160 0,-9 7,-19 10,-14 41,-53 31,-39 47,-61 -23,-44 -35,-82 L 24,1000 Q 14,999 7,990.5 0,982 0,971 V 786 Q 0,776 7,766.5 14,757 23,756 l 155,-24 q 11,-35 32,-76 -34,-48 -90,-115 -7,-11 -7,-20 0,-12 7,-20 22,-30 82,-89 60,-59 79,-59 11,0 21,7 l 115,90 q 34,-18 77,-32 11,-108 23,-154 7,-24 30,-24 h 186 q 11,0 20,7.5 9,7.5 10,17.5 l 23,153 q 34,10 75,31 l 118,-89 q 8,-7 20,-7 11,0 21,8 144,133 144,160 0,8 -7,19 -12,16 -42,54 -30,38 -45,60 23,48 34,82 l 152,23 q 10,2 17,10.5 7,8.5 7,19.5 z m 640,533 v 140 q 0,16 -149,31 -12,27 -30,52 51,113 51,138 0,4 -4,7 -122,71 -124,71 -8,0 -46,-47 -38,-47 -52,-68 -20,2 -30,2 -10,0 -30,-2 -14,21 -52,68 -38,47 -46,47 -2,0 -124,-71 -4,-3 -4,-7 0,-25 51,-138 -18,-25 -30,-52 -149,-15 -149,-31 v -140 q 0,-16 149,-31 13,-29 30,-52 -51,-113 -51,-138 0,-4 4,-7 4,-2 35,-20 31,-18 59,-34 28,-16 30,-16 8,0 46,46.5 38,46.5 52,67.5 20,-2 30,-2 10,0 30,2 51,-71 92,-112 l 6,-2 q 4,0 124,70 4,3 4,7 0,25 -51,138 17,23 30,52 149,15 149,31 z m 0,-1024 v 140 q 0,16 -149,31 -12,27 -30,52 51,113 51,138 0,4 -4,7 -122,71 -124,71 -8,0 -46,-47 -38,-47 -52,-68 -20,2 -30,2 -10,0 -30,-2 -14,21 -52,68 -38,47 -46,47 -2,0 -124,-71 -4,-3 -4,-7 0,-25 51,-138 -18,-25 -30,-52 -149,-15 -149,-31 V 298 q 0,-16 149,-31 13,-29 30,-52 -51,-113 -51,-138 0,-4 4,-7 4,-2 35,-20 31,-18 59,-34 28,-16 30,-16 8,0 46,46.5 38,46.5 52,67.5 20,-2 30,-2 10,0 30,2 51,-71 92,-112 l 6,-2 q 4,0 124,70 4,3 4,7 0,25 -51,138 17,23 30,52 149,15 149,31 z' } ], [ 'double-angle-left', { viewBox: '0 0 966 998', path: 'm 582,915 q 0,13 -10,23 l -50,50 q -10,10 -23,10 -13,0 -23,-10 L 10,522 Q 0,512 0,499 0,486 10,476 L 476,10 q 10,-10 23,-10 13,0 23,10 l 50,50 q 10,10 10,23 0,13 -10,23 L 179,499 572,892 q 10,10 10,23 z m 384,0 q 0,13 -10,23 l -50,50 q -10,10 -23,10 -13,0 -23,-10 L 394,522 q -10,-10 -10,-23 0,-13 10,-23 L 860,10 q 10,-10 23,-10 13,0 23,10 l 50,50 q 10,10 10,23 0,13 -10,23 L 563,499 956,892 q 10,10 10,23 z' } ], [ 'double-angle-up', { viewBox: '0 0 998 966', path: 'm 998,883 q 0,13 -10,23 l -50,50 q -10,10 -23,10 -13,0 -23,-10 L 499,563 106,956 Q 96,966 83,966 70,966 60,956 L 10,906 Q 0,896 0,883 0,870 10,860 L 476,394 q 10,-10 23,-10 13,0 23,10 l 466,466 q 10,10 10,23 z m 0,-384 q 0,13 -10,23 l -50,50 q -10,10 -23,10 -13,0 -23,-10 L 499,179 106,572 Q 96,582 83,582 70,582 60,572 L 10,522 Q 0,512 0,499 0,486 10,476 L 476,10 q 10,-10 23,-10 13,0 23,10 l 466,466 q 10,10 10,23 z' } ], + [ 'download-alt', { viewBox: '0 0 1664 1536', path: 'm 1280,1344 q 0,-26 -19,-45 -19,-19 -45,-19 -26,0 -45,19 -19,19 -19,45 0,26 19,45 19,19 45,19 26,0 45,-19 19,-19 19,-45 z m 256,0 q 0,-26 -19,-45 -19,-19 -45,-19 -26,0 -45,19 -19,19 -19,45 0,26 19,45 19,19 45,19 26,0 45,-19 19,-19 19,-45 z m 128,-224 v 320 q 0,40 -28,68 -28,28 -68,28 H 96 q -40,0 -68,-28 -28,-28 -28,-68 v -320 q 0,-40 28,-68 28,-28 68,-28 h 465 l 135,136 q 58,56 136,56 78,0 136,-56 l 136,-136 h 464 q 40,0 68,28 28,28 28,68 z M 1339,551 q 17,41 -14,70 l -448,448 q -18,19 -45,19 -27,0 -45,-19 L 339,621 q -31,-29 -14,-70 17,-39 59,-39 H 640 V 64 Q 640,38 659,19 678,0 704,0 h 256 q 26,0 45,19 19,19 19,45 v 448 h 256 q 42,0 59,39 z' } ], [ 'eraser', { viewBox: '0 0 1920 1280', path: 'M 896,1152 1232,768 l -768,0 -336,384 768,0 z M 1909,75 q 15,34 9.5,71.5 Q 1913,184 1888,212 L 992,1236 q -38,44 -96,44 l -768,0 q -38,0 -69.5,-20.5 -31.5,-20.5 -47.5,-54.5 -15,-34 -9.5,-71.5 5.5,-37.5 30.5,-65.5 L 928,44 Q 966,0 1024,0 l 768,0 q 38,0 69.5,20.5 Q 1893,41 1909,75 Z' } ], [ 'exclamation-triangle', { viewBox: '0 0 1794 1664', path: 'm 1025.0139,1375 0,-190 q 0,-14 -9.5,-23.5 -9.5,-9.5 -22.5,-9.5 l -192,0 q -13,0 -22.5,9.5 -9.5,9.5 -9.5,23.5 l 0,190 q 0,14 9.5,23.5 9.5,9.5 22.5,9.5 l 192,0 q 13,0 22.5,-9.5 9.5,-9.5 9.5,-23.5 z m -2,-374 18,-459 q 0,-12 -10,-19 -13,-11 -24,-11 l -220,0 q -11,0 -24,11 -10,7 -10,21 l 17,457 q 0,10 10,16.5 10,6.5 24,6.5 l 185,0 q 14,0 23.5,-6.5 9.5,-6.5 10.5,-16.5 z m -14,-934 768,1408 q 35,63 -2,126 -17,29 -46.5,46 -29.5,17 -63.5,17 l -1536,0 q -34,0 -63.5,-17 -29.5,-17 -46.5,-46 -37,-63 -2,-126 L 785.01389,67 q 17,-31 47,-49 30,-18 65,-18 35,0 65,18 30,18 47,49 z' } ], [ 'external-link', { viewBox: '0 0 1792 1536', path: 'm 1408,928 0,320 q 0,119 -84.5,203.5 Q 1239,1536 1120,1536 l -832,0 Q 169,1536 84.5,1451.5 0,1367 0,1248 L 0,416 Q 0,297 84.5,212.5 169,128 288,128 l 704,0 q 14,0 23,9 9,9 9,23 l 0,64 q 0,14 -9,23 -9,9 -23,9 l -704,0 q -66,0 -113,47 -47,47 -47,113 l 0,832 q 0,66 47,113 47,47 113,47 l 832,0 q 66,0 113,-47 47,-47 47,-113 l 0,-320 q 0,-14 9,-23 9,-9 23,-9 l 64,0 q 14,0 23,9 9,9 9,23 z m 384,-864 0,512 q 0,26 -19,45 -19,19 -45,19 -26,0 -45,-19 L 1507,445 855,1097 q -10,10 -23,10 -13,0 -23,-10 L 695,983 q -10,-10 -10,-23 0,-13 10,-23 L 1347,285 1171,109 q -19,-19 -19,-45 0,-26 19,-45 19,-19 45,-19 l 512,0 q 26,0 45,19 19,19 19,45 z' } ], @@ -62,13 +64,16 @@ const faIconsInit = (( ) => { [ 'power-off', { viewBox: '0 0 1536 1664', path: 'm 1536,896 q 0,156 -61,298 -61,142 -164,245 -103,103 -245,164 -142,61 -298,61 -156,0 -298,-61 Q 328,1542 225,1439 122,1336 61,1194 0,1052 0,896 0,714 80.5,553 161,392 307,283 q 43,-32 95.5,-25 52.5,7 83.5,50 32,42 24.5,94.5 Q 503,455 461,487 363,561 309.5,668 256,775 256,896 q 0,104 40.5,198.5 40.5,94.5 109.5,163.5 69,69 163.5,109.5 94.5,40.5 198.5,40.5 104,0 198.5,-40.5 Q 1061,1327 1130,1258 1199,1189 1239.5,1094.5 1280,1000 1280,896 1280,775 1226.5,668 1173,561 1075,487 1033,455 1025.5,402.5 1018,350 1050,308 q 31,-43 84,-50 53,-7 95,25 146,109 226.5,270 80.5,161 80.5,343 z m -640,-768 0,640 q 0,52 -38,90 -38,38 -90,38 -52,0 -90,-38 -38,-38 -38,-90 l 0,-640 q 0,-52 38,-90 38,-38 90,-38 52,0 90,38 38,38 38,90 z' } ], [ 'question-circle', { viewBox: '0 0 1536 1536', path: 'm 896,1248 v -192 q 0,-14 -9,-23 -9,-9 -23,-9 H 672 q -14,0 -23,9 -9,9 -9,23 v 192 q 0,14 9,23 9,9 23,9 h 192 q 14,0 23,-9 9,-9 9,-23 z m 256,-672 q 0,-88 -55.5,-163 Q 1041,338 958,297 875,256 788,256 q -243,0 -371,213 -15,24 8,42 l 132,100 q 7,6 19,6 16,0 25,-12 53,-68 86,-92 34,-24 86,-24 48,0 85.5,26 37.5,26 37.5,59 0,38 -20,61 -20,23 -68,45 -63,28 -115.5,86.5 Q 640,825 640,892 v 36 q 0,14 9,23 9,9 23,9 h 192 q 14,0 23,-9 9,-9 9,-23 0,-19 21.5,-49.5 Q 939,848 972,829 q 32,-18 49,-28.5 17,-10.5 46,-35 29,-24.5 44.5,-48 15.5,-23.5 28,-60.5 12.5,-37 12.5,-81 z m 384,192 q 0,209 -103,385.5 Q 1330,1330 1153.5,1433 977,1536 768,1536 559,1536 382.5,1433 206,1330 103,1153.5 0,977 0,768 0,559 103,382.5 206,206 382.5,103 559,0 768,0 977,0 1153.5,103 1330,206 1433,382.5 1536,559 1536,768 Z' } ], [ 'refresh', { viewBox: '0 0 1536 1536', path: 'm 1511,928 q 0,5 -1,7 -64,268 -268,434.5 Q 1038,1536 764,1536 618,1536 481.5,1481 345,1426 238,1324 l -129,129 q -19,19 -45,19 -26,0 -45,-19 Q 0,1434 0,1408 L 0,960 q 0,-26 19,-45 19,-19 45,-19 l 448,0 q 26,0 45,19 19,19 19,45 0,26 -19,45 l -137,137 q 71,66 161,102 90,36 187,36 134,0 250,-65 116,-65 186,-179 11,-17 53,-117 8,-23 30,-23 l 192,0 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 z m 25,-800 0,448 q 0,26 -19,45 -19,19 -45,19 l -448,0 q -26,0 -45,-19 -19,-19 -19,-45 0,-26 19,-45 L 1117,393 Q 969,256 768,256 q -134,0 -250,65 -116,65 -186,179 -11,17 -53,117 -8,23 -30,23 L 50,640 Q 37,640 27.5,630.5 18,621 18,608 l 0,-7 Q 83,333 288,166.5 493,0 768,0 914,0 1052,55.5 1190,111 1297,212 L 1427,83 q 19,-19 45,-19 26,0 45,19 19,19 19,45 z' } ], + [ 'save', { viewBox: '0 0 1536 1536', path: 'm 384,1408 h 768 V 1024 H 384 Z m 896,0 h 128 V 512 q 0,-14 -10,-38.5 Q 1388,449 1378,439 L 1097,158 q -10,-10 -34,-20 -24,-10 -39,-10 v 416 q 0,40 -28,68 -28,28 -68,28 H 352 q -40,0 -68,-28 -28,-28 -28,-68 V 128 H 128 V 1408 H 256 V 992 q 0,-40 28,-68 28,-28 68,-28 h 832 q 40,0 68,28 28,28 28,68 z M 896,480 V 160 q 0,-13 -9.5,-22.5 Q 877,128 864,128 H 672 q -13,0 -22.5,9.5 Q 640,147 640,160 v 320 q 0,13 9.5,22.5 9.5,9.5 22.5,9.5 h 192 q 13,0 22.5,-9.5 Q 896,493 896,480 Z m 640,32 v 928 q 0,40 -28,68 -28,28 -68,28 H 96 Q 56,1536 28,1508 0,1480 0,1440 V 96 Q 0,56 28,28 56,0 96,0 h 928 q 40,0 88,20 48,20 76,48 l 280,280 q 28,28 48,76 20,48 20,88 z' } ], [ 'search', { viewBox: '0 0 1664 1664', path: 'M 1152,704 Q 1152,519 1020.5,387.5 889,256 704,256 519,256 387.5,387.5 256,519 256,704 256,889 387.5,1020.5 519,1152 704,1152 889,1152 1020.5,1020.5 1152,889 1152,704 Z m 512,832 q 0,52 -38,90 -38,38 -90,38 -54,0 -90,-38 L 1103,1284 Q 924,1408 704,1408 561,1408 430.5,1352.5 300,1297 205.5,1202.5 111,1108 55.5,977.5 0,847 0,704 0,561 55.5,430.5 111,300 205.5,205.5 300,111 430.5,55.5 561,0 704,0 q 143,0 273.5,55.5 130.5,55.5 225,150 94.5,94.5 150,225 55.5,130.5 55.5,273.5 0,220 -124,399 l 343,343 q 37,37 37,90 z' } ], [ 'sliders', { viewBox: '0 0 1536 1408', path: 'm 352,1152 0,128 -352,0 0,-128 352,0 z m 352,-128 q 26,0 45,19 19,19 19,45 l 0,256 q 0,26 -19,45 -19,19 -45,19 l -256,0 q -26,0 -45,-19 -19,-19 -19,-45 l 0,-256 q 0,-26 19,-45 19,-19 45,-19 l 256,0 z m 160,-384 0,128 -864,0 0,-128 864,0 z m -640,-512 0,128 -224,0 0,-128 224,0 z m 1312,1024 0,128 -736,0 0,-128 736,0 z M 576,0 q 26,0 45,19 19,19 19,45 l 0,256 q 0,26 -19,45 -19,19 -45,19 l -256,0 q -26,0 -45,-19 -19,-19 -19,-45 L 256,64 Q 256,38 275,19 294,0 320,0 l 256,0 z m 640,512 q 26,0 45,19 19,19 19,45 l 0,256 q 0,26 -19,45 -19,19 -45,19 l -256,0 q -26,0 -45,-19 -19,-19 -19,-45 l 0,-256 q 0,-26 19,-45 19,-19 45,-19 l 256,0 z m 320,128 0,128 -224,0 0,-128 224,0 z m 0,-512 0,128 -864,0 0,-128 864,0 z' } ], [ 'spinner', { viewBox: '0 0 1664 1728', path: 'm 462,1394 q 0,53 -37.5,90.5 -37.5,37.5 -90.5,37.5 -52,0 -90,-38 -38,-38 -38,-90 0,-53 37.5,-90.5 37.5,-37.5 90.5,-37.5 53,0 90.5,37.5 37.5,37.5 37.5,90.5 z m 498,206 q 0,53 -37.5,90.5 Q 885,1728 832,1728 779,1728 741.5,1690.5 704,1653 704,1600 q 0,-53 37.5,-90.5 37.5,-37.5 90.5,-37.5 53,0 90.5,37.5 Q 960,1547 960,1600 Z M 256,896 q 0,53 -37.5,90.5 Q 181,1024 128,1024 75,1024 37.5,986.5 0,949 0,896 0,843 37.5,805.5 75,768 128,768 q 53,0 90.5,37.5 Q 256,843 256,896 Z m 1202,498 q 0,52 -38,90 -38,38 -90,38 -53,0 -90.5,-37.5 -37.5,-37.5 -37.5,-90.5 0,-53 37.5,-90.5 37.5,-37.5 90.5,-37.5 53,0 90.5,37.5 37.5,37.5 37.5,90.5 z M 494,398 q 0,66 -47,113 -47,47 -113,47 -66,0 -113,-47 -47,-47 -47,-113 0,-66 47,-113 47,-47 113,-47 66,0 113,47 47,47 47,113 z m 1170,498 q 0,53 -37.5,90.5 -37.5,37.5 -90.5,37.5 -53,0 -90.5,-37.5 Q 1408,949 1408,896 q 0,-53 37.5,-90.5 37.5,-37.5 90.5,-37.5 53,0 90.5,37.5 Q 1664,843 1664,896 Z M 1024,192 q 0,80 -56,136 -56,56 -136,56 -80,0 -136,-56 -56,-56 -56,-136 0,-80 56,-136 56,-56 136,-56 80,0 136,56 56,56 56,136 z m 530,206 q 0,93 -66,158.5 -66,65.5 -158,65.5 -93,0 -158.5,-65.5 Q 1106,491 1106,398 q 0,-92 65.5,-158 65.5,-66 158.5,-66 92,0 158,66 66,66 66,158 z' } ], [ 'times', { viewBox: '0 0 1188 1188', path: 'm 1188,956 q 0,40 -28,68 l -136,136 q -28,28 -68,28 -40,0 -68,-28 L 594,866 300,1160 q -28,28 -68,28 -40,0 -68,-28 L 28,1024 Q 0,996 0,956 0,916 28,888 L 322,594 28,300 Q 0,272 0,232 0,192 28,164 L 164,28 Q 192,0 232,0 272,0 300,28 L 594,322 888,28 q 28,-28 68,-28 40,0 68,28 l 136,136 q 28,28 28,68 0,40 -28,68 l -294,294 294,294 q 28,28 28,68 z' } ], [ 'trash-o', { viewBox: '0 0 1408 1536', path: 'm 512,608 v 576 q 0,14 -9,23 -9,9 -23,9 h -64 q -14,0 -23,-9 -9,-9 -9,-23 V 608 q 0,-14 9,-23 9,-9 23,-9 h 64 q 14,0 23,9 9,9 9,23 z m 256,0 v 576 q 0,14 -9,23 -9,9 -23,9 h -64 q -14,0 -23,-9 -9,-9 -9,-23 V 608 q 0,-14 9,-23 9,-9 23,-9 h 64 q 14,0 23,9 9,9 9,23 z m 256,0 v 576 q 0,14 -9,23 -9,9 -23,9 h -64 q -14,0 -23,-9 -9,-9 -9,-23 V 608 q 0,-14 9,-23 9,-9 23,-9 h 64 q 14,0 23,9 9,9 9,23 z m 128,724 V 384 H 256 v 948 q 0,22 7,40.5 7,18.5 14.5,27 7.5,8.5 10.5,8.5 h 832 q 3,0 10.5,-8.5 7.5,-8.5 14.5,-27 7,-18.5 7,-40.5 z M 480,256 H 928 L 880,139 q -7,-9 -17,-11 H 546 q -10,2 -17,11 z m 928,32 v 64 q 0,14 -9,23 -9,9 -23,9 h -96 v 948 q 0,83 -47,143.5 -47,60.5 -113,60.5 H 288 q -66,0 -113,-58.5 Q 128,1419 128,1336 V 384 H 32 Q 18,384 9,375 0,366 0,352 v -64 q 0,-14 9,-23 9,-9 23,-9 H 341 L 411,89 Q 426,52 465,26 504,0 544,0 h 320 q 40,0 79,26 39,26 54,63 l 70,167 h 309 q 14,0 23,9 9,9 9,23 z' } ], + [ 'undo', { viewBox: '0 0 1536 1536', path: 'm 1536,768 q 0,156 -61,298 -61,142 -164,245 -103,103 -245,164 -142,61 -298,61 -172,0 -327,-72.5 Q 286,1391 177,1259 q -7,-10 -6.5,-22.5 0.5,-12.5 8.5,-20.5 l 137,-138 q 10,-9 25,-9 16,2 23,12 73,95 179,147 106,52 225,52 104,0 198.5,-40.5 Q 1061,1199 1130,1130 1199,1061 1239.5,966.5 1280,872 1280,768 1280,664 1239.5,569.5 1199,475 1130,406 1061,337 966.5,296.5 872,256 768,256 670,256 580,291.5 490,327 420,393 l 137,138 q 31,30 14,69 -17,40 -59,40 H 64 Q 38,640 19,621 0,602 0,576 V 128 Q 0,86 40,69 79,52 109,83 L 239,212 Q 346,111 483.5,55.5 621,0 768,0 q 156,0 298,61 142,61 245,164 103,103 164,245 61,142 61,298 z' } ], [ 'unlink', { viewBox: '0 0 1664 1664', path: 'm 439,1271 -256,256 q -11,9 -23,9 -12,0 -23,-9 -9,-10 -9,-23 0,-13 9,-23 l 256,-256 q 10,-9 23,-9 13,0 23,9 9,10 9,23 0,13 -9,23 z m 169,41 v 320 q 0,14 -9,23 -9,9 -23,9 -14,0 -23,-9 -9,-9 -9,-23 v -320 q 0,-14 9,-23 9,-9 23,-9 14,0 23,9 9,9 9,23 z M 384,1088 q 0,14 -9,23 -9,9 -23,9 H 32 q -14,0 -23,-9 -9,-9 -9,-23 0,-14 9,-23 9,-9 23,-9 h 320 q 14,0 23,9 9,9 9,23 z m 1264,128 q 0,120 -85,203 l -147,146 q -83,83 -203,83 -121,0 -204,-85 L 675,1228 q -21,-21 -42,-56 l 239,-18 273,274 q 27,27 68,27.5 41,0.5 68,-26.5 l 147,-146 q 28,-28 28,-67 0,-40 -28,-68 l -274,-275 18,-239 q 35,21 56,42 l 336,336 q 84,86 84,204 z M 1031,492 792,510 519,236 q -28,-28 -68,-28 -39,0 -68,27 L 236,381 q -28,28 -28,67 0,40 28,68 l 274,274 -18,240 q -35,-21 -56,-42 L 100,652 Q 16,566 16,448 16,328 101,245 L 248,99 q 83,-83 203,-83 121,0 204,85 l 334,335 q 21,21 42,56 z m 633,84 q 0,14 -9,23 -9,9 -23,9 h -320 q -14,0 -23,-9 -9,-9 -9,-23 0,-14 9,-23 9,-9 23,-9 h 320 q 14,0 23,9 9,9 9,23 z M 1120,32 v 320 q 0,14 -9,23 -9,9 -23,9 -14,0 -23,-9 -9,-9 -9,-23 V 32 q 0,-14 9,-23 9,-9 23,-9 14,0 23,9 9,9 9,23 z m 407,151 -256,256 q -11,9 -23,9 -12,0 -23,-9 -9,-10 -9,-23 0,-13 9,-23 l 256,-256 q 10,-9 23,-9 13,0 23,9 9,10 9,23 0,13 -9,23 z' } ], [ 'unlock-alt', { viewBox: '0 0 1152 1536', path: 'm 1056,768 q 40,0 68,28 28,28 28,68 v 576 q 0,40 -28,68 -28,28 -68,28 H 96 Q 56,1536 28,1508 0,1480 0,1440 V 864 q 0,-40 28,-68 28,-28 68,-28 h 32 V 448 Q 128,263 259.5,131.5 391,0 576,0 761,0 892.5,131.5 1024,263 1024,448 q 0,26 -19,45 -19,19 -45,19 h -64 q -26,0 -45,-19 -19,-19 -19,-45 0,-106 -75,-181 -75,-75 -181,-75 -106,0 -181,75 -75,75 -75,181 v 320 z' } ], + [ 'upload-alt', { viewBox: '0 0 1664 1600', path: 'm 1280,1408 q 0,-26 -19,-45 -19,-19 -45,-19 -26,0 -45,19 -19,19 -19,45 0,26 19,45 19,19 45,19 26,0 45,-19 19,-19 19,-45 z m 256,0 q 0,-26 -19,-45 -19,-19 -45,-19 -26,0 -45,19 -19,19 -19,45 0,26 19,45 19,19 45,19 26,0 45,-19 19,-19 19,-45 z m 128,-224 v 320 q 0,40 -28,68 -28,28 -68,28 H 96 q -40,0 -68,-28 -28,-28 -28,-68 v -320 q 0,-40 28,-68 28,-28 68,-28 h 427 q 21,56 70.5,92 49.5,36 110.5,36 h 256 q 61,0 110.5,-36 49.5,-36 70.5,-92 h 427 q 40,0 68,28 28,28 28,68 z M 1339,536 q -17,40 -59,40 h -256 v 448 q 0,26 -19,45 -19,19 -45,19 H 704 q -26,0 -45,-19 -19,-19 -19,-45 V 576 H 384 q -42,0 -59,-40 -17,-39 14,-69 L 787,19 q 18,-19 45,-19 27,0 45,19 l 448,448 q 31,30 14,69 z' } ], // See /img/photon.svg [ 'ph-popups', { viewBox: '0 0 20 20', path: 'm 3.146,1.8546316 a 0.5006316,0.5006316 0 0 0 0.708,-0.708 l -1,-1 a 0.5006316,0.5006316 0 0 0 -0.708,0.708 z m -0.836,2.106 a 0.406,0.406 0 0 0 0.19,0.04 0.5,0.5 0 0 0 0.35,-0.851 0.493,0.493 0 0 0 -0.54,-0.109 0.361,0.361 0 0 0 -0.16,0.109 0.485,0.485 0 0 0 0,0.7 0.372,0.372 0 0 0 0.16,0.111 z m 3,-3 a 0.406,0.406 0 0 0 0.19,0.04 0.513,0.513 0 0 0 0.5,-0.5 0.473,0.473 0 0 0 -0.15,-0.351 0.5,0.5 0 0 0 -0.7,0 0.485,0.485 0 0 0 0,0.7 0.372,0.372 0 0 0 0.16,0.111 z m 13.19,1.04 a 0.5,0.5 0 0 0 0.354,-0.146 l 1,-1 a 0.5006316,0.5006316 0 0 0 -0.708,-0.708 l -1,1 a 0.5,0.5 0 0 0 0.354,0.854 z m 1.35,1.149 a 0.361,0.361 0 0 0 -0.16,-0.109 0.5,0.5 0 0 0 -0.38,0 0.361,0.361 0 0 0 -0.16,0.109 0.485,0.485 0 0 0 0,0.7 0.372,0.372 0 0 0 0.16,0.11 0.471,0.471 0 0 0 0.38,0 0.372,0.372 0 0 0 0.16,-0.11 0.469,0.469 0 0 0 0.15,-0.349 0.43,0.43 0 0 0 -0.04,-0.19 0.358,0.358 0 0 0 -0.11,-0.161 z m -3.54,-2.189 a 0.406,0.406 0 0 0 0.19,0.04 0.469,0.469 0 0 0 0.35,-0.15 0.353,0.353 0 0 0 0.11,-0.161 0.469,0.469 0 0 0 0,-0.379 0.358,0.358 0 0 0 -0.11,-0.161 0.361,0.361 0 0 0 -0.16,-0.109 0.493,0.493 0 0 0 -0.54,0.109 0.358,0.358 0 0 0 -0.11,0.161 0.43,0.43 0 0 0 -0.04,0.19 0.469,0.469 0 0 0 0.15,0.35 0.372,0.372 0 0 0 0.16,0.11 z m 2.544,15.1860004 a 0.5006316,0.5006316 0 0 0 -0.708,0.708 l 1,1 a 0.5006316,0.5006316 0 0 0 0.708,-0.708 z m 0.3,-2 a 0.473,0.473 0 0 0 -0.154,0.354 0.4,0.4 0 0 0 0.04,0.189 0.353,0.353 0 0 0 0.11,0.161 0.469,0.469 0 0 0 0.35,0.15 0.406,0.406 0 0 0 0.19,-0.04 0.372,0.372 0 0 0 0.16,-0.11 0.454,0.454 0 0 0 0.15,-0.35 0.473,0.473 0 0 0 -0.15,-0.351 0.5,0.5 0 0 0 -0.7,0 z m -3,3 a 0.473,0.473 0 0 0 -0.154,0.354 0.454,0.454 0 0 0 0.15,0.35 0.372,0.372 0 0 0 0.16,0.11 0.406,0.406 0 0 0 0.19,0.04 0.469,0.469 0 0 0 0.35,-0.15 0.353,0.353 0 0 0 0.11,-0.161 0.4,0.4 0 0 0 0.04,-0.189 0.473,0.473 0 0 0 -0.15,-0.351 0.5,0.5 0 0 0 -0.7,0 z M 18,5.0006316 a 3,3 0 0 0 -3,-3 H 7 a 3,3 0 0 0 -3,3 v 8.0000004 a 3,3 0 0 0 3,3 h 8 a 3,3 0 0 0 3,-3 z m -2,8.0000004 a 1,1 0 0 1 -1,1 H 7 a 1,1 0 0 1 -1,-1 V 7.0006316 H 16 Z M 16,6.0006316 H 6 v -1 a 1,1 0 0 1 1,-1 h 8 a 1,1 0 0 1 1,1 z M 11,18.000632 H 3 a 1,1 0 0 1 -1,-1 v -6 h 1 v -1 H 2 V 9.0006316 a 1,1 0 0 1 1,-1 v -2 a 3,3 0 0 0 -3,3 v 8.0000004 a 3,3 0 0 0 3,3 h 8 a 3,3 0 0 0 3,-3 h -2 a 1,1 0 0 1 -1,1 z' } ], [ 'ph-readermode-text-size', { viewBox: '0 0 20 12.5', path: 'M 10.422,11.223 A 0.712,0.712 0 0 1 10.295,11.007 L 6.581,0 H 4.68 L 0.933,11.309 0,11.447 V 12.5 H 3.594 V 11.447 L 2.655,11.325 A 0.3,0.3 0 0 1 2.468,11.211 0.214,0.214 0 0 1 2.419,10.974 L 3.341,8.387 h 3.575 l 0.906,2.652 a 0.18,0.18 0 0 1 -0.016,0.18 0.217,0.217 0 0 1 -0.139,0.106 L 6.679,11.447 V 12.5 h 4.62 V 11.447 L 10.663,11.325 A 0.512,0.512 0 0 1 10.422,11.223 Z M 3.659,7.399 5.063,2.57 6.5,7.399 Z M 19.27,11.464 A 0.406,0.406 0 0 1 19.009,11.337 0.368,0.368 0 0 1 18.902,11.072 V 6.779 A 3.838,3.838 0 0 0 18.67,5.318 1.957,1.957 0 0 0 18.01,4.457 2.48,2.48 0 0 0 16.987,4.044 7.582,7.582 0 0 0 15.67,3.938 a 6.505,6.505 0 0 0 -1.325,0.139 5.2,5.2 0 0 0 -1.2,0.4 2.732,2.732 0 0 0 -0.864,0.624 1.215,1.215 0 0 0 -0.331,0.833 0.532,0.532 0 0 0 0.119,0.383 0.665,0.665 0 0 0 0.257,0.172 0.916,0.916 0 0 0 0.375,0.041 h 1.723 V 4.942 A 4.429,4.429 0 0 1 14.611,4.91 2.045,2.045 0 0 1 14.836,4.885 c 0.09,0 0.192,-0.008 0.306,-0.008 a 1.849,1.849 0 0 1 0.808,0.151 1.247,1.247 0 0 1 0.71,0.89 2.164,2.164 0 0 1 0.049,0.51 c 0,0.076 -0.008,0.152 -0.008,0.228 0,0.076 -0.008,0.139 -0.008,0.221 v 0.2 q -1.152,0.252 -1.976,0.489 a 12.973,12.973 0 0 0 -1.391,0.474 4.514,4.514 0 0 0 -0.91,0.485 2.143,2.143 0 0 0 -0.527,0.523 1.594,1.594 0 0 0 -0.245,0.592 3.739,3.739 0 0 0 -0.061,0.693 2.261,2.261 0 0 0 0.171,0.9 2.024,2.024 0 0 0 0.469,0.682 2.084,2.084 0 0 0 0.693,0.432 2.364,2.364 0 0 0 0.852,0.151 3.587,3.587 0 0 0 1.068,-0.159 6.441,6.441 0 0 0 1.835,-0.877 l 0.22,0.832 H 20 v -0.783 z m -2.588,-0.719 a 4.314,4.314 0 0 1 -0.5,0.188 5.909,5.909 0 0 1 -0.493,0.123 2.665,2.665 0 0 1 -0.543,0.057 1.173,1.173 0 0 1 -0.861,-0.363 1.166,1.166 0 0 1 -0.245,-0.392 1.357,1.357 0 0 1 -0.086,-0.486 1.632,1.632 0 0 1 0.123,-0.657 1.215,1.215 0 0 1 0.432,-0.5 3.151,3.151 0 0 1 0.837,-0.392 12.429,12.429 0 0 1 1.334,-0.334 z' } ], diff --git a/src/logger-ui.html b/src/logger-ui.html index 79c13631940ec..6463d9341b8dd 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -3,11 +3,11 @@ - - - - - + + + + + diff --git a/src/whitelist.html b/src/whitelist.html index 41c15c4bfa0f4..b5a63a186e84e 100644 --- a/src/whitelist.html +++ b/src/whitelist.html @@ -8,7 +8,7 @@ - + @@ -22,14 +22,14 @@
        -

        +

        info-circle

        - - + +    - - + +

        From 6d9dc3ac0cb5cc6b937bc65c9665ab1cbc381089 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Dec 2020 11:25:59 -0500 Subject: [PATCH 3956/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index dd1b369fb6355..802a8ef77dbdd 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.2 +1.31.3.3 From a6fc978f709705338284ce87f32628e9aeaad139 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Dec 2020 11:29:09 -0500 Subject: [PATCH 3957/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/bg/messages.json | 2 +- src/_locales/ca/messages.json | 2 +- src/_locales/da/messages.json | 2 +- src/_locales/de/messages.json | 2 +- src/_locales/es/messages.json | 2 +- src/_locales/et/messages.json | 2 +- src/_locales/fr/messages.json | 2 +- src/_locales/hu/messages.json | 4 ++-- src/_locales/it/messages.json | 2 +- src/_locales/ja/messages.json | 2 +- src/_locales/ka/messages.json | 2 +- src/_locales/nl/messages.json | 2 +- src/_locales/pl/messages.json | 2 +- src/_locales/pt_BR/messages.json | 2 +- src/_locales/pt_PT/messages.json | 2 +- src/_locales/ru/messages.json | 2 +- src/_locales/sk/messages.json | 2 +- src/_locales/sr/messages.json | 2 +- src/_locales/sv/messages.json | 2 +- src/_locales/tr/messages.json | 2 +- src/_locales/zh_CN/messages.json | 2 +- src/_locales/zh_TW/messages.json | 4 ++-- 22 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/_locales/bg/messages.json b/src/_locales/bg/messages.json index 61ffd0b64531e..8cae41a2275d4 100644 --- a/src/_locales/bg/messages.json +++ b/src/_locales/bg/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Блокиране на елемента в рамката...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json index b0f4d3370baed..e36a3d7817e45 100644 --- a/src/_locales/ca/messages.json +++ b/src/_locales/ca/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Bloca l'element al marc...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json index ba928e046637e..68d81db0959e4 100644 --- a/src/_locales/da/messages.json +++ b/src/_locales/da/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blokér element i ramme...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json index 8c3df4ac0f000..499d0d968aa58 100644 --- a/src/_locales/de/messages.json +++ b/src/_locales/de/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Elemente in Frame sperren …", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/es/messages.json b/src/_locales/es/messages.json index c2cfcbcbb2c9b..4fdf93bc9cf4e 100644 --- a/src/_locales/es/messages.json +++ b/src/_locales/es/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Bloquear elemento en frame...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/et/messages.json b/src/_locales/et/messages.json index 1ba0e01ea14c6..de5cf546fde44 100644 --- a/src/_locales/et/messages.json +++ b/src/_locales/et/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blokeeri raamis olev element...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/fr/messages.json b/src/_locales/fr/messages.json index c41c083a93ab9..40bef27bddbfd 100644 --- a/src/_locales/fr/messages.json +++ b/src/_locales/fr/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Bloquer un élément de cadre", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json index e2ddb6e5cef0e..29b0ca38ee491 100644 --- a/src/_locales/hu/messages.json +++ b/src/_locales/hu/messages.json @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "módosított", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Elem blokkolása a keretben", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json index 7bfd5de0905db..c1af67be7b210 100644 --- a/src/_locales/it/messages.json +++ b/src/_locales/it/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blocca elemento in un frame", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json index 774ccc7e4070d..00f285ff224dd 100644 --- a/src/_locales/ja/messages.json +++ b/src/_locales/ja/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "フレーム内の要素をブロック...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ka/messages.json b/src/_locales/ka/messages.json index 49eb1a2a115de..7563d963f3051 100644 --- a/src/_locales/ka/messages.json +++ b/src/_locales/ka/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "ელემენტის შეზღუდვა ჩარჩოში...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/nl/messages.json b/src/_locales/nl/messages.json index e3e569666a60f..8a4faa628c491 100644 --- a/src/_locales/nl/messages.json +++ b/src/_locales/nl/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Element in frame blokkeren...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/pl/messages.json b/src/_locales/pl/messages.json index c6aede6357217..bdd3acfbf7448 100644 --- a/src/_locales/pl/messages.json +++ b/src/_locales/pl/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Zablokuj element w ramce...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/pt_BR/messages.json b/src/_locales/pt_BR/messages.json index 8dce0c5ea98a5..00f7094641600 100644 --- a/src/_locales/pt_BR/messages.json +++ b/src/_locales/pt_BR/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Bloquear elemento no frame...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/pt_PT/messages.json b/src/_locales/pt_PT/messages.json index 6051d9029208a..fa3e90a6e145b 100644 --- a/src/_locales/pt_PT/messages.json +++ b/src/_locales/pt_PT/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Bloquear elemento no frame...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ru/messages.json b/src/_locales/ru/messages.json index 6dba7374749da..4e861af3a5ae1 100644 --- a/src/_locales/ru/messages.json +++ b/src/_locales/ru/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Заблокировать элемент во фрейме...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json index 6b6437fb6733a..83e706fe076a7 100644 --- a/src/_locales/sk/messages.json +++ b/src/_locales/sk/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Zablokovať prvok v ráme…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/sr/messages.json b/src/_locales/sr/messages.json index dae1c84edd2e6..fec3135ae4414 100644 --- a/src/_locales/sr/messages.json +++ b/src/_locales/sr/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Блокирај елемент у оквиру...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index f97b48ffbc717..16fdf213c27bc 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blockera element i ramar...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 2356200e09cc1..24a85b4915a15 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Çerçevedeki öğeyi engelle...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json index f8a31b0e6b22e..d068d1fb819d8 100644 --- a/src/_locales/zh_CN/messages.json +++ b/src/_locales/zh_CN/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "屏蔽框架中的内容…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/zh_TW/messages.json b/src/_locales/zh_TW/messages.json index f9b0025d0c84d..56e8baeb42ea9 100644 --- a/src/_locales/zh_TW/messages.json +++ b/src/_locales/zh_TW/messages.json @@ -1,6 +1,6 @@ { "extName": { - "message": "uBlock₀", + "message": "uBlock Origin", "description": "extension name." }, "extShortDesc": { @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "阻擋框架中的內容…", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { From ff5390f3a07ea1721cd5dcb652196744389bf948 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 6 Dec 2020 12:10:51 -0500 Subject: [PATCH 3958/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 3714cd34ac385..1f42ccb1375e2 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.2", + "version": "1.31.3.3", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b2", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b2/uBlock0_1.31.3b2.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b3", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b3/uBlock0_1.31.3b3.firefox.signed.xpi" } ] } From 5d7a5a559d6c87c02ed606c6719b277702d7e46a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Dec 2020 10:49:05 -0500 Subject: [PATCH 3959/4093] Fix broken `redirect-rule=` priority parser Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1388 Regression from: - https://github.com/gorhill/uBlock/commit/cf2c638d8e953585651d8a31f3a67f19fbc62842 --- src/js/static-filtering-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index ccdfa506cefb7..dd4ff1b8a2996 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1169,8 +1169,8 @@ const Parser = class { if ( asDataURI ) { token = token.slice(1); } const match = /:-?\d+$/.exec(token); if ( match !== null ) { - token = token.slice(0, match.index); priority = parseInt(token.slice(match.index + 1), 10); + token = token.slice(0, match.index); } return { token, priority, asDataURI }; } From 904aa87e2aacb5fbfbb79ea702891e5be72d4b55 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Dec 2020 11:12:41 -0500 Subject: [PATCH 3960/4093] Fix various regression in behavior of `redirect-rule=` Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1388 Fixed the special `none` redirect resource no longer being enforced. Fixed the enforcement of `important` redirect rules over exceptions and non-important ones. --- src/js/redirect-engine.js | 1 + src/js/static-net-filtering.js | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 1b8780996f611..dc89afe674bf6 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -305,6 +305,7 @@ RedirectEngine.prototype.tokenToURL = function( /******************************************************************************/ RedirectEngine.prototype.hasToken = function(token) { + if ( token === 'none' ) { return true; } const asDataURI = token.charCodeAt(0) === 0x25 /* '%' */; if ( asDataURI ) { token = token.slice(1); diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 75fb0d6635c5b..2f0a494cd44d0 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4261,8 +4261,13 @@ FilterContainer.parseRedirectRequestValue = function(modifier) { }; FilterContainer.compareRedirectRequests = function(a, b) { - if ( (a.bits & AllowAction) !== 0 ) { return -1; } - if ( (b.bits & AllowAction) !== 0 ) { return 1; } + const abits = a.bits, bbits = b.bits; + if ( abits !== bbits ) { + if ( (abits & Important) !== 0 ) { return 1; } + if ( (bbits & Important) !== 0 ) { return -1; } + if ( (abits & AllowAction) !== 0 ) { return -1; } + if ( (bbits & AllowAction) !== 0 ) { return 1; } + } const { token: atok, priority: aint } = FilterContainer.parseRedirectRequestValue(a.modifier); if ( µb.redirectEngine.hasToken(atok) === false ) { return -1; } From ba6339ba75421bd26a95fe9010fe5b7fc622f1fc Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Dec 2020 11:16:18 -0500 Subject: [PATCH 3961/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 802a8ef77dbdd..1968083f0ba73 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.3 +1.31.3.4 From 5d838c209889c815281e69cb6355d06550a35ed1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Dec 2020 11:28:10 -0500 Subject: [PATCH 3962/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/hr/messages.json | 2 +- src/_locales/id/messages.json | 2 +- src/_locales/ko/messages.json | 4 ++-- src/_locales/ml/messages.json | 2 +- src/_locales/ms/messages.json | 2 +- src/_locales/ro/messages.json | 2 +- src/_locales/ta/messages.json | 2 +- src/_locales/te/messages.json | 2 +- src/_locales/uk/messages.json | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/_locales/hr/messages.json b/src/_locales/hr/messages.json index 75a174f88f5b9..29a6bf1685695 100644 --- a/src/_locales/hr/messages.json +++ b/src/_locales/hr/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blokiraj element u okviru...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index ac9dd894d3c0a..39c26693f08c2 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blok elemen dalam bingkai ...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json index c75245d548cbd..f7923efc4a868 100644 --- a/src/_locales/ko/messages.json +++ b/src/_locales/ko/messages.json @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "수정됨", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "프레임 내 구성 요소 차단", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ml/messages.json b/src/_locales/ml/messages.json index e6325ddfcece0..6b622ae50d22e 100644 --- a/src/_locales/ml/messages.json +++ b/src/_locales/ml/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "ഫ്രെയിമിലെ ഘടകം തടയുക ...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ms/messages.json b/src/_locales/ms/messages.json index 238b214544427..f8cfd422bbe0f 100644 --- a/src/_locales/ms/messages.json +++ b/src/_locales/ms/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blok elemen dalam bingkai ...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json index d39d9ca23da18..1ab06809e2a4f 100644 --- a/src/_locales/ro/messages.json +++ b/src/_locales/ro/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blochează element in cardu...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/ta/messages.json b/src/_locales/ta/messages.json index 81fd6e72b191e..3575e99139c5a 100644 --- a/src/_locales/ta/messages.json +++ b/src/_locales/ta/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "சட்டத்தில் தொகுதி உறுப்பு ...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json index 79bc7948d62ec..602f3d6e68af1 100644 --- a/src/_locales/te/messages.json +++ b/src/_locales/te/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "ఫ్రేమ్‌లో మూలకాన్ని బ్లాక్ చేయండి ...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json index f1d8a0ec06393..40c3f096601ed 100644 --- a/src/_locales/uk/messages.json +++ b/src/_locales/uk/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Заблокувати елемент у фреймі", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { From 78d7094616a25b11fe0ba22c5accc8e5263cfdeb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Dec 2020 13:50:56 -0500 Subject: [PATCH 3963/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 1f42ccb1375e2..4846adc619784 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.3", + "version": "1.31.3.4", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b3", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b3/uBlock0_1.31.3b3.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b4", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b4/uBlock0_1.31.3b4.firefox.signed.xpi" } ] } From 780b605badb7a0d2050c2dec7a8eacbd2eb01d2d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 7 Dec 2020 14:32:59 -0500 Subject: [PATCH 3964/4093] Fix missing magnifier in document-blocked page Related feedback: - https://github.com/gorhill/uBlock/commit/e559cb73b9ae28152db45f8059a5fae3429d726c#commitcomment-44887972 Regression from: - https://github.com/gorhill/uBlock/commit/e559cb73b9ae28152db45f8059a5fae3429d726c --- src/css/document-blocked.css | 20 ++++++++++---------- src/css/fa-icons.css | 4 +++- src/document-blocked.html | 2 +- src/img/fontawesome/fontawesome-defs.svg | 2 ++ src/js/document-blocked.js | 8 +++----- src/js/fa-icons.js | 2 ++ 6 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/css/document-blocked.css b/src/css/document-blocked.css index 7e402beec50cf..d7011be80ff03 100644 --- a/src/css/document-blocked.css +++ b/src/css/document-blocked.css @@ -54,34 +54,34 @@ a { position: relative; z-index: 10; } -#theURL > p > span { +#theURL > p > span:last-of-type { background-color: transparent; top: 100%; box-sizing: border-box; color: var(--fg-0-60); cursor: pointer; fill: var(--fg-0-60); - font-size: 1rem; + font-size: 1.2rem; padding: var(--default-gap-xxsmall); position: absolute; transform: translate(0, -50%); } -body[dir="ltr"] #theURL > p > span { +#theURL:not(.collapsed) > p > span:last-of-type > span:first-of-type { + display: none; + } +#theURL.collapsed > p > span:last-of-type > span:last-of-type { + display: none; + } +body[dir="ltr"] #theURL > p > span:last-of-type { right: 0; } -body[dir="rtl"] #theURL > p > span { +body[dir="rtl"] #theURL > p > span:last-of-type { left: 0; } #theURL > p:hover > span { color: var(--default-ink); fill: var(--default-ink); } -#theURL > p > span:before { - content: '\f010'; - } -#theURL.collapsed > p > span:before { - content: '\f00e'; - } #parsed { background-color: var(--bg-1); border: 1px solid var(--bg-code); diff --git a/src/css/fa-icons.css b/src/css/fa-icons.css index 5c8bf7b128fe7..a1d368d03cd1b 100644 --- a/src/css/fa-icons.css +++ b/src/css/fa-icons.css @@ -77,7 +77,9 @@ .fa-icon > .fa-icon_search, .fa-icon > .fa-icon_spinner, .fa-icon > .fa-icon_unlink, -.fa-icon > .fa-icon_upload-alt { +.fa-icon > .fa-icon_upload-alt, +.fa-icon > .fa-icon_zoom-in, +.fa-icon > .fa-icon_zoom-out { width: calc(1em * 1664 / 1792); } .fa-icon > .fa-icon_home { diff --git a/src/document-blocked.html b/src/document-blocked.html index 87120c85a6d81..a4a9540197790 100644 --- a/src/document-blocked.html +++ b/src/document-blocked.html @@ -15,7 +15,7 @@

        diff --git a/src/img/fontawesome/fontawesome-defs.svg b/src/img/fontawesome/fontawesome-defs.svg index d559817f80a90..fe33f8e80a81e 100644 --- a/src/img/fontawesome/fontawesome-defs.svg +++ b/src/img/fontawesome/fontawesome-defs.svg @@ -72,5 +72,7 @@ License - https://github.com/FortAwesome/Font-Awesome/tree/a8386aae19e200ddb0f68 + + diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js index e89b030082b2e..08b2fd7329728 100644 --- a/src/js/document-blocked.js +++ b/src/js/document-blocked.js @@ -98,7 +98,7 @@ let details = {}; /******************************************************************************/ -uDom.nodeFromSelector('#theURL > p').textContent = details.url; +uDom.nodeFromSelector('#theURL > p > span:first-of-type').textContent = details.url; uDom.nodeFromId('why').textContent = details.fs; /******************************************************************************/ @@ -179,11 +179,9 @@ uDom.nodeFromId('why').textContent = details.fs; return; } - const toggler = document.createElement('span'); - toggler.className = 'fa'; - uDom('#theURL > p').append(toggler); + const toggler = document.querySelector('#theURL > p > span:last-of-type'); - uDom(toggler).on('click', function() { + toggler.addEventListener('click', ( ) => { const cl = uDom.nodeFromId('theURL').classList; cl.toggle('collapsed'); vAPI.localStorage.setItem( diff --git a/src/js/fa-icons.js b/src/js/fa-icons.js index 6b64e21bf0a95..3c8f7dbb28154 100644 --- a/src/js/fa-icons.js +++ b/src/js/fa-icons.js @@ -74,6 +74,8 @@ const faIconsInit = (( ) => { [ 'unlink', { viewBox: '0 0 1664 1664', path: 'm 439,1271 -256,256 q -11,9 -23,9 -12,0 -23,-9 -9,-10 -9,-23 0,-13 9,-23 l 256,-256 q 10,-9 23,-9 13,0 23,9 9,10 9,23 0,13 -9,23 z m 169,41 v 320 q 0,14 -9,23 -9,9 -23,9 -14,0 -23,-9 -9,-9 -9,-23 v -320 q 0,-14 9,-23 9,-9 23,-9 14,0 23,9 9,9 9,23 z M 384,1088 q 0,14 -9,23 -9,9 -23,9 H 32 q -14,0 -23,-9 -9,-9 -9,-23 0,-14 9,-23 9,-9 23,-9 h 320 q 14,0 23,9 9,9 9,23 z m 1264,128 q 0,120 -85,203 l -147,146 q -83,83 -203,83 -121,0 -204,-85 L 675,1228 q -21,-21 -42,-56 l 239,-18 273,274 q 27,27 68,27.5 41,0.5 68,-26.5 l 147,-146 q 28,-28 28,-67 0,-40 -28,-68 l -274,-275 18,-239 q 35,21 56,42 l 336,336 q 84,86 84,204 z M 1031,492 792,510 519,236 q -28,-28 -68,-28 -39,0 -68,27 L 236,381 q -28,28 -28,67 0,40 28,68 l 274,274 -18,240 q -35,-21 -56,-42 L 100,652 Q 16,566 16,448 16,328 101,245 L 248,99 q 83,-83 203,-83 121,0 204,85 l 334,335 q 21,21 42,56 z m 633,84 q 0,14 -9,23 -9,9 -23,9 h -320 q -14,0 -23,-9 -9,-9 -9,-23 0,-14 9,-23 9,-9 23,-9 h 320 q 14,0 23,9 9,9 9,23 z M 1120,32 v 320 q 0,14 -9,23 -9,9 -23,9 -14,0 -23,-9 -9,-9 -9,-23 V 32 q 0,-14 9,-23 9,-9 23,-9 14,0 23,9 9,9 9,23 z m 407,151 -256,256 q -11,9 -23,9 -12,0 -23,-9 -9,-10 -9,-23 0,-13 9,-23 l 256,-256 q 10,-9 23,-9 13,0 23,9 9,10 9,23 0,13 -9,23 z' } ], [ 'unlock-alt', { viewBox: '0 0 1152 1536', path: 'm 1056,768 q 40,0 68,28 28,28 28,68 v 576 q 0,40 -28,68 -28,28 -68,28 H 96 Q 56,1536 28,1508 0,1480 0,1440 V 864 q 0,-40 28,-68 28,-28 68,-28 h 32 V 448 Q 128,263 259.5,131.5 391,0 576,0 761,0 892.5,131.5 1024,263 1024,448 q 0,26 -19,45 -19,19 -45,19 h -64 q -26,0 -45,-19 -19,-19 -19,-45 0,-106 -75,-181 -75,-75 -181,-75 -106,0 -181,75 -75,75 -75,181 v 320 z' } ], [ 'upload-alt', { viewBox: '0 0 1664 1600', path: 'm 1280,1408 q 0,-26 -19,-45 -19,-19 -45,-19 -26,0 -45,19 -19,19 -19,45 0,26 19,45 19,19 45,19 26,0 45,-19 19,-19 19,-45 z m 256,0 q 0,-26 -19,-45 -19,-19 -45,-19 -26,0 -45,19 -19,19 -19,45 0,26 19,45 19,19 45,19 26,0 45,-19 19,-19 19,-45 z m 128,-224 v 320 q 0,40 -28,68 -28,28 -68,28 H 96 q -40,0 -68,-28 -28,-28 -28,-68 v -320 q 0,-40 28,-68 28,-28 68,-28 h 427 q 21,56 70.5,92 49.5,36 110.5,36 h 256 q 61,0 110.5,-36 49.5,-36 70.5,-92 h 427 q 40,0 68,28 28,28 28,68 z M 1339,536 q -17,40 -59,40 h -256 v 448 q 0,26 -19,45 -19,19 -45,19 H 704 q -26,0 -45,-19 -19,-19 -19,-45 V 576 H 384 q -42,0 -59,-40 -17,-39 14,-69 L 787,19 q 18,-19 45,-19 27,0 45,19 l 448,448 q 31,30 14,69 z' } ], + [ 'zoom-in', { viewBox: '0 0 1664 1664', path: 'm 1024,672 v 64 q 0,13 -9.5,22.5 Q 1005,768 992,768 H 768 v 224 q 0,13 -9.5,22.5 -9.5,9.5 -22.5,9.5 h -64 q -13,0 -22.5,-9.5 Q 640,1005 640,992 V 768 H 416 q -13,0 -22.5,-9.5 Q 384,749 384,736 v -64 q 0,-13 9.5,-22.5 Q 403,640 416,640 H 640 V 416 q 0,-13 9.5,-22.5 Q 659,384 672,384 h 64 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 v 224 h 224 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 z m 128,32 Q 1152,519 1020.5,387.5 889,256 704,256 519,256 387.5,387.5 256,519 256,704 256,889 387.5,1020.5 519,1152 704,1152 889,1152 1020.5,1020.5 1152,889 1152,704 Z m 512,832 q 0,53 -37.5,90.5 -37.5,37.5 -90.5,37.5 -54,0 -90,-38 L 1103,1284 Q 924,1408 704,1408 561,1408 430.5,1352.5 300,1297 205.5,1202.5 111,1108 55.5,977.5 0,847 0,704 0,561 55.5,430.5 111,300 205.5,205.5 300,111 430.5,55.5 561,0 704,0 q 143,0 273.5,55.5 130.5,55.5 225,150 94.5,94.5 150,225 55.5,130.5 55.5,273.5 0,220 -124,399 l 343,343 q 37,37 37,90 z' } ], + [ 'zoom-out', { viewBox: '0 0 1664 1664', path: 'm 1024,672 v 64 q 0,13 -9.5,22.5 Q 1005,768 992,768 H 416 q -13,0 -22.5,-9.5 Q 384,749 384,736 v -64 q 0,-13 9.5,-22.5 Q 403,640 416,640 h 576 q 13,0 22.5,9.5 9.5,9.5 9.5,22.5 z m 128,32 Q 1152,519 1020.5,387.5 889,256 704,256 519,256 387.5,387.5 256,519 256,704 256,889 387.5,1020.5 519,1152 704,1152 889,1152 1020.5,1020.5 1152,889 1152,704 Z m 512,832 q 0,53 -37.5,90.5 -37.5,37.5 -90.5,37.5 -54,0 -90,-38 L 1103,1284 Q 924,1408 704,1408 561,1408 430.5,1352.5 300,1297 205.5,1202.5 111,1108 55.5,977.5 0,847 0,704 0,561 55.5,430.5 111,300 205.5,205.5 300,111 430.5,55.5 561,0 704,0 q 143,0 273.5,55.5 130.5,55.5 225,150 94.5,94.5 150,225 55.5,130.5 55.5,273.5 0,220 -124,399 l 343,343 q 37,37 37,90 z' } ], // See /img/photon.svg [ 'ph-popups', { viewBox: '0 0 20 20', path: 'm 3.146,1.8546316 a 0.5006316,0.5006316 0 0 0 0.708,-0.708 l -1,-1 a 0.5006316,0.5006316 0 0 0 -0.708,0.708 z m -0.836,2.106 a 0.406,0.406 0 0 0 0.19,0.04 0.5,0.5 0 0 0 0.35,-0.851 0.493,0.493 0 0 0 -0.54,-0.109 0.361,0.361 0 0 0 -0.16,0.109 0.485,0.485 0 0 0 0,0.7 0.372,0.372 0 0 0 0.16,0.111 z m 3,-3 a 0.406,0.406 0 0 0 0.19,0.04 0.513,0.513 0 0 0 0.5,-0.5 0.473,0.473 0 0 0 -0.15,-0.351 0.5,0.5 0 0 0 -0.7,0 0.485,0.485 0 0 0 0,0.7 0.372,0.372 0 0 0 0.16,0.111 z m 13.19,1.04 a 0.5,0.5 0 0 0 0.354,-0.146 l 1,-1 a 0.5006316,0.5006316 0 0 0 -0.708,-0.708 l -1,1 a 0.5,0.5 0 0 0 0.354,0.854 z m 1.35,1.149 a 0.361,0.361 0 0 0 -0.16,-0.109 0.5,0.5 0 0 0 -0.38,0 0.361,0.361 0 0 0 -0.16,0.109 0.485,0.485 0 0 0 0,0.7 0.372,0.372 0 0 0 0.16,0.11 0.471,0.471 0 0 0 0.38,0 0.372,0.372 0 0 0 0.16,-0.11 0.469,0.469 0 0 0 0.15,-0.349 0.43,0.43 0 0 0 -0.04,-0.19 0.358,0.358 0 0 0 -0.11,-0.161 z m -3.54,-2.189 a 0.406,0.406 0 0 0 0.19,0.04 0.469,0.469 0 0 0 0.35,-0.15 0.353,0.353 0 0 0 0.11,-0.161 0.469,0.469 0 0 0 0,-0.379 0.358,0.358 0 0 0 -0.11,-0.161 0.361,0.361 0 0 0 -0.16,-0.109 0.493,0.493 0 0 0 -0.54,0.109 0.358,0.358 0 0 0 -0.11,0.161 0.43,0.43 0 0 0 -0.04,0.19 0.469,0.469 0 0 0 0.15,0.35 0.372,0.372 0 0 0 0.16,0.11 z m 2.544,15.1860004 a 0.5006316,0.5006316 0 0 0 -0.708,0.708 l 1,1 a 0.5006316,0.5006316 0 0 0 0.708,-0.708 z m 0.3,-2 a 0.473,0.473 0 0 0 -0.154,0.354 0.4,0.4 0 0 0 0.04,0.189 0.353,0.353 0 0 0 0.11,0.161 0.469,0.469 0 0 0 0.35,0.15 0.406,0.406 0 0 0 0.19,-0.04 0.372,0.372 0 0 0 0.16,-0.11 0.454,0.454 0 0 0 0.15,-0.35 0.473,0.473 0 0 0 -0.15,-0.351 0.5,0.5 0 0 0 -0.7,0 z m -3,3 a 0.473,0.473 0 0 0 -0.154,0.354 0.454,0.454 0 0 0 0.15,0.35 0.372,0.372 0 0 0 0.16,0.11 0.406,0.406 0 0 0 0.19,0.04 0.469,0.469 0 0 0 0.35,-0.15 0.353,0.353 0 0 0 0.11,-0.161 0.4,0.4 0 0 0 0.04,-0.189 0.473,0.473 0 0 0 -0.15,-0.351 0.5,0.5 0 0 0 -0.7,0 z M 18,5.0006316 a 3,3 0 0 0 -3,-3 H 7 a 3,3 0 0 0 -3,3 v 8.0000004 a 3,3 0 0 0 3,3 h 8 a 3,3 0 0 0 3,-3 z m -2,8.0000004 a 1,1 0 0 1 -1,1 H 7 a 1,1 0 0 1 -1,-1 V 7.0006316 H 16 Z M 16,6.0006316 H 6 v -1 a 1,1 0 0 1 1,-1 h 8 a 1,1 0 0 1 1,1 z M 11,18.000632 H 3 a 1,1 0 0 1 -1,-1 v -6 h 1 v -1 H 2 V 9.0006316 a 1,1 0 0 1 1,-1 v -2 a 3,3 0 0 0 -3,3 v 8.0000004 a 3,3 0 0 0 3,3 h 8 a 3,3 0 0 0 3,-3 h -2 a 1,1 0 0 1 -1,1 z' } ], [ 'ph-readermode-text-size', { viewBox: '0 0 20 12.5', path: 'M 10.422,11.223 A 0.712,0.712 0 0 1 10.295,11.007 L 6.581,0 H 4.68 L 0.933,11.309 0,11.447 V 12.5 H 3.594 V 11.447 L 2.655,11.325 A 0.3,0.3 0 0 1 2.468,11.211 0.214,0.214 0 0 1 2.419,10.974 L 3.341,8.387 h 3.575 l 0.906,2.652 a 0.18,0.18 0 0 1 -0.016,0.18 0.217,0.217 0 0 1 -0.139,0.106 L 6.679,11.447 V 12.5 h 4.62 V 11.447 L 10.663,11.325 A 0.512,0.512 0 0 1 10.422,11.223 Z M 3.659,7.399 5.063,2.57 6.5,7.399 Z M 19.27,11.464 A 0.406,0.406 0 0 1 19.009,11.337 0.368,0.368 0 0 1 18.902,11.072 V 6.779 A 3.838,3.838 0 0 0 18.67,5.318 1.957,1.957 0 0 0 18.01,4.457 2.48,2.48 0 0 0 16.987,4.044 7.582,7.582 0 0 0 15.67,3.938 a 6.505,6.505 0 0 0 -1.325,0.139 5.2,5.2 0 0 0 -1.2,0.4 2.732,2.732 0 0 0 -0.864,0.624 1.215,1.215 0 0 0 -0.331,0.833 0.532,0.532 0 0 0 0.119,0.383 0.665,0.665 0 0 0 0.257,0.172 0.916,0.916 0 0 0 0.375,0.041 h 1.723 V 4.942 A 4.429,4.429 0 0 1 14.611,4.91 2.045,2.045 0 0 1 14.836,4.885 c 0.09,0 0.192,-0.008 0.306,-0.008 a 1.849,1.849 0 0 1 0.808,0.151 1.247,1.247 0 0 1 0.71,0.89 2.164,2.164 0 0 1 0.049,0.51 c 0,0.076 -0.008,0.152 -0.008,0.228 0,0.076 -0.008,0.139 -0.008,0.221 v 0.2 q -1.152,0.252 -1.976,0.489 a 12.973,12.973 0 0 0 -1.391,0.474 4.514,4.514 0 0 0 -0.91,0.485 2.143,2.143 0 0 0 -0.527,0.523 1.594,1.594 0 0 0 -0.245,0.592 3.739,3.739 0 0 0 -0.061,0.693 2.261,2.261 0 0 0 0.171,0.9 2.024,2.024 0 0 0 0.469,0.682 2.084,2.084 0 0 0 0.693,0.432 2.364,2.364 0 0 0 0.852,0.151 3.587,3.587 0 0 0 1.068,-0.159 6.441,6.441 0 0 0 1.835,-0.877 l 0.22,0.832 H 20 v -0.783 z m -2.588,-0.719 a 4.314,4.314 0 0 1 -0.5,0.188 5.909,5.909 0 0 1 -0.493,0.123 2.665,2.665 0 0 1 -0.543,0.057 1.173,1.173 0 0 1 -0.861,-0.363 1.166,1.166 0 0 1 -0.245,-0.392 1.357,1.357 0 0 1 -0.086,-0.486 1.632,1.632 0 0 1 0.123,-0.657 1.215,1.215 0 0 1 0.432,-0.5 3.151,3.151 0 0 1 0.837,-0.392 12.429,12.429 0 0 1 1.334,-0.334 z' } ], From 5d7b2918efbf927c59e6c0a4812b8ccf1b029adf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 10:00:47 -0500 Subject: [PATCH 3965/4093] Harden processing of changes in compiled list format Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1365 This commit adds the compiled magic version number to the compiled data itself, and consequently this allows uBO to no longer require that any given compiled list with a mismatched format to be detected and discarded at launch time. Given this change, uBO no longer needs to rely on the deletion of cached data at launch time to ensure it won't use no longer valid compiled lists. --- src/js/background.js | 7 +++++++ src/js/cosmetic-filtering.js | 12 ++++-------- src/js/html-filtering.js | 6 ++---- src/js/reverselookup.js | 4 ++-- src/js/scriptlet-filtering.js | 6 ++---- src/js/start.js | 8 +++----- src/js/static-net-filtering.js | 14 +++++++------- src/js/storage.js | 18 +++++++++++++++--- src/js/utils.js | 3 ++- 9 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index af21783a24c77..d73551cd9a324 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -154,6 +154,13 @@ const µBlock = (( ) => { // jshint ignore:line compiledFormatChanged: false, selfieIsInvalid: false, + compiledNetworkSection: 100, + compiledCosmeticSection: 200, + compiledScriptletSection: 300, + compiledHTMLSection: 400, + compiledSentinelSection: 1000, + compiledBadSubsection: 1, + restoreBackupSettings: { lastRestoreFile: '', lastRestoreTime: 0, diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 91155a627ea45..8c1943f7ac814 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -340,8 +340,7 @@ FilterContainer.prototype.keyFromSelector = function(selector) { /******************************************************************************/ FilterContainer.prototype.compile = function(parser, writer) { - // 1000 = cosmetic filtering - writer.select(1000); + writer.select(µb.compiledCosmeticSection); if ( parser.hasOptions() === false ) { this.compileGenericSelector(parser, writer); @@ -551,8 +550,7 @@ FilterContainer.prototype.fromCompiledContent = function(reader, options) { return; } - // 1000 = cosmetic filtering - reader.select(1000); + reader.select(µb.compiledCosmeticSection); let db, bucket; @@ -643,8 +641,7 @@ FilterContainer.prototype.fromCompiledContent = function(reader, options) { /******************************************************************************/ FilterContainer.prototype.skipGenericCompiledContent = function(reader) { - // 1000 = cosmetic filtering - reader.select(1000); + reader.select(µb.compiledCosmeticSection); while ( reader.next() ) { this.acceptedCount += 1; @@ -685,8 +682,7 @@ FilterContainer.prototype.skipGenericCompiledContent = function(reader) { /******************************************************************************/ FilterContainer.prototype.skipCompiledContent = function(reader) { - // 1000 = cosmetic filtering - reader.select(1000); + reader.select(µb.compiledCosmeticSection); while ( reader.next() ) { this.acceptedCount += 1; diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 91cf126af1c40..4ca03ce503815 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -304,8 +304,7 @@ return; } - // 1002 = html filtering - writer.select(1002); + writer.select(µb.compiledHTMLSection); // TODO: Mind negated hostnames, they are currently discarded. @@ -327,8 +326,7 @@ // Don't bother loading filters if stream filtering is not supported. if ( µb.canFilterResponseData === false ) { return; } - // 1002 = html filtering - reader.select(1002); + reader.select(µb.compiledHTMLSection); while ( reader.next() ) { acceptedCount += 1; diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index 5557f599f554b..7a228231ad227 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -64,7 +64,7 @@ if ( for ( const assetKey in listEntries ) { const entry = listEntries[assetKey]; if ( entry === undefined ) { continue; } - const content = extractBlocks(entry.content, 0, 1); + const content = extractBlocks(entry.content, 100, 101); let pos = 0; for (;;) { pos = content.indexOf(compiledFilter, pos); @@ -165,7 +165,7 @@ if ( for ( const assetKey in listEntries ) { const entry = listEntries[assetKey]; if ( entry === undefined ) { continue; } - let content = extractBlocks(entry.content, 1000, 2000), + let content = extractBlocks(entry.content, 200, 1000), isProcedural, found; let pos = 0; diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index c05341935cb10..f348b0ee31d86 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -230,8 +230,7 @@ }; api.compile = function(parser, writer) { - // 1001 = scriptlet injection - writer.select(1001); + writer.select(µb.compiledScriptletSection); // Only exception filters are allowed to be global. const { raw, exception } = parser.result; @@ -270,8 +269,7 @@ // 4 -1 api.fromCompiledContent = function(reader) { - // 1001 = scriptlet injection - reader.select(1001); + reader.select(µb.compiledScriptletSection); while ( reader.next() ) { acceptedCount += 1; diff --git a/src/js/start.js b/src/js/start.js index a1298677ab6dd..efe9bbd6fe738 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -190,7 +190,6 @@ const onUserSettingsReady = function(fetched) { const onCacheSettingsReady = async function(fetched) { if ( fetched.compiledMagic !== µb.systemSettings.compiledMagic ) { - await µb.assets.remove(/^compiled\//); µb.compiledFormatChanged = true; µb.selfieIsInvalid = true; } @@ -302,10 +301,9 @@ try { }), µb.cacheStorage.get( { compiledMagic: 0, selfieMagic: 0 } - ).then(fetched => - onCacheSettingsReady(fetched) - ).then(( ) => { - log.info(`Integrity of cached data processed ${Date.now()-vAPI.T0} ms after launch`); + ).then(fetched => { + log.info(`Cache magic numbers ready ${Date.now()-vAPI.T0} ms after launch`); + onCacheSettingsReady(fetched); }), vAPI.storage.get(createDefaultProps()).then(fetched => { log.info(`First fetch ready ${Date.now()-vAPI.T0} ms after launch`); diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 2f0a494cd44d0..f52c4c45b16f8 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3639,9 +3639,11 @@ FilterContainer.prototype.compile = function(parser, writer) { return false; } - // 0 = network filters - // 1 = network filters: bad filters - writer.select(parsed.badFilter ? 1 : 0); + writer.select( + parsed.badFilter + ? µb.compiledNetworkSection + µb.compiledBadSubsection + : µb.compiledNetworkSection + ); // Reminder: // `redirect=` is a combination of a `redirect-rule` filter and a @@ -3808,8 +3810,7 @@ FilterContainer.prototype.compileToAtomicFilter = function( /******************************************************************************/ FilterContainer.prototype.fromCompiledContent = function(reader) { - // 0 = network filters - reader.select(0); + reader.select(µb.compiledNetworkSection); while ( reader.next() ) { this.acceptedCount += 1; if ( this.goodFilters.has(reader.line) ) { @@ -3819,8 +3820,7 @@ FilterContainer.prototype.fromCompiledContent = function(reader) { } } - // 1 = network filters: bad filter directives - reader.select(1); + reader.select(µb.compiledNetworkSection + µb.compiledBadSubsection); while ( reader.next() ) { this.badFilters.add(reader.line); } diff --git a/src/js/storage.js b/src/js/storage.js index 3ff2359bbf579..e86d02cd9b378 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -733,12 +733,18 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { µBlock.getCompiledFilterList = async function(assetKey) { const compiledPath = 'compiled/' + assetKey; + // https://github.com/uBlockOrigin/uBlock-issues/issues/1365 + // Verify that the list version matches that of the current compiled + // format. if ( this.compiledFormatChanged === false && this.badLists.has(assetKey) === false ) { const compiledDetails = await this.assets.get(compiledPath); - if ( compiledDetails.content !== '' ) { + if ( + parseInt(compiledDetails.content, 10) === + this.systemSettings.compiledMagic + ) { compiledDetails.assetKey = assetKey; return compiledDetails; } @@ -878,7 +884,13 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { staticNetFilteringEngine.compile(parser, writer); } - return writer.toString(); + // https://github.com/uBlockOrigin/uBlock-issues/issues/1365 + // Embed version into compiled list itself: it is encoded in as the + // first digits followed by a whitespace. + const compiledContent + = `${this.systemSettings.compiledMagic}\n` + writer.toString(); + + return compiledContent; }; /******************************************************************************/ @@ -889,7 +901,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { µBlock.applyCompiledFilters = function(rawText, firstparty) { if ( rawText === '' ) { return; } - let reader = new this.CompiledLineIO.Reader(rawText); + const reader = new this.CompiledLineIO.Reader(rawText); this.staticNetFilteringEngine.fromCompiledContent(reader); this.staticExtFilteringEngine.fromCompiledContent(reader, { skipGenericCosmetic: this.userSettings.ignoreGenericCosmeticFilters, diff --git a/src/js/utils.js b/src/js/utils.js index 983b75120ac74..57f7370a14439 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -153,6 +153,7 @@ if ( this.block === undefined ) { this.blocks.set(blockId, (this.block = [])); } + return this; } toString() { let result = []; @@ -179,7 +180,7 @@ this.blocks = new Map(); this.properties = new Map(); let reBlockStart = new RegExp( - '^' + this.io.blockStartPrefix + '(\\d+)\\n', + `^${this.io.blockStartPrefix}(\\d+)\\n`, 'gm' ); let match = reBlockStart.exec(raw); From 9aef41738b66b25028403d8e0f25f7fb4deafb0e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 10:11:34 -0500 Subject: [PATCH 3966/4093] Prevent non-stable `uiTheme` from being used in stable build Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1389 Asking people to respect the warning in the documentation does not work, consequently the setting will be now disabled for stable releases. --- src/js/messaging.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index b8e8cdf3264cf..4e7b750b173b2 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -182,7 +182,9 @@ const onMessage = function(request, sender, callback) { case 'uiStyles': response = { uiStyles: µb.hiddenSettings.uiStyles, - uiTheme: µb.hiddenSettings.uiTheme, + uiTheme: vAPI.webextFlavor.soup.has('devbuild') + ? µb.hiddenSettings.uiTheme + : 'unset', }; break; From 5c9b9b6aa35e2bf9194c1407a478a1322982a4df Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 10:41:05 -0500 Subject: [PATCH 3967/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 1968083f0ba73..fc4836f18bc15 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.4 +1.31.3.5 From a440bcbc4675e19f7966245767acf426c1054b57 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 10:43:02 -0500 Subject: [PATCH 3968/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/he/messages.json | 2 +- src/_locales/hi/messages.json | 2 +- src/_locales/lt/messages.json | 22 +++++++++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json index 33ecb87e6e6c8..37c495c2fd0a8 100644 --- a/src/_locales/he/messages.json +++ b/src/_locales/he/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "חסום אלמנט בתוך מסגרת...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json index 6844f29ecc897..cebfef4778e80 100644 --- a/src/_locales/hi/messages.json +++ b/src/_locales/hi/messages.json @@ -1072,7 +1072,7 @@ "description": "short for 'gigabytes'" }, "clickToLoad": { - "message": "Click to load", + "message": "लोड करने के लिए क्लिक करें", "description": "Message used in frame placeholders" }, "dummy": { diff --git a/src/_locales/lt/messages.json b/src/_locales/lt/messages.json index b0ba79c68fec0..b602a8657921a 100644 --- a/src/_locales/lt/messages.json +++ b/src/_locales/lt/messages.json @@ -12,7 +12,7 @@ "description": "English: uBlock₀ — Dashboard" }, "dashboardUnsavedWarning": { - "message": "Warning! You have unsaved changes", + "message": "Dėmesio! Turite neišsaugotų pakeitimų", "description": "A warning in the dashboard when navigating away from unsaved changes" }, "dashboardUnsavedWarningStay": { @@ -100,7 +100,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "Blocked since install", + "message": "Užblokuota nuo įdiegimo", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -180,7 +180,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { - "message": "Pop-up windows", + "message": "Iškylantysis langas", "description": "Caption for the no-popups per-site switch" }, "popupNoLargeMedia_v2": { @@ -200,11 +200,11 @@ "description": "Caption for the no-scripting per-site switch" }, "popupMoreButton_v2": { - "message": "More", + "message": "Daugiau", "description": "Label to be used to show popup panel sections" }, "popupLessButton_v2": { - "message": "Less", + "message": "Mažiau", "description": "Label to be used to hide popup panel sections" }, "popupTipGlobalRules": { @@ -264,7 +264,7 @@ "description": "appears in popup" }, "popupVersion": { - "message": "Version", + "message": "Versija", "description": "Example of use: Version 1.26.4" }, "pickerCreate": { @@ -468,7 +468,7 @@ "description": "used as a tooltip for the out-of-date icon beside a list" }, "3pViewContent": { - "message": "view content", + "message": "peržiūrėti turinį", "description": "used as a tooltip for eye icon beside a list" }, "3pLastUpdate": { @@ -552,7 +552,7 @@ "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { - "message": "Sort:", + "message": "Rikiuoti pagal:", "description": "English: label for sort option." }, "rulesSortByType": { @@ -860,11 +860,11 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "Programinis kodas", "description": "Link text to source code repo" }, "aboutTranslations": { - "message": "Translations", + "message": "Vertimai", "description": "Link text to translations repo" }, "aboutFilterLists": { @@ -912,7 +912,7 @@ "description": "No longer used" }, "subscribeButton": { - "message": "Subscribe", + "message": "Prenumeruoti", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { From 3ff6617ea3079328e80602d2bf8034e03c8fb9e1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 11:16:17 -0500 Subject: [PATCH 3969/4093] Fix block filter reported in the logger despite being excepted Reported internally by @uBlock-user. Also, fixed broken caching of `cname` exception, which forced uBO to repeatedly evaluate whether a `cname` exception exists when a block `cname`-cloaked request is encountered. --- src/js/pagestore.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 037c84875969f..cecabc3b7ca7d 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -377,7 +377,9 @@ const PageStore = class { setFrameURL(frameId, frameURL) { let frameStore = this.frames.get(frameId); if ( frameStore !== undefined ) { - frameStore.init(frameURL); + if ( frameURL !== frameStore.rawURL ) { + frameStore.init(frameURL); + } } else { frameStore = FrameStore.factory(frameURL); this.frames.set(frameId, frameStore); @@ -613,7 +615,7 @@ const PageStore = class { result = snfe.matchString(fctxt); if ( result !== 0 ) { if ( loggerEnabled ) { - fctxt.filter = snfe.toLogData(); + fctxt.setFilter(snfe.toLogData()); } // https://github.com/uBlockOrigin/uBlock-issues/issues/943 // Blanket-except blocked aliased canonical hostnames? @@ -872,20 +874,16 @@ const PageStore = class { ? frameStore.rawURL : fctxt.getDocOrigin() ); - if ( result === 2 ) { - exceptCname = µb.logger.enabled - ? µb.staticNetFilteringEngine.toLogData() - : true; - } else { - exceptCname = false; - } + exceptCname = result === 2 + ? µb.staticNetFilteringEngine.toLogData() + : false; if ( frameStore instanceof Object ) { frameStore.exceptCname = exceptCname; } } if ( exceptCname === false ) { return false; } if ( exceptCname instanceof Object ) { - fctxt.pushFilter(exceptCname); + fctxt.setFilter(exceptCname); } return true; } From 42a9f99489770b17636d44e363147c713d3eefbd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 12:05:02 -0500 Subject: [PATCH 3970/4093] Create main.yml This probably won't work... --- .github/workflows/main.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000000..1ed2d303546a6 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,32 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the action will run. +on: + # Triggers the workflow on push or pull request events but only for the master branch + create: + branches: + - 'master' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + + # Runs a set of commands using the runners shell + - name: Create packages + run: | + ./tools/make-chromium.sh $GITHUB_REF + ./tools/make-firefox.sh $GITHUB_REF + ./tools/make-thunderbird.sh $GITHUB_REF From 958c1cf9ea0c23a32256249912a2f729ce9a206c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 12:37:09 -0500 Subject: [PATCH 3971/4093] Update main.yml --- .github/workflows/main.yml | 44 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1ed2d303546a6..bac4a97ec047d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,32 +1,32 @@ -# This is a basic workflow to help you get started with Actions +name: GitHub CI -name: CI - -# Controls when the action will run. on: - # Triggers the workflow on push or pull request events but only for the master branch create: - branches: - - 'master' - - # Allows you to run this workflow manually from the Actions tab + branches: master workflow_dispatch: -# A workflow run is made up of one or more jobs that can run sequentially or in parallel +# I used as template to get started: +# https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/.github/workflows/releases.yml + jobs: - # This workflow contains a single job called "build" build: - # The type of runner that the job will run on + name: Build packages runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - # Runs a set of commands using the runners shell - - name: Create packages + - name: Build all + if: startsWith(github.ref, 'refs/tags/') run: | - ./tools/make-chromium.sh $GITHUB_REF - ./tools/make-firefox.sh $GITHUB_REF - ./tools/make-thunderbird.sh $GITHUB_REF + ./tools/make-chromium.sh ${{ github.ref }} + ./tools/make-firefox.sh ${{ github.ref }} + ./tools/make-thunderbird.sh ${{ github.ref }} + + - name: Upload packages + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + files: | + uBlock/dist/build/uBlock0_${{ github.ref }}.chromium.zip + uBlock/dist/build/uBlock0_${{ github.ref }}.firefox.xpi + uBlock/dist/build/uBlock0_${{ github.ref }}.thunderbird.xpi From 4c2b1493bc333fbcd79c41a843374649d865e4bd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 13:26:00 -0500 Subject: [PATCH 3972/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 4846adc619784..10ff84f14fd62 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,10 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.4", + "version": "1.31.3.5", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b4", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b4/uBlock0_1.31.3b4.firefox.signed.xpi" + "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b5", + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b5/uBlock0_1.31.3b5.firefox.signed.xpi" } ] } From ec8a9dcd8286f11298fa0c3c93182fedb302e30d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 8 Dec 2020 13:35:28 -0500 Subject: [PATCH 3973/4093] Update main.yml --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bac4a97ec047d..cb2bbc276e466 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,6 +27,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: files: | - uBlock/dist/build/uBlock0_${{ github.ref }}.chromium.zip - uBlock/dist/build/uBlock0_${{ github.ref }}.firefox.xpi - uBlock/dist/build/uBlock0_${{ github.ref }}.thunderbird.xpi + ./dist/build/uBlock0_${{ github.ref }}.chromium.zip + ./dist/build/uBlock0_${{ github.ref }}.firefox.xpi + ./dist/build/uBlock0_${{ github.ref }}.thunderbird.xpi From b553a66f7072b7cfbc231bf6b87ba718b31fc36e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 07:56:32 -0500 Subject: [PATCH 3974/4093] Remove `update_info_url` to prevent Firefox from using the link Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1391 --- dist/firefox/updates.template.json | 1 - 1 file changed, 1 deletion(-) diff --git a/dist/firefox/updates.template.json b/dist/firefox/updates.template.json index 3a78fdb85d20e..8600e11684336 100644 --- a/dist/firefox/updates.template.json +++ b/dist/firefox/updates.template.json @@ -5,7 +5,6 @@ { "version": "$ext_version", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/$tag_version", "update_link": "https://github.com/gorhill/uBlock/releases/download/$tag_version/uBlock0_$tag_version.firefox.signed.xpi" } ] From 0b5f53923fc4fd7e6927ce57cd9226a61bcbdb88 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 08:16:28 -0500 Subject: [PATCH 3975/4093] Add basic compatibility with ABP's `rewrite` option Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/857 The recognized resources are: - abp-resource:blank-mp3 - abp-resource:blank-js ABP's tokens are excluded from auto-complete so as to not get in the way of uBO's filter list maintainers. --- src/js/codemirror/ubo-static-filtering.js | 17 ++++++++++++++--- src/js/pagestore.js | 19 +++++++++---------- src/js/redirect-engine.js | 11 ++++++++--- src/js/static-filtering-parser.js | 2 ++ 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 183af09099d78..1b987b22b8218 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -185,7 +185,7 @@ CodeMirror.defineMode('ubo-static-filtering', function() { // Warn about unknown redirect tokens. if ( string.charCodeAt(pos - 1) === 0x3D /* '=' */ && - /[$,]redirect(-rule)?=$/.test(string.slice(0, pos)) + /[$,](redirect(-rule)?|rewrite)=$/.test(string.slice(0, pos)) ) { style = 'value'; const end = parser.skipUntil( @@ -418,6 +418,12 @@ const initHints = function() { return (item[1] & 0b01) !== 0; }) ); + const excludedHints = new Set([ + 'genericblock', + 'object-subrequest', + 'rewrite', + 'webrtc', + ]); const pickBestHints = function(cursor, seedLeft, seedRight, hints) { const seed = (seedLeft + seedRight).trim(); @@ -471,6 +477,7 @@ const initHints = function() { const isException = parser.isException(); const hints = []; for ( let [ text, bits ] of parser.netOptionTokenDescriptors ) { + if ( excludedHints.has(text) ) { continue; } if ( isNegated && (bits & parser.OPTCanNegate) === 0 ) { continue; } if ( isException ) { if ( (bits & parser.OPTBlockOnly) !== 0 ) { continue; } @@ -488,6 +495,7 @@ const initHints = function() { const getNetRedirectHints = function(cursor, seedLeft, seedRight) { const hints = []; for ( const text of redirectNames.keys() ) { + if ( text.startsWith('abp-resource:') ) { continue; } hints.push(text); } return pickBestHints(cursor, seedLeft, seedRight, hints); @@ -495,7 +503,10 @@ const initHints = function() { const getNetHints = function(cursor, line) { const beg = cursor.ch; - if ( parser.optionsSpan.len === 0 ) { + if ( + parser.optionsAnchorSpan.len === 0 && + line.endsWith('$') === false + ) { if ( /[^\w\x80-\xF4#,.-]/.test(line) === false ) { return getOriginHints(cursor, line); } @@ -511,7 +522,7 @@ const initHints = function() { if ( assignPos === -1 ) { return getNetOptionHints(cursor, matchLeft[0], matchRight[0]); } - if ( /^redirect(-rule)?=/.test(matchLeft[0]) ) { + if ( /^(redirect(-rule)?|rewrite)=/.test(matchLeft[0]) ) { return getNetRedirectHints( cursor, matchLeft[0].slice(assignPos + 1), diff --git a/src/js/pagestore.js b/src/js/pagestore.js index cecabc3b7ca7d..980ba3afbb17d 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -377,16 +377,15 @@ const PageStore = class { setFrameURL(frameId, frameURL) { let frameStore = this.frames.get(frameId); if ( frameStore !== undefined ) { - if ( frameURL !== frameStore.rawURL ) { - frameStore.init(frameURL); - } - } else { - frameStore = FrameStore.factory(frameURL); - this.frames.set(frameId, frameStore); - this.frameAddCount += 1; - if ( (this.frameAddCount & 0b111111) === 0 ) { - this.pruneFrames(); - } + return frameURL === frameStore.rawURL + ? frameStore + : frameStore.init(frameURL); + } + frameStore = FrameStore.factory(frameURL); + this.frames.set(frameId, frameStore); + this.frameAddCount += 1; + if ( (this.frameAddCount & 0b111111) === 0 ) { + this.pruneFrames(); } return frameStore; } diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index dc89afe674bf6..bd1147fd3a7e0 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -121,7 +121,7 @@ const redirectableResources = new Map([ data: 'text', } ], [ 'noop-0.1s.mp3', { - alias: 'noopmp3-0.1s', + alias: [ 'noopmp3-0.1s', 'abp-resource:blank-mp3' ], data: 'blob', } ], [ 'noop-1s.mp4', { @@ -132,7 +132,7 @@ const redirectableResources = new Map([ alias: 'noopframe', } ], [ 'noop.js', { - alias: 'noopjs', + alias: [ 'noopjs', 'abp-resource:blank-js' ], data: 'text', } ], [ 'noop.txt', { @@ -461,7 +461,12 @@ RedirectEngine.prototype.loadBuiltinResources = function() { params: details.params, }); this.resources.set(name, entry); - if ( details.alias !== undefined ) { + if ( details.alias === undefined ) { return; } + if ( Array.isArray(details.alias) ) { + for ( const alias of details.alias ) { + this.aliases.set(alias, name); + } + } else { this.aliases.set(details.alias, name); } }; diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index dd4ff1b8a2996..3d4a4cb452fd5 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2183,6 +2183,7 @@ const netOptionTokenDescriptors = new Map([ [ 'queryprune', OPTTokenQueryprune | OPTMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], [ 'removeparam', OPTTokenQueryprune | OPTMayAssign | OPTModifierType | OPTNonCspableType | OPTNonRedirectableType ], [ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ], + [ 'rewrite', OPTTokenRedirect | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ], [ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTAllowMayAssign | OPTModifierType | OPTNonCspableType ], [ 'script', OPTTokenScript | OPTCanNegate | OPTNetworkType | OPTModifiableType | OPTRedirectableType | OPTNonCspableType ], [ 'shide', OPTTokenShide | OPTNonNetworkType | OPTNonCspableType | OPTNonRedirectableType ], @@ -2241,6 +2242,7 @@ Parser.netOptionTokenIds = new Map([ [ 'queryprune', OPTTokenQueryprune ], [ 'removeparam', OPTTokenQueryprune ], [ 'redirect', OPTTokenRedirect ], + [ 'rewrite', OPTTokenRedirect ], [ 'redirect-rule', OPTTokenRedirectRule ], [ 'script', OPTTokenScript ], [ 'shide', OPTTokenShide ], From dca1073a9a118d2aa9b6a1b4c0add039e57e3edb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 09:10:18 -0500 Subject: [PATCH 3976/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index fc4836f18bc15..6c21dca0926ce 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.5 +1.31.3.6 From 045f6a4cac6457280e909b4f1a9289b8bd783547 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 10:14:36 -0500 Subject: [PATCH 3977/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cb2bbc276e466..483980690e4ec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,17 +16,18 @@ jobs: - name: Build all if: startsWith(github.ref, 'refs/tags/') run: | - ./tools/make-chromium.sh ${{ github.ref }} - ./tools/make-firefox.sh ${{ github.ref }} - ./tools/make-thunderbird.sh ${{ github.ref }} + /tools/make-chromium.sh $VERSION + /tools/make-firefox.sh $VERSION + /tools/make-thunderbird.sh $VERSION - name: Upload packages uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ format(github.ref, 'refs/tags/', '') }} with: files: | - ./dist/build/uBlock0_${{ github.ref }}.chromium.zip - ./dist/build/uBlock0_${{ github.ref }}.firefox.xpi - ./dist/build/uBlock0_${{ github.ref }}.thunderbird.xpi + /dist/build/uBlock0_$VERSION.chromium.zip + /dist/build/uBlock0_$VERSION.firefox.xpi + /dist/build/uBlock0_$VERSION.thunderbird.xpi From 61b1d87b52e81659e91f192e17460e6d2e6c7eac Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 10:16:02 -0500 Subject: [PATCH 3978/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 483980690e4ec..6ff57edb938c1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,9 +16,9 @@ jobs: - name: Build all if: startsWith(github.ref, 'refs/tags/') run: | - /tools/make-chromium.sh $VERSION - /tools/make-firefox.sh $VERSION - /tools/make-thunderbird.sh $VERSION + tools/make-chromium.sh $VERSION + tools/make-firefox.sh $VERSION + tools/make-thunderbird.sh $VERSION - name: Upload packages uses: softprops/action-gh-release@v1 @@ -28,6 +28,6 @@ jobs: VERSION: ${{ format(github.ref, 'refs/tags/', '') }} with: files: | - /dist/build/uBlock0_$VERSION.chromium.zip - /dist/build/uBlock0_$VERSION.firefox.xpi - /dist/build/uBlock0_$VERSION.thunderbird.xpi + dist/build/uBlock0_$VERSION.chromium.zip + dist/build/uBlock0_$VERSION.firefox.xpi + dist/build/uBlock0_$VERSION.thunderbird.xpi From 392888506cbda5e45920f1d573630db2f8712c65 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 10:26:06 -0500 Subject: [PATCH 3979/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6ff57edb938c1..7d638224e35ac 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,9 +16,9 @@ jobs: - name: Build all if: startsWith(github.ref, 'refs/tags/') run: | - tools/make-chromium.sh $VERSION - tools/make-firefox.sh $VERSION - tools/make-thunderbird.sh $VERSION + "$GITHUB_WORKSPACE/tools/make-chromium.sh $VERSION" + "$GITHUB_WORKSPACE/tools/make-firefox.sh $VERSION" + "$GITHUB_WORKSPACE/tools/make-thunderbird.sh $VERSION" - name: Upload packages uses: softprops/action-gh-release@v1 @@ -28,6 +28,6 @@ jobs: VERSION: ${{ format(github.ref, 'refs/tags/', '') }} with: files: | - dist/build/uBlock0_$VERSION.chromium.zip - dist/build/uBlock0_$VERSION.firefox.xpi - dist/build/uBlock0_$VERSION.thunderbird.xpi + "$GITHUB_WORKSPACE/dist/build/uBlock0_$VERSION.chromium.zip" + "$GITHUB_WORKSPACE/dist/build/uBlock0_$VERSION.firefox.xpi" + "$GITHUB_WORKSPACE/dist/build/uBlock0_$VERSION.thunderbird.xpi" From ea89baf1fd6dd616110c9cde1599c0f9b16cd517 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 10:35:02 -0500 Subject: [PATCH 3980/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7d638224e35ac..601324b53c5ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,22 +3,24 @@ name: GitHub CI on: create: branches: master - workflow_dispatch: # I used as template to get started: # https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/.github/workflows/releases.yml +# https://github.com/dessant/search-by-image/blob/master/.github/workflows/ci.yml jobs: build: name: Build packages runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/') steps: - - name: Build all - if: startsWith(github.ref, 'refs/tags/') + - name: Clone repository + uses: actions/checkout@v2 + - name: Build all packages run: | - "$GITHUB_WORKSPACE/tools/make-chromium.sh $VERSION" - "$GITHUB_WORKSPACE/tools/make-firefox.sh $VERSION" - "$GITHUB_WORKSPACE/tools/make-thunderbird.sh $VERSION" + tools/make-chromium.sh $VERSION + tools/make-firefox.sh $VERSION + tools/make-thunderbird.sh $VERSION - name: Upload packages uses: softprops/action-gh-release@v1 @@ -28,6 +30,6 @@ jobs: VERSION: ${{ format(github.ref, 'refs/tags/', '') }} with: files: | - "$GITHUB_WORKSPACE/dist/build/uBlock0_$VERSION.chromium.zip" - "$GITHUB_WORKSPACE/dist/build/uBlock0_$VERSION.firefox.xpi" - "$GITHUB_WORKSPACE/dist/build/uBlock0_$VERSION.thunderbird.xpi" + dist/build/uBlock0_$VERSION.chromium.zip + dist/build/uBlock0_$VERSION.firefox.xpi + dist/build/uBlock0_$VERSION.thunderbird.xpi From 50ba57f8fc7bf17c735a67f6bedada250ecd13bf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 10:47:10 -0500 Subject: [PATCH 3981/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 601324b53c5ad..7c22aed6047f4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,6 +16,11 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v2 + - name: Clone uAssets + run: + - pushd .. + - git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git + - popd - name: Build all packages run: | tools/make-chromium.sh $VERSION From 2d4924ab9db13783b640547acf846e5bb58de357 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 10:48:30 -0500 Subject: [PATCH 3982/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7c22aed6047f4..3878ae577cd63 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,9 +18,9 @@ jobs: uses: actions/checkout@v2 - name: Clone uAssets run: - - pushd .. - - git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git - - popd + pushd .. + git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git + popd - name: Build all packages run: | tools/make-chromium.sh $VERSION From f12f1d4c85dd0db753cad34e3491d7f6d3430a80 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 10:51:34 -0500 Subject: [PATCH 3983/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3878ae577cd63..450e1079813c9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: - name: Clone repository uses: actions/checkout@v2 - name: Clone uAssets - run: + run: | pushd .. git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git popd @@ -26,7 +26,6 @@ jobs: tools/make-chromium.sh $VERSION tools/make-firefox.sh $VERSION tools/make-thunderbird.sh $VERSION - - name: Upload packages uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') From 6691282bc2361ff0c5de56fe8514a961e51d5845 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:03:12 -0500 Subject: [PATCH 3984/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 450e1079813c9..599ac13f22040 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,6 +21,9 @@ jobs: pushd .. git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git popd + - name: Set version + run: | + echo "VERSION=${{ format(github.ref, 'refs/tags/', '') }}" >> $GITHUB_ENV - name: Build all packages run: | tools/make-chromium.sh $VERSION @@ -31,7 +34,6 @@ jobs: if: startsWith(github.ref, 'refs/tags/') env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ format(github.ref, 'refs/tags/', '') }} with: files: | dist/build/uBlock0_$VERSION.chromium.zip From 210fbf9353c39319e35e56233b44689b17dceb18 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:09:14 -0500 Subject: [PATCH 3985/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 599ac13f22040..959028469966f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: popd - name: Set version run: | - echo "VERSION=${{ format(github.ref, 'refs/tags/', '') }}" >> $GITHUB_ENV + echo "VERSION=$(basename -- ${{ github.ref }})" >> $GITHUB_ENV - name: Build all packages run: | tools/make-chromium.sh $VERSION From 22a4848d8f0c12df3294e8d3008ee1e51aeb2792 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:23:14 -0500 Subject: [PATCH 3986/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 959028469966f..ea14bf2c47f8d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,13 +29,30 @@ jobs: tools/make-chromium.sh $VERSION tools/make-firefox.sh $VERSION tools/make-thunderbird.sh $VERSION - - name: Upload packages - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') + - name: Upload Chromium package + uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - files: | - dist/build/uBlock0_$VERSION.chromium.zip - dist/build/uBlock0_$VERSION.firefox.xpi - dist/build/uBlock0_$VERSION.thunderbird.xpi + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: dist/build/uBlock0_$VERSION.chromium.zip + asset_name: dist/build/uBlock0_$VERSION.chromium.zip + asset_content_type: application/octet-stream + - name: Upload Firefox package + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: dist/build/uBlock0_$VERSION.firefox.zip + asset_name: dist/build/uBlock0_$VERSION.firefox.zip + asset_content_type: application/octet-stream + - name: Upload Thunderbird package + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: dist/build/uBlock0_$VERSION.thunderbird.zip + asset_name: dist/build/uBlock0_$VERSION.thunderbird.zip + asset_content_type: application/octet-stream From 8a754f65c19cd864dcf6f34980c1f2a513f22391 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:30:09 -0500 Subject: [PATCH 3987/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ea14bf2c47f8d..3814eb3dbf1c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,6 +29,15 @@ jobs: tools/make-chromium.sh $VERSION tools/make-firefox.sh $VERSION tools/make-thunderbird.sh $VERSION + - name: Create GitHub release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ github.token }} + with: + tag_name: ${{ steps.release_info.outputs.TAG }} + release_name: ${{ steps.release_info.outputs.TAG }} + draft: true - name: Upload Chromium package uses: actions/upload-release-asset@v1 env: From 28590e019de6889e5c8f78ed65767b39000b6085 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:32:56 -0500 Subject: [PATCH 3988/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3814eb3dbf1c7..4192fd096f3f3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,7 @@ jobs: tools/make-chromium.sh $VERSION tools/make-firefox.sh $VERSION tools/make-thunderbird.sh $VERSION - - name: Create GitHub release + - name: Create GitHub release id: create_release uses: actions/create-release@v1 env: From 25bf99834faec3d9c56338ee6c5d1b913ee35781 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:35:33 -0500 Subject: [PATCH 3989/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4192fd096f3f3..bcf47fa1ebf2d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,8 +35,8 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} with: - tag_name: ${{ steps.release_info.outputs.TAG }} - release_name: ${{ steps.release_info.outputs.TAG }} + tag_name: $VERSION + release_name: $VERSION draft: true - name: Upload Chromium package uses: actions/upload-release-asset@v1 From ece83e5834e1fcf060b2bf3937b981b11e3ffff0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:47:09 -0500 Subject: [PATCH 3990/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bcf47fa1ebf2d..338ef24d7da5e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,31 +21,32 @@ jobs: pushd .. git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git popd - - name: Set version + - name: Get release information + id: release_info run: | - echo "VERSION=$(basename -- ${{ github.ref }})" >> $GITHUB_ENV - - name: Build all packages - run: | - tools/make-chromium.sh $VERSION - tools/make-firefox.sh $VERSION - tools/make-thunderbird.sh $VERSION + echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - name: Create GitHub release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ github.token }} with: - tag_name: $VERSION - release_name: $VERSION + tag_name: steps.release_info.outputs.VERSION + release_name: steps.release_info.outputs.VERSION draft: true + - name: Build all packages + run: | + tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-firefox.sh ${{ steps.release_info.outputs.VERSION }} + tools/make-thunderbird.sh ${{ steps.release_info.outputs.VERSION }} - name: Upload Chromium package uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_$VERSION.chromium.zip - asset_name: dist/build/uBlock0_$VERSION.chromium.zip + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip + asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip asset_content_type: application/octet-stream - name: Upload Firefox package uses: actions/upload-release-asset@v1 @@ -53,8 +54,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_$VERSION.firefox.zip - asset_name: dist/build/uBlock0_$VERSION.firefox.zip + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.zip + asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.zip asset_content_type: application/octet-stream - name: Upload Thunderbird package uses: actions/upload-release-asset@v1 @@ -62,6 +63,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_$VERSION.thunderbird.zip - asset_name: dist/build/uBlock0_$VERSION.thunderbird.zip + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.zip + asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.zip asset_content_type: application/octet-stream From 95b4ecf5dd668d205dd08dd5155004a0e6560364 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:53:18 -0500 Subject: [PATCH 3991/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 338ef24d7da5e..a7d22f135a4ec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,6 +34,7 @@ jobs: tag_name: steps.release_info.outputs.VERSION release_name: steps.release_info.outputs.VERSION draft: true + prerelease: true - name: Build all packages run: | tools/make-chromium.sh ${{ steps.release_info.outputs.VERSION }} @@ -54,8 +55,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.zip - asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.zip + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi + asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi asset_content_type: application/octet-stream - name: Upload Thunderbird package uses: actions/upload-release-asset@v1 @@ -63,6 +64,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.zip - asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.zip + asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi + asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi asset_content_type: application/octet-stream From 0415e28eaeb19706b8550e47ffd37272ac303b00 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 11:58:46 -0500 Subject: [PATCH 3992/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a7d22f135a4ec..e082c0e8f3008 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,8 +31,8 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} with: - tag_name: steps.release_info.outputs.VERSION - release_name: steps.release_info.outputs.VERSION + tag_name: ${{ steps.release_info.outputs.VERSION }} + release_name: ${{ steps.release_info.outputs.VERSION }} draft: true prerelease: true - name: Build all packages From 777b2122142efb293105a201cc9e2a1d0fefc5d5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 12:00:59 -0500 Subject: [PATCH 3993/4093] Attempt to make make GitHub Actions work --- .github/workflows/main.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e082c0e8f3008..31041f93019d5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -33,7 +33,6 @@ jobs: with: tag_name: ${{ steps.release_info.outputs.VERSION }} release_name: ${{ steps.release_info.outputs.VERSION }} - draft: true prerelease: true - name: Build all packages run: | @@ -47,7 +46,7 @@ jobs: with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip - asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip + asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.chromium.zip asset_content_type: application/octet-stream - name: Upload Firefox package uses: actions/upload-release-asset@v1 @@ -56,7 +55,7 @@ jobs: with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi - asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi + asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.firefox.xpi asset_content_type: application/octet-stream - name: Upload Thunderbird package uses: actions/upload-release-asset@v1 @@ -65,5 +64,5 @@ jobs: with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi - asset_name: dist/build/uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi + asset_name: uBlock0_${{ steps.release_info.outputs.VERSION }}.thunderbird.xpi asset_content_type: application/octet-stream From 8c6fb17ac9bd0d5661d8e47ff262892be9d96eaf Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 12:10:42 -0500 Subject: [PATCH 3994/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 10ff84f14fd62..b920ca2a174d6 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,10 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.5", + "version": "1.31.3.6", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_info_url": "https://github.com/gorhill/uBlock/releases/tag/1.31.3b5", - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b5/uBlock0_1.31.3b5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b6/uBlock0_1.31.3b6.firefox.signed.xpi" } ] } From e287c940dd898fcf911f2fd12bcbc596d43c8492 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 9 Dec 2020 13:33:03 -0500 Subject: [PATCH 3995/4093] Remove usage of Travis CI --- .travis.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index cd8832ef6cb33..0000000000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -language: ruby -sudo: false -env: - matrix: - - BROWSER=chromium EXT=zip - - BROWSER=firefox EXT=xpi - - BROWSER=thunderbird EXT=xpi -script: "./tools/make-${BROWSER}.sh ${TRAVIS_TAG}" -deploy: - provider: releases - prerelease: true - api_key: - secure: ujdEXakLw9fncnI70udUSrRvB0nNUogGr08/eC5UqS4uf5GJ6W6LAB2kNUIn2BaJujQEdYEeH7nFmRKzbqwQs4N1Qm9qOq75zOV6FP/922lSMuPuKB7rXVNRK88WYs/j7g40JEDKEjEYKGa4m3OGnDnywIoKtAsI3kqcCBOe34A= - file: dist/build/uBlock0_${TRAVIS_TAG}.${BROWSER}.${EXT} - skip_cleanup: true - on: - repo: gorhill/uBlock - tags: true - all_branches: true From 646ddff8fe7c012a690bba780def15c58dd09cd3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 08:26:03 -0500 Subject: [PATCH 3996/4093] Minor changes --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 31041f93019d5..95121d64513fd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,8 +4,7 @@ on: create: branches: master -# I used as template to get started: -# https://github.com/DNSCrypt/dnscrypt-proxy/blob/master/.github/workflows/releases.yml +# I used the following project as template to get started: # https://github.com/dessant/search-by-image/blob/master/.github/workflows/ci.yml jobs: @@ -16,11 +15,14 @@ jobs: steps: - name: Clone repository uses: actions/checkout@v2 + with: + persist-credentials: false - name: Clone uAssets run: | pushd .. git clone --depth 1 https://github.com/uBlockOrigin/uAssets.git popd + # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html - name: Get release information id: release_info run: | From aa011e040c227dde9c3605d7b4bae7439e25f428 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 10:00:27 -0500 Subject: [PATCH 3997/4093] Fix thunderbird build script --- tools/make-thunderbird.sh | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tools/make-thunderbird.sh b/tools/make-thunderbird.sh index b73dd901c812e..5a8edd7c24434 100755 --- a/tools/make-thunderbird.sh +++ b/tools/make-thunderbird.sh @@ -16,20 +16,8 @@ cp -R $DES/_locales/nb $DES/_locales/no cp platform/thunderbird/manifest.json $DES/ cp platform/firefox/webext.js $DES/js/ -cp platform/firefox/vapi-usercss.js $DES/js/ cp platform/firefox/vapi-webrequest.js $DES/js/ -echo "*** uBlock0.thunderbird: concatenating content scripts" -cat $DES/js/vapi-usercss.js > /tmp/contentscript.js -echo >> /tmp/contentscript.js -grep -v "^'use strict';$" $DES/js/vapi-usercss.real.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 -rm $DES/js/vapi-usercss.real.js -rm $DES/js/vapi-usercss.pseudo.js - # Firefox/webext-specific rm $DES/img/icon_128.png From 15afd59e1ee606fbea4e66623d5baf0f291d6231 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 10:03:15 -0500 Subject: [PATCH 3998/4093] Fix unstyling of nodes no longer matching procedural filters Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1392 Regression from: - https://github.com/gorhill/uBlock/commit/35aefed92616cbfb75f12f37c7ea7fb3a3cc3369 --- src/js/contentscript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index bd0768c1e6240..fb5e1932ed6ab 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -867,6 +867,7 @@ vAPI.injectScriptlet = function(doc, text) { for ( const node of nodes ) { node.setAttribute(this.masterToken, ''); node.setAttribute(styleToken, ''); + this.styledNodes.add(node); } } From f5c77a711f873f357ee04fb2db1ec02b30afc6e1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 10:07:25 -0500 Subject: [PATCH 3999/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 6c21dca0926ce..00228e58d13c8 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.6 +1.31.3.7 From 2f841259aec719e919bfd313661afbbd9e419c12 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 10:15:47 -0500 Subject: [PATCH 4000/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index b920ca2a174d6..0de46cc4fb150 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.6", + "version": "1.31.3.7", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b6/uBlock0_1.31.3b6.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b7/uBlock0_1.31.3b7.firefox.signed.xpi" } ] } From cb71fb494cd57bee3265164bb1ea9ed2ead2ee89 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 12:51:26 -0500 Subject: [PATCH 4001/4093] Fix DOM watcher not reporting removal of elements Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1392 Regression from: - https://github.com/gorhill/uBlock/commit/6112a68faf46d89c58defdd9e14d4342747c9523 --- src/js/contentscript.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/contentscript.js b/src/js/contentscript.js index fb5e1932ed6ab..3ce10096f8df4 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -362,7 +362,7 @@ vAPI.SafeAnimationFrame = class { }; // https://github.com/chrisaljoudi/uBlock/issues/205 - // Do not handle added node directly from within mutation observer. + // Do not handle added node directly from within mutation observer. const observerHandler = function(mutations) { let i = mutations.length; while ( i-- ) { @@ -376,7 +376,7 @@ vAPI.SafeAnimationFrame = class { removedNodeLists.push(nodeList); } } - if ( addedNodeLists.length !== 0 || removedNodes ) { + if ( addedNodeLists.length !== 0 || removedNodeLists.length !== 0 ) { safeObserverHandlerTimer.start( addedNodeLists.length < 100 ? 1 : undefined ); From 8331500cd89d371f50fa8ff4686bd4957fc7ee44 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 13:15:07 -0500 Subject: [PATCH 4002/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 00228e58d13c8..71c49ec1f6373 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.7 +1.31.3.8 From 75ac182fe15d0ca9f18ca7f76986b0762ff9f3fb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 10 Dec 2020 13:21:39 -0500 Subject: [PATCH 4003/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 0de46cc4fb150..e6a8dc011ce69 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.7", + "version": "1.31.3.8", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b7/uBlock0_1.31.3b7.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b8/uBlock0_1.31.3b8.firefox.signed.xpi" } ] } From ba11a700139bbc648e4ae5b2bc7af90ef03db5df Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 08:29:23 -0500 Subject: [PATCH 4004/4093] Add new scriptlet: no-fetch-if The new scriptlet allows to defuse calls to fetch() by returning a promise which always resolve to an empty response. There is only one argument, which is a space-separated list of conditions which must be ALL fulfilled in order for the defusing to take place. Each condition is a pair of property name and property value separated by a column. Valid property names are those documented as valid `init` options: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch The URL of the fetch() is a special case and does not have to be associated with a property name. Example of usage: ...##+js(no-fetch-if, method:HEAD) Which means: defuse the call to fetch() if there is an explicit option which contains `HEAD`. Another example: ...##+js(no-fetch-if, adsbygoogle.js) Which means: defuse the call to fetch() if the URL contains `adsbygoogle.js`. Multiple conditions can be provided: ...##+js(no-fetch-if, adsbygoogle.js method:HEAD) If at least one condition does not match, the defusing will not take place. The string against which to match can be a literal regular expression: ...##+js(no-fetch-if, /adsbygoogle.js$/ method:/HEAD|POST/) Additonally, the following deprecated scriplets have been removed: - requestAnimationFrame-if.js - setInterval-defuser.js - setTimeout-logger.js --- assets/resources/scriptlets.js | 142 ++++++++++++++------------------- 1 file changed, 60 insertions(+), 82 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 7875ec4617190..4ed5681fa6b0c 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -583,6 +583,65 @@ })(); +/// no-fetch-if.js +(function() { + let arg1 = '{{1}}'; + if ( arg1 === '{{1}}' ) { arg1 = ''; } + const needles = []; + for ( const condition of arg1.split(/\s+/) ) { + const pos = condition.indexOf(':'); + let key, value; + if ( pos !== -1 ) { + key = condition.slice(0, pos); + value = condition.slice(pos + 1); + } else { + key = 'url'; + value = condition; + } + if ( value === '' ) { + value = '^'; + } else if ( value.startsWith('/') && value.endsWith('/') ) { + value = value.slice(1, -1); + } else { + value = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + needles.push({ key, re: new RegExp(value) }); + } + self.fetch = new Proxy(self.fetch, { + apply: function(target, thisArg, args) { + let proceed = true; + try { + const url = args[0] instanceof self.Request + ? args[0].url + : args[0]; + const props = new Map([ [ 'url', url ] ]); + const init = args[1]; + if ( init instanceof Object ) { + for ( const prop in init ) { + if ( init.hasOwnProperty(prop) === false ) { continue; } + props.set( prop, init[prop]); + } + } + proceed = false; + for ( const { key, re } of needles ) { + if ( + props.has(key) === false || + re.test(props.get(key)) === false + ) { + proceed = true; + break; + } + } + } catch(ex) { + } + return proceed + ? Reflect.apply(target, thisArg, args) + : Promise.resolve(new Response()); + } + }); +})(); + + /// remove-attr.js /// alias ra.js (function() { @@ -646,36 +705,6 @@ })(); -/// requestAnimationFrame-if.js -/// alias raf-if.js -// Deprecated, use "no-requestAnimationFrame-if.js" -(function() { - let needle = '{{1}}'; - const not = needle.charAt(0) === '!'; - if ( not ) { needle = needle.slice(1); } - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.startsWith('/') && needle.endsWith('/') ) { - needle = needle.slice(1,-1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - const log = needle === '.?' && not === false ? console.log : undefined; - needle = new RegExp(needle); - window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, { - apply: function(target, thisArg, args) { - const a = String(args[0]); - if ( log !== undefined ) { - log('uBO: requestAnimationFrame("%s")', a); - } else if ( needle.test(a) === not ) { - args[0] = function(){}; - } - return target.apply(thisArg, args); - } - }); -})(); - - /// no-requestAnimationFrame-if.js /// alias norafif.js (function() { @@ -827,32 +856,6 @@ })(); -/// setInterval-defuser.js -/// alias sid.js -(function() { - let needle = '{{1}}'; - const delay = parseInt('{{2}}', 10); - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.startsWith('/') && needle.endsWith('/') ) { - needle = needle.slice(1,-1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - window.setInterval = new Proxy(window.setInterval, { - apply: function(target, thisArg, args) { - const a = args[0]; - const b = args[1]; - if ( (isNaN(delay) || b === delay) && needle.test(a.toString()) ) { - args[0] = function(){}; - } - return target.apply(thisArg, args); - } - }); -})(); - - /// no-setInterval-if.js /// alias nosiif.js (function() { @@ -902,34 +905,9 @@ })(); -/// setTimeout-defuser.js -/// alias std.js -(function() { - let needle = '{{1}}'; - const delay = parseInt('{{2}}', 10); - if ( needle === '' || needle === '{{1}}' ) { - needle = '.?'; - } else if ( needle.startsWith('/') && needle.endsWith('/') ) { - needle = needle.slice(1,-1); - } else { - needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - } - needle = new RegExp(needle); - window.setTimeout = new Proxy(window.setTimeout, { - apply: function(target, thisArg, args) { - const a = args[0]; - const b = args[1]; - if ( (isNaN(delay) || b === delay) && needle.test(a.toString()) ) { - args[0] = function(){}; - } - return target.apply(thisArg, args); - } - }); -})(); - - /// no-setTimeout-if.js /// alias nostif.js +/// alias setTimeout-defuser.js (function() { let needle = '{{1}}'; const needleNot = needle.charAt(0) === '!'; From 45373275db31158e97feac513b4ef5fc0f8a563a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 08:50:58 -0500 Subject: [PATCH 4005/4093] New revision for dev build --- src/_locales/id/messages.json | 2 +- src/_locales/nb/messages.json | 2 +- src/_locales/tr/messages.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index 39c26693f08c2..c12c30bc090f3 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Blok elemen dalam bingkai ...", + "message": "Blokir elemen di dalam frame...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json index a51b38abfa16e..b46528aab4c2f 100644 --- a/src/_locales/nb/messages.json +++ b/src/_locales/nb/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Blokker element i ramme...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { diff --git a/src/_locales/tr/messages.json b/src/_locales/tr/messages.json index 24a85b4915a15..12a324871d931 100644 --- a/src/_locales/tr/messages.json +++ b/src/_locales/tr/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Çerçevedeki öğeyi engelle...", + "message": "Çerçevedeki ögeyi engelle...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { From 28bc132912a0cf283ecd3bd979b10c2074c36e9a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 08:51:41 -0500 Subject: [PATCH 4006/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 71c49ec1f6373..ed0e3139aca49 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.8 +1.31.3.9 From 497dc9a58a87c03e278026521babb2a785c25da5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 08:56:13 -0500 Subject: [PATCH 4007/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index e6a8dc011ce69..06e94d09771e8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.8", + "version": "1.31.3.9", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b8/uBlock0_1.31.3b8.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b9/uBlock0_1.31.3b9.firefox.signed.xpi" } ] } From b6ed83bc5c840431ed03cddaed1daeb395db3b0e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 09:28:29 -0500 Subject: [PATCH 4008/4093] Add logging ability to new scriptlet no-fetch-if When no-fetch-if scriptlet is used without argument, the parameters passed to no-fetch-if will be output to the console, as `uBO: fetch([...list of arguments...])`. --- assets/resources/scriptlets.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 4ed5681fa6b0c..da6c885c8a18e 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -589,6 +589,7 @@ if ( arg1 === '{{1}}' ) { arg1 = ''; } const needles = []; for ( const condition of arg1.split(/\s+/) ) { + if ( condition === '' ) { continue; } const pos = condition.indexOf(':'); let key, value; if ( pos !== -1 ) { @@ -607,6 +608,7 @@ } needles.push({ key, re: new RegExp(value) }); } + const log = needles.length === 0 ? console.log.bind(console) : undefined; self.fetch = new Proxy(self.fetch, { apply: function(target, thisArg, args) { let proceed = true; @@ -622,7 +624,13 @@ props.set( prop, init[prop]); } } - proceed = false; + if ( log !== undefined ) { + const out = Array.from(props) + .map(a => `${a[0]}:${a[1]}`) + .join(' '); + log(`uBO: fetch(${out})`); + } + proceed = needles.length === 0; for ( const { key, re } of needles ) { if ( props.has(key) === false || @@ -652,10 +660,7 @@ if ( selector === '' || selector === '{{2}}' ) { selector = `[${tokens.join('],[')}]`; } - const rmattr = function(ev) { - if ( ev ) { - window.removeEventListener(ev.type, rmattr, true); - } + const rmattr = function() { try { const nodes = document.querySelectorAll(selector); for ( const node of nodes ) { @@ -667,7 +672,7 @@ } }; if ( document.readyState === 'loading' ) { - window.addEventListener('DOMContentLoaded', rmattr, true); + window.addEventListener('DOMContentLoaded', rmattr, { once: true }); } else { rmattr(); } From 286663c9577f57ff5c4f3c20cf4dd152980675fa Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 09:30:43 -0500 Subject: [PATCH 4009/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ed0e3139aca49..f969231216604 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.9 +1.31.3.10 From c41be5b828f7bc5125c83ddbc5913f9155cb5605 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 09:35:41 -0500 Subject: [PATCH 4010/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 06e94d09771e8..27c7e53d6f90a 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.9", + "version": "1.31.3.10", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b9/uBlock0_1.31.3b9.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b10/uBlock0_1.31.3b10.firefox.signed.xpi" } ] } From 24755d4300f9e8f5c32a3c965c98de13ab26a6f4 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 10:34:33 -0500 Subject: [PATCH 4011/4093] Fix broken alias `nostif` Related feedback: - https://github.com/gorhill/uBlock/commit/ba11a700139bbc648e4ae5b2bc7af90ef03db5df#r45030152 Regression from: - https://github.com/gorhill/uBlock/commit/ba11a700139bbc648e4ae5b2bc7af90ef03db5df --- src/js/redirect-engine.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index bd1147fd3a7e0..20c59cb57d6a6 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -375,11 +375,11 @@ RedirectEngine.prototype.resourcesFromString = function(text) { if ( line.startsWith('/// ') ) { if ( details === undefined ) { - details = {}; + details = []; } const [ prop, value ] = line.slice(4).trim().split(/\s+/); if ( value !== undefined ) { - details[prop] = value; + details.push({ prop, value }); } continue; } @@ -401,8 +401,11 @@ RedirectEngine.prototype.resourcesFromString = function(text) { RedirectEntry.fromContent(mime, content) ); - if ( details instanceof Object && details.alias ) { - this.aliases.set(details.alias, name); + if ( Array.isArray(details) ) { + for ( const { prop, value } of details ) { + if ( prop !== 'alias' ) { continue; } + this.aliases.set(value, name); + } } fields = undefined; From 78cf76dd9543c3e3adfe75587b347509b196857d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 10:36:13 -0500 Subject: [PATCH 4012/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index f969231216604..8c2c03c7b625f 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.10 +1.31.3.11 From d0a0984ecebd317e26ca4072617d003b31fa7ab2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 10:41:08 -0500 Subject: [PATCH 4013/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 27c7e53d6f90a..f2864c84255db 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.10", + "version": "1.31.3.11", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b10/uBlock0_1.31.3b10.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b11/uBlock0_1.31.3b11.firefox.signed.xpi" } ] } From 7d90f97aa1cbf2728508506f6dd7a75c054b85d1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 12:34:09 -0500 Subject: [PATCH 4014/4093] Enable the blocking of CSP reports by default Related issue: - https://github.com/LiCybora/NanoDefenderFirefox/issues/196 --- src/js/hnswitches.js | 2 +- src/js/start.js | 32 ++++++++------------------------ 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/js/hnswitches.js b/src/js/hnswitches.js index 826943755cf6b..4f00517de6e4d 100644 --- a/src/js/hnswitches.js +++ b/src/js/hnswitches.js @@ -26,7 +26,7 @@ /******************************************************************************/ -µBlock.HnSwitches = (function() { +µBlock.HnSwitches = (( ) => { /******************************************************************************/ diff --git a/src/js/start.js b/src/js/start.js index efe9bbd6fe738..df3225d8e9753 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -111,33 +111,16 @@ const onVersionReady = function(lastVersion) { µb.redirectEngine.invalidateResourcesSelfie(); const lastVersionInt = vAPI.app.intFromVersion(lastVersion); + if ( lastVersionInt === 0 ) { return; } - // https://github.com/uBlockOrigin/uBlock-issues/issues/494 - // Remove useless per-site switches. - if ( lastVersionInt <= 1019003007 ) { - µb.sessionSwitches.toggle('no-scripting', 'behind-the-scene', 0); - µb.permanentSwitches.toggle('no-scripting', 'behind-the-scene', 0); + // https://github.com/LiCybora/NanoDefenderFirefox/issues/196 + // Toggle on the blocking of CSP reports by default. + if ( lastVersionInt <= 1031003011 ) { + µb.sessionSwitches.toggle('no-csp-reports', '*', 1); + µb.permanentSwitches.toggle('no-csp-reports', '*', 1); µb.saveHostnameSwitches(); } - // Configure new popup panel according to classic popup panel - // configuration. - if ( lastVersionInt !== 0 ) { - if ( lastVersionInt <= 1026003014 ) { - µb.userSettings.popupPanelSections = - µb.userSettings.dynamicFilteringEnabled === true ? 0b11111 : 0b01111; - µb.userSettings.dynamicFilteringEnabled = undefined; - µb.saveUserSettings(); - } else if ( - lastVersionInt <= 1026003016 && - (µb.userSettings.popupPanelSections & 1) !== 0 - ) { - µb.userSettings.popupPanelSections = - (µb.userSettings.popupPanelSections << 1 | 1) & 0b111111; - µb.saveUserSettings(); - } - } - vAPI.storage.set({ version: vAPI.app.version }); }; @@ -240,13 +223,14 @@ const fromFetch = function(to, fetched) { const createDefaultProps = function() { const fetchableProps = { 'dynamicFilteringString': [ + 'no-csp-reports: * true', 'behind-the-scene * * noop', 'behind-the-scene * image noop', 'behind-the-scene * 3p noop', 'behind-the-scene * inline-script noop', 'behind-the-scene * 1p-script noop', 'behind-the-scene * 3p-script noop', - 'behind-the-scene * 3p-frame noop' + 'behind-the-scene * 3p-frame noop', ].join('\n'), 'urlFilteringString': '', 'hostnameSwitchesString': [ From cc86e373ecdc81470083729f001c89e769da0bb9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 11 Dec 2020 12:35:18 -0500 Subject: [PATCH 4015/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 8c2c03c7b625f..54f59675015fa 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.11 +1.31.3.12 From 6df32675b16b591313b40231583b4d06dda9e526 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Dec 2020 08:19:40 -0500 Subject: [PATCH 4016/4093] Add approximate reporting of tabless network requests Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1204 Not much can be done beside reporting to tabless network requests to all tabs for which the context is a match. A short term local cache is used to avoid having to iterate through all existing tabs for each tabless network request just to find and report to the matching ones -- users reporting having a lot of opened tabs at once is not so uncommon. --- src/js/start.js | 2 +- src/js/tab.js | 19 ++++++------ src/js/traffic.js | 79 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/js/start.js b/src/js/start.js index df3225d8e9753..b86e45c077e76 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -69,7 +69,7 @@ const initializeTabs = async function() { if ( tab.discarded === true ) { continue; } const { id, url } = tab; µb.tabContextManager.commit(id, url); - µb.bindTabToPageStats(id); + µb.bindTabToPageStore(id); // https://github.com/chrisaljoudi/uBlock/issues/129 // Find out whether content scripts need to be injected // programmatically. This may be necessary for web pages which diff --git a/src/js/tab.js b/src/js/tab.js index 55b400dd5e214..cc09eb7bb9f40 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -371,10 +371,10 @@ // It is a popup, block and remove the tab. if ( popupType === 'popup' ) { - µb.unbindTabFromPageStats(targetTabId); + µb.unbindTabFromPageStore(targetTabId); vAPI.tabs.remove(targetTabId, false); } else { - µb.unbindTabFromPageStats(openerTabId); + µb.unbindTabFromPageStore(openerTabId); vAPI.tabs.remove(openerTabId, true); } @@ -850,7 +850,7 @@ vAPI.Tabs = class extends vAPI.Tabs { onClosed(tabId) { super.onClosed(tabId); if ( vAPI.isBehindTheSceneTabId(tabId) ) { return; } - µBlock.unbindTabFromPageStats(tabId); + µBlock.unbindTabFromPageStore(tabId); µBlock.contextMenu.update(); } @@ -874,7 +874,7 @@ vAPI.Tabs = class extends vAPI.Tabs { const µb = µBlock; if ( details.frameId === 0 ) { µb.tabContextManager.commit(details.tabId, details.url); - let pageStore = µb.bindTabToPageStats(details.tabId, 'tabCommitted'); + let pageStore = µb.bindTabToPageStore(details.tabId, 'tabCommitted'); if ( pageStore ) { pageStore.journalAddRootFrame('committed', details.url); } @@ -896,7 +896,7 @@ vAPI.Tabs = class extends vAPI.Tabs { if ( !tab.url || tab.url === '' ) { return; } if ( !changeInfo.url ) { return; } µBlock.tabContextManager.commit(tabId, changeInfo.url); - µBlock.bindTabToPageStats(tabId, 'tabUpdated'); + µBlock.bindTabToPageStore(tabId, 'tabUpdated'); } }; @@ -907,12 +907,12 @@ vAPI.tabs = new vAPI.Tabs(); // Create an entry for the tab if it doesn't exist. -µBlock.bindTabToPageStats = function(tabId, context) { +µBlock.bindTabToPageStore = function(tabId, context) { this.updateToolbarIcon(tabId, 0b111); // Do not create a page store for URLs which are of no interests if ( this.tabContextManager.exists(tabId) === false ) { - this.unbindTabFromPageStats(tabId); + this.unbindTabFromPageStore(tabId); return null; } @@ -954,8 +954,7 @@ vAPI.tabs = new vAPI.Tabs(); /******************************************************************************/ -µBlock.unbindTabFromPageStats = function(tabId) { - //console.debug('µBlock> unbindTabFromPageStats(%d)', tabId); +µBlock.unbindTabFromPageStore = function(tabId) { const pageStore = this.pageStores.get(tabId); if ( pageStore === undefined ) { return; } pageStore.dispose(); @@ -1143,7 +1142,7 @@ vAPI.tabs = new vAPI.Tabs(); const checkTab = async tabId => { const tab = await vAPI.tabs.get(tabId); if ( tab instanceof Object && tab.discarded !== true ) { return; } - µBlock.unbindTabFromPageStats(tabId); + µBlock.unbindTabFromPageStore(tabId); }; const pageStoreJanitor = function() { diff --git a/src/js/traffic.js b/src/js/traffic.js index 17cdb615ac41b..e0734b6615f31 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -205,7 +205,7 @@ const onBeforeRootFrameRequest = function(fctxt) { fctxt.type = 'main_frame'; } - const pageStore = µb.bindTabToPageStats(fctxt.tabId, 'beforeRequest'); + const pageStore = µb.bindTabToPageStore(fctxt.tabId, 'beforeRequest'); if ( pageStore !== null ) { pageStore.journalAddRootFrame('uncommitted', requestURL); pageStore.journalAddRequest(requestHostname, result); @@ -294,35 +294,15 @@ const toBlockDocResult = function(url, hostname, logData) { // Intercept and filter behind-the-scene requests. -// https://github.com/gorhill/uBlock/issues/870 -// Finally, Chromium 49+ gained the ability to report network request of type -// `beacon`, so now we can block them according to the state of the -// "Disable hyperlink auditing/beacon" setting. - const onBeforeBehindTheSceneRequest = function(fctxt) { const µb = µBlock; const pageStore = µb.pageStoreFromTabId(fctxt.tabId); if ( pageStore === null ) { return; } - // https://bugs.chromium.org/p/chromium/issues/detail?id=637577#c15 - // Do not filter behind-the-scene network request of type `beacon`: there - // is no point. In any case, this will become a non-issue once - // is - // fixed. - - // Blocking behind-the-scene requests can break a lot of stuff: prevent - // browser updates, prevent extension updates, prevent extensions from - // working properly, etc. - // So we filter if and only if the "advanced user" mode is selected. // https://github.com/gorhill/uBlock/issues/3150 // Ability to globally block CSP reports MUST also apply to // behind-the-scene network requests. - // 2018-03-30: - // Filter all behind-the-scene network requests like any other network - // requests. Hopefully this will not break stuff as it used to be the - // case. - let result = 0; // https://github.com/uBlockOrigin/uBlock-issues/issues/339 @@ -341,8 +321,8 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { // The "any-tab" scope is not whitelist-able, and in such case we must // use the origin URL as the scope. Most such requests aren't going to - // be blocked, so we further test for whitelisting and modify the - // result only when the request is being blocked. + // be blocked, so we test for whitelisting and modify the result only + // when the request is being blocked. if ( result === 1 && µb.getNetFilteringSwitch(fctxt.tabOrigin) === false @@ -352,6 +332,9 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { } } + // https://github.com/uBlockOrigin/uBlock-issues/issues/1204 + onBeforeBehindTheSceneRequest.journalAddRequest(fctxt, result); + if ( µb.logger.enabled ) { fctxt.setRealm('network').toLogger(); } @@ -369,6 +352,54 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { } }; +// https://github.com/uBlockOrigin/uBlock-issues/issues/1204 +// Report the tabless network requests to all page stores matching the +// document origin. This is an approximation, there is unfortunately no +// way to know for sure which exact page triggered a tabless network +// request. + +onBeforeBehindTheSceneRequest.journalAddRequest = (( ) => { + let hostname = ''; + let pageStores = new Set(); + let pageStoresToken = 0; + let gcTimer; + + const reset = function() { + hostname = ''; + pageStores = new Set(); + pageStoresToken = 0; + }; + + const gc = ( ) => { + gcTimer = undefined; + if ( pageStoresToken !== µBlock.pageStoresToken ) { return reset(); } + gcTimer = vAPI.setTimeout(gc, 30011); + }; + + return function(fctxt, result) { + const { docHostname } = fctxt; + if ( + docHostname !== hostname || + pageStoresToken !== µBlock.pageStoresToken + ) { + hostname = docHostname; + pageStores = new Set(); + for ( const pageStore of µBlock.pageStores.values() ) { + if ( pageStore.tabHostname !== docHostname ) { continue; } + pageStores.add(pageStore); + } + pageStoresToken = µBlock.pageStoresToken; + if ( gcTimer !== undefined ) { + clearTimeout(gcTimer); + } + gcTimer = vAPI.setTimeout(gc, 30011); + } + for ( const pageStore of pageStores ) { + pageStore.journalAddRequest(fctxt.hostname, result); + } + }; +})(); + /******************************************************************************/ // To handle: @@ -394,7 +425,7 @@ const onHeadersReceived = function(details) { let pageStore = µb.pageStoreFromTabId(fctxt.tabId); if ( pageStore === null ) { if ( isRootDoc === false ) { return; } - pageStore = µb.bindTabToPageStats(fctxt.tabId, 'beforeRequest'); + pageStore = µb.bindTabToPageStore(fctxt.tabId, 'beforeRequest'); } if ( pageStore.getNetFilteringSwitch(fctxt) === false ) { return; } From 64571a336eb01bb9721f67cec6efd43d1cd40c9b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Dec 2020 08:40:48 -0500 Subject: [PATCH 4017/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index f2864c84255db..d87dbf7ee035f 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.11", + "version": "1.31.3.12", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b11/uBlock0_1.31.3b11.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b12/uBlock0_1.31.3b12.firefox.signed.xpi" } ] } From b779f1f7c9deea32970b93e11a8d33ef7efe5db8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Dec 2020 14:33:49 -0500 Subject: [PATCH 4018/4093] Dynamically reload 3p css when noop-ing "3rd-party" cell This should improve usability of uBO's hard-mode and "relax blocking mode" operations. This is the new default behavior. The previous behavior of forcing a reload of the page can be re-enabled by simply setting the `3p` bit of the advanced setting `blockingProfiles` to 1. --- src/js/background.js | 2 +- src/js/commands.js | 5 ++- src/js/scriptlets/load-3p-css.js | 70 ++++++++++++++++++++++++++++++++ src/js/ublock.js | 10 +++++ 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/js/scriptlets/load-3p-css.js diff --git a/src/js/background.js b/src/js/background.js index d73551cd9a324..1c9cd520517fa 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -43,7 +43,7 @@ const µBlock = (( ) => { // jshint ignore:line autoUpdateDelayAfterLaunch: 180, autoUpdatePeriod: 7, benchmarkDatasetURL: 'unset', - blockingProfiles: '11111/#F00 11011/#C0F 11001/#00F 00001', + blockingProfiles: '11111/#F00 11010/#C0F 11001/#00F 00001', cacheStorageAPI: 'unset', cacheStorageCompression: true, cacheControlForFirefox1376932: 'no-cache, no-store, must-revalidate', diff --git a/src/js/commands.js b/src/js/commands.js index c30b8627b64d1..a695427fb52a2 100644 --- a/src/js/commands.js +++ b/src/js/commands.js @@ -88,6 +88,8 @@ const relaxBlockingMode = (( ) => { // TODO: Reset to original blocking profile? if ( newProfileBits === undefined ) { return; } + const noReload = (newProfileBits & 0b00000001) === 0; + if ( (curProfileBits & 0b00000010) !== 0 && (newProfileBits & 0b00000010) === 0 @@ -104,6 +106,7 @@ const relaxBlockingMode = (( ) => { (newProfileBits & 0b00000100) === 0 ) { µb.toggleFirewallRule({ + tabId: noReload ? tab.id : undefined, srcHostname: hn, desHostname: '*', requestType: '3p', @@ -135,7 +138,7 @@ const relaxBlockingMode = (( ) => { } // Reload the target tab? - if ( (newProfileBits & 0b00000001) === 0 ) { return; } + if ( noReload ) { return; } // Reload: use a timer to coalesce bursts of reload commands. let timer = reloadTimers.get(tab.id); diff --git a/src/js/scriptlets/load-3p-css.js b/src/js/scriptlets/load-3p-css.js new file mode 100644 index 0000000000000..1d0df772c29f3 --- /dev/null +++ b/src/js/scriptlets/load-3p-css.js @@ -0,0 +1,70 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2020-present 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'; + +/******************************************************************************/ + +(( ) => { + if ( typeof vAPI !== 'object' ) { return; } + + if ( vAPI.load3rdPartyCSS ) { return; } + vAPI.load3rdPartyCSS = true; + + const links = document.querySelectorAll('link[rel="stylesheet"]'); + if ( links.length === 0 ) { return; } + + const toLoadMaybe = new Map(Array.from(links).map(a => [ a.href, a ])); + + for ( const sheet of Array.from(document.styleSheets) ) { + let loaded = false; + try { + loaded = sheet.rules.length !== 0; + } catch(ex) { + } + if ( loaded ) { continue; } + const link = toLoadMaybe.get(sheet.href); + if ( link === undefined ) { continue; } + toLoadMaybe.delete(sheet.href); + const clone = link.cloneNode(true); + link.replaceWith(clone); + } +})(); + + + + + + + + +/******************************************************************************* + + DO NOT: + - Remove the following code + - Add code beyond the following code + Reason: + - https://github.com/gorhill/uBlock/pull/3721 + - uBO never uses the return value from injected content scripts + +**/ + +void 0; diff --git a/src/js/ublock.js b/src/js/ublock.js index bc765036ff47e..9076f3da31f6b 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -509,9 +509,19 @@ const matchBucket = function(url, hostname, bucket, start) { // https://github.com/chrisaljoudi/uBlock/issues/420 this.cosmeticFilteringEngine.removeFromSelectorCache(srcHostname, 'net'); + if ( details.tabId === undefined ) { return; } + if ( requestType.startsWith('3p') ) { this.updateToolbarIcon(details.tabId, 0b100); } + + if ( requestType === '3p' && action === 3 ) { + vAPI.tabs.executeScript(details.tabId, { + file: '/js/scriptlets/load-3p-css.js', + allFrames: true, + runAt: 'document_idle', + }); + } }; /******************************************************************************/ From 18e6f30c1c6ddcd16caaf570322545a1bf3cf3d9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Dec 2020 14:38:44 -0500 Subject: [PATCH 4019/4093] Minor code review Related commit: - https://github.com/gorhill/uBlock/commit/6df32675b16b591313b40231583b4d06dda9e526 --- 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 e0734b6615f31..8c42ee96a9b9b 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -358,7 +358,7 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { // way to know for sure which exact page triggered a tabless network // request. -onBeforeBehindTheSceneRequest.journalAddRequest = (( ) => { +{ let hostname = ''; let pageStores = new Set(); let pageStoresToken = 0; @@ -376,7 +376,7 @@ onBeforeBehindTheSceneRequest.journalAddRequest = (( ) => { gcTimer = vAPI.setTimeout(gc, 30011); }; - return function(fctxt, result) { + onBeforeBehindTheSceneRequest.journalAddRequest = (fctxt, result) => { const { docHostname } = fctxt; if ( docHostname !== hostname || @@ -398,7 +398,7 @@ onBeforeBehindTheSceneRequest.journalAddRequest = (( ) => { pageStore.journalAddRequest(fctxt.hostname, result); } }; -})(); +} /******************************************************************************/ From 2e1e4b52eb90344bad92f1c883290eb207b88f07 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Dec 2020 14:40:01 -0500 Subject: [PATCH 4020/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 54f59675015fa..d6d95661cb87b 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.12 +1.31.3.13 From fd960e19e8187e7801221899d9820888104e2521 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Dec 2020 14:45:48 -0500 Subject: [PATCH 4021/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index d87dbf7ee035f..2fa0711a916f2 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.12", + "version": "1.31.3.13", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b12/uBlock0_1.31.3b12.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b13/uBlock0_1.31.3b13.firefox.signed.xpi" } ] } From 1ff8132216082b4461bfaf8f0dad4910d8aa86a0 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 13 Dec 2020 12:14:37 -0500 Subject: [PATCH 4022/4093] Simplify code Related commit: - https://github.com/gorhill/uBlock/commit/b779f1f7c9deea32970b93e11a8d33ef7efe5db8 --- src/js/scriptlets/load-3p-css.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/js/scriptlets/load-3p-css.js b/src/js/scriptlets/load-3p-css.js index 1d0df772c29f3..92f402d92c164 100644 --- a/src/js/scriptlets/load-3p-css.js +++ b/src/js/scriptlets/load-3p-css.js @@ -26,13 +26,9 @@ (( ) => { if ( typeof vAPI !== 'object' ) { return; } - if ( vAPI.load3rdPartyCSS ) { return; } - vAPI.load3rdPartyCSS = true; - - const links = document.querySelectorAll('link[rel="stylesheet"]'); - if ( links.length === 0 ) { return; } - - const toLoadMaybe = new Map(Array.from(links).map(a => [ a.href, a ])); + if ( vAPI.dynamicReloadToken === undefined ) { + vAPI.dynamicReloadToken = vAPI.randomToken(); + } for ( const sheet of Array.from(document.styleSheets) ) { let loaded = false; @@ -41,10 +37,11 @@ } catch(ex) { } if ( loaded ) { continue; } - const link = toLoadMaybe.get(sheet.href); - if ( link === undefined ) { continue; } - toLoadMaybe.delete(sheet.href); + const link = sheet.ownerNode || null; + if ( link === null || link.localName !== 'link' ) { continue; } + if ( link.hasAttribute(vAPI.dynamicReloadToken) ) { continue; } const clone = link.cloneNode(true); + clone.setAttribute(vAPI.dynamicReloadToken, ''); link.replaceWith(clone); } })(); From d4425ad753641f497e4b017f4ea6b60a28e97a83 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 14 Dec 2020 08:38:30 -0500 Subject: [PATCH 4023/4093] Fix bad access to hostname info from filtering context Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1398 Regression from: - https://github.com/gorhill/uBlock/commit/6df32675b16b591313b40231583b4d06dda9e526 --- src/js/traffic.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/traffic.js b/src/js/traffic.js index 8c42ee96a9b9b..ce25ef13cf5a7 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -377,7 +377,7 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { }; onBeforeBehindTheSceneRequest.journalAddRequest = (fctxt, result) => { - const { docHostname } = fctxt; + const docHostname = fctxt.getDocHostname(); if ( docHostname !== hostname || pageStoresToken !== µBlock.pageStoresToken @@ -395,7 +395,7 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { gcTimer = vAPI.setTimeout(gc, 30011); } for ( const pageStore of pageStores ) { - pageStore.journalAddRequest(fctxt.hostname, result); + pageStore.journalAddRequest(fctxt.getHostname(), result); } }; } From 56305cc0317a977122a9fa26389b26069ecf34a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 14 Dec 2020 08:39:58 -0500 Subject: [PATCH 4024/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d6d95661cb87b..6163f2eabc03d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.13 +1.31.3.14 From c45b93a25c5cce56d65f581bab959e2e58ff4cad Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 14 Dec 2020 08:50:19 -0500 Subject: [PATCH 4025/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 2fa0711a916f2..bdac8bd126ae8 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.13", + "version": "1.31.3.14", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b13/uBlock0_1.31.3b13.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b14/uBlock0_1.31.3b14.firefox.signed.xpi" } ] } From b22cf24bd52db5bb2e7976f5cb67e3f543d8ebc1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 14 Dec 2020 11:22:08 -0500 Subject: [PATCH 4026/4093] Fix look-up of specific-generic filters entity-less hostnames Related feedback: - https://github.com/uBlockOrigin/uBlock-issues/issues/688#issuecomment-743755956 --- src/js/html-filtering.js | 14 ++++++++------ src/js/scriptlet-filtering.js | 10 ++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/js/html-filtering.js b/src/js/html-filtering.js index 4ca03ce503815..3d0f03cc97cc7 100644 --- a/src/js/html-filtering.js +++ b/src/js/html-filtering.js @@ -368,12 +368,14 @@ hostname, [ plains, exceptions, procedurals, exceptions ] ); - if ( details.entity !== '' ) { - filterDB.retrieve( - `${hostname.slice(0, -details.domain.length)}${details.entity}`, - [ plains, exceptions, procedurals, exceptions ] - ); - } + const entity = details.entity !== '' + ? `${hostname.slice(0, -details.domain.length)}${details.entity}` + : '*'; + filterDB.retrieve( + entity, + [ plains, exceptions, procedurals, exceptions ], + 1 + ); if ( plains.size === 0 && procedurals.size === 0 ) { return; } diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index f348b0ee31d86..a03e4a04f65d9 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -318,12 +318,10 @@ sessionScriptletDB.retrieve([ null, $exceptions ]); } scriptletDB.retrieve(hostname, [ $scriptlets, $exceptions ]); - if ( request.entity !== '' ) { - scriptletDB.retrieve( - `${hostname.slice(0, -request.domain.length)}${request.entity}`, - [ $scriptlets, $exceptions ] - ); - } + const entity = request.entity !== '' + ? `${hostname.slice(0, -request.domain.length)}${request.entity}` + : '*'; + scriptletDB.retrieve(entity, [ $scriptlets, $exceptions ], 1); if ( $scriptlets.size === 0 ) { return; } const loggerEnabled = µb.logger.enabled; From 8060ddb2834604cfed31316a4ee527635f8027b7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 14 Dec 2020 11:26:04 -0500 Subject: [PATCH 4027/4093] Avoid duplicates in editor's auto-completion of origins Related commit: - https://github.com/gorhill/uBlock/commit/daf464b3c30e9c0c5f5991ba1bde8f9dca1d7078 --- src/js/messaging.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index 4e7b750b173b2..1566c709dac5a 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1048,7 +1048,7 @@ const getLists = async function(callback) { // TODO: also return origin of embedded frames? const getOriginHints = function() { const punycode = self.punycode; - const out = []; + const out = new Set(); for ( const tabId of µb.pageStores.keys() ) { if ( tabId === -1 ) { continue; } const tabContext = µb.tabContextManager.lookup(tabId); @@ -1056,11 +1056,11 @@ const getOriginHints = function() { let { rootDomain, rootHostname } = tabContext; if ( rootDomain.endsWith('-scheme') ) { continue; } const isPunycode = rootHostname.includes('xn--'); - out.push(isPunycode ? punycode.toUnicode(rootDomain) : rootDomain); + out.add(isPunycode ? punycode.toUnicode(rootDomain) : rootDomain); if ( rootHostname === rootDomain ) { continue; } - out.push(isPunycode ? punycode.toUnicode(rootHostname) : rootHostname); + out.add(isPunycode ? punycode.toUnicode(rootHostname) : rootHostname); } - return out; + return Array.from(out); }; // My rules From e28c2cc3c6e4d9e4c34327947595622f99878956 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 15 Dec 2020 08:27:59 -0500 Subject: [PATCH 4028/4093] Auto-complete of origin pattern for `||`-based patterns Related commit: - https://github.com/gorhill/uBlock/commit/daf464b3c30e9c0c5f5991ba1bde8f9dca1d7078 --- src/js/codemirror/ubo-static-filtering.js | 25 +++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 1b987b22b8218..b7d3295210e34 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -455,18 +455,28 @@ const initHints = function() { }; }; - const getOriginHints = function(cursor, line) { + const getOriginHints = function(cursor, line, suffix = '') { const beg = cursor.ch; const matchLeft = /[^,|=~]*$/.exec(line.slice(0, beg)); const matchRight = /^[^#,|]*/.exec(line.slice(beg)); if ( matchLeft === null || matchRight === null ) { return; } const hints = []; for ( const text of originHints ) { - hints.push(text); + hints.push(text + suffix); } return pickBestHints(cursor, matchLeft[0], matchRight[0], hints); }; + const getNetPatternHints = function(cursor, line) { + if ( /\|\|[\w.-]*$/.test(line.slice(0, cursor.ch)) ) { + return getOriginHints(cursor, line, '^'); + } + // Maybe a static extended filter is meant to be crafted. + if ( /[^\w\x80-\xF4#,.-]/.test(line) === false ) { + return getOriginHints(cursor, line); + } + }; + const getNetOptionHints = function(cursor, seedLeft, seedRight) { const isNegated = seedLeft.startsWith('~'); if ( isNegated ) { @@ -503,16 +513,9 @@ const initHints = function() { const getNetHints = function(cursor, line) { const beg = cursor.ch; - if ( - parser.optionsAnchorSpan.len === 0 && - line.endsWith('$') === false - ) { - if ( /[^\w\x80-\xF4#,.-]/.test(line) === false ) { - return getOriginHints(cursor, line); - } - return; + if ( beg <= parser.slices[parser.optionsAnchorSpan.i+1] ) { + return getNetPatternHints(cursor, line); } - if ( beg < parser.slices[parser.optionsSpan.i+1] ) { return; } const lineBefore = line.slice(0, beg); const lineAfter = line.slice(beg); let matchLeft = /[^$,]*$/.exec(lineBefore); From 4d3e032f360d0c6249a98cf6d05139b30e43fe12 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 15 Dec 2020 09:22:06 -0500 Subject: [PATCH 4029/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 6163f2eabc03d..c8cdfa9633b8f 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.14 +1.31.3.100 From 0052dc123b4bda190a34ab454b7d72f233ca4e94 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 15 Dec 2020 09:36:04 -0500 Subject: [PATCH 4030/4093] Fix `no-csp-reports` default enabled switch state Related feedback: - https://github.com/gorhill/uBlock/commit/7d90f97aa1cbf2728508506f6dd7a75c054b85d1#commitcomment-45138096 --- src/js/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/start.js b/src/js/start.js index b86e45c077e76..509aa3c4f28dc 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -223,7 +223,6 @@ const fromFetch = function(to, fetched) { const createDefaultProps = function() { const fetchableProps = { 'dynamicFilteringString': [ - 'no-csp-reports: * true', 'behind-the-scene * * noop', 'behind-the-scene * image noop', 'behind-the-scene * 3p noop', @@ -234,6 +233,7 @@ const createDefaultProps = function() { ].join('\n'), 'urlFilteringString': '', 'hostnameSwitchesString': [ + 'no-csp-reports: * true', 'no-large-media: behind-the-scene false', ].join('\n'), 'lastRestoreFile': '', From a090b2b564819dee3b9dec11984d74186d788af1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 15 Dec 2020 09:38:20 -0500 Subject: [PATCH 4031/4093] Fix auto-completion for epicker in Firefox --- src/js/epicker-ui.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/js/epicker-ui.js b/src/js/epicker-ui.js index 5b6f73a4a86ff..6fc8a362dac65 100644 --- a/src/js/epicker-ui.js +++ b/src/js/epicker-ui.js @@ -84,7 +84,8 @@ const cmEditor = new CodeMirror(document.querySelector('.codeMirrorContainer'), vAPI.messaging.send('dashboard', { what: 'getAutoCompleteDetails' }).then(response => { - if ( response instanceof Object === false ) { return; } + // For unknown reasons, `instanceof Object` does not work here in Firefox. + if ( typeof response !== 'object' ) { return; } const mode = cmEditor.getMode(); if ( mode.setHints instanceof Function ) { mode.setHints(response); From 058f16005537df3b8862f9058dce1dcd8b3bdd67 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 15 Dec 2020 09:45:45 -0500 Subject: [PATCH 4032/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index bdac8bd126ae8..dffccad215728 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.14", + "version": "1.31.3.100", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3b14/uBlock0_1.31.3b14.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc0/uBlock0_1.31.3rc0.firefox.signed.xpi" } ] } From 89cac090a47be7cf830fd9fbc1523146c3ae3910 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Dec 2020 06:55:46 -0500 Subject: [PATCH 4033/4093] Mind `important` only for valid redirect tokens Related feedback: - https://github.com/uBlockOrigin/uBlock-issues/issues/1366#issuecomment-745744824 --- src/js/static-net-filtering.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index f52c4c45b16f8..c38159bbb4a3c 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4261,19 +4261,18 @@ FilterContainer.parseRedirectRequestValue = function(modifier) { }; FilterContainer.compareRedirectRequests = function(a, b) { - const abits = a.bits, bbits = b.bits; + const { token: atok, priority: aint, bits: abits } = + FilterContainer.parseRedirectRequestValue(a.modifier); + if ( µb.redirectEngine.hasToken(atok) === false ) { return -1; } + const { token: btok, priority: bint, bits: bbits } = + FilterContainer.parseRedirectRequestValue(b.modifier); + if ( µb.redirectEngine.hasToken(btok) === false ) { return 1; } if ( abits !== bbits ) { if ( (abits & Important) !== 0 ) { return 1; } if ( (bbits & Important) !== 0 ) { return -1; } if ( (abits & AllowAction) !== 0 ) { return -1; } if ( (bbits & AllowAction) !== 0 ) { return 1; } } - const { token: atok, priority: aint } = - FilterContainer.parseRedirectRequestValue(a.modifier); - if ( µb.redirectEngine.hasToken(atok) === false ) { return -1; } - const { token: btok, priority: bint } = - FilterContainer.parseRedirectRequestValue(b.modifier); - if ( µb.redirectEngine.hasToken(btok) === false ) { return 1; } return aint - bint; }; From 095924aa5047ab6c41f37bc0476ff2c710d4f582 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Dec 2020 07:02:01 -0500 Subject: [PATCH 4034/4093] New revision for dev build --- src/_locales/ar/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json index e4339041c6900..d96851c9e897e 100644 --- a/src/_locales/ar/messages.json +++ b/src/_locales/ar/messages.json @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "احظر العنصر في البرواز...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { From bc9b8a13300d7a6de93d0632369be15b05b7646e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Dec 2020 07:02:55 -0500 Subject: [PATCH 4035/4093] Enable broad no-csp-reports rule only in Firefox Related commit: - https://github.com/gorhill/uBlock/commit/7d90f97aa1cbf2728508506f6dd7a75c054b85d1 --- src/js/start.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/js/start.js b/src/js/start.js index 509aa3c4f28dc..7ac10c0f0c8cc 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -233,7 +233,6 @@ const createDefaultProps = function() { ].join('\n'), 'urlFilteringString': '', 'hostnameSwitchesString': [ - 'no-csp-reports: * true', 'no-large-media: behind-the-scene false', ].join('\n'), 'lastRestoreFile': '', @@ -243,6 +242,10 @@ const createDefaultProps = function() { 'netWhitelist': µb.netWhitelistDefault, 'version': '0.0.0.0' }; + // https://github.com/LiCybora/NanoDefenderFirefox/issues/196 + if ( vAPI.webextFlavor.soup.has('firefox') ) { + fetchableProps.hostnameSwitchesString += '\nno-csp-reports: * true'; + } toFetch(µb.localSettings, fetchableProps); toFetch(µb.userSettings, fetchableProps); toFetch(µb.restoreBackupSettings, fetchableProps); From 86eb6850caae4ffcfefd7b42c9786e2a129aaa2a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Dec 2020 07:05:38 -0500 Subject: [PATCH 4036/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index c8cdfa9633b8f..e0e8181eda89b 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.100 +1.31.3.101 From 2ddf6904f074d2e3c356811bf0830b7bcc9424eb Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 16 Dec 2020 07:30:32 -0500 Subject: [PATCH 4037/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index dffccad215728..c9ed72e19fdc5 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.100", + "version": "1.31.3.101", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc0/uBlock0_1.31.3rc0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc1/uBlock0_1.31.3rc1.firefox.signed.xpi" } ] } From a307cf5e6ae9f12451beb24ebb1a1c8910ae8cf6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Dec 2020 08:12:06 -0500 Subject: [PATCH 4038/4093] Mind restore-from-backup for no-csp-reports rule Related commit: - https://github.com/gorhill/uBlock/commit/7d90f97aa1cbf2728508506f6dd7a75c054b85d1 --- src/js/messaging.js | 10 ++++++++++ src/js/start.js | 7 +++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index 1566c709dac5a..4021fe5d42d72 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -926,6 +926,16 @@ const backupUserData = async function() { const restoreUserData = async function(request) { const userData = request.userData; + // https://github.com/LiCybora/NanoDefenderFirefox/issues/196 + // Backup data could be from Chromium platform or from an older + // Firefox version. + if ( + vAPI.webextFlavor.soup.has('firefox') && + vAPI.app.intFromVersion(userData.version) <= 1031003011 + ) { + userData.hostnameSwitchesString += '\nno-csp-reports: * true'; + } + // https://github.com/chrisaljoudi/uBlock/issues/1102 // Ensure all currently cached assets are flushed from storage AND memory. µb.assets.rmrf(); diff --git a/src/js/start.js b/src/js/start.js index 7ac10c0f0c8cc..79a1a441a9a1d 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -114,8 +114,11 @@ const onVersionReady = function(lastVersion) { if ( lastVersionInt === 0 ) { return; } // https://github.com/LiCybora/NanoDefenderFirefox/issues/196 - // Toggle on the blocking of CSP reports by default. - if ( lastVersionInt <= 1031003011 ) { + // Toggle on the blocking of CSP reports by default for Firefox. + if ( + vAPI.webextFlavor.soup.has('firefox') && + lastVersionInt <= 1031003011 + ) { µb.sessionSwitches.toggle('no-csp-reports', '*', 1); µb.permanentSwitches.toggle('no-csp-reports', '*', 1); µb.saveHostnameSwitches(); From ab641efc1359ccd429ca09c66fbb53138422f221 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Dec 2020 09:34:37 -0500 Subject: [PATCH 4039/4093] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index e0e8181eda89b..fd63613b968da 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.101 +1.31.3.102 From 2867ae175fd22aac3fffad2630f28dec35c9b893 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 17 Dec 2020 10:15:50 -0500 Subject: [PATCH 4040/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index c9ed72e19fdc5..56b63fa17f284 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.101", + "version": "1.31.3.102", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc1/uBlock0_1.31.3rc1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc2/uBlock0_1.31.3rc2.firefox.signed.xpi" } ] } From 990cff576de131488734050331348714050d1ff8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 18 Dec 2020 12:07:08 -0500 Subject: [PATCH 4041/4093] Fix case of scriptlet injection not working `about:` frames This is an issue in uBO affecting only Chromium-based browsers. Related feedback: https://github.com/uBlockOrigin/uBlock-issues/issues/688#issuecomment-748179731 --- src/js/messaging.js | 8 +++++++- src/js/scriptlet-filtering.js | 1 - src/js/tab.js | 9 ++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/js/messaging.js b/src/js/messaging.js index 4021fe5d42d72..0b778394c32ad 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -627,7 +627,13 @@ const retrieveContentScriptParameters = function(sender, request) { response.specificCosmeticFilters = µb.cosmeticFilteringEngine.retrieveSpecificSelectors(request, response); - if ( µb.canInjectScriptletsNow === false ) { + // https://github.com/uBlockOrigin/uBlock-issues/issues/688#issuecomment-748179731 + // For non-network URIs, scriptlet injection is deferred to here. The + // effective URL is available here in `request.url`. + if ( + µb.canInjectScriptletsNow === false || + µb.URI.isNetworkURI(sender.frameURL) === false + ) { response.scriptlets = µb.scriptletFilteringEngine.retrieve(request); } diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index a03e4a04f65d9..39b38396ba6d0 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -377,7 +377,6 @@ api.injectNow = function(details) { if ( typeof details.frameId !== 'number' ) { return; } - if ( µb.URI.isNetworkURI(details.url) === false ) { return; } const request = { tabId: details.tabId, frameId: details.frameId, diff --git a/src/js/tab.js b/src/js/tab.js index cc09eb7bb9f40..3f70aec0e5f4f 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -868,7 +868,10 @@ vAPI.Tabs = class extends vAPI.Tabs { // properly setup if network requests are fired from within the tab. // Example: Chromium + case #6 at // http://raymondhill.net/ublock/popup.html - + // https://github.com/uBlockOrigin/uBlock-issues/issues/688#issuecomment-748179731 + // For non-network URIs, defer scriptlet injection to content script. The + // reason for this is that we need the effective URL and this information + // is not available at this point. onNavigation(details) { super.onNavigation(details); const µb = µBlock; @@ -879,8 +882,8 @@ vAPI.Tabs = class extends vAPI.Tabs { pageStore.journalAddRootFrame('committed', details.url); } } - if ( µb.canInjectScriptletsNow ) { - let pageStore = µb.pageStoreFromTabId(details.tabId); + if ( µb.canInjectScriptletsNow && µb.URI.isNetworkURI(details.url) ) { + const pageStore = µb.pageStoreFromTabId(details.tabId); if ( pageStore !== null && pageStore.getNetFilteringSwitch() ) { µb.scriptletFilteringEngine.injectNow(details); } From c1f913fbde5923f031060408847dcdc334f116f2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 18 Dec 2020 12:09:45 -0500 Subject: [PATCH 4042/4093] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index fd63613b968da..6f4e692994d2c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.102 +1.31.3.103 From 53d893a1b36c6791a6a37830455aec0a15679b56 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 18 Dec 2020 12:15:25 -0500 Subject: [PATCH 4043/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 56b63fa17f284..005f2dc2d5ef4 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.102", + "version": "1.31.3.103", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc2/uBlock0_1.31.3rc2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc3/uBlock0_1.31.3rc3.firefox.signed.xpi" } ] } From 187f1831f0e87a4ca779197a775e179ff4dd84b9 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Dec 2020 11:54:24 -0500 Subject: [PATCH 4044/4093] Allow more local resources to be redirected as data: URIs Related feedback: - https://github.com/uBlockOrigin/uBlock-issues/issues/1388#issuecomment-748625280 --- src/js/redirect-engine.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 20c59cb57d6a6..5d8fd8a7b18b2 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -58,6 +58,7 @@ const redirectableResources = new Map([ } ], [ 'amazon_ads.js', { alias: 'amazon-adsystem.com/aax2/amzn_ads.js', + data: 'text', } ], [ 'amazon_apstag.js', { } ], @@ -72,30 +73,36 @@ const redirectableResources = new Map([ } ], [ 'doubleclick_instream_ad_status.js', { alias: 'doubleclick.net/instream/ad_status.js', + data: 'text', } ], [ 'empty', { data: 'text', // Important! } ], [ 'google-analytics_analytics.js', { alias: 'google-analytics.com/analytics.js', + data: 'text', } ], [ 'google-analytics_cx_api.js', { alias: 'google-analytics.com/cx/api.js', } ], [ 'google-analytics_ga.js', { alias: 'google-analytics.com/ga.js', + data: 'text', } ], [ 'google-analytics_inpage_linkid.js', { alias: 'google-analytics.com/inpage_linkid.js', } ], [ 'googlesyndication_adsbygoogle.js', { alias: 'googlesyndication.com/adsbygoogle.js', + data: 'text', } ], [ 'googletagmanager_gtm.js', { alias: 'googletagmanager.com/gtm.js', + data: 'text', } ], [ 'googletagservices_gpt.js', { alias: 'googletagservices.com/gpt.js', + data: 'text', } ], [ 'hd-main.js', { } ], From a68ad0f30b0b8a8d3a44d6654e2fbfdafa26a14f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Dec 2020 11:56:33 -0500 Subject: [PATCH 4045/4093] new revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 6f4e692994d2c..2dd08ab3edbf6 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.103 +1.31.3.104 From 86a2bcaff0624971f1cd931a4f015da9bc56a981 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Dec 2020 12:08:06 -0500 Subject: [PATCH 4046/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/id/messages.json | 2 +- src/_locales/oc/messages.json | 68 +++++++++++++++++------------------ src/_locales/sv/messages.json | 4 +-- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json index c12c30bc090f3..739c80c075a7d 100644 --- a/src/_locales/id/messages.json +++ b/src/_locales/id/messages.json @@ -912,7 +912,7 @@ "description": "No longer used" }, "subscribeButton": { - "message": "Langganan", + "message": "Berlangganan", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { diff --git a/src/_locales/oc/messages.json b/src/_locales/oc/messages.json index 00eaf919afdc8..0f8342f693866 100644 --- a/src/_locales/oc/messages.json +++ b/src/_locales/oc/messages.json @@ -188,7 +188,7 @@ "description": "Caption for the no-large-media per-site switch" }, "popupNoCosmeticFiltering_v2": { - "message": "Cosmetic filtering", + "message": "Filtres cosmetics", "description": "Caption for the no-cosmetic-filtering per-site switch" }, "popupNoRemoteFonts_v2": { @@ -232,7 +232,7 @@ "description": "" }, "popup3pAnyRulePrompt": { - "message": "3rd-party", + "message": "Tèrça partida", "description": "" }, "popup3pPassiveRulePrompt": { @@ -360,7 +360,7 @@ "description": "" }, "settingsNoRemoteFontsPrompt": { - "message": "Block remote fonts", + "message": "Blocar las poliças distantas", "description": "" }, "settingsNoScriptingPrompt": { @@ -372,11 +372,11 @@ "description": "background information: https://github.com/gorhill/uBlock/issues/3150" }, "settingsLastRestorePrompt": { - "message": "Last restore:", + "message": "Darrièra restauracion :", "description": "English: Last restore:" }, "settingsLastBackupPrompt": { - "message": "Last backup:", + "message": "Darrièra salvagarda :", "description": "English: Last backup:" }, "3pListsOfBlockedHostsPrompt": { @@ -428,15 +428,15 @@ "description": "Header for the uBlock filters section in 'Filter lists pane'" }, "3pGroupAds": { - "message": "Ads", + "message": "Publicitats", "description": "English: Ads" }, "3pGroupPrivacy": { - "message": "Privacy", + "message": "Confidencialitat", "description": "English: Privacy" }, "3pGroupMalware": { - "message": "Malware domains", + "message": "Domenis malfasents", "description": "English: Malware domains" }, "3pGroupAnnoyances": { @@ -552,7 +552,7 @@ "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { - "message": "Sort:", + "message": "Triar :", "description": "English: label for sort option." }, "rulesSortByType": { @@ -560,11 +560,11 @@ "description": "English: a sort option for list of rules." }, "rulesSortBySource": { - "message": "Source", + "message": "Font", "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { - "message": "Destination", + "message": "Destinacion", "description": "English: a sort option for list of rules." }, "whitelistPrompt": { @@ -576,7 +576,7 @@ "description": "English: Import and append" }, "whitelistExport": { - "message": "Export", + "message": "Exportar", "description": "English: Export" }, "whitelistExportFilename": { @@ -600,7 +600,7 @@ "description": "English: URL" }, "logRequestsHeaderFilter": { - "message": "Filter", + "message": "Filtre", "description": "English: Filter" }, "logAll": { @@ -692,7 +692,7 @@ "description": "Label to identify a filter field" }, "loggerEntryDetailsFilterList": { - "message": "Filter list", + "message": "Lista de filtre", "description": "Label to identify a filter list field" }, "loggerEntryDetailsRule": { @@ -820,11 +820,11 @@ "description": "A label for the partyness column" }, "loggerExportFormatList": { - "message": "List", + "message": "Lista", "description": "Label for radio-button to pick export format" }, "loggerExportFormatTable": { - "message": "Table", + "message": "Tablèu", "description": "Label for radio-button to pick export format" }, "loggerExportEncodePlain": { @@ -844,15 +844,15 @@ "description": "English: project' wiki on GitHub" }, "aboutSupport": { - "message": "Support", + "message": "Assisténcia", "description": "A link for where to get support" }, "aboutIssues": { - "message": "Issue tracker", + "message": "Seguidor d’avarias", "description": "Text for a link to official issue tracker" }, "aboutCode": { - "message": "Source code (GPLv3)", + "message": "Còdi font (GPLv3)", "description": "English: Source code (GPLv3)" }, "aboutContributors": { @@ -860,11 +860,11 @@ "description": "English: Contributors" }, "aboutSourceCode": { - "message": "Source code", + "message": "Còdi font", "description": "Link text to source code repo" }, "aboutTranslations": { - "message": "Translations", + "message": "Traduccions", "description": "Link text to translations repo" }, "aboutFilterLists": { @@ -916,27 +916,27 @@ "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { - "message": "a minute ago", + "message": "fa una minuta", "description": "English: a minute ago" }, "elapsedManyMinutesAgo": { - "message": "{{value}} minutes ago", + "message": "fa {{value}} minutas", "description": "English: {{value}} minutes ago" }, "elapsedOneHourAgo": { - "message": "an hour ago", + "message": "fa una ora", "description": "English: an hour ago" }, "elapsedManyHoursAgo": { - "message": "{{value}} hours ago", + "message": "fa {{value}} oras", "description": "English: {{value}} hours ago" }, "elapsedOneDayAgo": { - "message": "a day ago", + "message": "a un jorn", "description": "English: a day ago" }, "elapsedManyDaysAgo": { - "message": "{{value}} days ago", + "message": "fa {{value}} jorns", "description": "English: {{value}} days ago" }, "showDashboardButton": { @@ -1012,7 +1012,7 @@ "description": "A warning to users at the top of 'Advanced settings' page" }, "genericSubmit": { - "message": "Submit", + "message": "Mandar", "description": "for generic 'Submit' buttons" }, "genericApplyChanges": { @@ -1036,7 +1036,7 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "shortcutCapturePlaceholder": { - "message": "Type a shortcut", + "message": "Picar un acorchi", "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { @@ -1044,7 +1044,7 @@ "description": "Tooltip for the button used to lock scrolling between the views in the 'My rules' pane" }, "genericCopyToClipboard": { - "message": "Copy to clipboard", + "message": "Copiar al quichapapièrs", "description": "Label for buttons used to copy something to the clipboard" }, "toggleBlockingProfile": { @@ -1060,19 +1060,19 @@ "description": " In Setting pane, renders as (example): Storage used: 13.2 MB" }, "KB": { - "message": "KB", + "message": "Ko", "description": "short for 'kilobytes'" }, "MB": { - "message": "MB", + "message": "Mo", "description": "short for 'megabytes'" }, "GB": { - "message": "GB", + "message": "Go", "description": "short for 'gigabytes'" }, "clickToLoad": { - "message": "Click to load", + "message": "Clicatz per cargar", "description": "Message used in frame placeholders" }, "dummy": { diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 16fdf213c27bc..7ae9a834bcc28 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -44,7 +44,7 @@ "description": "appears as tab name in dashboard" }, "shortcutsPageName": { - "message": "Kortkommandon", + "message": "Genväg", "description": "appears as tab name in dashboard" }, "statsPageName": { @@ -1036,7 +1036,7 @@ "description": "A context menu entry, present when large media elements have been blocked on the current site" }, "shortcutCapturePlaceholder": { - "message": "Ange ett kortkommando", + "message": "Ange en genväg", "description": "Placeholder string for input field used to capture a keyboard shortcut" }, "genericMergeViewScrollLock": { From f6234516f40f3faa62edcc1744896ae714d15ea1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 20 Dec 2020 12:20:31 -0500 Subject: [PATCH 4047/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 005f2dc2d5ef4..15b9927a5249d 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.103", + "version": "1.31.3.104", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc3/uBlock0_1.31.3rc3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc4/uBlock0_1.31.3rc4.firefox.signed.xpi" } ] } From ea71e93c81e4cb319c3456273761fb821f6b420d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Dec 2020 09:20:56 -0500 Subject: [PATCH 4048/4093] Reset Chromium-specific `color-scheme` CSS property Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1408 --- src/js/scriptlets/epicker.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/js/scriptlets/epicker.js b/src/js/scriptlets/epicker.js index ae1e70e2a8df9..9f44c70478ce2 100644 --- a/src/js/scriptlets/epicker.js +++ b/src/js/scriptlets/epicker.js @@ -1223,11 +1223,21 @@ const pickerCSSStyle = [ 'width: 100%', 'z-index: 2147483647', '' -].join(' !important;'); +]; + +// https://github.com/uBlockOrigin/uBlock-issues/issues/1408 +// We need to reset Chromium-specific `color-scheme` property +// for our iframe widget. +if ( + CSS.supports instanceof Function && + CSS.supports('color-scheme', 'initial') +) { + pickerCSSStyle.push('color-scheme: initial'); +} const pickerCSS = ` :root > [${vAPI.sessionId}] { - ${pickerCSSStyle} + ${pickerCSSStyle.join(' !important;')} } :root [${vAPI.sessionId}-clickblind] { pointer-events: none !important; From f6a4f00613d4c7f463e068c00c9f462bcacb8ded Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Dec 2020 09:22:11 -0500 Subject: [PATCH 4049/4093] New revision for release candidate --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 2dd08ab3edbf6..d54d1b02caf6e 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.104 +1.31.3.105 From 37d3b928bb6d264d706de2d4729b3faae21bed57 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 21 Dec 2020 09:30:58 -0500 Subject: [PATCH 4050/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 15b9927a5249d..85ccf72764aab 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.104", + "version": "1.31.3.105", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc4/uBlock0_1.31.3rc4.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc5/uBlock0_1.31.3rc5.firefox.signed.xpi" } ] } From 7e56a782e80042e1d5a6fa2e83f1c4959db29a63 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Dec 2020 08:14:06 -0500 Subject: [PATCH 4051/4093] New revision for stable release --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d54d1b02caf6e..359c41089a42e 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.31.3.105 +1.32.0 From 5d617484e5025dc32165c30b530127c45db31703 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Dec 2020 09:06:26 -0500 Subject: [PATCH 4052/4093] Upgrade CodeMirror library to 5.59.0 (from 5.46.0) --- src/lib/codemirror/README.md | 1 - src/lib/codemirror/addon/comment/comment.js | 4 +- src/lib/codemirror/addon/display/panel.js | 52 +- .../codemirror/addon/edit/matchbrackets.js | 16 +- src/lib/codemirror/addon/fold/foldcode.js | 9 +- src/lib/codemirror/addon/fold/foldgutter.js | 33 +- src/lib/codemirror/addon/hint/show-hint.js | 105 ++- src/lib/codemirror/addon/merge/merge.js | 18 +- .../addon/scroll/annotatescrollbar.js | 14 +- .../codemirror/addon/search/searchcursor.js | 35 +- src/lib/codemirror/lib/codemirror.css | 24 +- src/lib/codemirror/lib/codemirror.js | 772 +++++++++--------- 12 files changed, 617 insertions(+), 466 deletions(-) diff --git a/src/lib/codemirror/README.md b/src/lib/codemirror/README.md index 2a7b1f5ebae8d..92debf4488a8b 100644 --- a/src/lib/codemirror/README.md +++ b/src/lib/codemirror/README.md @@ -2,7 +2,6 @@ [![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror) [![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror) -[![Join the chat at https://gitter.im/codemirror/CodeMirror](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/codemirror/CodeMirror) CodeMirror is a versatile text editor implemented in JavaScript for the browser. It is specialized for editing code, and comes with over diff --git a/src/lib/codemirror/addon/comment/comment.js b/src/lib/codemirror/addon/comment/comment.js index 8394e85a4daad..dac48d03876b4 100644 --- a/src/lib/codemirror/addon/comment/comment.js +++ b/src/lib/codemirror/addon/comment/comment.js @@ -13,7 +13,7 @@ var noOptions = {}; var nonWS = /[^\s\u00a0]/; - var Pos = CodeMirror.Pos; + var Pos = CodeMirror.Pos, cmp = CodeMirror.cmpPos; function firstNonWS(str) { var found = str.search(nonWS); @@ -126,7 +126,9 @@ if (i != end || lastLineHasText) self.replaceRange(lead + pad, Pos(i, 0)); } else { + var atCursor = cmp(self.getCursor("to"), to) == 0, empty = !self.somethingSelected() self.replaceRange(endString, to); + if (atCursor) self.setSelection(empty ? to : self.getCursor("from"), to) self.replaceRange(startString, from); } }); diff --git a/src/lib/codemirror/addon/display/panel.js b/src/lib/codemirror/addon/display/panel.js index 5faf1d560eb91..4c9f2c0fcaf46 100644 --- a/src/lib/codemirror/addon/display/panel.js +++ b/src/lib/codemirror/addon/display/panel.js @@ -1,15 +1,15 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE -(function(mod) { +(function (mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); -})(function(CodeMirror) { - CodeMirror.defineExtension("addPanel", function(node, options) { +})(function (CodeMirror) { + CodeMirror.defineExtension("addPanel", function (node, options) { options = options || {}; if (!this.state.panels) initPanels(this); @@ -25,8 +25,7 @@ wrapper.insertBefore(node, options.before.node); } else if (replace) { wrapper.insertBefore(node, options.replace.node); - info.panels++; - options.replace.clear(); + options.replace.clear(true); } else if (options.position == "bottom") { wrapper.appendChild(node); } else if (options.position == "before-bottom") { @@ -38,14 +37,15 @@ } var height = (options && options.height) || node.offsetHeight; - this._setSize(null, info.heightLeft -= height); - if (!replace) { - info.panels++; - } + + var panel = new Panel(this, node, options, height); + info.panels.push(panel); + + this.setSize(); if (options.stable && isAtTop(this, node)) - this.scrollTo(null, this.getScrollInfo().top + height) + this.scrollTo(null, this.getScrollInfo().top + height); - return new Panel(this, node, options, height); + return panel; }); function Panel(cm, node, options, height) { @@ -56,22 +56,23 @@ this.cleared = false; } - Panel.prototype.clear = function() { + /* when skipRemove is true, clear() was called from addPanel(). + * Thus removePanels() should not be called (issue 5518) */ + Panel.prototype.clear = function (skipRemove) { if (this.cleared) return; this.cleared = true; var info = this.cm.state.panels; - this.cm._setSize(null, info.heightLeft += this.height); + info.panels.splice(info.panels.indexOf(this), 1); + this.cm.setSize(); if (this.options.stable && isAtTop(this.cm, this.node)) this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height) info.wrapper.removeChild(this.node); - if (--info.panels == 0) removePanels(this.cm); + if (info.panels.length == 0 && !skipRemove) removePanels(this.cm); }; - Panel.prototype.changed = function(height) { - var newHeight = height == null ? this.node.offsetHeight : height; - var info = this.cm.state.panels; - this.cm._setSize(null, info.heightLeft -= (newHeight - this.height)); - this.height = newHeight; + Panel.prototype.changed = function () { + this.height = this.node.getBoundingClientRect().height; + this.cm.setSize(); }; function initPanels(cm) { @@ -80,8 +81,7 @@ var height = parseInt(style.height); var info = cm.state.panels = { setHeight: wrap.style.height, - heightLeft: height, - panels: 0, + panels: [], wrapper: document.createElement("div") }; wrap.parentNode.insertBefore(info.wrapper, wrap); @@ -90,8 +90,8 @@ if (hasFocus) cm.focus(); cm._setSize = cm.setSize; - if (height != null) cm.setSize = function(width, newHeight) { - if (newHeight == null) return this._setSize(width, newHeight); + if (height != null) cm.setSize = function (width, newHeight) { + if (!newHeight) newHeight = info.wrapper.offsetHeight; info.setHeight = newHeight; if (typeof newHeight != "number") { var px = /^(\d+\.?\d*)px$/.exec(newHeight); @@ -100,10 +100,12 @@ } else { info.wrapper.style.height = newHeight; newHeight = info.wrapper.offsetHeight; - info.wrapper.style.height = ""; } } - cm._setSize(width, info.heightLeft += (newHeight - height)); + var editorheight = newHeight - info.panels + .map(function (p) { return p.node.getBoundingClientRect().height; }) + .reduce(function (a, b) { return a + b; }, 0); + cm._setSize(width, editorheight); height = newHeight; }; } diff --git a/src/lib/codemirror/addon/edit/matchbrackets.js b/src/lib/codemirror/addon/edit/matchbrackets.js index 2a147282c458d..0377408802de3 100644 --- a/src/lib/codemirror/addon/edit/matchbrackets.js +++ b/src/lib/codemirror/addon/edit/matchbrackets.js @@ -117,17 +117,25 @@ }); } + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { if (old && old != CodeMirror.Init) { cm.off("cursorActivity", doMatchBrackets); - if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { - cm.state.matchBrackets.currentlyHighlighted(); - cm.state.matchBrackets.currentlyHighlighted = null; - } + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); } if (val) { cm.state.matchBrackets = typeof val == "object" ? val : {}; cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) } }); diff --git a/src/lib/codemirror/addon/fold/foldcode.js b/src/lib/codemirror/addon/fold/foldcode.js index e146fb9f3ee94..887df3fe04665 100644 --- a/src/lib/codemirror/addon/fold/foldcode.js +++ b/src/lib/codemirror/addon/fold/foldcode.js @@ -42,7 +42,7 @@ } if (!range || range.cleared || force === "unfold") return; - var myWidget = makeWidget(cm, options); + var myWidget = makeWidget(cm, options, range); CodeMirror.on(myWidget, "mousedown", function(e) { myRange.clear(); CodeMirror.e_preventDefault(e); @@ -58,8 +58,13 @@ CodeMirror.signal(cm, "fold", cm, range.from, range.to); } - function makeWidget(cm, options) { + function makeWidget(cm, options, range) { var widget = getOption(cm, options, "widget"); + + if (typeof widget == "function") { + widget = widget(range.from, range.to); + } + if (typeof widget == "string") { var text = document.createTextNode(widget); widget = document.createElement("span"); diff --git a/src/lib/codemirror/addon/fold/foldgutter.js b/src/lib/codemirror/addon/fold/foldgutter.js index 988c67c450617..7d46a609b02fe 100644 --- a/src/lib/codemirror/addon/fold/foldgutter.js +++ b/src/lib/codemirror/addon/fold/foldgutter.js @@ -16,7 +16,7 @@ cm.clearGutter(cm.state.foldGutter.options.gutter); cm.state.foldGutter = null; cm.off("gutterClick", onGutterClick); - cm.off("change", onChange); + cm.off("changes", onChange); cm.off("viewportChange", onViewportChange); cm.off("fold", onFold); cm.off("unfold", onFold); @@ -26,7 +26,7 @@ cm.state.foldGutter = new State(parseOptions(val)); updateInViewport(cm); cm.on("gutterClick", onGutterClick); - cm.on("change", onChange); + cm.on("changes", onChange); cm.on("viewportChange", onViewportChange); cm.on("fold", onFold); cm.on("unfold", onFold); @@ -51,8 +51,13 @@ function isFolded(cm, line) { var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0)); - for (var i = 0; i < marks.length; ++i) - if (marks[i].__isFold && marks[i].find().from.line == line) return marks[i]; + for (var i = 0; i < marks.length; ++i) { + if (marks[i].__isFold) { + var fromPos = marks[i].find(-1); + if (fromPos && fromPos.line === line) + return marks[i]; + } + } } function marker(spec) { @@ -66,24 +71,36 @@ } function updateFoldInfo(cm, from, to) { - var opts = cm.state.foldGutter.options, cur = from; + var opts = cm.state.foldGutter.options, cur = from - 1; var minSize = cm.foldOption(opts, "minFoldSize"); var func = cm.foldOption(opts, "rangeFinder"); + // we can reuse the built-in indicator element if its className matches the new state + var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded); + var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen); cm.eachLine(from, to, function(line) { + ++cur; var mark = null; + var old = line.gutterMarkers; + if (old) old = old[opts.gutter]; if (isFolded(cm, cur)) { + if (clsFolded && old && clsFolded.test(old.className)) return; mark = marker(opts.indicatorFolded); } else { var pos = Pos(cur, 0); var range = func && func(cm, pos); - if (range && range.to.line - range.from.line >= minSize) + if (range && range.to.line - range.from.line >= minSize) { + if (clsOpen && old && clsOpen.test(old.className)) return; mark = marker(opts.indicatorOpen); + } } + if (!mark && !old) return; cm.setGutterMarker(line, opts.gutter, mark); - ++cur; }); } + // copied from CodeMirror/src/util/dom.js + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + function updateInViewport(cm) { var vp = cm.getViewport(), state = cm.state.foldGutter; if (!state) return; @@ -100,7 +117,7 @@ if (gutter != opts.gutter) return; var folded = isFolded(cm, line); if (folded) folded.clear(); - else cm.foldCode(Pos(line, 0), opts.rangeFinder); + else cm.foldCode(Pos(line, 0), opts); } function onChange(cm) { diff --git a/src/lib/codemirror/addon/hint/show-hint.js b/src/lib/codemirror/addon/hint/show-hint.js index e3cd209d79017..5ef1bba6457ff 100644 --- a/src/lib/codemirror/addon/hint/show-hint.js +++ b/src/lib/codemirror/addon/hint/show-hint.js @@ -1,6 +1,8 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE +// declare global: DOMRect + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); @@ -85,12 +87,19 @@ }, pick: function(data, i) { - var completion = data.list[i]; - if (completion.hint) completion.hint(this.cm, data, completion); - else this.cm.replaceRange(getText(completion), completion.from || data.from, - completion.to || data.to, "complete"); - CodeMirror.signal(data, "pick", completion); - this.close(); + var completion = data.list[i], self = this; + this.cm.operation(function() { + if (completion.hint) + completion.hint(self.cm, data, completion); + else + self.cm.replaceRange(getText(completion), completion.from || data.from, + completion.to || data.to, "complete"); + CodeMirror.signal(data, "pick", completion); + self.cm.scrollIntoView(); + }); + if (this.options.closeOnPick) { + this.close(); + } }, cursorActivity: function() { @@ -99,11 +108,18 @@ this.debounce = 0; } + var identStart = this.startPos; + if(this.data) { + identStart = this.data.from; + } + var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line); if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch || - pos.ch < this.startPos.ch || this.cm.somethingSelected() || + pos.ch < identStart.ch || this.cm.somethingSelected() || (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) { - this.close(); + if (this.options.closeOnCursorActivity) { + this.close(); + } } else { var self = this; this.debounce = requestAnimationFrame(function() {self.update();}); @@ -229,30 +245,47 @@ elt.hintId = i; } + var container = completion.options.container || ownerDocument.body; var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); var left = pos.left, top = pos.bottom, below = true; - hints.style.left = left + "px"; - hints.style.top = top + "px"; + var offsetLeft = 0, offsetTop = 0; + if (container !== ownerDocument.body) { + // We offset the cursor position because left and top are relative to the offsetParent's top left corner. + var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1; + var offsetParent = isContainerPositioned ? container : container.offsetParent; + var offsetParentPosition = offsetParent.getBoundingClientRect(); + var bodyPosition = ownerDocument.body.getBoundingClientRect(); + offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft); + offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop); + } + hints.style.left = (left - offsetLeft) + "px"; + hints.style.top = (top - offsetTop) + "px"; + // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth); var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight); - (completion.options.container || ownerDocument.body).appendChild(hints); - var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; - var scrolls = hints.scrollHeight > hints.clientHeight + 1 - var startScroll = cm.getScrollInfo(); + container.appendChild(hints); + var box = completion.options.moveOnOverlap ? hints.getBoundingClientRect() : new DOMRect(); + var scrolls = completion.options.paddingForScrollbar ? hints.scrollHeight > hints.clientHeight + 1 : false; + + // Compute in the timeout to avoid reflow on init + var startScroll; + setTimeout(function() { startScroll = cm.getScrollInfo(); }); + + var overlapY = box.bottom - winH; if (overlapY > 0) { var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); if (curTop - height > 0) { // Fits above cursor - hints.style.top = (top = pos.top - height) + "px"; + hints.style.top = (top = pos.top - height - offsetTop) + "px"; below = false; } else if (height > winH) { hints.style.height = (winH - 5) + "px"; - hints.style.top = (top = pos.bottom - box.top) + "px"; + hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px"; var cursor = cm.getCursor(); if (data.from.ch != cursor.ch) { pos = cm.cursorCoords(cursor); - hints.style.left = (left = pos.left) + "px"; + hints.style.left = (left = pos.left - offsetLeft) + "px"; box = hints.getBoundingClientRect(); } } @@ -263,7 +296,7 @@ hints.style.width = (winW - 5) + "px"; overlapX -= (box.right - box.left) - winW; } - hints.style.left = (left = pos.left - overlapX) + "px"; + hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px"; } if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) node.style.paddingRight = cm.display.nativeBarWidth + "px" @@ -311,6 +344,12 @@ setTimeout(function(){cm.focus();}, 20); }); + // The first hint doesn't need to be scrolled to on init + var selectedHintRange = this.getSelectedHintRange(); + if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) { + this.scrollToActive(); + } + CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]); return true; } @@ -351,15 +390,31 @@ if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, ""); node = this.hints.childNodes[this.selectedHint = i]; node.className += " " + ACTIVE_HINT_ELEMENT_CLASS; - if (node.offsetTop < this.hints.scrollTop) - this.hints.scrollTop = node.offsetTop - 3; - else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) - this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3; + this.scrollToActive() CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node); }, + scrollToActive: function() { + var selectedHintRange = this.getSelectedHintRange(); + var node1 = this.hints.childNodes[selectedHintRange.from]; + var node2 = this.hints.childNodes[selectedHintRange.to]; + var firstNode = this.hints.firstChild; + if (node1.offsetTop < this.hints.scrollTop) + this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop; + else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight) + this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop; + }, + screenAmount: function() { return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1; + }, + + getSelectedHintRange: function() { + var margin = this.completion.options.scrollMargin || 0; + return { + from: Math.max(0, this.selectedHint - margin), + to: Math.min(this.data.list.length - 1, this.selectedHint + margin), + }; } }; @@ -437,11 +492,15 @@ completeSingle: true, alignWithWord: true, closeCharacters: /[\s()\[\]{};:>,]/, + closeOnCursorActivity: true, + closeOnPick: true, closeOnUnfocus: true, completeOnSingleClick: true, container: null, customKeys: null, - extraKeys: null + extraKeys: null, + paddingForScrollbar: true, + moveOnOverlap: true, }; CodeMirror.defineOption("hintOptions", null); diff --git a/src/lib/codemirror/addon/merge/merge.js b/src/lib/codemirror/addon/merge/merge.js index 63373f75edb47..827edb71334a4 100644 --- a/src/lib/codemirror/addon/merge/merge.js +++ b/src/lib/codemirror/addon/merge/merge.js @@ -443,22 +443,26 @@ aligners[i].clear(); aligners.length = 0; - var cm = [dv.edit, dv.orig], scroll = []; + var cm = [dv.edit, dv.orig], scroll = [], offset = [] if (other) cm.push(other.orig); - for (var i = 0; i < cm.length; i++) + for (var i = 0; i < cm.length; i++) { scroll.push(cm[i].getScrollInfo().top); + offset.push(-cm[i].getScrollerElement().getBoundingClientRect().top) + } + if (offset[0] != offset[1] || cm.length == 3 && offset[1] != offset[2]) + alignLines(cm, offset, [0, 0, 0], aligners) for (var ln = 0; ln < linesToAlign.length; ln++) - alignLines(cm, linesToAlign[ln], aligners); + alignLines(cm, offset, linesToAlign[ln], aligners); for (var i = 0; i < cm.length; i++) cm[i].scrollTo(null, scroll[i]); } - function alignLines(cm, lines, aligners) { - var maxOffset = 0, offset = []; + function alignLines(cm, cmOffset, lines, aligners) { + var maxOffset = -1e8, offset = []; for (var i = 0; i < cm.length; i++) if (lines[i] != null) { - var off = cm[i].heightAtLine(lines[i], "local"); + var off = cm[i].heightAtLine(lines[i], "local") - cmOffset[i]; offset[i] = off; maxOffset = Math.max(maxOffset, off); } @@ -918,7 +922,7 @@ hasMarker: function(n) { var handle = this.cm.getLineHandle(n) if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++) - if (handle.markedSpans[i].mark.collapsed && handle.markedSpans[i].to != null) + if (handle.markedSpans[i].marker.collapsed && handle.markedSpans[i].to != null) return true return false }, diff --git a/src/lib/codemirror/addon/scroll/annotatescrollbar.js b/src/lib/codemirror/addon/scroll/annotatescrollbar.js index 356625811eca2..c12e44cda5b7d 100644 --- a/src/lib/codemirror/addon/scroll/annotatescrollbar.js +++ b/src/lib/codemirror/addon/scroll/annotatescrollbar.js @@ -43,7 +43,7 @@ cm.on("markerAdded", this.resizeHandler); cm.on("markerCleared", this.resizeHandler); if (options.listenForChanges !== false) - cm.on("change", this.changeHandler = function() { + cm.on("changes", this.changeHandler = function() { scheduleRedraw(250); }); } @@ -72,10 +72,16 @@ var wrapping = cm.getOption("lineWrapping"); var singleLineH = wrapping && cm.defaultTextHeight() * 1.5; var curLine = null, curLineObj = null; + function getY(pos, top) { if (curLine != pos.line) { - curLine = pos.line; - curLineObj = cm.getLineHandle(curLine); + curLine = pos.line + curLineObj = cm.getLineHandle(pos.line) + var visual = cm.getLineHandleVisualStart(curLineObj) + if (visual != curLineObj) { + curLine = cm.getLineNumber(visual) + curLineObj = visual + } } if ((curLineObj.widgets && curLineObj.widgets.length) || (wrapping && curLineObj.height > singleLineH)) @@ -116,7 +122,7 @@ this.cm.off("refresh", this.resizeHandler); this.cm.off("markerAdded", this.resizeHandler); this.cm.off("markerCleared", this.resizeHandler); - if (this.changeHandler) this.cm.off("change", this.changeHandler); + if (this.changeHandler) this.cm.off("changes", this.changeHandler); this.div.parentNode.removeChild(this.div); }; }); diff --git a/src/lib/codemirror/addon/search/searchcursor.js b/src/lib/codemirror/addon/search/searchcursor.js index aae36dfe5316d..d5869578872a6 100644 --- a/src/lib/codemirror/addon/search/searchcursor.js +++ b/src/lib/codemirror/addon/search/searchcursor.js @@ -72,24 +72,26 @@ } } - function lastMatchIn(string, regexp) { - var cutOff = 0, match - for (;;) { - regexp.lastIndex = cutOff + function lastMatchIn(string, regexp, endMargin) { + var match, from = 0 + while (from <= string.length) { + regexp.lastIndex = from var newMatch = regexp.exec(string) - if (!newMatch) return match - match = newMatch - cutOff = match.index + (match[0].length || 1) - if (cutOff == string.length) return match + if (!newMatch) break + var end = newMatch.index + newMatch[0].length + if (end > string.length - endMargin) break + if (!match || end > match.index + match[0].length) + match = newMatch + from = newMatch.index + 1 } + return match } function searchRegexpBackward(doc, regexp, start) { regexp = ensureFlags(regexp, "g") for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) { var string = doc.getLine(line) - if (ch > -1) string = string.slice(0, ch) - var match = lastMatchIn(string, regexp) + var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch) if (match) return {from: Pos(line, match.index), to: Pos(line, match.index + match[0].length), @@ -98,16 +100,17 @@ } function searchRegexpBackwardMultiline(doc, regexp, start) { + if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start) regexp = ensureFlags(regexp, "gm") - var string, chunk = 1 + var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch for (var line = start.line, first = doc.firstLine(); line >= first;) { - for (var i = 0; i < chunk; i++) { + for (var i = 0; i < chunkSize && line >= first; i++) { var curLine = doc.getLine(line--) - string = string == null ? curLine.slice(0, start.ch) : curLine + "\n" + string + string = string == null ? curLine : curLine + "\n" + string } - chunk *= 2 + chunkSize *= 2 - var match = lastMatchIn(string, regexp) + var match = lastMatchIn(string, regexp, endMargin) if (match) { var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n") var startLine = line + before.length, startCh = before[before.length - 1].length @@ -237,7 +240,7 @@ var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to)) // Implements weird auto-growing behavior on null-matches for - // backwards-compatiblity with the vim code (unfortunately) + // backwards-compatibility with the vim code (unfortunately) while (result && CodeMirror.cmpPos(result.from, result.to) == 0) { if (reverse) { if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1) diff --git a/src/lib/codemirror/lib/codemirror.css b/src/lib/codemirror/lib/codemirror.css index c7a8ae70478fc..5ea2d2be2abad 100644 --- a/src/lib/codemirror/lib/codemirror.css +++ b/src/lib/codemirror/lib/codemirror.css @@ -13,12 +13,13 @@ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } -.CodeMirror pre { +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { padding: 0 4px; /* Horizontal padding of content */ } .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { - background-color: white; /* The little square between H and V scrollbars */ + background-color: transparent; /* The little square between H and V scrollbars */ } /* GUTTER */ @@ -96,7 +97,7 @@ .CodeMirror-rulers { position: absolute; - left: 0; right: 0; top: -50px; bottom: -20px; + left: 0; right: 0; top: -50px; bottom: 0; overflow: hidden; } .CodeMirror-ruler { @@ -163,17 +164,17 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} .CodeMirror-scroll { overflow: scroll !important; /* Things will break if this is overridden */ - /* 30px is the magic margin used to hide the element's real scrollbars */ + /* 50px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ - margin-bottom: -30px; margin-right: -30px; - padding-bottom: 30px; + margin-bottom: -50px; margin-right: -50px; + padding-bottom: 50px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; } .CodeMirror-sizer { position: relative; - border-right: 30px solid transparent; + border-right: 50px solid transparent; } /* The fake, visible scrollbars. Used to force redraw during scrolling @@ -183,6 +184,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} position: absolute; z-index: 6; display: none; + outline: none; } .CodeMirror-vscrollbar { right: 0; top: 0; @@ -211,7 +213,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} height: 100%; display: inline-block; vertical-align: top; - margin-bottom: -30px; + margin-bottom: -50px; } .CodeMirror-gutter-wrapper { position: absolute; @@ -236,7 +238,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } -.CodeMirror pre { +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; @@ -255,7 +258,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} -webkit-font-variant-ligatures: contextual; font-variant-ligatures: contextual; } -.CodeMirror-wrap pre { +.CodeMirror-wrap pre.CodeMirror-line, +.CodeMirror-wrap pre.CodeMirror-line-like { word-wrap: break-word; white-space: pre-wrap; word-break: normal; diff --git a/src/lib/codemirror/lib/codemirror.js b/src/lib/codemirror/lib/codemirror.js index 6d8bd9f280749..cb2186789b8c4 100644 --- a/src/lib/codemirror/lib/codemirror.js +++ b/src/lib/codemirror/lib/codemirror.js @@ -10,7 +10,7 @@ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : - (global.CodeMirror = factory()); + (global = global || self, global.CodeMirror = factory()); }(this, (function () { 'use strict'; // Kludges for bugs and behavior differences that can't be feature @@ -32,7 +32,7 @@ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); var phantom = /PhantomJS/.test(userAgent); - var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); + var ios = !edge && /AppleWebKit/.test(userAgent) && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); var android = /Android/.test(userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); @@ -173,10 +173,28 @@ } } - var Delayed = function() {this.id = null;}; + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; Delayed.prototype.set = function (ms, f) { - clearTimeout(this.id); - this.id = setTimeout(f, ms); + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } }; function indexOf(array, elt) { @@ -186,7 +204,7 @@ } // Number of pixels added to scroller and sizer to hide scrollbar - var scrollerGap = 30; + var scrollerGap = 50; // Returned or thrown by various protocols to signal 'I'm not // handling this'. @@ -467,14 +485,15 @@ for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} order.push(new BidiSpan(0, start, i$7)); } else { - var pos = i$7, at = order.length; + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} for (var j$2 = pos; j$2 < i$7;) { if (countsAsNum.test(types[j$2])) { - if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } var nstart = j$2; for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; pos = j$2; } else { ++j$2; } } @@ -518,8 +537,8 @@ } else if (emitter.attachEvent) { emitter.attachEvent("on" + type, f); } else { - var map$$1 = emitter._handlers || (emitter._handlers = {}); - map$$1[type] = (map$$1[type] || noHandlers).concat(f); + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); } }; @@ -533,11 +552,11 @@ } else if (emitter.detachEvent) { emitter.detachEvent("on" + type, f); } else { - var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]; + var map = emitter._handlers, arr = map && map[type]; if (arr) { var index = indexOf(arr, f); if (index > -1) - { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } } } } @@ -665,11 +684,11 @@ try { return te.selectionStart != te.selectionEnd } catch(e) { return false } } : function (te) { - var range$$1; - try {range$$1 = te.ownerDocument.selection.createRange();} + var range; + try {range = te.ownerDocument.selection.createRange();} catch(e) {} - if (!range$$1 || range$$1.parentElement() != te) { return false } - return range$$1.compareEndPoints("StartToEnd", range$$1) != 0 + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 }; var hasCopyEvent = (function () { @@ -817,10 +836,8 @@ return this.pos > start }; StringStream.prototype.eatSpace = function () { - var this$1 = this; - var start = this.pos; - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; } + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } return this.pos > start }; StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; @@ -1016,11 +1033,9 @@ }; Context.prototype.baseToken = function (n) { - var this$1 = this; - if (!this.baseTokens) { return null } while (this.baseTokens[this.baseTokenPos] <= n) - { this$1.baseTokenPos += 2; } + { this.baseTokenPos += 2; } var type = this.baseTokens[this.baseTokenPos + 1]; return {type: type && type.replace(/( |^)overlay .*/, ""), size: this.baseTokens[this.baseTokenPos] - n} @@ -1186,7 +1201,7 @@ var prop = lineClass[1] ? "bgClass" : "textClass"; if (output[prop] == null) { output[prop] = lineClass[2]; } - else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) { output[prop] += " " + lineClass[2]; } } } return type @@ -1509,8 +1524,8 @@ // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. - function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) { - var line = getLine(doc, lineNo$$1); + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; @@ -1826,7 +1841,7 @@ } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; - if (style || startStyle || endStyle || mustWrap || css) { + if (style || startStyle || endStyle || mustWrap || css || attributes) { var fullStyle = style || ""; if (startStyle) { fullStyle += startStyle; } if (endStyle) { fullStyle += endStyle; } @@ -2191,10 +2206,10 @@ function updateLineWidgets(cm, lineView, dims) { if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { next = node.nextSibling; - if (node.className == "CodeMirror-linewidget") - { lineView.node.removeChild(node); } + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } } insertLineWidgets(cm, lineView, dims); } @@ -2224,7 +2239,7 @@ if (!line.widgets) { return } var wrap = ensureLineWrapped(lineView); for (var i = 0, ws = line.widgets; i < ws.length; ++i) { - var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } positionLineWidget(widget, node, lineView, dims); cm.display.input.setUneditable(node); @@ -2284,7 +2299,7 @@ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} function paddingH(display) { if (display.cachedPaddingH) { return display.cachedPaddingH } - var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } @@ -2412,36 +2427,36 @@ var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; - function nodeAndOffsetInLineMap(map$$1, ch, bias) { + function nodeAndOffsetInLineMap(map, ch, bias) { var node, start, end, collapse, mStart, mEnd; // First, search the line map for the text node corresponding to, // or closest to, the target character. - for (var i = 0; i < map$$1.length; i += 3) { - mStart = map$$1[i]; - mEnd = map$$1[i + 1]; + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; if (ch < mStart) { start = 0; end = 1; collapse = "left"; } else if (ch < mEnd) { start = ch - mStart; end = start + 1; - } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) { + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { end = mEnd - mStart; start = end - 1; if (ch >= mEnd) { collapse = "right"; } } if (start != null) { - node = map$$1[i + 2]; + node = map[i + 2]; if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) { collapse = bias; } if (bias == "left" && start == 0) - { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) { - node = map$$1[(i -= 3) + 2]; + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; collapse = "left"; } } if (bias == "right" && start == mEnd - mStart) - { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) { - node = map$$1[(i += 3) + 2]; + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; collapse = "right"; } } break @@ -2678,7 +2693,7 @@ function PosWithInfo(line, ch, sticky, outside, xRel) { var pos = Pos(line, ch, sticky); pos.xRel = xRel; - if (outside) { pos.outside = true; } + if (outside) { pos.outside = outside; } return pos } @@ -2687,16 +2702,16 @@ function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; - if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) } + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineN > last) - { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) } + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } if (x < 0) { x = 0; } var lineObj = getLine(doc, lineN); for (;;) { var found = coordsCharInner(cm, lineObj, lineN, x, y); - var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0)); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); if (!collapsed) { return found } var rangeEnd = collapsed.find(1); if (rangeEnd.line == lineN) { return rangeEnd } @@ -2724,13 +2739,13 @@ return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x } - function coordsCharInner(cm, lineObj, lineNo$$1, x, y) { + function coordsCharInner(cm, lineObj, lineNo, x, y) { // Move y into line-local coordinate space y -= heightAtLine(lineObj); var preparedMeasure = prepareMeasureForLine(cm, lineObj); // When directly calling `measureCharPrepared`, we have to adjust // for the widgets at this line. - var widgetHeight$$1 = widgetTopHeight(lineObj); + var widgetHeight = widgetTopHeight(lineObj); var begin = 0, end = lineObj.text.length, ltr = true; var order = getOrder(lineObj, cm.doc.direction); @@ -2738,7 +2753,7 @@ // which bidi section the coordinates fall into. if (order) { var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) - (cm, lineObj, lineNo$$1, preparedMeasure, order, x, y); + (cm, lineObj, lineNo, preparedMeasure, order, x, y); ltr = part.level != 1; // The awkward -1 offsets are needed because findFirst (called // on these below) will treat its first bound as inclusive, @@ -2754,7 +2769,7 @@ var chAround = null, boxAround = null; var ch = findFirst(function (ch) { var box = measureCharPrepared(cm, preparedMeasure, ch); - box.top += widgetHeight$$1; box.bottom += widgetHeight$$1; + box.top += widgetHeight; box.bottom += widgetHeight; if (!boxIsAfter(box, x, y, false)) { return false } if (box.top <= y && box.left <= x) { chAround = ch; @@ -2778,27 +2793,27 @@ // left of the character and compare it's vertical position to the // coordinates sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : - (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight$$1 <= y) == ltr ? + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? "after" : "before"; // Now get accurate coordinates for this place, in order to get a // base X position - var coords = cursorCoords(cm, Pos(lineNo$$1, ch, sticky), "line", lineObj, preparedMeasure); + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); baseX = coords.left; - outside = y < coords.top || y >= coords.bottom; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; } ch = skipExtendingChars(lineObj.text, ch, 1); - return PosWithInfo(lineNo$$1, ch, sticky, outside, x - baseX) + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) } - function coordsBidiPart(cm, lineObj, lineNo$$1, preparedMeasure, order, x, y) { + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { // Bidi parts are sorted left-to-right, and in a non-line-wrapping // situation, we can take this ordering to correspond to the visual // ordering. This finds the first part whose end is after the given // coordinates. var index = findFirst(function (i) { var part = order[i], ltr = part.level != 1; - return boxIsAfter(cursorCoords(cm, Pos(lineNo$$1, ltr ? part.to : part.from, ltr ? "before" : "after"), + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), "line", lineObj, preparedMeasure), x, y, true) }, 0, order.length - 1); var part = order[index]; @@ -2807,7 +2822,7 @@ // that start, move one part back. if (index > 0) { var ltr = part.level != 1; - var start = cursorCoords(cm, Pos(lineNo$$1, ltr ? part.from : part.to, ltr ? "after" : "before"), + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), "line", lineObj, preparedMeasure); if (boxIsAfter(start, x, y, true) && start.top > y) { part = order[index - 1]; } @@ -2853,7 +2868,7 @@ function textHeight(display) { if (display.cachedTextHeight != null) { return display.cachedTextHeight } if (measureText == null) { - measureText = elt("pre"); + measureText = elt("pre", null, "CodeMirror-line-like"); // Measure a bunch of lines, for browsers that compute // fractional heights. for (var i = 0; i < 49; ++i) { @@ -2873,7 +2888,7 @@ function charWidth(display) { if (display.cachedCharWidth != null) { return display.cachedCharWidth } var anchor = elt("span", "xxxxxxxxxx"); - var pre = elt("pre", [anchor]); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); removeChildrenAndAdd(display.measure, pre); var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; if (width > 2) { display.cachedCharWidth = width; } @@ -2945,9 +2960,9 @@ var x, y, space = display.lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top; } - catch (e) { return null } + catch (e$1) { return null } var coords = coordsChar(cm, x, y), line; - if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); } @@ -3128,13 +3143,13 @@ for (var i = 0; i < doc.sel.ranges.length; i++) { if (!primary && i == doc.sel.primIndex) { continue } - var range$$1 = doc.sel.ranges[i]; - if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue } - var collapsed = range$$1.empty(); + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); if (collapsed || cm.options.showCursorWhenSelecting) - { drawSelectionCursor(cm, range$$1.head, curFragment); } + { drawSelectionCursor(cm, range.head, curFragment); } if (!collapsed) - { drawSelectionRange(cm, range$$1, selFragment); } + { drawSelectionRange(cm, range, selFragment); } } return result } @@ -3161,7 +3176,7 @@ function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } // Draws the given range as a highlighted selection - function drawSelectionRange(cm, range$$1, output) { + function drawSelectionRange(cm, range, output) { var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var padding = paddingH(cm.display), leftSide = padding.left; @@ -3230,7 +3245,7 @@ return {start: start, end: end} } - var sFrom = range$$1.from(), sTo = range$$1.to(); + var sFrom = range.from(), sTo = range.to(); if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { @@ -3261,26 +3276,31 @@ var on = true; display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) - { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, - cm.options.cursorBlinkRate); } + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } else if (cm.options.cursorBlinkRate < 0) { display.cursorDiv.style.visibility = "hidden"; } } function ensureFocus(cm) { - if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } } function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true; setTimeout(function () { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; - onBlur(cm); + if (cm.state.focused) { onBlur(cm); } } }, 100); } function onFocus(cm, e) { - if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } if (cm.options.readOnly == "nocursor") { return } if (!cm.state.focused) { @@ -3462,14 +3482,15 @@ if (newTop != screentop) { result.scrollTop = newTop; } } - var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; - var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; var tooWide = rect.right - rect.left > screenw; if (tooWide) { rect.right = rect.left + screenw; } if (rect.left < 10) { result.scrollLeft = 0; } else if (rect.left < screenleft) - { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } else if (rect.right > screenw + screenleft - 3) { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } return result @@ -3497,9 +3518,9 @@ if (y != null) { cm.curOp.scrollTop = y; } } - function scrollToRange(cm, range$$1) { + function scrollToRange(cm, range) { resolveScrollToPos(cm); - cm.curOp.scrollToPos = range$$1; + cm.curOp.scrollToPos = range; } // When an operation has its scrollToPos property set, and another @@ -3507,11 +3528,11 @@ // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. function resolveScrollToPos(cm) { - var range$$1 = cm.curOp.scrollToPos; - if (range$$1) { + var range = cm.curOp.scrollToPos; + if (range) { cm.curOp.scrollToPos = null; - var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to); - scrollToCoordsRange(cm, from, to, range$$1.margin); + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); } } @@ -3536,7 +3557,7 @@ } function setScrollTop(cm, val, forceScroll) { - val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); if (cm.display.scroller.scrollTop == val && !forceScroll) { return } cm.doc.scrollTop = val; cm.display.scrollbars.setScrollTop(val); @@ -3546,7 +3567,7 @@ // Sync scroller and scrollbar, ensure the gutter elements are // aligned. function setScrollLeft(cm, val, isScroller, forceScroll) { - val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } cm.doc.scrollLeft = val; alignHorizontally(cm); @@ -3658,9 +3679,9 @@ // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. var box = bar.getBoundingClientRect(); - var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); - if (elt$$1 != bar) { bar.style.pointerEvents = "none"; } + if (elt != bar) { bar.style.pointerEvents = "none"; } else { delay.set(1000, maybeDisable); } } delay.set(1000, maybeDisable); @@ -4000,10 +4021,8 @@ { this.events.push(arguments); } }; DisplayUpdate.prototype.finish = function () { - var this$1 = this; - for (var i = 0; i < this.events.length; i++) - { signal.apply(null, this$1.events[i]); } + { signal.apply(null, this.events[i]); } }; function maybeClipScrollbars(cm) { @@ -4037,12 +4056,13 @@ function restoreSelection(snapshot) { if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } snapshot.activeElt.focus(); - if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { - var sel = window.getSelection(), range$$1 = document.createRange(); - range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset); - range$$1.collapse(false); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var sel = window.getSelection(), range = document.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); sel.removeAllRanges(); - sel.addRange(range$$1); + sel.addRange(range); sel.extend(snapshot.focusNode, snapshot.focusOffset); } } @@ -4135,6 +4155,8 @@ update.visible = visibleLines(cm.display, cm.doc, viewport); if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); } if (!updateDisplayIfNeeded(cm, update)) { break } updateHeightsInViewport(cm); @@ -4535,40 +4557,32 @@ Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; Selection.prototype.equals = function (other) { - var this$1 = this; - if (other == this) { return true } if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } for (var i = 0; i < this.ranges.length; i++) { - var here = this$1.ranges[i], there = other.ranges[i]; + var here = this.ranges[i], there = other.ranges[i]; if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } } return true }; Selection.prototype.deepCopy = function () { - var this$1 = this; - var out = []; for (var i = 0; i < this.ranges.length; i++) - { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); } + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } return new Selection(out, this.primIndex) }; Selection.prototype.somethingSelected = function () { - var this$1 = this; - for (var i = 0; i < this.ranges.length; i++) - { if (!this$1.ranges[i].empty()) { return true } } + { if (!this.ranges[i].empty()) { return true } } return false }; Selection.prototype.contains = function (pos, end) { - var this$1 = this; - if (!end) { end = pos; } for (var i = 0; i < this.ranges.length; i++) { - var range = this$1.ranges[i]; + var range = this.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) { return i } } @@ -4694,16 +4708,16 @@ } // Perform a change on the document data structure. - function updateDoc(doc, change, markedSpans, estimateHeight$$1) { + function updateDoc(doc, change, markedSpans, estimateHeight) { function spansFor(n) {return markedSpans ? markedSpans[n] : null} function update(line, text, spans) { - updateLine(line, text, spans, estimateHeight$$1); + updateLine(line, text, spans, estimateHeight); signalLater(line, "change", line, change); } function linesFor(start, end) { var result = []; for (var i = start; i < end; ++i) - { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); } + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } return result } @@ -4727,7 +4741,7 @@ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { var added$1 = linesFor(1, text.length - 1); - added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1)); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added$1); } @@ -5064,11 +5078,9 @@ var obj = { ranges: sel.ranges, update: function(ranges) { - var this$1 = this; - this.ranges = []; for (var i = 0; i < ranges.length; i++) - { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); } }, origin: options && options.origin @@ -5147,8 +5159,15 @@ var line = getLine(doc, pos.line); if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var sp = line.markedSpans[i], m = sp.marker; - if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && - (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { signal(m, "beforeCursorEnter"); if (m.explicitlyCleared) { @@ -5160,14 +5179,14 @@ if (oldPos) { var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); - if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) + if (dir < 0 ? preventCursorRight : preventCursorLeft) { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) { return skipAtomicInner(doc, near, pos, dir, mayClear) } } var far = m.find(dir < 0 ? -1 : 1); - if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) + if (dir < 0 ? preventCursorLeft : preventCursorRight) { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null } @@ -5396,6 +5415,9 @@ if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } else { updateDoc(doc, change, spans); } setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } } // Handle the interaction of a change to a document with the editor @@ -5545,13 +5567,11 @@ // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html function LeafChunk(lines) { - var this$1 = this; - this.lines = lines; this.parent = null; var height = 0; for (var i = 0; i < lines.length; ++i) { - lines[i].parent = this$1; + lines[i].parent = this; height += lines[i].height; } this.height = height; @@ -5562,11 +5582,9 @@ // Remove the n lines at offset 'at'. removeInner: function(at, n) { - var this$1 = this; - for (var i = at, e = at + n; i < e; ++i) { - var line = this$1.lines[i]; - this$1.height -= line.height; + var line = this.lines[i]; + this.height -= line.height; cleanUpLine(line); signalLater(line, "delete"); } @@ -5581,31 +5599,25 @@ // Insert the given array of lines at offset 'at', count them as // having the given height. insertInner: function(at, lines, height) { - var this$1 = this; - this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); - for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; } + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { - var this$1 = this; - for (var e = at + n; at < e; ++at) - { if (op(this$1.lines[at])) { return true } } + { if (op(this.lines[at])) { return true } } } }; function BranchChunk(children) { - var this$1 = this; - this.children = children; var size = 0, height = 0; for (var i = 0; i < children.length; ++i) { var ch = children[i]; size += ch.chunkSize(); height += ch.height; - ch.parent = this$1; + ch.parent = this; } this.size = size; this.height = height; @@ -5616,16 +5628,14 @@ chunkSize: function() { return this.size }, removeInner: function(at, n) { - var this$1 = this; - this.size -= n; for (var i = 0; i < this.children.length; ++i) { - var child = this$1.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; child.removeInner(at, rm); - this$1.height -= oldHeight - child.height; - if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; } + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) { break } at = 0; } else { at -= sz; } @@ -5642,18 +5652,14 @@ }, collapse: function(lines) { - var this$1 = this; - - for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); } + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } }, insertInner: function(at, lines, height) { - var this$1 = this; - this.size += lines.length; this.height += height; for (var i = 0; i < this.children.length; ++i) { - var child = this$1.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { @@ -5663,11 +5669,11 @@ for (var pos = remaining; pos < child.lines.length;) { var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; - this$1.children.splice(++i, 0, leaf); - leaf.parent = this$1; + this.children.splice(++i, 0, leaf); + leaf.parent = this; } child.lines = child.lines.slice(0, remaining); - this$1.maybeSpill(); + this.maybeSpill(); } break } @@ -5699,10 +5705,8 @@ }, iterN: function(at, n, op) { - var this$1 = this; - for (var i = 0; i < this.children.length; ++i) { - var child = this$1.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var used = Math.min(n, sz - at); if (child.iterN(at, used, op)) { return true } @@ -5716,20 +5720,16 @@ // Line widgets are block elements displayed above or below a line. var LineWidget = function(doc, node, options) { - var this$1 = this; - if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) - { this$1[opt] = options[opt]; } } } + { this[opt] = options[opt]; } } } this.doc = doc; this.node = node; }; LineWidget.prototype.clear = function () { - var this$1 = this; - var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); if (no == null || !ws) { return } - for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } if (!ws.length) { line.widgets = null; } var height = widgetHeight(this); updateLineHeight(line, Math.max(0, line.height - height)); @@ -5772,7 +5772,7 @@ changeLine(doc, handle, "widget", function (line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) { widgets.push(widget); } - else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } widget.line = line; if (cm && !lineIsHidden(doc, line)) { var aboveVisible = heightAtLine(line) < doc.scrollTop; @@ -5812,8 +5812,6 @@ // Clear the marker. TextMarker.prototype.clear = function () { - var this$1 = this; - if (this.explicitlyCleared) { return } var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) { startOperation(cm); } @@ -5823,19 +5821,19 @@ } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { - var line = this$1.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this$1); - if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); } + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } else if (cm) { if (span.to != null) { max = lineNo(line); } if (span.from != null) { min = lineNo(line); } } line.markedSpans = removeMarkedSpan(line.markedSpans, span); - if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) { updateLineHeight(line, textHeight(cm.display)); } } if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { - var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual); + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; @@ -5861,13 +5859,11 @@ // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function (side, lineObj) { - var this$1 = this; - if (side == null && this.type == "bookmark") { side = 1; } var from, to; for (var i = 0; i < this.lines.length; ++i) { - var line = this$1.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this$1); + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from); if (side == -1) { return from } @@ -6001,21 +5997,17 @@ // implemented as a meta-marker-object controlling multiple normal // markers. var SharedTextMarker = function(markers, primary) { - var this$1 = this; - this.markers = markers; this.primary = primary; for (var i = 0; i < markers.length; ++i) - { markers[i].parent = this$1; } + { markers[i].parent = this; } }; SharedTextMarker.prototype.clear = function () { - var this$1 = this; - if (this.explicitlyCleared) { return } this.explicitlyCleared = true; for (var i = 0; i < this.markers.length; ++i) - { this$1.markers[i].clear(); } + { this.markers[i].clear(); } signalLater(this, "clear"); }; @@ -6158,11 +6150,11 @@ clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { - var range$$1 = this.sel.primary(), pos; - if (start == null || start == "head") { pos = range$$1.head; } - else if (start == "anchor") { pos = range$$1.anchor; } - else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); } - else { pos = range$$1.from(); } + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } return pos }, listSelections: function() { return this.sel.ranges }, @@ -6185,13 +6177,11 @@ extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { - var this$1 = this; - if (!ranges.length) { return } var out = []; for (var i = 0; i < ranges.length; i++) - { out[i] = new Range(clipPos(this$1, ranges[i].anchor), - clipPos(this$1, ranges[i].head)); } + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head)); } if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } setSelection(this, normalizeSelection(this.cm, out, primary), options); }), @@ -6202,23 +6192,19 @@ }), getSelection: function(lineSep) { - var this$1 = this; - var ranges = this.sel.ranges, lines; for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); lines = lines ? lines.concat(sel) : sel; } if (lineSep === false) { return lines } else { return lines.join(lineSep || this.lineSeparator()) } }, getSelections: function(lineSep) { - var this$1 = this; - var parts = [], ranges = this.sel.ranges; for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); - if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); } + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } parts[i] = sel; } return parts @@ -6230,16 +6216,14 @@ this.replaceSelections(dup, collapse, origin || "+input"); }, replaceSelections: docMethodOp(function(code, collapse, origin) { - var this$1 = this; - var changes = [], sel = this.sel; for (var i = 0; i < sel.ranges.length; i++) { - var range$$1 = sel.ranges[i]; - changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}; + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; } var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) - { makeChange(this$1, changes[i$1]); } + { makeChange(this, changes[i$1]); } if (newSel) { setSelectionReplaceHistory(this, newSel); } else if (this.cm) { ensureCursorVisible(this.cm); } }), @@ -6257,7 +6241,12 @@ for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } return {undo: done, redo: undone} }, - clearHistory: function() {this.history = new History(this.history.maxGeneration);}, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history.maxGeneration); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, markClean: function() { this.cleanGeneration = this.changeGeneration(true); @@ -6378,18 +6367,18 @@ }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to); - var found = [], lineNo$$1 = from.line; + var found = [], lineNo = from.line; this.iter(from.line, to.line + 1, function (line) { var spans = line.markedSpans; if (spans) { for (var i = 0; i < spans.length; i++) { var span = spans[i]; - if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to || - span.from == null && lineNo$$1 != from.line || - span.from != null && lineNo$$1 == to.line && span.from >= to.ch) && + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) { found.push(span.marker.parent || span.marker); } } } - ++lineNo$$1; + ++lineNo; }); return found }, @@ -6404,14 +6393,14 @@ }, posFromIndex: function(off) { - var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length; + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; this.iter(function (line) { var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true } off -= sz; - ++lineNo$$1; + ++lineNo; }); - return clipPos(this, Pos(lineNo$$1, ch)) + return clipPos(this, Pos(lineNo, ch)) }, indexFromPos: function (coords) { coords = clipPos(this, coords); @@ -6450,15 +6439,13 @@ return copy }, unlinkDoc: function(other) { - var this$1 = this; - if (other instanceof CodeMirror) { other = other.doc; } if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { - var link = this$1.linked[i]; + var link = this.linked[i]; if (link.doc != other) { continue } - this$1.linked.splice(i, 1); - other.unlinkDoc(this$1); - detachSharedMarkers(findSharedMarkers(this$1)); + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); break } } // If the histories were shared, split them again @@ -6510,28 +6497,39 @@ // and insert it. if (files && files.length && window.FileReader && window.File) { var n = files.length, text = Array(n), read = 0; - var loadFile = function (file, i) { - if (cm.options.allowDropFileTypes && - indexOf(cm.options.allowDropFileTypes, file.type) == -1) - { return } - - var reader = new FileReader; - reader.onload = operation(cm, function () { - var content = reader.result; - if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; } - text[i] = content; - if (++read == n) { + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { pos = clipPos(cm.doc, pos); var change = {from: pos, to: pos, - text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), origin: "paste"}; makeChange(cm.doc, change); - setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return } - }); + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; reader.readAsText(file); }; - for (var i = 0; i < n; ++i) { loadFile(files[i], i); } + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { @@ -6553,7 +6551,7 @@ cm.display.input.focus(); } } - catch(e){} + catch(e$1){} } } @@ -6649,7 +6647,7 @@ 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", - 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" }; @@ -6756,18 +6754,18 @@ return keymap } - function lookupKey(key, map$$1, handle, context) { - map$$1 = getKeyMap(map$$1); - var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]; + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; if (found === false) { return "nothing" } if (found === "...") { return "multi" } if (found != null && handle(found)) { return "handled" } - if (map$$1.fallthrough) { - if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]") - { return lookupKey(key, map$$1.fallthrough, handle, context) } - for (var i = 0; i < map$$1.fallthrough.length; i++) { - var result = lookupKey(key, map$$1.fallthrough[i], handle, context); + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); if (result) { return result } } } @@ -6784,7 +6782,7 @@ var base = name; if (event.altKey && base != "Alt") { name = "Alt-" + name; } if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } - if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } return name } @@ -6841,6 +6839,7 @@ function endOfLine(visually, cm, lineObj, lineNo, dir) { if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } var order = getOrder(lineObj, cm.doc.direction); if (order) { var part = dir < 0 ? lst(order) : order[0]; @@ -7009,7 +7008,7 @@ goGroupRight: function (cm) { return cm.moveH(1, "group"); }, goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, goWordRight: function (cm) { return cm.moveH(1, "word"); }, - delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, @@ -7095,7 +7094,7 @@ var line = getLine(cm.doc, start.line); var order = getOrder(line, cm.doc.direction); if (!order || order[0].level == 0) { - var firstNonWS = Math.max(0, line.text.search(/\S/)); + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) } @@ -7198,6 +7197,7 @@ var lastStoppedKey = null; function onKeyDown(e) { var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } cm.curOp.focus = activeElt(); if (signalDOMEvent(cm, e)) { return } // IE does strange things with escape. @@ -7211,6 +7211,8 @@ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) { cm.replaceSelection("", null, "cut"); } } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } // Turn mouse into crosshair when Alt is held on Mac. if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) @@ -7239,6 +7241,7 @@ function onKeyPress(e) { var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} @@ -7378,6 +7381,10 @@ var dragEnd = operation(cm, function (e) { if (webkit) { display.scroller.draggable = false; } cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } off(display.wrapper.ownerDocument, "mouseup", dragEnd); off(display.wrapper.ownerDocument, "mousemove", mouseMove); off(display.scroller, "dragstart", dragStart); @@ -7387,8 +7394,8 @@ if (!behavior.addNew) { extendSelection(cm.doc, pos, null, null, behavior.extend); } // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) - if (webkit || ie && ie_version == 9) - { setTimeout(function () {display.wrapper.ownerDocument.body.focus(); display.input.focus();}, 20); } + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } else { display.input.focus(); } } @@ -7401,15 +7408,15 @@ if (webkit) { display.scroller.draggable = true; } cm.state.draggingText = dragEnd; dragEnd.copy = !behavior.moveOnDrag; - // IE's approach to draggable - if (display.scroller.dragDrop) { display.scroller.dragDrop(); } on(display.wrapper.ownerDocument, "mouseup", dragEnd); on(display.wrapper.ownerDocument, "mousemove", mouseMove); on(display.scroller, "dragstart", dragStart); on(display.scroller, "drop", dragEnd); - delayBlurEvent(cm); + cm.state.delayingBlurEvent = true; setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } } function rangeForUnit(cm, pos, unit) { @@ -7422,6 +7429,7 @@ // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } var display = cm.display, doc = cm.doc; e_preventDefault(event); @@ -7442,11 +7450,11 @@ start = posFromMouse(cm, event, true, true); ourIndex = -1; } else { - var range$$1 = rangeForUnit(cm, start, behavior.unit); + var range = rangeForUnit(cm, start, behavior.unit); if (behavior.extend) - { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); } + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } else - { ourRange = range$$1; } + { ourRange = range; } } if (!behavior.addNew) { @@ -7489,14 +7497,14 @@ cm.scrollIntoView(pos); } else { var oldRange = ourRange; - var range$$1 = rangeForUnit(cm, pos, behavior.unit); + var range = rangeForUnit(cm, pos, behavior.unit); var anchor = oldRange.anchor, head; - if (cmp(range$$1.anchor, anchor) > 0) { - head = range$$1.head; - anchor = minPos(oldRange.from(), range$$1.anchor); + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); } else { - head = range$$1.anchor; - anchor = maxPos(oldRange.to(), range$$1.head); + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); } var ranges$1 = startSel.ranges.slice(0); ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); @@ -7558,17 +7566,17 @@ // Used when mouse-selecting to adjust the anchor to the proper side // of a bidi jump depending on the visual position of the head. - function bidiSimplify(cm, range$$1) { - var anchor = range$$1.anchor; - var head = range$$1.head; + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; var anchorLine = getLine(cm.doc, anchor.line); - if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range$$1 } + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } var order = getOrder(anchorLine); - if (!order) { return range$$1 } + if (!order) { return range } var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; - if (part.from != anchor.ch && part.to != anchor.ch) { return range$$1 } + if (part.from != anchor.ch && part.to != anchor.ch) { return range } var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); - if (boundary == 0 || boundary == order.length) { return range$$1 } + if (boundary == 0 || boundary == order.length) { return range } // Compute the relative visual position of the head compared to the // anchor (<0 is to the left, >0 to the right) @@ -7587,7 +7595,7 @@ var usePart = order[boundary + (leftSide ? -1 : 0)]; var from = leftSide == (usePart.level == 1); var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; - return anchor.ch == ch && anchor.sticky == sticky ? range$$1 : new Range(new Pos(anchor.line, ch, sticky), head) + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) } @@ -7600,7 +7608,7 @@ mY = e.touches[0].clientY; } else { try { mX = e.clientX; mY = e.clientY; } - catch(e) { return false } + catch(e$1) { return false } } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } if (prevent) { e_preventDefault(e); } @@ -7700,7 +7708,7 @@ for (var i = newBreaks.length - 1; i >= 0; i--) { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } }); - option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) { + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != Init) { cm.refresh(); } }); @@ -7764,6 +7772,12 @@ } cm.display.input.readOnlyChanged(val); }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); option("dragDrop", true, dragDropChanged); option("allowDropFileTypes", null); @@ -7874,15 +7888,17 @@ attachDoc(this, doc); if ((options.autofocus && !mobile) || this.hasFocus()) - { setTimeout(bind(onFocus, this), 20); } + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } else { onBlur(this); } for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) - { optionHandlers[opt](this$1, options[opt], Init); } } + { optionHandlers[opt](this, options[opt], Init); } } maybeUpdateLineNumberWidth(this); if (options.finishInit) { options.finishInit(this); } - for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } endOperation(this); // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. @@ -7916,6 +7932,9 @@ // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); // Used to suppress mouse event handling when a touch happens var touchFinished, prevTouch = {end: 0}; @@ -8104,14 +8123,14 @@ var updateInput = cm.curOp.updateInput; // Normal behavior is to insert the new text into every selection for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { - var range$$1 = sel.ranges[i$1]; - var from = range$$1.from(), to = range$$1.to(); - if (range$$1.empty()) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { if (deleted && deleted > 0) // Handle deletion { from = Pos(from.line, from.ch - deleted); } else if (cm.state.overwrite && !paste) // Handle overwrite { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } - else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) { from = to = Pos(from.line, 0); } } var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, @@ -8144,21 +8163,21 @@ var sel = cm.doc.sel; for (var i = sel.ranges.length - 1; i >= 0; i--) { - var range$$1 = sel.ranges[i]; - if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue } - var mode = cm.getModeAt(range$$1.head); + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); var indented = false; if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { - indented = indentLine(cm, range$$1.head.line, "smart"); + indented = indentLine(cm, range.head.line, "smart"); break } } } else if (mode.electricInput) { - if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch))) - { indented = indentLine(cm, range$$1.head.line, "smart"); } + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } } - if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } } } @@ -8223,13 +8242,13 @@ getOption: function(option) {return this.options[option]}, getDoc: function() {return this.doc}, - addKeyMap: function(map$$1, bottom) { - this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1)); + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); }, - removeKeyMap: function(map$$1) { + removeKeyMap: function(map) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) - { if (maps[i] == map$$1 || maps[i].name == map$$1) { + { if (maps[i] == map || maps[i].name == map) { maps.splice(i, 1); return true } } @@ -8246,15 +8265,13 @@ regChange(this); }), removeOverlay: methodOp(function(spec) { - var this$1 = this; - var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { var cur = overlays[i].modeSpec; if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); - this$1.state.modeGen++; - regChange(this$1); + this.state.modeGen++; + regChange(this); return } } @@ -8268,24 +8285,22 @@ if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } }), indentSelection: methodOp(function(how) { - var this$1 = this; - var ranges = this.doc.sel.ranges, end = -1; for (var i = 0; i < ranges.length; i++) { - var range$$1 = ranges[i]; - if (!range$$1.empty()) { - var from = range$$1.from(), to = range$$1.to(); + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); var start = Math.max(end, from.line); - end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; for (var j = start; j < end; ++j) - { indentLine(this$1, j, how); } - var newRanges = this$1.doc.sel.ranges; + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) - { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } - } else if (range$$1.head.line > end) { - indentLine(this$1, range$$1.head.line, how, true); - end = range$$1.head.line; - if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); } + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } } } }), @@ -8327,8 +8342,6 @@ }, getHelpers: function(pos, type) { - var this$1 = this; - var found = []; if (!helpers.hasOwnProperty(type)) { return found } var help = helpers[type], mode = this.getModeAt(pos); @@ -8346,7 +8359,7 @@ } for (var i$1 = 0; i$1 < help._global.length; i$1++) { var cur = help._global[i$1]; - if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) { found.push(cur.val); } } return found @@ -8359,10 +8372,10 @@ }, cursorCoords: function(start, mode) { - var pos, range$$1 = this.doc.sel.primary(); - if (start == null) { pos = range$$1.head; } + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } else if (typeof start == "object") { pos = clipPos(this.doc, start); } - else { pos = start ? range$$1.from() : range$$1.to(); } + else { pos = start ? range.from() : range.to(); } return cursorCoords(this, pos, mode || "page") }, @@ -8446,13 +8459,11 @@ triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), findPosH: function(from, amount, unit, visually) { - var this$1 = this; - var dir = 1; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { - cur = findPosH(this$1.doc, cur, dir, unit, visually); + cur = findPosH(this.doc, cur, dir, unit, visually); if (cur.hitSide) { break } } return cur @@ -8461,11 +8472,11 @@ moveH: methodOp(function(dir, unit) { var this$1 = this; - this.extendSelectionsBy(function (range$$1) { - if (this$1.display.shift || this$1.doc.extend || range$$1.empty()) - { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) } + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } else - { return dir < 0 ? range$$1.from() : range$$1.to() } + { return dir < 0 ? range.from() : range.to() } }, sel_move); }), @@ -8474,23 +8485,21 @@ if (sel.somethingSelected()) { doc.replaceSelection("", null, "+delete"); } else - { deleteNearSelection(this, function (range$$1) { - var other = findPosH(doc, range$$1.head, dir, unit, false); - return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other} + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} }); } }), findPosV: function(from, amount, unit, goalColumn) { - var this$1 = this; - var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { - var coords = cursorCoords(this$1, cur, "div"); + var coords = cursorCoords(this, cur, "div"); if (x == null) { x = coords.left; } else { coords.left = x; } - cur = findPosV(this$1, coords, dir, unit); + cur = findPosV(this, coords, dir, unit); if (cur.hitSide) { break } } return cur @@ -8501,14 +8510,14 @@ var doc = this.doc, goals = []; var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); - doc.extendSelectionsBy(function (range$$1) { + doc.extendSelectionsBy(function (range) { if (collapse) - { return dir < 0 ? range$$1.from() : range$$1.to() } - var headPos = cursorCoords(this$1, range$$1.head, "div"); - if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; } + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } goals.push(headPos.left); var pos = findPosV(this$1, headPos, dir, unit); - if (unit == "page" && range$$1 == doc.sel.primary()) + if (unit == "page" && range == doc.sel.primary()) { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } return pos }, sel_move); @@ -8555,22 +8564,22 @@ clientHeight: displayHeight(this), clientWidth: displayWidth(this)} }, - scrollIntoView: methodOp(function(range$$1, margin) { - if (range$$1 == null) { - range$$1 = {from: this.doc.sel.primary().head, to: null}; + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; if (margin == null) { margin = this.options.cursorScrollMargin; } - } else if (typeof range$$1 == "number") { - range$$1 = {from: Pos(range$$1, 0), to: null}; - } else if (range$$1.from == null) { - range$$1 = {from: range$$1, to: null}; + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; } - if (!range$$1.to) { range$$1.to = range$$1.from; } - range$$1.margin = margin || 0; + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; - if (range$$1.from.line != null) { - scrollToRange(this, range$$1); + if (range.from.line != null) { + scrollToRange(this, range); } else { - scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin); + scrollToCoordsRange(this, range.from, range.to, range.margin); } }), @@ -8581,11 +8590,11 @@ if (width != null) { this.display.wrapper.style.width = interpret(width); } if (height != null) { this.display.wrapper.style.height = interpret(height); } if (this.options.lineWrapping) { clearLineMeasurementCache(this); } - var lineNo$$1 = this.display.viewFrom; - this.doc.iter(lineNo$$1, this.display.viewTo, function (line) { + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) - { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } } - ++lineNo$$1; + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; }); this.curOp.forceUpdate = true; signal(this, "refresh", this); @@ -8602,7 +8611,7 @@ clearCaches(this); scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); updateGutterSpace(this.display); - if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) { estimateLineHeights(this); } signal(this, "refresh", this); }), @@ -8644,34 +8653,40 @@ } // Used for horizontal relative motion. Dir is -1 or 1 (left or - // right), unit can be "char", "column" (like char, but doesn't - // cross line boundaries), "word" (across next word), or "group" (to - // the start of next group of word or non-word-non-whitespace - // chars). The visually param controls whether, in right-to-left - // text, direction 1 means to move towards the next index in the - // string, or towards the character to the right of the current - // position. The resulting position will have a hitSide=true - // property if it reached the end of the document. + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { var oldPos = pos; var origDir = dir; var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; function findNextLine() { - var l = pos.line + dir; + var l = pos.line + lineDir; if (l < doc.first || l >= doc.first + doc.size) { return false } pos = new Pos(l, pos.ch, pos.sticky); return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { var next; - if (visually) { + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (unit > 0 ? 0 : -1)); + if (isNaN(ch)) { next = null; } + else { next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (ch >= 0xD800 && ch < 0xDC00 ? 2 : 1))), + -dir); } + } else if (visually) { next = moveVisually(doc.cm, lineObj, pos, dir); } else { next = moveLogically(lineObj, pos, dir); } if (next == null) { if (!boundToLine && findNextLine()) - { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); } + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } else { return false } } else { @@ -8680,7 +8695,7 @@ return true } - if (unit == "char") { + if (unit == "char" || unit == "codepoint") { moveOnce(); } else if (unit == "column") { moveOnce(true); @@ -8750,8 +8765,16 @@ var div = input.div = display.lineDiv; disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + on(div, "paste", function (e) { - if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } // IE doesn't fire input events, so we schedule a read for the pasted content in this way if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } }); @@ -8776,7 +8799,7 @@ }); function onCopyCut(e) { - if (signalDOMEvent(cm, e)) { return } + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } @@ -8818,9 +8841,18 @@ on(div, "cut", onCopyCut); }; + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + ContentEditableInput.prototype.prepareSelection = function () { var result = prepareSelection(this.cm, false); - result.focus = this.cm.state.focused; + result.focus = document.activeElement == this.div; return result }; @@ -8856,8 +8888,8 @@ var end = to.line < cm.display.viewTo && posToDOM(cm, to); if (!end) { var measure = view[view.length - 1].measure; - var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; - end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; } if (!start || !end) { @@ -8916,7 +8948,7 @@ ContentEditableInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor") { - if (!this.selectionInEditor()) + if (!this.selectionInEditor() || document.activeElement != this.div) { this.showSelection(this.prepareSelection(), true); } this.div.focus(); } @@ -9146,11 +9178,11 @@ addText(cmText); return } - var markerID = node.getAttribute("cm-marker"), range$$1; + var markerID = node.getAttribute("cm-marker"), range; if (markerID) { var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); - if (found.length && (range$$1 = found[0].find(0))) - { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); } + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } return } if (node.getAttribute("contenteditable") == "false") { return } @@ -9218,13 +9250,13 @@ function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { - var map$$1 = i < 0 ? measure.map : maps[i]; - for (var j = 0; j < map$$1.length; j += 3) { - var curNode = map$$1[j + 2]; + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; if (curNode == textNode || curNode == topNode) { var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); - var ch = map$$1[j] + offset; - if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; } + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } return Pos(line, ch) } } @@ -9358,6 +9390,15 @@ this.textarea = this.wrapper.firstChild; }; + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + TextareaInput.prototype.prepareSelection = function () { // Redraw the selection and/or cursor var cm = this.cm, display = cm.display, doc = cm.doc; @@ -9598,6 +9639,7 @@ TextareaInput.prototype.readOnlyChanged = function (val) { if (!val) { this.reset(); } this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; }; TextareaInput.prototype.setUneditable = function () {}; @@ -9649,7 +9691,7 @@ textarea.style.display = ""; if (textarea.form) { off(textarea.form, "submit", save); - if (typeof textarea.form.submit == "function") + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") { textarea.form.submit = realSubmit; } } }; @@ -9748,7 +9790,7 @@ addLegacyProps(CodeMirror); - CodeMirror.version = "5.46.0"; + CodeMirror.version = "5.59.0"; return CodeMirror; From 6810868e962f65c48cd58e34e812eb113d87b082 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Dec 2020 09:06:56 -0500 Subject: [PATCH 4053/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 359c41089a42e..31c8797a7eae3 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.0 +1.32.1.0 From 8f91acd5b3bc64ae57fb4f91c820400cf104cb1c Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 22 Dec 2020 09:30:56 -0500 Subject: [PATCH 4054/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 85ccf72764aab..5dadc89cc8ab1 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.31.3.105", + "version": "1.32.1.0", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.31.3rc5/uBlock0_1.31.3rc5.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.1b0/uBlock0_1.32.1b0.firefox.signed.xpi" } ] } From 1c37e29e0a0b2da2cb179454abea4463e1bb87b6 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Dec 2020 07:35:26 -0500 Subject: [PATCH 4055/4093] Fix handling of fragment when applying `queryprune` Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1415 --- src/js/static-net-filtering.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index c38159bbb4a3c..335f52f15d13e 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -4284,7 +4284,9 @@ FilterContainer.prototype.filterQuery = function(fctxt) { const url = fctxt.url; const qpos = url.indexOf('?'); if ( qpos === -1 ) { return; } - const params = new Map(new self.URLSearchParams(url.slice(qpos + 1))); + let hpos = url.indexOf('#', qpos + 1); + if ( hpos === -1 ) { hpos = url.length; } + const params = new Map(new self.URLSearchParams(url.slice(qpos + 1, hpos))); const out = []; for ( const directive of directives ) { if ( params.size === 0 ) { break; } @@ -4333,9 +4335,12 @@ FilterContainer.prototype.filterQuery = function(fctxt) { fctxt.redirectURL = url.slice(0, qpos); if ( params.size !== 0 ) { fctxt.redirectURL += '?' + Array.from(params).map(a => - `${a[0]}=${encodeURIComponent(a[1])}` + a[1] === '' ? a[0] : `${a[0]}=${encodeURIComponent(a[1])}` ).join('&'); } + if ( hpos !== url.length ) { + fctxt.redirectURL += url.slice(hpos); + } return out; }; From ab06a01062721fede16f27a5ba96f47afed11378 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Dec 2020 08:26:30 -0500 Subject: [PATCH 4056/4093] Better handle Request argument in no-fetch-if As per internal feedback. --- assets/resources/scriptlets.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index da6c885c8a18e..286394cb29779 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -613,16 +613,21 @@ apply: function(target, thisArg, args) { let proceed = true; try { - const url = args[0] instanceof self.Request - ? args[0].url - : args[0]; - const props = new Map([ [ 'url', url ] ]); - const init = args[1]; - if ( init instanceof Object ) { - for ( const prop in init ) { - if ( init.hasOwnProperty(prop) === false ) { continue; } - props.set( prop, init[prop]); + let details; + if ( args[0] instanceof self.Request ) { + details = args[0]; + } else { + details = Object.assign({ url: args[0] }, args[1]); + } + const props = new Map(); + for ( const prop in details ) { + let v = details[prop]; + if ( typeof v !== 'string' ) { + try { v = JSON.stringify(v); } + catch(ex) { } } + if ( typeof v !== 'string' ) { continue; } + props.set(prop, v); } if ( log !== undefined ) { const out = Array.from(props) From 596f085fa55e4d3ed343da9ebe57f31ddb6835be Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Dec 2020 08:34:45 -0500 Subject: [PATCH 4057/4093] Allow default word selection when not using better selection Double-click in editor will just fall back to default word selection when NOT using enhanced word selection. --- src/js/codemirror/ubo-static-filtering.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index b7d3295210e34..f4f07f5ce887e 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -720,7 +720,7 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { end = ch + r[0].length; } } else if ( /\bvariable\b/.test(token) ) { - const l = /[#.]?[a-z0-9_-]+$/i.exec(s.slice(0, ch)); + const l = /[#.][a-z0-9_-]+$/i.exec(s.slice(0, ch)); const r = /^[a-z0-9_-]+/i.exec(s.slice(ch)); if ( l && r ) { beg = l.index; From 9685558162e6df84d679bc146727f79376f14c20 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Dec 2020 08:37:58 -0500 Subject: [PATCH 4058/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 31c8797a7eae3..5dcf20130b1af 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.1.0 +1.32.1.1 From 1e172d7b81e3585aa9b4a7485903c2b793c4a8f3 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 24 Dec 2020 08:45:23 -0500 Subject: [PATCH 4059/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 5dadc89cc8ab1..00a7a60cf8b57 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.1.0", + "version": "1.32.1.1", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.1b0/uBlock0_1.32.1b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.1b1/uBlock0_1.32.1b1.firefox.signed.xpi" } ] } From fdcb110feba531aa7b529361efc6620ec054c7af Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Dec 2020 08:49:12 -0500 Subject: [PATCH 4060/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 5dcf20130b1af..ffb48e64f4679 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.1.1 +1.32.1.2 From 426395aa0373249cda1f2ca4bb69b4f7a5f33722 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Dec 2020 08:52:42 -0500 Subject: [PATCH 4061/4093] Improve extraction of tokens from regex-based filters Regex-based static network filters are those most likely to cause performance degradation, and as such the best guard against undue performance degradation caused by regex-based filters is the ability to extract valid and good tokens from regex patterns. This commit introduces a complete regex parser so that the static network filtering engine can now safely extract tokens regardless of the complexity of the regex pattern. The regex parser is a library imported from: https://github.com/foo123/RegexAnalyzer The syntax highlighter adds an underline to regex-based filters as a visual aid to filter authors so as to avoid mistakenly creating regex-based filters. This commit further colors the underline as a warning when a regex-based filter is found to be untokenizable. Filter list authors are invited to spot these untokenizable regex-based filters in their lists to verify that no mistake were made for those filters, causing them to be untokenizabke. For example, what appears to be a mistake: /^https?:\/\/.*\/sw.js?.[a-zA-Z0-9%]{50,}/ Though the mistake is minor, the regex-based filter above is untokenizable as a result, and become tokenizable when the `.` is properly escaped: /^https?:\/\/.*\/sw\.js?.[a-zA-Z0-9%]{50,}/ Filter list authors can use this search expression in the asset viewer to find instances of regex-based filters: /^(@@)?\/[^\n]+\/(\$|$)/ --- src/1p-filters.html | 1 + src/about.html | 1 + src/asset-viewer.html | 1 + src/background.html | 1 + src/js/codemirror/ubo-static-filtering.js | 4 +- src/js/static-filtering-parser.js | 115 ++ src/js/static-net-filtering.js | 32 +- src/lib/regexanalyzer/README.md | 14 + src/lib/regexanalyzer/regex.js | 2156 +++++++++++++++++++++ 9 files changed, 2300 insertions(+), 25 deletions(-) create mode 100644 src/lib/regexanalyzer/README.md create mode 100644 src/lib/regexanalyzer/regex.js diff --git a/src/1p-filters.html b/src/1p-filters.html index e17733d53c70f..ecd186277bbb3 100644 --- a/src/1p-filters.html +++ b/src/1p-filters.html @@ -49,6 +49,7 @@ + diff --git a/src/about.html b/src/about.html index 71a321ec7358e..02d03547ee294 100644 --- a/src/about.html +++ b/src/about.html @@ -39,6 +39,7 @@ +

        diff --git a/src/asset-viewer.html b/src/asset-viewer.html index dfcf92c32650f..d94fc0bdc152b 100644 --- a/src/asset-viewer.html +++ b/src/asset-viewer.html @@ -33,6 +33,7 @@ + diff --git a/src/background.html b/src/background.html index 108ebd89cc213..7d183dfa446e1 100644 --- a/src/background.html +++ b/src/background.html @@ -9,6 +9,7 @@ + diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index f4f07f5ce887e..0d38c14ed7a86 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -274,7 +274,9 @@ CodeMirror.defineMode('ubo-static-filtering', function() { if ( parser.patternIsRegex() ) { stream.pos = parser.slices[parser.optionsAnchorSpan.i+1]; parserSlot = parser.optionsAnchorSpan.i; - return 'variable notice'; + return parser.patternIsTokenizable() + ? 'variable notice' + : 'variable warning'; } if ( (parser.slices[parserSlot] & (parser.BITAsterisk | parser.BITCaret)) !== 0 ) { stream.pos += parser.slices[parserSlot+2]; diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 3d4a4cb452fd5..d3c81d46e420d 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -1003,6 +1003,18 @@ const Parser = class { return (this.flavorBits & BITFlavorNetRegex) !== 0; } + patternIsTokenizable() { + // TODO: not necessarily true, this needs more work. + if ( this.patternIsRegex === false ) { return true; } + const s = Parser.tokenizableStrFromRegex(this.getNetPattern()); + try { + return /(? { + + const firstCharCodeClass = s => { + return /^[\x01%0-9A-Za-z]/.test(s) ? 1 : 0; + }; + + const lastCharCodeClass = s => { + return /[\x01%0-9A-Za-z]$/.test(s) ? 1 : 0; + }; + + const toTokenizableString = node => { + switch ( node.type ) { + case 1: /* T_SEQUENCE, 'Sequence' */ { + let s = ''; + for ( let i = 0; i < node.val.length; i++ ) { + s += toTokenizableString(node.val[i]); + } + return s; + } + case 2: /* T_ALTERNATION,'Alternation' */ + case 8: /* T_CHARGROUP, 'CharacterGroup' */ { + let firstChar = 0; + let lastChar = 0; + for ( let i = 0; i < node.val.length; i++ ) { + const s = toTokenizableString(node.val[i]); + if ( firstChar === 0 && firstCharCodeClass(s) === 1 ) { + firstChar = 1; + } + if ( lastChar === 0 && lastCharCodeClass(s) === 1 ) { + lastChar = 1; + } + if ( firstChar === 1 && lastChar === 1 ) { break; } + } + return String.fromCharCode(firstChar, lastChar); + } + case 4: /* T_GROUP, 'Group' */ { + if ( node.flags.NegativeLookAhead === 1 ) { return '\x01'; } + if ( node.flags.NegativeLookBehind === 1 ) { return '\x01'; } + return toTokenizableString(node.val); + } + case 16: /* T_QUANTIFIER, 'Quantifier' */ { + const s = toTokenizableString(node.val); + const first = firstCharCodeClass(s); + const last = lastCharCodeClass(s); + if ( node.flags.min === 0 && first === 0 && last === 0 ) { + return ''; + } + return String.fromCharCode(first, last); + } + case 64: /* T_HEXCHAR, 'HexChar' */ { + return String.fromCharCode(parseInt(node.val.slice(1), 16)); + } + case 128: /* T_SPECIAL, 'Special' */ { + const flags = node.flags; + if ( flags.MatchEnd === 1 ) { return '\x00'; } + if ( flags.MatchStart === 1 ) { return '\x00'; } + if ( flags.MatchWordBoundary === 1 ) { return '\x00'; } + return '\x01'; + } + case 256: /* T_CHARS, 'Characters' */ { + for ( let i = 0; i < node.val.length; i++ ) { + if ( firstCharCodeClass(node.val[i]) === 1 ) { + return '\x01'; + } + } + return '\x00'; + } + // Ranges are assumed to always involve token-related characters. + case 512: /* T_CHARRANGE, 'CharacterRange' */ { + return '\x01'; + } + case 1024: /* T_STRING, 'String' */ { + return node.val; + } + case 2048: /* T_COMMENT, 'Comment' */ { + return ''; + } + default: + break; + } + return '\x01'; + }; + + return function(reStr) { + if ( + self.Regex instanceof Object === false || + self.Regex.Analyzer instanceof Object === false + ) { + return ''; + } + try { + return toTokenizableString(self.Regex.Analyzer(reStr, false).tree()); + } catch(ex) { + } + return ''; + }; +})(); + +/******************************************************************************/ + if ( typeof vAPI === 'object' && vAPI !== null ) { vAPI.StaticFilteringParser = Parser; } else { diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 335f52f15d13e..1edc3619cd2c6 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2622,15 +2622,9 @@ const FilterParser = class { if ( other !== undefined ) { return Object.assign(this, other); } - this.cantWebsocket = vAPI.cantWebsocket; this.noTokenHash = urlTokenizer.noTokenHash; - this.reIsolateHostname = /^(\*?\.)?([^\x00-\x24\x26-\x2C\x2F\x3A-\x5E\x60\x7B-\x7F]+)(.*)/; this.reBadCSP = /(?:=|;)\s*report-(?:to|uri)\b/; this.reToken = /[%0-9A-Za-z]+/g; - this.reRegexTokenAbort = /[\(\)\[\]]/; - this.reRegexBadPrefix = /(^|[^\\]\.|\\[%SDWsdw]|[^\\][()*+?[\\\]{}])$/; - this.reRegexBadSuffix = /^([^\\]\.|\\[%SDWsdw]|[()*+?[\]{}]|$)/; - this.reGoodToken = /[%0-9a-z]{1,}/g; this.domainOptList = []; this.tokenIdToNormalizedType = new Map([ [ parser.OPTTokenCname, bitFromType('cname') ], @@ -3175,32 +3169,22 @@ const FilterParser = class { // not `bads`. extractTokenFromRegex() { this.reToken.lastIndex = 0; - const pattern = this.pattern; + const pattern = + vAPI.StaticFilteringParser.tokenizableStrFromRegex(this.pattern); let bestToken; let bestBadness = 0x7FFFFFFF; for (;;) { const matches = this.reToken.exec(pattern); if ( matches === null ) { break; } - let token = matches[0]; - let prefix = pattern.slice(0, matches.index); - let suffix = pattern.slice(this.reToken.lastIndex); - if ( - this.reRegexTokenAbort.test(prefix) && - this.reRegexTokenAbort.test(suffix) - ) { + const { 0: token, index } = matches; + if ( index === 0 || pattern.charAt(index - 1) === '\x01' ) { continue; } - if ( token.charCodeAt(0) === 0x62 /* 'b' */ ) { - const match = /\\+$/.exec(prefix); - if ( match !== null && (match[0].length & 1) !== 0 ) { - prefix += 'b'; - token = token.slice(1); - } - } + const { lastIndex } = this.reToken; if ( - this.reRegexBadPrefix.test(prefix) || ( - token.length < this.maxTokenLen && - this.reRegexBadSuffix.test(suffix) + token.length < this.maxTokenLen && ( + lastIndex === pattern.length || + pattern.charAt(lastIndex) === '\x01' ) ) { continue; diff --git a/src/lib/regexanalyzer/README.md b/src/lib/regexanalyzer/README.md new file mode 100644 index 0000000000000..8b9c2706a24b7 --- /dev/null +++ b/src/lib/regexanalyzer/README.md @@ -0,0 +1,14 @@ +https://github.com/foo123/RegexAnalyzer/issues/1#issuecomment-750039255 + +> The (implied) license is as free as it can get. You can modify it and use +> it anywhere you want if it suits you. +> +> An attribution to original author would be appreciated but even this is not +> mandatory. +> +> Copy Left + +References: + +- https://en.wikipedia.org/wiki/Copyleft +- http://gplv3.fsf.org/wiki/index.php/Compatible_licenses diff --git a/src/lib/regexanalyzer/regex.js b/src/lib/regexanalyzer/regex.js new file mode 100644 index 0000000000000..788f03ee81c44 --- /dev/null +++ b/src/lib/regexanalyzer/regex.js @@ -0,0 +1,2156 @@ +/** +* +* Regex +* @version: 1.1.0 +* +* A simple & generic Regular Expression Analyzer & Composer for PHP, Python, Node.js / Browser / XPCOM Javascript +* https://github.com/foo123/RegexAnalyzer +* +**/ +!function( root, name, factory ){ +"use strict"; +if ( ('undefined'!==typeof Components)&&('object'===typeof Components.classes)&&('object'===typeof Components.classesByID)&&Components.utils&&('function'===typeof Components.utils['import']) ) /* XPCOM */ + (root.$deps = root.$deps||{}) && (root.EXPORTED_SYMBOLS = [name]) && (root[name] = root.$deps[name] = factory.call(root)); +else if ( ('object'===typeof module)&&module.exports ) /* CommonJS */ + (module.$deps = module.$deps||{}) && (module.exports = module.$deps[name] = factory.call(root)); +else if ( ('undefined'!==typeof System)&&('function'===typeof System.register)&&('function'===typeof System['import']) ) /* ES6 module */ + System.register(name,[],function($__export){$__export(name, factory.call(root));}); +else if ( ('function'===typeof define)&&define.amd&&('function'===typeof require)&&('function'===typeof require.specified)&&require.specified(name) /*&& !require.defined(name)*/ ) /* AMD */ + define(name,['module'],function(module){factory.moduleUri = module.uri; return factory.call(root);}); +else if ( !(name in root) ) /* Browser/WebWorker/.. */ + (root[name] = factory.call(root)||1)&&('function'===typeof(define))&&define.amd&&define(function(){return root[name];} ); +}( /* current root */ 'undefined' !== typeof self ? self : this, + /* module name */ "Regex", + /* module factory */ function ModuleFactory__Regex( undef ){ +"use strict"; +var __version__ = "1.1.0", + + PROTO = 'prototype', OP = Object[PROTO], AP = Array[PROTO], + Keys = Object.keys, to_string = OP.toString, HAS = OP.hasOwnProperty, + fromCharCode = String.fromCharCode, CHAR = 'charAt', CHARCODE = 'charCodeAt', toJSON = JSON.stringify, + INF = Infinity, ESC = '\\', + specialChars = { + "." : "MatchAnyChar", + "|" : "MatchEither", + "?" : "MatchZeroOrOne", + "*" : "MatchZeroOrMore", + "+" : "MatchOneOrMore", + "^" : "MatchStart", + "$" : "MatchEnd", + "{" : "StartRepeats", + "}" : "EndRepeats", + "(" : "StartGroup", + ")" : "EndGroup", + "[" : "StartCharGroup", + "]" : "EndCharGroup" + }, + /* + http://www.javascriptkit.com/javatutors/redev2.shtml + + \f matches form-feed. + \r matches carriage return. + \n matches linefeed. + \t matches horizontal tab. + \v matches vertical tab. + \0 matches NUL character. + [\b] matches backspace. + \s matches whitespace (short for [\f\n\r\t\v\u00A0\u2028\u2029]). + \S matches anything but a whitespace (short for [^\f\n\r\t\v\u00A0\u2028\u2029]). + \w matches any alphanumerical character (word characters) including underscore (short for [a-zA-Z0-9_]). + \W matches any non-word characters (short for [^a-zA-Z0-9_]). + \d matches any digit (short for [0-9]). + \D matches any non-digit (short for [^0-9]). + \b matches a word boundary (the position between a word and a space). + \B matches a non-word boundary (short for [^\b]). + \cX matches a control character. E.g: \cm matches control-M. + \xhh matches the character with two characters of hexadecimal code hh. + \uhhhh matches the Unicode character with four characters of hexadecimal code hhhh. + */ + specialCharsEscaped = { + "\\" : "ESC", + "/" : "/", + "0" : "NULChar", + "f" : "FormFeed", + "n" : "LineFeed", + "r" : "CarriageReturn", + "t" : "HorizontalTab", + "v" : "VerticalTab", + "b" : "MatchWordBoundary", + "B" : "MatchNonWordBoundary", + "s" : "MatchSpaceChar", + "S" : "MatchNonSpaceChar", + "w" : "MatchWordChar", + "W" : "MatchNonWordChar", + "d" : "MatchDigitChar", + "D" : "MatchNonDigitChar" + }, + T_SEQUENCE = 1, + T_ALTERNATION = 2, + T_GROUP = 4, + T_CHARGROUP = 8, + T_QUANTIFIER = 16, + T_UNICODECHAR = 32, + T_HEXCHAR = 64, + T_SPECIAL = 128, + T_CHARS = 256, + T_CHARRANGE = 512, + T_STRING = 1024, + T_COMMENT = 2048 +; + +function is_array( x ) +{ + return (x instanceof Array) || ('[object Array]' === to_string.call(x)); +} +function is_string( x ) +{ + return (x instanceof String) || ('[object String]' === to_string.call(x)); +} +function is_regexp( x ) +{ + return (x instanceof RegExp) || ('[object RegExp]' === to_string.call(x)); +} +function array( x ) +{ + return is_array(x) ? x : [x]; +} +function clone( obj, cloned ) +{ + cloned = cloned || {}; + for (var p in obj) if ( HAS.call(obj,p) ) cloned[p] = obj[p]; + return cloned; +} +function RE_OBJ( re ) +{ + var self = this; + self.re = re; + self.len = re.length; + self.pos = 0; + self.index = 0; + self.groupIndex = 0; + self.group = {}; + self.inGroup = 0; +} +RE_OBJ[PROTO] = { + constructor: RE_OBJ + ,re: null + ,len: null + ,pos: null + ,index: null + ,groupIndex: null + ,inGroup: null + ,groups: null + ,dispose: function( ) { + var self = this; + self.re = null; + self.len = null; + self.pos = null; + self.index = null; + self.groupIndex = null; + self.group = null; + self.inGroup = null; + } +}; +function Node( type, value, flags ) +{ + var self = this; + if ( !(self instanceof Node) ) return new Node(type, value, flags); + self.type = type; + self.val = value; + self.flags = flags || {}; + switch(type) + { + case T_SEQUENCE: + self.typeName = "Sequence"; break; + case T_ALTERNATION: + self.typeName = "Alternation"; break; + case T_GROUP: + self.typeName = "Group"; break; + case T_CHARGROUP: + self.typeName = "CharacterGroup"; break; + case T_CHARS: + self.typeName = "Characters"; break; + case T_CHARRANGE: + self.typeName = "CharacterRange"; break; + case T_STRING: + self.typeName = "String"; break; + case T_QUANTIFIER: + self.typeName = "Quantifier"; break; + case T_UNICODECHAR: + self.typeName = "UnicodeChar"; break; + case T_HEXCHAR: + self.typeName = "HexChar"; break; + case T_SPECIAL: + self.typeName = "Special"; break; + case T_COMMENT: + self.typeName = "Comment"; break; + default: + self.typeName = "unspecified"; break; + } +}; +Node.toObjectStatic = function toObject( v ) { + if (v instanceof Node) + { + return v.flags && Object.keys(v.flags).length ? { + type: v.typeName, + value: toObject(v.val), + flags: v.flags + } : { + type: v.typeName, + value: toObject(v.val) + }; + } + else if (is_array(v)) + { + return v.map(toObject); + } + return v; +}; +Node[PROTO] = { + constructor: Node + ,type: null + ,typeName: null + ,val: null + ,flags: null + ,dispose: function( ) { + var self = this; + self.val = null; + self.flags = null; + self.type = null; + self.typeName = null; + return self; + } + ,toObject: function( ) { + return Node.toObjectStatic(this); + } +}; + +var rnd = function( a, b ){ return Math.round((b-a)*Math.random()+a); }, + RE = function( re, fl ){ return new RegExp(re, fl||''); }, + slice = function( a ) { return AP.slice.apply(a, AP.slice.call(arguments, 1)); }, + flatten = function( a ) { + var r = [], i = 0; + while (i < a.length) r = r.concat(a[i++]); + return r; + }, + getArgs = function( args, asArray ) { + /*var a = slice(args); + if ( asArray && a[0] && + ( a[0] instanceof Array || '[object Array]' == to_string.call(a[0]) ) + ) + a = a[0];*/ + return flatten( slice( args ) ); //a; + }, + esc_re = function( s, esc, chargroup ) { + var es = '', l = s.length, i=0, c; + //escaped_re = /([.*+?^${}()|[\]\/\\\-])/g + if ( chargroup ) + { + while( i < l ) + { + c = s[CHAR](i++); + es += (/*('?' === c) || ('*' === c) || ('+' === c) ||*/ + ('-' === c) || /*('.' === c) ||*/ ('^' === c) || ('$' === c) || ('|' === c) || + ('{' === c) || ('}' === c) || ('(' === c) || (')' === c) || + ('[' === c) || (']' === c) || ('/' === c) || (esc === c) ? esc : '') + c; + } + } + else + { + while( i < l ) + { + c = s[CHAR](i++); + es += (('?' === c) || ('*' === c) || ('+' === c) || + /*('-' === c) ||*/ ('.' === c) || ('^' === c) || ('$' === c) || ('|' === c) || + ('{' === c) || ('}' === c) || ('(' === c) || (')' === c) || + ('[' === c) || (']' === c) || ('/' === c) || (esc === c) ? esc : '') + c; + } + } + return es; + }, + pad = function( s, n, z ) { + var ps = String(s); + z = z || '0'; + while ( ps.length < n ) ps = z + ps; + return ps; + }, + char_code = function( c ) { return c[CHARCODE](0); }, + char_code_range = function( s ) { return [s[CHARCODE](0), s[CHARCODE](s.length-1)]; }, + //char_codes = function( s_or_a ) { return (s_or_a.substr ? s_or_a.split("") : s_or_a).map( char_code ); }, + // http://stackoverflow.com/questions/12376870/create-an-array-of-characters-from-specified-range + character_range = function(first, last) { + if ( first && is_array(first) ) { last = first[1]; first = first[0]; } + var ch, chars, start = first[CHARCODE](0), end = last[CHARCODE](0); + + if ( end === start ) return [ fromCharCode( start ) ]; + + chars = []; + for (ch = start; ch <= end; ++ch) chars.push( fromCharCode( ch ) ); + return chars; + }, + concat = function(p1, p2) { + if ( p2 ) + { + var p, l; + if ( is_array(p2) ) + { + for (p=0,l=p2.length; p= minlen ? l : false; + }, + match_char_range = function( RANGE, s, pos, minlen, maxlen ) { + pos = pos || 0; + minlen = minlen || 1; + maxlen = maxlen || INF; + var lp = pos, l = 0, sl = s.length, ch; + while ( (lp < sl) && (l <= maxlen) && ((ch=s[CHARCODE](lp)) >= RANGE[0] && ch <= RANGE[1]) ) + { + lp++; l++; + } + return l >= minlen ? l : false; + }, + match_char_ranges = function( RANGES, s, pos, minlen, maxlen ) { + pos = pos || 0; + minlen = minlen || 1; + maxlen = maxlen || INF; + var lp = pos, l = 0, sl = s.length, ch, + i, Rl = RANGES.length, RANGE, found = true; + while ( (lp < sl) && (l <= maxlen) && found ) + { + ch = s[CHARCODE](lp); found = false; + for (i=0; i= RANGE[0] && ch <= RANGE[1] ) + { + lp++; l++; found = true; + break; + } + } + } + return l >= minlen ? l : false; + }, + + punct = function( ){ + return PUNCTS[CHAR](rnd(0, PUNCTS.length-1)); + }, + space = function( positive ){ + return false !== positive + ? SPACES[CHAR](rnd(0, SPACES.length-1)) + : (punct()+digit()+alpha())[CHAR](rnd(0,2)) + ; + }, + digit = function( positive ){ + return false !== positive + ? DIGITS[CHAR](rnd(0, DIGITS.length-1)) + : (punct()+space()+alpha())[CHAR](rnd(0,2)) + ; + }, + alpha = function( positive ){ + return false !== positive + ? ALPHAS[CHAR](rnd(0, ALPHAS.length-1)) + : (punct()+space()+digit())[CHAR](rnd(0,2)) + ; + }, + word = function( positive ){ + return false !== positive + ? (ALPHAS+DIGITS)[CHAR](rnd(0, ALPHAS.length+DIGITS.length-1)) + : (punct()+space())[CHAR](rnd(0,1)) + ; + }, + any = function( ){ + return ALL[CHAR](rnd(0, ALL.length-1)); + }, + character = function( chars, positive ){ + if ( false !== positive ) return chars.length ? chars[rnd(0, chars.length-1)] : ''; + var choices = ALL_ARY.filter(function(c){ return 0 > chars.indexOf(c); }); + return choices.length ? choices[rnd(0, choices.length-1)] : ''; + }, + random_upper_or_lower = function( c ) { return 0.5 < Math.random() ? c.toLowerCase( ) : c.toUpperCase( ); }, + case_insensitive = function( chars, asArray ) { + if ( asArray ) + { + if ( chars[CHAR] ) chars = chars.split(''); + chars = chars.map( random_upper_or_lower ); + //if ( !asArray ) chars = chars.join(''); + return chars; + } + else + { + return random_upper_or_lower( chars ); + } + }, + + walk = function walk( ret, node, state ) { + if ( (null == node) || !state ) return ret; + + var i, l, r, type = node instanceof Node ? node.type : null; + + // walk the tree + if ( null === type ) + { + // custom, let reduce handle it + ret = state.reduce( ret, node, state ); + } + + else if ( state.IGNORE & type ) + { + /* nothing */ + } + + else if ( state.MAP & type ) + { + r = state.map( ret, node, state ); + if ( null != state.ret ) + { + ret = state.reduce( ret, node, state ); + state.ret = null; + } + else if ( null != r ) + { + r = array(r); + for(i=0,l=r?r.length:0; i= state.maxLength ) + { + numrepeats = node.flags.min; + } + else + { + mmin = node.flags.min; + mmax = -1 === node.flags.max ? (mmin+1+2*state.maxLength) : node.flags.max; + numrepeats = rnd(mmin, mmax); + } + if ( numrepeats ) + { + repeats = new Array(numrepeats); + for(var i=0; i max ) + { + max = cur; + } + } + } + if ( l ) state.ret = max; + return null; + } + else if ( T_CHARGROUP === type ) + { + return node.val.length ? node.val[0] : null; + } + else if ( T_QUANTIFIER === type ) + { + max = walk(0, node.val, state); + if ( -1 === max ) + { + state.ret = -1; + } + else if ( 0 < max ) + { + if ( -1 === node.flags.max ) + { + state.ret = -1; + } + else if ( 0 < node.flags.max ) + { + state.ret = node.flags.max*max; + } + else + { + state.ret = max; + } + } + return null; + } + else if ( (T_GROUP === type) && node.flags.GroupIndex ) + { + var max = walk(0, node.val, state); + state.group[node.flags.GroupIndex] = max; + state.ret = max; + return null; + } + else + { + return node.val; + } + }, + map_1st = function map_1st( ret, node, state ) { + var type = node.type; + if ( T_SEQUENCE === type ) + { + var seq=[], i=0, l=node.val.length, n; + for(i=0; i 2) && ('x' === s[CHAR](0)) ) + { + if ( match_char_ranges(HEXDIGITS_RANGES, s, 1, 2, 2) ) return [m=s.slice(0,3), m.slice(1)]; + } + return false; + }, + match_unicode = function( s ) { + var m = false; + if ( (s.length > 4) && ('u' === s[CHAR](0)) ) + { + if ( match_char_ranges(HEXDIGITS_RANGES, s, 1, 4, 4) ) return [m=s.slice(0,5), m.slice(1)]; + } + return false; + }, + match_repeats = function( s ) { + var l, sl = s.length, pos = 0, m = false, hasComma = false; + if ( (sl > 2) && ('{' === s[CHAR](pos)) ) + { + m = ['', '', null]; + pos++; + if ( l=match_chars(SPACES, s, pos) ) pos += l; + if ( l=match_char_range(DIGITS_RANGE, s, pos) ) + { + m[1] = s.slice(pos, pos+l); + pos += l; + } + else + { + return false; + } + if ( l=match_chars(SPACES, s, pos) ) pos += l; + if ( (pos < sl) && (',' === s[CHAR](pos)) ) {pos += 1; hasComma = true;} + if ( l=match_chars(SPACES, s, pos) ) pos += l; + if ( l=match_char_range(DIGITS_RANGE, s, pos) ) + { + m[2] = s.slice(pos, pos+l); + pos += l; + } + if ( l=match_chars(SPACES, s, pos) ) pos += l; + if ( (pos < sl) && ('}' === s[CHAR](pos)) ) + { + pos++; + m[0] = s.slice(0, pos); + if ( !hasComma ) m[2] = m[1]; + return m; + } + else + { + return false; + } + } + return false; + }, + chargroup = function chargroup( re_obj ) { + var sequence = [], chars = [], allchars = [], flags = {}, flag, ch, lre, + prevch, range, isRange = false, m, isUnicode, isHex, escaped = false; + + if ( '^' === re_obj.re[CHAR]( re_obj.pos ) ) + { + flags[ "NegativeMatch" ] = 1; + re_obj.pos++; + } + + lre = re_obj.len; + while ( re_obj.pos < lre ) + { + isUnicode = false; + isHex = false; + m = null; + prevch = ch; + ch = re_obj.re[CHAR]( re_obj.pos++ ); + + escaped = ESC === ch; + if ( escaped ) ch = re_obj.re[CHAR]( re_obj.pos++ ); + + if ( escaped ) + { + // unicode character + if ( 'u' === ch ) + { + m = match_unicode( re_obj.re.substr( re_obj.pos-1 ) ); + re_obj.pos += m[0].length-1; + ch = Node(T_UNICODECHAR, m[0], {"Char": fromCharCode(parseInt(m[1], 16)), "Code": m[1]}); + isUnicode = true; isHex = false; + } + + // hex character + else if ( 'x' === ch ) + { + m = match_hex( re_obj.re.substr( re_obj.pos-1 ) ); + re_obj.pos += m[0].length-1; + ch = Node(T_HEXCHAR, m[0], {"Char": fromCharCode(parseInt(m[1], 16)), "Code": m[1]}); + isUnicode = true; isHex = true; + } + } + + if ( isRange ) + { + if ( chars.length ) + { + allchars = allchars.concat( chars ); + chars = []; + } + range[1] = ch; + isRange = false; + sequence.push( Node(T_CHARRANGE, range) ); + } + else + { + if ( escaped ) + { + if ( isUnicode ) + { + if ( chars.length ) + { + allchars = allchars.concat( chars ); + chars = []; + } + sequence.push( ch ); + } + + else if ( HAS.call(specialCharsEscaped,ch) && ('/' !== ch) ) + { + if ( chars.length ) + { + allchars = allchars.concat( chars ); + chars = []; + } + flag = {}; + flag[ specialCharsEscaped[ch] ] = 1; + sequence.push( Node(T_SPECIAL, ch, flag) ); + } + + else + { + chars.push( ch ); + } + } + + else + { + // end of char group + if ( ']' === ch ) + { + if ( chars.length ) + { + allchars = allchars.concat( chars ); + chars = []; + } + // map all chars into one node + if ( allchars.length ) sequence.push( Node(T_CHARS, allchars) ); + return Node(T_CHARGROUP, sequence, flags); + } + + else if ( '-' === ch ) + { + range = [prevch, '']; + if ( prevch instanceof Node ) sequence.pop(); else chars.pop(); + isRange = true; + } + + else + { + chars.push( ch ); + } + } + } + } + if ( chars.length ) + { + allchars = allchars.concat( chars ); + chars = []; + } + // map all chars into one node + if ( allchars.length ) sequence.push( Node(T_CHARS, allchars) ); + return Node(T_CHARGROUP, sequence, flags); + }, + + analyze_re = function analyze_re( re_obj ) { + var lre, ch, m, word = '', wordlen = 0, + alternation = [], sequence = [], flags = {}, + flag, escaped = false, pre, pre3, captured; + + if ( re_obj.inGroup > 0 ) + { + pre = re_obj.re.substr(re_obj.pos, 2); + pre3 = re_obj.re.substr(re_obj.pos, 3); + captured = 1; + + if ( "?P=" === pre3 ) + { + flags[ "BackReference" ] = 1; + flags[ "GroupName" ] = ''; + re_obj.pos += 3; + lre = re_obj.len; + while ( re_obj.pos < lre ) + { + ch = re_obj.re[CHAR]( re_obj.pos++ ); + if ( ")" === ch ) break; + flags[ "GroupName" ] += ch; + } + flags[ "GroupIndex" ] = HAS.call(re_obj.group,flags[ "GroupName" ]) ? re_obj.group[flags[ "GroupName" ]] : null; + return Node(T_SPECIAL, String(flags[ "GroupIndex" ]), flags); + } + + else if ( "?#" === pre ) + { + flags[ "Comment" ] = 1; + re_obj.pos += 2; + word = ''; + lre = re_obj.len; + while ( re_obj.pos < lre ) + { + ch = re_obj.re[CHAR]( re_obj.pos++ ); + if ( ")" === ch ) break; + word += ch; + } + return Node(T_COMMENT, word); + } + + else if ( "?:" === pre ) + { + flags[ "NotCaptured" ] = 1; + re_obj.pos += 2; + captured = 0; + } + + else if ( "?=" === pre ) + { + flags[ "LookAhead" ] = 1; + re_obj.pos += 2; + captured = 0; + } + + else if ( "?!" === pre ) + { + flags[ "NegativeLookAhead" ] = 1; + re_obj.pos += 2; + captured = 0; + } + + else if ( "?<=" === pre3 ) + { + flags[ "LookBehind" ] = 1; + re_obj.pos += 3; + captured = 0; + } + + else if ( "?" === ch ) break; + flags[ "GroupName" ] += ch; + } + } + + ++re_obj.index; + if ( captured ) + { + ++re_obj.groupIndex; + flags[ "GroupIndex" ] = re_obj.groupIndex; + re_obj.group[flags[ "GroupIndex" ]] = flags[ "GroupIndex" ]; + if ( flags[ "GroupName" ] ) re_obj.group[flags[ "GroupName" ]] = flags[ "GroupIndex" ]; + } + } + + lre = re_obj.len; + while ( re_obj.pos < lre ) + { + ch = re_obj.re[CHAR]( re_obj.pos++ ); + + // \\abc + escaped = ESC === ch; + if ( escaped ) ch = re_obj.re[CHAR]( re_obj.pos++ ); + + if ( escaped ) + { + // unicode character + if ( 'u' === ch ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + m = match_unicode( re_obj.re.substr( re_obj.pos-1 ) ); + re_obj.pos += m[0].length-1; + sequence.push( Node(T_UNICODECHAR, m[0], {"Char": fromCharCode(parseInt(m[1], 16)), "Code": m[1]}) ); + } + + // hex character + else if ( 'x' === ch ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + m = match_hex( re_obj.re.substr( re_obj.pos-1 ) ); + re_obj.pos += m[0].length-1; + sequence.push( Node(T_HEXCHAR, m[0], {"Char": fromCharCode(parseInt(m[1], 16)), "Code": m[1]}) ); + } + + else if ( HAS.call(specialCharsEscaped,ch) && ('/' !== ch) ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + flag = {}; + flag[ specialCharsEscaped[ch] ] = 1; + sequence.push( Node(T_SPECIAL, ch, flag) ); + } + + else if ( ('1' <= ch) && ('9' >= ch) ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + word = ch; + while (re_obj.pos < lre) + { + ch = re_obj.re[CHAR]( re_obj.pos ); + if ( ('0' <= ch) && ('9' >= ch) ) { word += ch; re_obj.pos++; } + else break; + } + flag = {}; + flag[ 'BackReference' ] = 1; + flag[ 'GroupIndex' ] = parseInt(word, 10); + sequence.push( Node(T_SPECIAL, word, flag) ); + word = ''; + } + + else + { + word += ch; + wordlen += 1; + } + } + + else + { + // group end + if ( (re_obj.inGroup > 0) && (')' === ch) ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + if ( alternation.length ) + { + alternation.push( Node(T_SEQUENCE, sequence) ); + sequence = []; + flag = {}; + flag[ specialChars['|'] ] = 1; + return Node(T_GROUP, Node(T_ALTERNATION, alternation, flag), flags); + } + else + { + return Node(T_GROUP, Node(T_SEQUENCE, sequence), flags); + } + } + + // parse alternation + else if ( '|' === ch ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + alternation.push( Node(T_SEQUENCE, sequence) ); + sequence = []; + } + + // parse character group + else if ( '[' === ch ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + sequence.push( chargroup( re_obj ) ); + } + + // parse sub-group + else if ( '(' === ch ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + re_obj.inGroup += 1; + sequence.push( analyze_re( re_obj ) ); + re_obj.inGroup -= 1; + } + + // parse num repeats + else if ( '{' === ch ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + m = match_repeats( re_obj.re.substr( re_obj.pos-1 ) ); + re_obj.pos += m[0].length-1; + flag = { val: m[0], "MatchMinimum": m[1], "MatchMaximum": m[2] || "unlimited", "min": parseInt(m[1],10), "max": m[2] ? parseInt(m[2],10) : -1 }; + flag[ specialChars[ch] ] = 1; + if ( (re_obj.pos < lre) && ('?' === re_obj.re[CHAR](re_obj.pos)) ) + { + flag[ "isGreedy" ] = 0; + re_obj.pos++; + } + else + { + flag[ "isGreedy" ] = 1; + } + var prev = sequence.pop(); + if ( (T_STRING === prev.type) && (prev.val.length > 1) ) + { + sequence.push( Node(T_STRING, prev.val.slice(0, -1)) ); + prev.val = prev.val.slice(-1); + } + sequence.push( Node(T_QUANTIFIER, prev, flag) ); + } + + // quantifiers + else if ( ('*' === ch) || ('+' === ch) || ('?' === ch) ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + flag = {}; + flag[ specialChars[ch] ] = 1; + flag["min"] = '+' === ch ? 1 : 0; + flag["max"] = '?' === ch ? 1 : -1; + if ( (re_obj.pos < lre) && ('?' === re_obj.re[CHAR](re_obj.pos)) ) + { + flag[ "isGreedy" ] = 0; + re_obj.pos++; + } + else + { + flag[ "isGreedy" ] = 1; + } + var prev = sequence.pop(); + if ( (T_STRING === prev.type) && (prev.val.length > 1) ) + { + sequence.push( Node(T_STRING, prev.val.slice(0, -1)) ); + prev.val = prev.val.slice(-1); + } + sequence.push( Node(T_QUANTIFIER, prev, flag) ); + } + + // special characters like ^, $, ., etc.. + else if ( HAS.call(specialChars,ch) ) + { + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + flag = {}; + flag[ specialChars[ch] ] = 1; + sequence.push( Node(T_SPECIAL, ch, flag) ); + } + + else + { + word += ch; + wordlen += 1; + } + } + } + + if ( wordlen ) + { + sequence.push( Node(T_STRING, word) ); + word = ''; + wordlen = 0; + } + + if ( alternation.length ) + { + alternation.push( Node(T_SEQUENCE, sequence) ); + sequence = []; + flag = {}; + flags[ specialChars['|'] ] = 1; + return Node(T_ALTERNATION, alternation, flag); + } + return Node(T_SEQUENCE, sequence); + } +; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions +// https://docs.python.org/3/library/re.html +// http://php.net/manual/en/reference.pcre.pattern.syntax.php +// A simple regular expression analyzer +function Analyzer( re, delim ) +{ + if ( !(this instanceof Analyzer) ) return new Analyzer(re, delim); + if ( re ) this.input( re, delim ); +} +Analyzer.VERSION = __version__; +Analyzer[PROTO] = { + + constructor: Analyzer, + + ast: null, + re: null, + fl: null, + src: null, + grp: null, + min: null, + max: null, + ch: null, + + dispose: function( ) { + var self = this; + self.ast = null; + self.re = null; + self.fl = null; + self.src = null; + self.grp = null; + self.min = null; + self.max = null; + self.ch = null; + return self; + }, + + reset: function( ) { + var self = this; + self.ast = null; + self.src = null; + self.grp = null; + self.min = null; + self.max = null; + self.ch = null; + return self; + }, + + input: function( re, delim ) { + var self = this; + if ( !arguments.length ) return self.re; + if ( re ) + { + delim = false === delim ? false : (delim || '/'); + var l, ch, fl = {}; + re = re.toString( ); + l = re.length; + + if ( delim ) + { + // parse re flags, if any + while ( 0 < l ) + { + ch = re[CHAR](l-1); + if ( delim === ch ) break; + else { fl[ ch ] = 1; l--; } + } + + if ( 0 < l ) + { + // remove re delimiters + if ( (delim === re[CHAR](0)) && (delim === re[CHAR](l-1)) ) re = re.slice(1, l-1); + else re = re.slice(0, l); + } + else + { + re = ''; + } + } + + // re is different, reset the ast, etc + if ( self.re !== re ) self.reset(); + self.re = re; self.fl = fl; + } + return self; + }, + + analyze: function( ) { + var self = this; + if ( (null != self.re) && (null === self.ast) ) + { + var re = new RE_OBJ(self.re); + self.ast = analyze_re( re ); + re.dispose(); + } + return self; + }, + + synthesize: function( escaped ) { + var self = this, state, re; + if ( null == self.re ) return self; + if ( null === self.ast ) + { + self.analyze( ); + self.src = null; + self.grp = null; + } + if ( null === self.src ) + { + state = { + MAP : T_SEQUENCE|T_ALTERNATION|T_GROUP|T_CHARGROUP|T_QUANTIFIER, + REDUCE : T_UNICODECHAR|T_HEXCHAR|T_SPECIAL|T_CHARS|T_CHARRANGE|T_STRING, + IGNORE : T_COMMENT, + map : map_src, + reduce : reduce_src, + escaped : false !== escaped, + group : {} + }; + re = walk({src:'',group:{}}, self.ast, state); + self.src = re.src; self.grp = re.group; + } + return self; + }, + + source: function( ) { + var self = this; + if ( null == self.re ) return null; + if ( null === self.src ) self.synthesize(); + return self.src; + }, + + groups: function( raw ) { + var self = this; + if ( null == self.re ) return null; + if ( null === self.grp ) self.synthesize(); + return true===raw ? sel.grp : clone(self.grp); + }, + + compile: function( flags ) { + var self = this; + if ( null == self.re ) return null; + flags = flags || self.fl || {}; + return new RegExp(self.source(), (flags.g||flags.G?'g':'')+(flags.i||flags.I?'i':'')+(flags.m||flags.M?'m':'')+(flags.y||flags.Y?'y':'')); + }, + + tree: function( flat ) { + var self = this; + if ( null == self.re ) return null; + if ( null === self.ast ) self.analyze( ); + return true===flat ? self.ast.toObject() : self.ast; + }, + + // experimental feature + sample: function( maxlen, numsamples ) { + var self = this, state; + if ( null == self.re ) return null; + if ( null === self.ast ) self.analyze( ); + state = { + MAP : T_SEQUENCE|T_ALTERNATION|T_GROUP|T_CHARGROUP|T_QUANTIFIER, + REDUCE : T_UNICODECHAR|T_HEXCHAR|T_SPECIAL|T_CHARS|T_CHARRANGE|T_STRING, + IGNORE : T_COMMENT, + map : map_any, + reduce : reduce_str, + maxLength : (maxlen|0) || 1, + isCaseInsensitive : null != self.fl.i, + group : {} + }; + numsamples = (numsamples|0) || 1; + if ( 1 < numsamples ) + { + var samples = new Array(numsamples); + for(var i=0; i Date: Sat, 26 Dec 2020 09:15:23 -0500 Subject: [PATCH 4062/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index ffb48e64f4679..48a57bf4fd54c 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.1.2 +1.32.3.0 From 8e810832ba7a77933371374e2df5ca3823a5c125 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Dec 2020 09:20:28 -0500 Subject: [PATCH 4063/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 00a7a60cf8b57..8044a583e2c08 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.1.1", + "version": "1.32.3.0", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.1b1/uBlock0_1.32.1b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b0/uBlock0_1.32.3b0.firefox.signed.xpi" } ] } From b05347708710ed940f966cd500afb72a1ef9fba1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Dec 2020 10:15:07 -0500 Subject: [PATCH 4064/4093] Fix potentially missing context in logger for `popup` entries Reported internally. --- src/js/tab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/tab.js b/src/js/tab.js index 3f70aec0e5f4f..ebdce0c422b8a 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -343,7 +343,7 @@ fctxt.setURL(targetURL) .setTabId(openerTabId) .setTabOriginFromURL(rootOpenerURL) - .setDocOriginFromURL(localOpenerURL); + .setDocOriginFromURL(localOpenerURL || rootOpenerURL); } else { fctxt.setURL(rootOpenerURL) .setTabId(targetTabId) From 934bd3e80d915cec5f63118cfc7d2ce68abb4e51 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Dec 2020 10:16:54 -0500 Subject: [PATCH 4065/4093] new revision fro dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 48a57bf4fd54c..d0641e4120429 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.3.0 +1.32.3.1 From f80371e84474356c2bddce7f7f6ec29d2e2da034 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 26 Dec 2020 10:25:29 -0500 Subject: [PATCH 4066/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 8044a583e2c08..cb42f3ea82d27 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.3.0", + "version": "1.32.3.1", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b0/uBlock0_1.32.3b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b1/uBlock0_1.32.3b1.firefox.signed.xpi" } ] } From 6d3ad553b47805ad9eb9e9f5f75e9380b740e39d Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Dec 2020 09:32:50 -0500 Subject: [PATCH 4067/4093] Fix word-based selection in filter list editor/viewer This commit fixes mouse double-click-and-drag operations, which was broken due to the implementation of a custom word selection in the filter list editor/viewer. --- src/js/codemirror/ubo-static-filtering.js | 93 ++++++++++++----------- src/js/static-filtering-parser.js | 2 +- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 0d38c14ed7a86..891c3c072d0bf 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -336,12 +336,20 @@ CodeMirror.defineMode('ubo-static-filtering', function() { return 'comment'; } if ( parser.category === parser.CATStaticExtFilter ) { - const style = colorExtSpan(stream); - return style ? `ext ${style}` : 'ext'; + const style = colorExtSpan(stream) || ''; + let flavor = ''; + if ( (parser.flavorBits & parser.BITFlavorExtCosmetic) !== 0 ) { + flavor = 'line-cm-ext-dom'; + } else if ( (parser.flavorBits & parser.BITFlavorExtScriptlet) !== 0 ) { + flavor = 'line-cm-ext-js'; + } else if ( (parser.flavorBits & parser.BITFlavorExtHTML) !== 0 ) { + flavor = 'line-cm-ext-html'; + } + return `${flavor} ${style}`.trim(); } if ( parser.category === parser.CATStaticNetFilter ) { const style = colorNetSpan(stream); - return style ? `net ${style}` : 'net'; + return style ? `line-cm-net ${style}` : 'line-cm-net'; } stream.skipToEnd(); return null; @@ -679,25 +687,10 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { // Enhanced word selection { - const Pass = CodeMirror.Pass; - const selectWordAt = function(cm, pos) { const { line, ch } = pos; - - // Leave current selection alone - if ( cm.somethingSelected() ) { - const from = cm.getCursor('from'); - const to = cm.getCursor('to'); - if ( - (line > from.line || line === from.line && ch > from.ch) && - (line < to.line || line === to.line && ch < to.ch) - ) { - return Pass; - } - } - const s = cm.getLine(line); - const token = cm.getTokenTypeAt(pos); + const { type: token } = cm.getTokenAt(pos); let beg, end; // Select URL in comments @@ -712,30 +705,34 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { } } - // Better word selection for cosmetic filters - else if ( /\bext\b/.test(token) ) { - if ( /\bvalue\b/.test(token) ) { - const l = /[^,.]*$/i.exec(s.slice(0, ch)); - const r = /^[^#,]*/i.exec(s.slice(ch)); - if ( l && r ) { - beg = l.index; - end = ch + r[0].length; - } - } else if ( /\bvariable\b/.test(token) ) { - const l = /[#.][a-z0-9_-]+$/i.exec(s.slice(0, ch)); - const r = /^[a-z0-9_-]+/i.exec(s.slice(ch)); - if ( l && r ) { - beg = l.index; - end = ch + r[0].length; - if ( /\bdef\b/.test(cm.getTokenTypeAt({ line, ch: beg + 1 })) ) { - beg += 1; - } + // Better word selection for extended filters: prefix + else if ( + /\bline-cm-ext-(?:dom|html|js)\b/.test(token) && + /\bvalue\b/.test(token) + ) { + const l = /[^,.]*$/i.exec(s.slice(0, ch)); + const r = /^[^#,]*/i.exec(s.slice(ch)); + if ( l && r ) { + beg = l.index; + end = ch + r[0].length; + } + } + + // Better word selection for cosmetic and HTML filters: suffix + else if ( /\bline-cm-ext-(?:dom|html)\b/.test(token) ) { + const l = /[#.]?[a-z0-9_-]+$/i.exec(s.slice(0, ch)); + const r = /^[a-z0-9_-]+/i.exec(s.slice(ch)); + if ( l && r ) { + beg = l.index; + end = ch + r[0].length; + if ( /\bdef\b/.test(cm.getTokenTypeAt({ line, ch: beg + 1 })) ) { + beg += 1; } } } // Better word selection for network filters - else if ( /\bnet\b/.test(token) ) { + else if ( /\bline-cm-net\b/.test(token) ) { if ( /\bvalue\b/.test(token) ) { const l = /[^ ,.=|]*$/i.exec(s.slice(0, ch)); const r = /^[^ #,|]*/i.exec(s.slice(ch)); @@ -753,16 +750,22 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { } } - if ( beg === undefined ) { return Pass; } - cm.setSelection( - { line, ch: beg }, - { line, ch: end } - ); + if ( beg === undefined ) { + const { anchor, head } = cm.findWordAt(pos); + return { from: anchor, to: head }; + } + + return { + from: { line, ch: beg }, + to: { line, ch: end }, + }; }; CodeMirror.defineInitHook(cm => { - cm.addKeyMap({ - 'LeftDoubleClick': selectWordAt, + cm.setOption('configureMouse', function(cm, repeat) { + return { + unit: repeat === 'double' ? selectWordAt : null, + }; }); }); } diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index d3c81d46e420d..4c484f5d86095 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -2782,7 +2782,7 @@ Parser.tokenizableStrFromRegex = (( ) => { } return s; } - case 2: /* T_ALTERNATION,'Alternation' */ + case 2: /* T_ALTERNATION, 'Alternation' */ case 8: /* T_CHARGROUP, 'CharacterGroup' */ { let firstChar = 0; let lastChar = 0; From 224acc4a9fe38912881c34f6db9597f19ed864d1 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Dec 2020 09:36:30 -0500 Subject: [PATCH 4068/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index d0641e4120429..0b47a5dd338af 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.3.1 +1.32.3.2 From 1d2907890bedafa39488ecc3fcb688f850374242 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 27 Dec 2020 09:45:25 -0500 Subject: [PATCH 4069/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index cb42f3ea82d27..96486ede5ed4d 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.3.1", + "version": "1.32.3.2", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b1/uBlock0_1.32.3b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b2/uBlock0_1.32.3b2.firefox.signed.xpi" } ] } From d910111d4ad42b27b9604d08139defdc116744a8 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Dec 2020 07:03:52 -0500 Subject: [PATCH 4070/4093] Fix parsing of trailing resource Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1419 --- src/js/redirect-engine.js | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 5d8fd8a7b18b2..131b9b11c7d6a 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -349,12 +349,14 @@ RedirectEngine.prototype.resourceContentFromName = function(name, mime) { // Consider 'none' a reserved keyword, to be used to disable redirection. RedirectEngine.prototype.resourcesFromString = function(text) { - const lineIter = new µBlock.LineIterator(removeTopCommentBlock(text)); + const lineIter = new µBlock.LineIterator( + removeTopCommentBlock(text) + '\n\n' + ); const reNonEmptyLine = /\S/; let fields, encoded, details; while ( lineIter.eot() === false ) { - let line = lineIter.next(); + const line = lineIter.next(); if ( line.startsWith('#') ) { continue; } if ( line.startsWith('// ') ) { continue; } @@ -396,18 +398,16 @@ RedirectEngine.prototype.resourcesFromString = function(text) { continue; } + // No more data, add the resource. const name = this.aliases.get(fields[0]) || fields[0]; const mime = fields[1]; const content = µBlock.orphanizeString( fields.slice(2).join(encoded ? '' : '\n') ); - - // No more data, add the resource. this.resources.set( name, RedirectEntry.fromContent(mime, content) ); - if ( Array.isArray(details) ) { for ( const { prop, value } of details ) { if ( prop !== 'alias' ) { continue; } @@ -419,22 +419,6 @@ RedirectEngine.prototype.resourcesFromString = function(text) { details = undefined; } - // Process pending resource data. - if ( fields !== undefined ) { - const name = fields[0]; - const mime = fields[1]; - const content = µBlock.orphanizeString( - fields.slice(2).join(encoded ? '' : '\n') - ); - this.resources.set( - name, - RedirectEntry.fromContent(mime, content) - ); - if ( details instanceof Object && details.alias ) { - this.aliases.set(details.alias, name); - } - } - this.modifyTime = Date.now(); }; From 4ba3adc28c04a76da85f63b0984ce0361a19bf5a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Dec 2020 07:07:04 -0500 Subject: [PATCH 4071/4093] Fix comment --- src/js/redirect-engine.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 131b9b11c7d6a..58f6b1bec794d 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -343,10 +343,10 @@ RedirectEngine.prototype.resourceContentFromName = function(name, mime) { /******************************************************************************/ -// TODO: combine same key-redirect pairs into a single regex. - // https://github.com/uBlockOrigin/uAssets/commit/deefe875551197d655f79cb540e62dfc17c95f42 // Consider 'none' a reserved keyword, to be used to disable redirection. +// https://github.com/uBlockOrigin/uBlock-issues/issues/1419 +// Append newlines to raw text to ensure processing of trailing resource. RedirectEngine.prototype.resourcesFromString = function(text) { const lineIter = new µBlock.LineIterator( From 2e7f1b8d08925b88564df215be6b97f6f933b60a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Dec 2020 07:13:57 -0500 Subject: [PATCH 4072/4093] Improve validation of synctactically bad regexes The following regex are not rejected as invalid when using built-in regex objects: /abc]/ /a7,18}/ /a{7,18/ However, as per documentation, they are not supposed to be valid, as `{` and `}` are special characters and as such should be escaped: /abc\]/ /a7,18\}/ /a\{7,18/ With this commit, the regexes will additionally be validated using the regex analyzer library in the editor to ensure strict regex syntax compliance so as to avoid what are likely mistakes in regex crafting by authors. --- src/js/static-filtering-parser.js | 92 ++++++++++++++++++++----------- src/js/static-net-filtering.js | 2 +- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/js/static-filtering-parser.js b/src/js/static-filtering-parser.js index 4c484f5d86095..1214457d02cdf 100644 --- a/src/js/static-filtering-parser.js +++ b/src/js/static-filtering-parser.js @@ -114,6 +114,9 @@ const Parser = class { this.rePlainEntity = /^(?:[\w-]+\.)+\*$/; this.reEntity = /^[^*]+\.\*$/; this.punycoder = new URL(self.location); + // TODO: mind maxTokenLength + this.reGoodRegexToken + = /[^\x01%0-9A-Za-z][%0-9A-Za-z]{7,}|[^\x01%0-9A-Za-z][%0-9A-Za-z]{1,6}[^\x01%0-9A-Za-z]/; this.selectorCompiler = new this.SelectorCompiler(this); // TODO: reuse for network filtering analysis this.result = { @@ -639,12 +642,8 @@ const Parser = class { } analyzeNetExtra() { - // Validate regex if ( this.patternIsRegex() ) { - try { - void new RegExp(this.getNetPattern()); - } - catch (ex) { + if ( this.regexUtils.isValid(this.getNetPattern()) === false ) { this.markSpan(this.patternSpan, BITError); } } else if ( @@ -1006,13 +1005,9 @@ const Parser = class { patternIsTokenizable() { // TODO: not necessarily true, this needs more work. if ( this.patternIsRegex === false ) { return true; } - const s = Parser.tokenizableStrFromRegex(this.getNetPattern()); - try { - return /(? { +Parser.regexUtils = Parser.prototype.regexUtils = (( ) => { const firstCharCodeClass = s => { return /^[\x01%0-9A-Za-z]/.test(s) ? 1 : 0; @@ -2773,12 +2768,12 @@ Parser.tokenizableStrFromRegex = (( ) => { return /[\x01%0-9A-Za-z]$/.test(s) ? 1 : 0; }; - const toTokenizableString = node => { + const toTokenizableStr = node => { switch ( node.type ) { case 1: /* T_SEQUENCE, 'Sequence' */ { let s = ''; for ( let i = 0; i < node.val.length; i++ ) { - s += toTokenizableString(node.val[i]); + s += toTokenizableStr(node.val[i]); } return s; } @@ -2787,7 +2782,7 @@ Parser.tokenizableStrFromRegex = (( ) => { let firstChar = 0; let lastChar = 0; for ( let i = 0; i < node.val.length; i++ ) { - const s = toTokenizableString(node.val[i]); + const s = toTokenizableStr(node.val[i]); if ( firstChar === 0 && firstCharCodeClass(s) === 1 ) { firstChar = 1; } @@ -2801,10 +2796,10 @@ Parser.tokenizableStrFromRegex = (( ) => { case 4: /* T_GROUP, 'Group' */ { if ( node.flags.NegativeLookAhead === 1 ) { return '\x01'; } if ( node.flags.NegativeLookBehind === 1 ) { return '\x01'; } - return toTokenizableString(node.val); + return toTokenizableStr(node.val); } case 16: /* T_QUANTIFIER, 'Quantifier' */ { - const s = toTokenizableString(node.val); + const s = toTokenizableStr(node.val); const first = firstCharCodeClass(s); const last = lastCharCodeClass(s); if ( node.flags.min === 0 && first === 0 && last === 0 ) { @@ -2817,10 +2812,18 @@ Parser.tokenizableStrFromRegex = (( ) => { } case 128: /* T_SPECIAL, 'Special' */ { const flags = node.flags; - if ( flags.MatchEnd === 1 ) { return '\x00'; } - if ( flags.MatchStart === 1 ) { return '\x00'; } - if ( flags.MatchWordBoundary === 1 ) { return '\x00'; } - return '\x01'; + if ( + flags.EndCharGroup === 1 || // dangling `]` + flags.EndGroup === 1 || // dangling `)` + flags.EndRepeats === 1 // dangling `}` + ) { + throw new Error('Unmatched bracket'); + } + return flags.MatchEnd === 1 || + flags.MatchStart === 1 || + flags.MatchWordBoundary === 1 + ? '\x00' + : '\x01'; } case 256: /* T_CHARS, 'Characters' */ { for ( let i = 0; i < node.val.length; i++ ) { @@ -2846,18 +2849,41 @@ Parser.tokenizableStrFromRegex = (( ) => { return '\x01'; }; - return function(reStr) { - if ( - self.Regex instanceof Object === false || - self.Regex.Analyzer instanceof Object === false - ) { + const Regex = self.Regex; + if ( + Regex instanceof Object === false || + Regex.Analyzer instanceof Object === false + ) { + return { + isValid: function(reStr) { + try { + void new RegExp(reStr); + } catch(ex) { + return false; + } + return true; + }, + toTokenizableStr: ( ) => '', + }; + } + + return { + isValid: function(reStr) { + try { + void new RegExp(reStr); + void toTokenizableStr(Regex.Analyzer(reStr, false).tree()); + } catch(ex) { + return false; + } + return true; + }, + toTokenizableStr: function(reStr) { + try { + return toTokenizableStr(Regex.Analyzer(reStr, false).tree()); + } catch(ex) { + } return ''; - } - try { - return toTokenizableString(self.Regex.Analyzer(reStr, false).tree()); - } catch(ex) { - } - return ''; + }, }; })(); diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 1edc3619cd2c6..2f2e08fc67667 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3170,7 +3170,7 @@ const FilterParser = class { extractTokenFromRegex() { this.reToken.lastIndex = 0; const pattern = - vAPI.StaticFilteringParser.tokenizableStrFromRegex(this.pattern); + vAPI.StaticFilteringParser.regexUtils.toTokenizableStr(this.pattern); let bestToken; let bestBadness = 0x7FFFFFFF; for (;;) { From 5d17289d4a47de69b4c71c69eebd941e26ad1f9a Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Dec 2020 07:32:36 -0500 Subject: [PATCH 4073/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 0b47a5dd338af..3c737d1055ee7 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.3.2 +1.32.3.3 From 54e77b1b542e791de6784085e1831bb82fd227b7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Dec 2020 07:35:19 -0500 Subject: [PATCH 4074/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/sv/messages.json | 2 +- src/_locales/th/messages.json | 2 +- src/_locales/vi/messages.json | 34 +++++++++++++++++----------------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json index 7ae9a834bcc28..0ce5cdd16947f 100644 --- a/src/_locales/sv/messages.json +++ b/src/_locales/sv/messages.json @@ -100,7 +100,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "Blockerad sedan installation", + "message": "Blockerat sedan installation", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { diff --git a/src/_locales/th/messages.json b/src/_locales/th/messages.json index 4ee525268da64..d9eefe730228a 100644 --- a/src/_locales/th/messages.json +++ b/src/_locales/th/messages.json @@ -120,7 +120,7 @@ "description": "English: Enter element picker mode" }, "popupTipLog": { - "message": "เปิดบันทึกการทำงาน", + "message": "เปิดแอปโทรศัพท์", "description": "Tooltip used for the logger icon in the panel" }, "popupTipNoPopups": { diff --git a/src/_locales/vi/messages.json b/src/_locales/vi/messages.json index c84fa8f759240..263c92b27d4e6 100644 --- a/src/_locales/vi/messages.json +++ b/src/_locales/vi/messages.json @@ -40,7 +40,7 @@ "description": "appears as tab name in dashboard" }, "whitelistPageName": { - "message": "Danh sách trắng", + "message": "Trang tin cậy", "description": "appears as tab name in dashboard" }, "shortcutsPageName": { @@ -100,7 +100,7 @@ "description": "For the new mobile-friendly popup design" }, "popupBlockedSinceInstall_v2": { - "message": "Đã chặn từ khi cài đặt", + "message": "Bị chặn kể từ khi cài đặt", "description": "For the new mobile-friendly popup design" }, "popupDomainsConnected_v2": { @@ -164,11 +164,11 @@ "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts1": { - "message": "Nhấp để chặn font từ xa trên trang này", + "message": "Bấm để chặn phông từ xa trên trang này", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoRemoteFonts2": { - "message": "Nhấp để hủy chặn font từ xa trên trang này", + "message": "Bấm để không chặn phông từ xa trên trang này", "description": "Tooltip for the no-remote-fonts per-site switch" }, "popupTipNoScripting1": { @@ -176,7 +176,7 @@ "description": "Tooltip for the no-scripting per-site switch" }, "popupTipNoScripting2": { - "message": "Nhấp để ngừng vô hiệu hóa JavaScript trên trang này", + "message": "Nhấn vào đây để không còn vô hiệu hóa JavaScript trên trang web này", "description": "Tooltip for the no-scripting per-site switch" }, "popupNoPopups_v2": { @@ -440,7 +440,7 @@ "description": "English: Malware domains" }, "3pGroupAnnoyances": { - "message": "Khó chịu", + "message": "Phiền toái", "description": "The header identifying the filter lists in the category 'annoyances'" }, "3pGroupMultipurpose": { @@ -480,11 +480,11 @@ "description": "used as a tooltip for the spinner icon beside a list" }, "3pNetworkError": { - "message": "Một lỗi mạng khiến cho tài nguyên không thể cập nhật.", + "message": "Lỗi mạng ngăn tài nguyên được cập nhật.", "description": "used as a tooltip for error icon beside a list" }, "1pFormatHint": { - "message": "Một bộ lọc trên mỗi dòng. Một bộ lọc có thể là một tên máy chủ đơn thuần, hoặc một bộ lọc tương thích với Adblock Plus. Những dòng bắt đầu với ! sẽ bị bỏ qua.", + "message": "Một bộ lọc cho mỗi dòng. Bộ lọc có thể là tên máy chủ thuần hoặc bộ lọc tương thích với EasyList. Các dòng có tiền tố ! sẽ bị bỏ qua.", "description": "Short information about how to create custom filters" }, "1pImport": { @@ -612,7 +612,7 @@ "description": "Pretty name for behind-the-scene network requests" }, "loggerCurrentTab": { - "message": "Tab hiện tại", + "message": "Thẻ hiện tại", "description": "Appears in the logger's tab selector" }, "loggerReloadTip": { @@ -628,7 +628,7 @@ "description": "Tooltip for the popup panel button in the logger page" }, "loggerInfoTip": { - "message": "uBlock Origin wiki: Tác vụ log", + "message": "uBlock Origin wiki: Các logger", "description": "Tooltip for the top-right info label in the logger page" }, "loggerClearTip": { @@ -672,7 +672,7 @@ "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltinModified": { - "message": "modified", + "message": "sửa đổi", "description": "A keyword in the built-in row filtering expression" }, "loggerRowFiltererBuiltin1p": { @@ -776,11 +776,11 @@ "description": "Below this sentence, the filter list(s) in which the filter was found" }, "loggerStaticFilteringFinderSentence2": { - "message": "Bộ lọc tĩnh {{filter}} không thể tìm thấy trong bất kỳ danh sách bộ lọc hiện được bật nào", + "message": "Không tìm thấy bộ lọc tĩnh trong bất kỳ danh sách bộ lọc hiện đang được bật", "description": "Message to show when a filter cannot be found in any filter lists" }, "loggerSettingDiscardPrompt": { - "message": "Các mục log không đáp ứng cả ba điều kiện dưới đây sẽ tự động bị loại bỏ:", + "message": "Các mục Logger mà không đáp ứng tất cả ba điều kiện dưới đây sẽ được tự động loại bỏ:", "description": "Logger setting: A sentence to describe the purpose of the settings below" }, "loggerSettingPerEntryMaxAge": { @@ -832,7 +832,7 @@ "description": "Label for radio-button to pick export text format" }, "loggerExportEncodeMarkdown": { - "message": "Markdown", + "message": "Đánh dấu", "description": "Label for radio-button to pick export text format" }, "aboutChangelog": { @@ -912,7 +912,7 @@ "description": "No longer used" }, "subscribeButton": { - "message": "Subscribe", + "message": "Đăng ký", "description": "For the button used to subscribe to a filter list" }, "elapsedOneMinuteAgo": { @@ -1028,7 +1028,7 @@ "description": "" }, "contextMenuBlockElementInFrame": { - "message": "Block element in frame...", + "message": "Chặn phần tử trong khung ...", "description": "An entry in the browser's contextual menu" }, "contextMenuTemporarilyAllowLargeMediaElements": { @@ -1072,7 +1072,7 @@ "description": "short for 'gigabytes'" }, "clickToLoad": { - "message": "Click to load", + "message": "Bấm để tải", "description": "Message used in frame placeholders" }, "dummy": { From 6c2577ce7e98858e3eade16f9e6e5a5e8f119013 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 28 Dec 2020 07:40:45 -0500 Subject: [PATCH 4075/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 96486ede5ed4d..cfa70c1fe8765 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.3.2", + "version": "1.32.3.3", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b2/uBlock0_1.32.3b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b3/uBlock0_1.32.3b3.firefox.signed.xpi" } ] } From b003c4de3d19ca591b4e578f99a590e091d91d24 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 29 Dec 2020 08:33:48 -0500 Subject: [PATCH 4076/4093] Import punycode library in "My rules" Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1424 --- src/dyna-rules.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dyna-rules.html b/src/dyna-rules.html index 0feb56565e671..bb9b28260124c 100644 --- a/src/dyna-rules.html +++ b/src/dyna-rules.html @@ -52,6 +52,7 @@ + From 48bf0ffb1b8ae8038819a4308eb509fe5ed4ddb7 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 29 Dec 2020 09:05:03 -0500 Subject: [PATCH 4077/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 3c737d1055ee7..329d74c9de95d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.3.3 +1.32.3.4 From 1669d122df581146a1e67e912ed365a5983bb405 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 29 Dec 2020 09:05:28 -0500 Subject: [PATCH 4078/4093] Add resource for noop VMAP Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1425 The resource content is a copy/paste of AdGuard's code: - https://github.com/AdguardTeam/Scriptlets/blob/bc5eec198903856413e3e114a1d636c34146173d/src/redirects/static-redirects.yml#L134 --- src/js/redirect-engine.js | 5 +++++ src/web_accessible_resources/noop-vmap1.0.xml | 1 + 2 files changed, 6 insertions(+) create mode 100644 src/web_accessible_resources/noop-vmap1.0.xml diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js index 58f6b1bec794d..0466a8ba16edb 100644 --- a/src/js/redirect-engine.js +++ b/src/js/redirect-engine.js @@ -146,6 +146,10 @@ const redirectableResources = new Map([ alias: 'nooptext', data: 'text', } ], + [ 'noop-vmap1.0.xml', { + alias: 'noopvmap-1.0', + data: 'text', + } ], [ 'outbrain-widget.js', { alias: 'widgets.outbrain.com/outbrain.js', } ], @@ -173,6 +177,7 @@ const extToMimeMap = new Map([ [ 'mp4', 'video/mp4' ], [ 'png', 'image/png' ], [ 'txt', 'text/plain' ], + [ 'xml', 'text/xml' ], ]); const typeToMimeMap = new Map([ diff --git a/src/web_accessible_resources/noop-vmap1.0.xml b/src/web_accessible_resources/noop-vmap1.0.xml new file mode 100644 index 0000000000000..acd6fb83845f1 --- /dev/null +++ b/src/web_accessible_resources/noop-vmap1.0.xml @@ -0,0 +1 @@ + From ce9dbbbf4c42ff96941924332798ee0bf79e0041 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 29 Dec 2020 09:08:29 -0500 Subject: [PATCH 4079/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 329d74c9de95d..e75a400bd564d 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.3.4 +1.32.5.0 From 8052c0ca14c3545fbf389cc49d3f38043dcb5954 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 29 Dec 2020 09:10:39 -0500 Subject: [PATCH 4080/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index cfa70c1fe8765..0dc360a93bebc 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.3.3", + "version": "1.32.5.0", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.3b3/uBlock0_1.32.3b3.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.5b0/uBlock0_1.32.5b0.firefox.signed.xpi" } ] } From c2357c5cd6070e310292f0d18ab1c8faefa67d20 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Jan 2021 10:23:40 -0500 Subject: [PATCH 4081/4093] Just extract token from queryprune -- don't create pattern Related commit: - https://github.com/gorhill/uBlock/commit/6ac09a28560ebe7de6c20841b5ffb024fe97442d Patternless `queryprune` ar enow preserved as being pattern-less while still attempting to extract a token from the `queryprune` value. This allows to report the filter in the logger same as its original form. --- src/js/static-net-filtering.js | 52 ++++++++++++++-------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 2f2e08fc67667..5026aabc8c152 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -3114,23 +3114,20 @@ const FilterParser = class { makeToken() { if ( this.pattern === '*' ) { - if ( - this.modifyType !== this.parser.OPTTokenQueryprune || - this.makePatternFromQuerypruneValue() === false - ) { + if ( this.modifyType !== this.parser.OPTTokenQueryprune ) { return; } + return this.extractTokenFromQuerypruneValue(); } if ( this.isRegex ) { - return this.extractTokenFromRegex(); + return this.extractTokenFromRegex(this.pattern); } - this.extractTokenFromPattern(); + this.extractTokenFromPattern(this.pattern); } // Note: a one-char token is better than a documented bad token. - extractTokenFromPattern() { + extractTokenFromPattern(pattern) { this.reToken.lastIndex = 0; - const pattern = this.pattern; let bestMatch = null; let bestBadness = 0x7FFFFFFF; for (;;) { @@ -3167,10 +3164,9 @@ const FilterParser = class { // https://github.com/uBlockOrigin/uBlock-issues/issues/1145#issuecomment-657036902 // Mind `\b` directives: `/\bads\b/` should result in token being `ads`, // not `bads`. - extractTokenFromRegex() { + extractTokenFromRegex(pattern) { + pattern = vAPI.StaticFilteringParser.regexUtils.toTokenizableStr(pattern); this.reToken.lastIndex = 0; - const pattern = - vAPI.StaticFilteringParser.regexUtils.toTokenizableStr(this.pattern); let bestToken; let bestBadness = 0x7FFFFFFF; for (;;) { @@ -3204,23 +3200,19 @@ const FilterParser = class { } } - makePatternFromQuerypruneValue() { - let pattern = this.modifyValue; + extractTokenFromQuerypruneValue() { + const pattern = this.modifyValue; if ( pattern === '*' || pattern.charCodeAt(0) === 0x7E /* '~' */ ) { - return false; + return; } const match = /^\/(.+)\/i?$/.exec(pattern); if ( match !== null ) { - pattern = match[1]; - this.isRegex = true; - } else if ( pattern.startsWith('|') ) { - pattern = '\\b' + pattern.slice(1); - this.isRegex = true; - } else { - pattern = encodeURIComponent(pattern).toLowerCase() + '='; + return this.extractTokenFromRegex(match[1]); } - this.pattern = pattern; - return true; + if ( pattern.startsWith('|') ) { + return this.extractTokenFromRegex('\\b' + pattern.slice(1)); + } + this.extractTokenFromPattern(encodeURIComponent(pattern).toLowerCase()); } hasNoOptionUnits() { @@ -3246,14 +3238,14 @@ const FilterParser = class { } }; -FilterParser.prototype.DOMAIN_BIT = 0b00000001; -FilterParser.prototype.DENYALLOW_BIT = 0b00000010; -FilterParser.prototype.HEADER_BIT = 0b00000100; -FilterParser.prototype.STRICT_PARTY_BIT = 0b00001000; +FilterParser.prototype.DOMAIN_BIT = 0b00000001; +FilterParser.prototype.DENYALLOW_BIT = 0b00000010; +FilterParser.prototype.HEADER_BIT = 0b00000100; +FilterParser.prototype.STRICT_PARTY_BIT = 0b00001000; -FilterParser.prototype.CSP_BIT = 0b00010000; -FilterParser.prototype.QUERYPRUNE_BIT = 0b00100000; -FilterParser.prototype.REDIRECT_BIT = 0b01000000; +FilterParser.prototype.CSP_BIT = 0b00010000; +FilterParser.prototype.QUERYPRUNE_BIT = 0b00100000; +FilterParser.prototype.REDIRECT_BIT = 0b01000000; /******************************************************************************/ From 4275b308bb8e61d165b898960df60f8df3b9f518 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Jan 2021 10:29:43 -0500 Subject: [PATCH 4082/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index e75a400bd564d..9b65e8300d7d3 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.5.0 +1.32.5.1 From 4d42969a1dadf036f1571ff0767a1e73bd9cee1b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Jan 2021 10:31:23 -0500 Subject: [PATCH 4083/4093] Import translation work from https://crowdin.com/project/ublock --- src/_locales/sl/messages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json index 9cb02a0b7e497..917960dbf1410 100644 --- a/src/_locales/sl/messages.json +++ b/src/_locales/sl/messages.json @@ -552,7 +552,7 @@ "description": "English: dynamic rule syntax and full documentation." }, "rulesSort": { - "message": "Sort:", + "message": "Razvrsti", "description": "English: label for sort option." }, "rulesSortByType": { @@ -560,7 +560,7 @@ "description": "English: a sort option for list of rules." }, "rulesSortBySource": { - "message": "Source", + "message": "Vir", "description": "English: a sort option for list of rules." }, "rulesSortByDestination": { From 4f53e08741ced7f5b591144e96967737c6703d58 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 1 Jan 2021 10:46:04 -0500 Subject: [PATCH 4084/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 0dc360a93bebc..f237b526a69e5 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.5.0", + "version": "1.32.5.1", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.5b0/uBlock0_1.32.5b0.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.5b1/uBlock0_1.32.5b1.firefox.signed.xpi" } ] } From 70cabc1cc6ab6086ca3cfe2d42299f4e3a117492 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 2 Jan 2021 11:52:16 -0500 Subject: [PATCH 4085/4093] Better report secondary requests with quick redirections Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1241 uBO will not discard secondary requests fired before a root frame is committed, by ensuring that if newly uncommitted root frames are of the same origin as previous one(s), the uncommited journal slot pointer is not updated. --- src/js/pagestore.js | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 980ba3afbb17d..3e168aea0405c 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -226,9 +226,9 @@ const PageStore = class { constructor(tabId, context) { this.extraData = new Map(); this.journal = []; - this.journalTimer = null; - this.journalLastCommitted = this.journalLastUncommitted = undefined; - this.journalLastUncommittedURL = undefined; + this.journalTimer = undefined; + this.journalLastCommitted = this.journalLastUncommitted = -1; + this.journalLastUncommittedOrigin = undefined; this.netFilteringCache = NetFilteringResultCache.factory(); this.init(tabId, context); } @@ -351,12 +351,12 @@ const PageStore = class { this.largeMediaTimer = null; } this.disposeFrameStores(); - if ( this.journalTimer !== null ) { + if ( this.journalTimer !== undefined ) { clearTimeout(this.journalTimer); - this.journalTimer = null; + this.journalTimer = undefined; } this.journal = []; - this.journalLastUncommittedURL = undefined; + this.journalLastUncommittedOrigin = undefined; if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) { pageStoreJunkyard.push(this); } @@ -463,7 +463,7 @@ const PageStore = class { hostname, result === 1 ? 0x00000001 : 0x00010000 ); - if ( this.journalTimer === null ) { + if ( this.journalTimer === undefined ) { this.journalTimer = vAPI.setTimeout( ( ) => { this.journalProcess(true); }, µb.hiddenSettings.requestJournalProcessPeriod @@ -475,18 +475,23 @@ const PageStore = class { if ( type === 'committed' ) { this.journalLastCommitted = this.journal.length; if ( - this.journalLastUncommitted !== undefined && + this.journalLastUncommitted !== -1 && this.journalLastUncommitted < this.journalLastCommitted && - this.journalLastUncommittedURL === url + this.journalLastUncommittedOrigin === vAPI.hostnameFromURI(url) ) { this.journalLastCommitted = this.journalLastUncommitted; - this.journalLastUncommitted = undefined; } } else if ( type === 'uncommitted' ) { - this.journalLastUncommitted = this.journal.length; - this.journalLastUncommittedURL = url; + const newOrigin = vAPI.hostnameFromURI(url); + if ( + this.journalLastUncommitted === -1 || + this.journalLastUncommittedOrigin !== newOrigin + ) { + this.journalLastUncommitted = this.journal.length; + this.journalLastUncommittedOrigin = newOrigin; + } } - if ( this.journalTimer !== null ) { + if ( this.journalTimer !== undefined ) { clearTimeout(this.journalTimer); } this.journalTimer = vAPI.setTimeout( @@ -495,16 +500,14 @@ const PageStore = class { ); } - journalProcess(fromTimer) { - if ( !fromTimer ) { - clearTimeout(this.journalTimer); - } - this.journalTimer = null; + journalProcess(fromTimer = false) { + if ( fromTimer === false ) { clearTimeout(this.journalTimer); } + this.journalTimer = undefined; const journal = this.journal; + const pivot = this.journalLastCommitted || 0; const now = Date.now(); let aggregateCounts = 0; - let pivot = this.journalLastCommitted || 0; // Everything after pivot originates from current page. for ( let i = pivot; i < journal.length; i += 2 ) { @@ -520,7 +523,7 @@ const PageStore = class { } this.perLoadBlockedRequestCount += aggregateCounts & 0xFFFF; this.perLoadAllowedRequestCount += aggregateCounts >>> 16 & 0xFFFF; - this.journalLastCommitted = undefined; + this.journalLastUncommitted = this.journalLastCommitted = -1; // https://github.com/chrisaljoudi/uBlock/issues/905#issuecomment-76543649 // No point updating the badge if it's not being displayed. From 873054435b00d29fafb8fef94a399c78f0ce7c42 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 2 Jan 2021 12:00:43 -0500 Subject: [PATCH 4086/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index 9b65e8300d7d3..cd1e3297c0f05 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.5.1 +1.32.5.2 From 752191a04f4a0cbe53edd052fde5bfd905f5d682 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 2 Jan 2021 12:07:31 -0500 Subject: [PATCH 4087/4093] Make sure journal slot pointers are properly reset For when the same page store is recycled later. --- src/js/pagestore.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 3e168aea0405c..729590de59314 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -357,6 +357,7 @@ const PageStore = class { } this.journal = []; this.journalLastUncommittedOrigin = undefined; + this.journalLastCommitted = this.journalLastUncommitted = -1; if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) { pageStoreJunkyard.push(this); } From 3cf9a187febdacac80e7d970a6a8a41b6a8206ca Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 2 Jan 2021 12:16:09 -0500 Subject: [PATCH 4088/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index f237b526a69e5..9366a16af1efb 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.5.1", + "version": "1.32.5.2", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.5b1/uBlock0_1.32.5b1.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.5b2/uBlock0_1.32.5b2.firefox.signed.xpi" } ] } From 2bb33aac206b8dec2d5ded0442c599afd88ea477 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 2 Jan 2021 12:41:13 -0500 Subject: [PATCH 4089/4093] Be sure to use only a valid journal slot pointer Related commit: - https://github.com/gorhill/uBlock/commit/70cabc1cc6ab6086ca3cfe2d42299f4e3a117492 --- src/js/pagestore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 729590de59314..0444e2018783a 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -506,7 +506,7 @@ const PageStore = class { this.journalTimer = undefined; const journal = this.journal; - const pivot = this.journalLastCommitted || 0; + const pivot = Math.max(0, this.journalLastCommitted); const now = Date.now(); let aggregateCounts = 0; From 415152aa57b9518c4f2ed48a4822e700d938212f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 2 Jan 2021 12:42:18 -0500 Subject: [PATCH 4090/4093] New revision for dev build --- dist/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/version b/dist/version index cd1e3297c0f05..8740614171a89 100644 --- a/dist/version +++ b/dist/version @@ -1 +1 @@ -1.32.5.2 +1.32.5.3 From ced4330d74eb180c340e0fbd74c2ef8654dc4b45 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 2 Jan 2021 12:50:58 -0500 Subject: [PATCH 4091/4093] Make Firefox dev build auto-update --- dist/firefox/updates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/firefox/updates.json b/dist/firefox/updates.json index 9366a16af1efb..5c81ee367c285 100644 --- a/dist/firefox/updates.json +++ b/dist/firefox/updates.json @@ -3,9 +3,9 @@ "uBlock0@raymondhill.net": { "updates": [ { - "version": "1.32.5.2", + "version": "1.32.5.3", "browser_specific_settings": { "gecko": { "strict_min_version": "55" } }, - "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.5b2/uBlock0_1.32.5b2.firefox.signed.xpi" + "update_link": "https://github.com/gorhill/uBlock/releases/download/1.32.5b3/uBlock0_1.32.5b3.firefox.signed.xpi" } ] } From 7bf389e21c07effc8f0505213a750e4fe0c77004 Mon Sep 17 00:00:00 2001 From: incidegirmenci <76945163+incidegirmenci@users.noreply.github.com> Date: Mon, 4 Jan 2021 13:38:46 +0300 Subject: [PATCH 4092/4093] Create .DS_Store --- docs/.DS_Store | Bin 0 -> 6148 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/.DS_Store diff --git a/docs/.DS_Store b/docs/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e4f3a7391dd7dcbc5f252dbad51cf3318477b0bd GIT binary patch literal 6148 zcmeHK&2G~`5S~pFx(-6*fYe@)eBo9_X&Y7IfMnA2&_gAJ5gY)uHnxZ**IUI7p@bm6 z27L*hgD2o|;M<>6O+$}GfM%rGZ+3QOZGUU;c!@|1CdoEYhlnIpW5Yx98`0xhSES)Q zEub)Se55XosG?KKSEAYRH!`5tu1y&glu$}n*6%P^@8Vp=7}qbyQ)u_65m-bXJ*NYB zhjaw&V#Skd8Q-pDwD4Y}RH7}HBgMm;Bjag#=I-Kqe3oZr)$9EbjkRX$&bpLs*}m6) z8;;a8tfFdC4x;gE-Fp^QquALml*%vdD1Mox)8o#AeN|LZR-|K-oFyqj-n`0+L`?^3 zQY59BC$s}ndeS@YY|dsod%Z1xx8I*{`LoA6u=~4v^SLKC9&JB4JUKo8H2XaN@>TPL zF9M&0k^2r8Z~@~A86U!Nk*nf8yo-!QBqKAx3@`(?#DLpwq4Z+M?My(3m3tunD&j*!p)-=lBi-hqXnFK!i;N+En3=7{aE*@4L9bVQtZ-lW>O* z;jS#)2}PKzV}9R+lL#zw%M36B%M7g9Zb#SugPYI)%Sqg02AF~WiUH9ahQk3K$<@}C xhtsvzhn_&S=(yVAWeN;aiV>?z@g~#={Ju7Tfy3G&JP`g7Ff?$(4E#|BegS Date: Mon, 4 Jan 2021 13:39:04 +0300 Subject: [PATCH 4093/4093] Create .DS_Store --- .DS_Store | Bin 0 -> 6148 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..247c8c6951b73f0c0ddebb2230c9270b9560e4c9 GIT binary patch literal 6148 zcmeHK&2G~`5T0$);-pl~0f}CaeBo9_X&Y7IfE4lriUcjf1rC5(yAG`-$Btr$03j%M zo&cT!iRa)6cot6Z&F)g!NqR+y?nJxadi{;p`^|VaOGKhM@^^?TL}Z~b7R#uX7~kiz zU@N+311RJ(QcAjEeCmaQa-QqSeLq1FBM={LI(S-rwfDi{w*)kDd0j`eAd#ZLn(x8`6} z@$)jCH%am-id%ArT z$KgfW^_~U2@p0uwD~?h(hZKOQ&ZQRFA`IgYwvStYA*np9yWNxiyZ4h1(~qAq8JNHVD{WKbANYdCg%EF?VHC#E z8Ty#NP7e{B7IhFH2Xz$zemgJz3bV5zzD|!2_5*m{hmV5*A1)0pr^eSzYFIR&5UG^X zYlLM;{e1XjGGzHBq)-PVwJ{#Q+d}%tw8(lCkUGPqX^~~E;tgW_!lj9kWnuzX*>p-- z>sMI>Gk2qaQDCV8ygt|{j17%5g>vaYC655WBD$ra%^N@a*cuxeX9{r#CX_2sxe9&6 z5Xv3>w&pc7&J-$l68i8V^vFV=P=p*E&$lI=L_?vejRHo2yaEMvS>gS^bNTr{?_{ow z0!D%VN&!)9JMAW>r1#c^$?;xmqnx0yF>j_&x}ehAv25^GyoDkS_iSze8yaT{(E~Go O1f&e6G79`v1%3mR3fzML literal 0 HcmV?d00001