aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md28
-rw-r--r--assets/checksums.txt4
-rw-r--r--assets/ublock/filters.txt51
-rw-r--r--assets/ublock/unbreak.txt22
-rw-r--r--dist/description/description-de.txt4
-rw-r--r--dist/description/description-en.txt18
-rw-r--r--dist/description/description-sq.txt6
-rw-r--r--doc/benchmarks/vim-test-abp-vs-ublock.pngbin300922 -> 230223 bytes
-rw-r--r--platform/chromium/manifest.json2
-rw-r--r--platform/chromium/vapi-background.js8
-rw-r--r--platform/firefox/bootstrap.js12
-rw-r--r--platform/firefox/frameModule.js47
-rw-r--r--platform/firefox/frameScript.js33
-rw-r--r--platform/firefox/install.rdf21
-rw-r--r--platform/firefox/vapi-background.js287
-rw-r--r--platform/firefox/vapi-client.js37
-rw-r--r--platform/opera/manifest.json2
-rw-r--r--src/1p-filters.html2
-rw-r--r--src/_locales/ar/messages.json6
-rw-r--r--src/_locales/ca/messages.json4
-rw-r--r--src/_locales/cs/messages.json4
-rw-r--r--src/_locales/da/messages.json20
-rw-r--r--src/_locales/de/messages.json16
-rw-r--r--src/_locales/el/messages.json14
-rw-r--r--src/_locales/fy/messages.json2
-rw-r--r--src/_locales/he/messages.json4
-rw-r--r--src/_locales/hi/messages.json4
-rw-r--r--src/_locales/hu/messages.json2
-rw-r--r--src/_locales/id/messages.json4
-rw-r--r--src/_locales/it/messages.json8
-rw-r--r--src/_locales/ja/messages.json20
-rw-r--r--src/_locales/ko/messages.json2
-rw-r--r--src/_locales/nb/messages.json4
-rw-r--r--src/_locales/ro/messages.json6
-rw-r--r--src/_locales/sk/messages.json4
-rw-r--r--src/_locales/sl/messages.json16
-rw-r--r--src/_locales/sq/messages.json50
-rw-r--r--src/_locales/sv/messages.json4
-rw-r--r--src/_locales/te/messages.json4
-rw-r--r--src/_locales/uk/messages.json10
-rw-r--r--src/_locales/zh_CN/messages.json2
-rw-r--r--src/background.html1
-rw-r--r--src/css/dashboard-common.css6
-rw-r--r--src/css/popup.css1
-rw-r--r--src/css/settings.css6
-rw-r--r--src/js/3p-filters.js14
-rw-r--r--src/js/assets.js80
-rw-r--r--src/js/background.js2
-rw-r--r--src/js/contentscript-end.js12
-rw-r--r--src/js/contentscript-start.js4
-rw-r--r--src/js/cosmetic-filtering.js120
-rw-r--r--src/js/document-blocked.js11
-rw-r--r--src/js/messaging.js2
-rw-r--r--src/js/reverselookup-worker.js32
-rw-r--r--src/js/rpcreceiver.js53
-rw-r--r--src/js/static-net-filtering.js5
-rw-r--r--src/popup.html2
-rw-r--r--src/settings.html4
58 files changed, 846 insertions, 303 deletions
diff --git a/README.md b/README.md
index 422a8d7..a5915b6 100644
--- a/README.md
+++ b/README.md
@@ -3,21 +3,11 @@
***
-**Important:** uBlock Origin is **NOT** an "ad blocker": [it is a wide-spectrum blocker](https://github.com/gorhill/uBlock/wiki/Blocking-mode) -- which happens to be able to function as a mere "ad blocker". The default behavior of uBlock Origin when newly installed is to block ads, trackers and malware sites -- through [_EasyList_](https://easylist.adblockplus.org/en/policy#easylist), [_EasyPrivacy_](https://easylist.adblockplus.org/en/policy#easyprivacy), [_Peter Lowe’s ad/tracking/malware servers_](http://pgl.yoyo.org/adservers/policy.php), various lists of [malware](http://www.malwaredomainlist.com/) [sites](http://www.malwaredomains.com/), and uBlock Origin's [own filter lists](https://github.com/gorhill/uBlock/tree/master/assets/ublock).
+**uBlock Origin is _completely_ unrelated to the web site `ublock.org`:** the donations sought by the [individual](https://github.com/chrisaljoudi/) behind `ublock.org` are _not_ benefiting any of those who contributed most to create uBlock Origin ([developers](https://github.com/gorhill/uBlock/graphs/contributors), [translators](https://crowdin.com/project/ublock), and all those who put efforts in opening detailed issues). For the differences in features between uBlock Origin and uBlock, you are more likely than anywhere else to find an unbiased explanation in this [Wikipedia article](http://en.wikipedia.org/wiki/UBlock).
***
-There is an idea floating around that uBlock Origin is a _lesser_ branch relative to [uBlock](https://github.com/chrisaljoudi/uBlock)<sup>[1]</sup>.
-
-The current reality is that there are *two branches*, not an official one and a lesser one. I [keep developing my branch](https://github.com/gorhill/uBlock/wiki/uBlock-vs.-uBlock-Origin:-Facts#ublock-is-the-main-branch-ublock-origin-is-just-bug-fixes) the same way and in the same spirit I have since [the beginning of uBlock in June 23, 2014](http://www.wilderssecurity.com/threads/ublock-a-lean-and-fast-blocker.365273/), so in substance uBlock Origin _is_ the original uBlock.
-
-**uBlock Origin is _completely_ unrelated to the web site `ublock.org`:** the donations sought by `ublock.org` are _not_ benefiting any of those who contributed most to create uBlock ([developers](https://github.com/gorhill/uBlock/graphs/contributors)<sup>[2]</sup>, [translators](https://crowdin.com/project/ublock), and all those who put efforts in opening detailed issues). For the differences in features between uBlock Origin and uBlock, you are more likely than anywhere else to find an unbiased explanation in this [Wikipedia article](http://en.wikipedia.org/wiki/UBlock).
-
-Somewhere toward the end of May, I decided I will not contribute code anymore to this [uBlock branch](https://github.com/chrisaljoudi/uBlock).<sup>[3]</sup>
-
-<sub>[1] [An example](https://www.ublock.org/faq/). [Another one](https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/reviews/716364/).</sub><br>
-<sub>[2] Keep in mind these charts do not take into account all the work done on the wiki (doc, benchmarks), which work was for the most part not contributed by the individual behind `ublock.org`. </sub><br>
-<sub>[3] Reasons: [this](https://en.wikipedia.org/w/index.php?title=UBlock&type=revision&diff=662527440&oldid=662107368) vs. [this](https://github.com/chrisaljoudi/uBlock/commits/master?author=gorhill), and [this](https://www.reddit.com/r/ublock/comments/38lf1y/any_difference_between_ublock_and_ublock_origin/crwhmwt), and overall because of serious incompatibilities in the spirit of the project.</sub><br>
+uBlock Origin is **NOT** an "ad blocker": [it is a wide-spectrum blocker](https://github.com/gorhill/uBlock/wiki/Blocking-mode) -- which happens to be able to function as a mere "ad blocker". The default behavior of uBlock Origin when newly installed is to block ads, trackers and malware sites -- through [_EasyList_](https://easylist.adblockplus.org/en/policy#easylist), [_EasyPrivacy_](https://easylist.adblockplus.org/en/policy#easyprivacy), [_Peter Lowe’s ad/tracking/malware servers_](http://pgl.yoyo.org/adservers/policy.php), various lists of [malware](http://www.malwaredomainlist.com/) [sites](http://www.malwaredomains.com/), and uBlock Origin's [own filter lists](https://github.com/gorhill/uBlock/tree/master/assets/ublock).
***
@@ -89,24 +79,22 @@ Once you install uBlock₀, you may easily un-select any of the pre-selected fil
#### Memory
<div align="center">
-On average, uBlock₀ <b>really</b> does make your browser run leaner. <sup>[1]</sup><br><br>
+On average, uBlock Origin does make your browser run leaner. <sup>[1]</sup><br><br>
-Chromium <sup>[2]</sup><br>
-<img src="https://raw.githubusercontent.com/gorhill/uBlock/master/doc/benchmarks/mem-usage-overall-chart-20141224.png" /><br><br>
+Chromium<br>
+<img src="https://cloud.githubusercontent.com/assets/585534/10074141/15f04128-629c-11e5-9155-177fd4909083.png" /><br><br>
Firefox<br>
-<img src="https://raw.githubusercontent.com/gorhill/uBlock/master/doc/benchmarks/mem-usage-overall-chart-20150205.png" /><br><br>
+<img src="https://cloud.githubusercontent.com/assets/585534/10074130/0577118c-629c-11e5-9902-bf367c6a96c3.png" /><br><br>
</div>
<sup>[1] Details of the benchmark available at <a href="https://github.com/gorhill/uBlock/wiki/Firefox-version:-benchmarking-memory-footprint">Firefox version: benchmarking memory footprint</a>.</sup><br>
-<sup>[2] Important note: There is currently a [bug in Chromium v39 to v41 which causes a new memory leak each time the popup UI of an extension is opened](https://code.google.com/p/chromium/issues/detail?id=441500). This affects <i>all</i> extensions. Keep this in mind when measuring Chromium's memory usage. In the benchmarks, I avoided opening the popups completely.</sup><br>
-
#### CPU
<p align="center">
-uBlock₀ is also easy on the CPU<br>
+uBlock Origin is also easy on the CPU<br>
<img src="https://raw.githubusercontent.com/gorhill/uBlock/master/doc/benchmarks/cpu-usage-overall-chart-20141226.png" /><br>
<sup>Details of the benchmark available in <a href="https://github.com/gorhill/uBlock/blob/master/doc/benchmarks/cpu-usage-overall-20141226.ods">this LibreOffice spreadsheet</a>.</sup>
</p>
@@ -133,7 +121,7 @@ Feel free to read [about the extension's required permissions](https://github.co
#### Chromium
-You can install the latest version [manually](https://github.com/gorhill/uBlock/tree/master/dist#install), from the [Chrome Store](https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm), or from the [Opera store](https://addons.opera.com/en-gb/extensions/details/ublock/).
+You can install the latest version [manually](https://github.com/gorhill/uBlock/tree/master/dist#install), from the [Chrome Store](https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm), or from the [Opera store](https://addons.opera.com/en-gb/extensions/details/ublock/) (uBlock Origin is published under the name "µBlock" in the Opera store -- the name can't be changed).
#### Firefox / Firefox for Android
diff --git a/assets/checksums.txt b/assets/checksums.txt
index 74b777a..fb5fbf6 100644
--- a/assets/checksums.txt
+++ b/assets/checksums.txt
@@ -1,6 +1,6 @@
-6f506c670bd425f4743c3056d5a51d83 assets/ublock/unbreak.txt
+7e679d07b2350b215a5f01456e84fe73 assets/ublock/unbreak.txt
75448208315e80706632ec8a6df0e6d3 assets/ublock/privacy.txt
-1fcf43b9a244944394d30421e60fda83 assets/ublock/filters.txt
+fa30a140569cca02898fa2e1114d6143 assets/ublock/filters.txt
d2c1dc3f8c4528e6d6a9083afc30b938 assets/ublock/badware.txt
c9c5cc56bec563bc1885847f925b9be2 assets/ublock/mirror-candidates.txt
d6649cbd5533bb5ea3ccd8de060d21fe assets/ublock/filter-lists.json
diff --git a/assets/ublock/filters.txt b/assets/ublock/filters.txt
index 35578b5..e0099f7 100644
--- a/assets/ublock/filters.txt
+++ b/assets/ublock/filters.txt
@@ -9,8 +9,10 @@ www.zerohedge.com##.similar-box
# entity = domain minus public suffix
# The following filters were taken out of EasyList and given an entity name,
# which makes them candidates to be injected early in the page.
-# Last updated: 2015-01-27
+# Last updated: 2015-10-15
google.*###center_col > #\5f Emc
+google.*###center_col > #main > .dfrd > .mnr-c > .c._oc._zs
+google.*###center_col > #res > #topstuff + #search > div > #ires > #rso > #flun
google.*###center_col > #resultStats + #tads
google.*###center_col > #resultStats + #tads + #res + #tads
google.*###center_col > #resultStats + div + #res + #tads
@@ -18,14 +20,19 @@ google.*###center_col > #resultStats + div[style="border:1px solid #dedede;margi
google.*###center_col > #taw > #tvcap > .rscontainer
google.*###center_col > div[style="font-size:14px;margin-right:0;min-height:5px"] > div[style="font-size:14px;margin:0 4px;padding:1px 5px;background:#fff8e7"]
google.*###cnt #center_col > #res > #topstuff > .ts
+google.*###cnt #center_col > #taw > #tvcap > .c._oc._Lp
google.*###main_col > #center_col div[style="font-size:14px;margin:0 4px;padding:1px 5px;background:#fff7ed"]
google.*###mbEnd[cellspacing="0"][cellpadding="0"]
google.*###mclip_container:last-child
google.*###mn #center_col > div > h2.spon:first-child
google.*###mn #center_col > div > h2.spon:first-child + ol:last-child
+google.*###mn div[style="position:relative"] > #center_col > ._Ak
+google.*###mn div[style="position:relative"] > #center_col > div > ._dPg
google.*###resultspanel > #topads
google.*###rhs_block > #mbEnd
google.*###rhs_block > .ts[cellspacing="0"][cellpadding="0"][style="padding:0"]
+google.*###rhs_block > ol > .rhsvw > .kp-blk > .xpdopen > ._OKe > ol > ._DJe > .luhb-div
+google.*###rhs_block > script + .c._oc._Ve.rhsvw
google.*###rhswrapper > #rhssection[border="0"][bgcolor="#ffffff"]
google.*###ssmiwdiv[jsdisplay]
google.*###tads + div + .c
@@ -43,6 +50,7 @@ google.*##.GPMV2XEDA2 > .GPMV2XEDP1 > .GPMV2XEDJBB
google.*##.ch[onclick="ga(this,event)"]
google.*##.commercial-unit-desktop-rhs
google.*##.commercial-unit-desktop-top
+google.*##.commercial-unit-mobile-top
google.*##.lads[width="100%"][style="background:#FFF8DD"]
google.*##.mod > ._jH + .rscontainer
google.*##.mw > #rcnt > #center_col > #taw > #tvcap > .c
@@ -117,11 +125,42 @@ speedtest.net###swiffycontainer canvas
# https://github.com/gorhill/uBlock/issues/651
ink361.com##.getridofAdsBlock[href^="javascript:"]
-# washingtonpost.com
-||js.washingtonpost.com/wpost2/js/combo$script,first-party
-www.washingtonpost.com###drawbridge-signup-overlay
-www.washingtonpost.com##.jqmOverlay
-
# https://www.reddit.com/r/techsupport/comments/3kyd0m/sponsored_content_ads_that_only_show_up_with/
explosm.net##.inner-wrap > .row > .columns.large-4.medium-12.small-12
explosm.net##.inner-wrap > .row > .columns.large-8.medium-12 > hr + div:not(.row)
+
+# http://www.extremetech.com/mobile/214358-ad-blocking-for-the-masses-part-two-adblock-browser
+# "anyone else trying it on ET find that one ziffdavis.com ad bypasses ublock0 even though it's in the default lists for block"
+||inspiringsweater.xyz^
+||deservedlimp.xyz^
+
+# https://www.reddit.com/r/facebook/comments/3ld5gn/ive_discovered_that_i_cant_access_facebook/
+# Until all users have latest EasyList
+@@RPNcr3s-aD2.js$script,domain=facebook.com
+
+# https://github.com/gorhill/uBlock/issues/275
+jeu.info###page [class^="banniere-"]
+||jeu.info^$inline-script
+
+# https://github.com/gorhill/uBlock/issues/759
+# Until fixed in EasyPrivacy
+@@||eplayerhtml5.performgroup.com/js/tsEplayerHtml5/js/Eplayer/js/quantcast/quant.js$script,first-party
+@@||eplayerhtml5.performgroup.com/js/tsEplayerHtml5/js/Eplayer/js/quantcast/vquant.js$script,first-party
+
+# http://arstechnica.com/civis/viewtopic.php?f=3&t=1296357
+arstechnica.com###lg-pushdown
+
+# This prevents spurious redirection, but breaks comments
+||js.washingtonpost.com/wpost/js/combo?*/util/util.identity-management-1.1.0.js$script,first-party
+
+# Useful inline script tag filters until filter list maintainers start to use
+# it in their respective filter lists
+arstechnica.com##script:contains(ars.READY.push)
+auto-motor-und-sport.de,boerse-online.de,echo-online.de,fem.com,finanzen.net,focus.de,gamestar.de,kabeleins.de,pcwelt.de,prosieben.de,prosiebenmaxx.de,rp-online.de,sat1.de,sat1gold.de,sixx.de,the-voice-of-germany.de,tvspielfilm.de,wetter.com,wetteronline.de##script:contains(uabInject)
+bild.de##script:contains(/^\s*de.bild.cmsKonfig/)
+bild.de##script:contains(Javascript)
+dayt.se##script:contains(adblock.php)
+dpstream.net,jeux-porno.net##script:contains(/^\s*?var\s+?uniqueParam\s*?=/)
+explosm.net##script:contains(/^__durl=/)
+extremetube.com,gaytube.com,keezmovies.com,mofosex.com,pornhub.com,redtube.com,spankwire.com,thumbzilla.com,tube8.*,xtube.com,youporn.com,youporngay.com##script:contains(FastPopSessionRequestNumber)
+opensubtitles.org##script:contains(defaultCheckForAdBlockExistance)
diff --git a/assets/ublock/unbreak.txt b/assets/ublock/unbreak.txt
index a22a93d..e097651 100644
--- a/assets/ublock/unbreak.txt
+++ b/assets/ublock/unbreak.txt
@@ -114,8 +114,9 @@ ovh.strim.io#@##tweets
# From Chrome store: "Go to site http://www.vd.nl/ ... links from dropdown top menu do not work"
# https://github.com/gorhill/uBlock/issues/349 : "conflicting with reddit's gold popup box"
+# https://github.com/chrisaljoudi/uBlock/issues/1546
# To counter Dan Pollock's, hpHosts, MVPS, Peter Lowe's
-@@||google-analytics.com/analytics.js$domain=reddit.com|www.vd.nl
+@@||google-analytics.com/analytics.js$domain=reddit.com|trello.com|www.vd.nl
# https://github.com/chrisaljoudi/uBlock/issues/570
# To counter `yui.yahooapis.com` in hpHosts
@@ -218,3 +219,22 @@ ovh.strim.io#@##tweets
# https://forums.lanik.us/viewtopic.php?f=64&t=24764
@@/b/ss/*&aqe=$image,domain=aeroplan.com
+
+# https://github.com/gorhill/uBlock/issues/780
+@@||www.google-analytics.com/plugins/ua/linkid.js$script,domain=support.amd.com
+@@||www.google-analytics.com/analytics.js$script,domain=support.amd.com
+@@||www.googletagmanager.com/gtm.js$script,domain=support.amd.com
+
+# https://www.reddit.com/r/uBlockOrigin/comments/3oca9w/how_to_keep_ublock_from_interfering_with_button/
+@@||partner.googleadservices.com/gpt/$script,domain=www.merriam-webster.com
+@@||www.atpworldtour.com/assets/js/util/googleAnalytics.js$script,first-party
+
+# https://twitter.com/bociusz/status/653693746626871296
+# To counter Peter Lowe's `pagefair.com`, but only when on `pagefair.com`
+@@||pagefair.com^$first-party
+
+# Chrome store: "Some of the useful content on website such as pictures and maps on redfin get blocked"
+@@||ssl.cdn-redfin.com/*/redfin/common/analytics/PixelTracking.js$script,domain=redfin.com
+
+# Chrome store: "uBlock Origin seems to think Southwest.com's background and header image are ads"
+@@||www.southwest.com/*/mbox.js$script,first-party
diff --git a/dist/description/description-de.txt b/dist/description/description-de.txt
index 22c3657..2134687 100644
--- a/dist/description/description-de.txt
+++ b/dist/description/description-de.txt
@@ -35,7 +35,7 @@ Ohne die vorgegebenen Filterlisten ist diese Erweiterung nichts. Wenn du also et
***
Kostenlos.
-Open source mit Public License (GPLv3)
+Open-Source-Software unter der General Public License (GPLv3)
Für Benutzer von Benutzern.
Mitwirkende @ Github: https://github.com/gorhill/uBlock/graphs/contributors
@@ -45,5 +45,5 @@ Mitwirkende @ Crowdin: https://crowdin.net/project/ublock
Dies ist eine ziemlich frühe Version - bitte denke daran, wenn du sie bewertest.
-Change log des Projekts:
+Änderungsprotokoll:
https://github.com/gorhill/uBlock/releases
diff --git a/dist/description/description-en.txt b/dist/description/description-en.txt
index 8ce4f1d..9fbac7d 100644
--- a/dist/description/description-en.txt
+++ b/dist/description/description-en.txt
@@ -24,26 +24,28 @@ More lists are available for you to select if you wish:
- Spam404
- And many others
-Of course, the more filters enabled, the higher the memory footprint. Yet, even after adding Fanboy's two extra lists, hpHosts’s Ad and tracking servers, uBlock₀ still has a lower memory footprint than other very popular blockers out there.
+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 Origin still has a lower memory footprint than other very popular blockers out there.
Also, be aware that selecting some of these extra lists may lead to higher likelihood of web site breakage -- especially those lists which are normally used as hosts file.
***
-Without the preset lists of filters, this extension is nothing. So if ever you really do want to contribute something, think about the people working hard to maintain the filter lists you are using, which were made available to use by all for free.
-
-***
-
Free.
Open source with public license (GPLv3)
For users by users.
-Contributors @ Github: https://github.com/gorhill/uBlock/graphs/contributors
-Contributors @ Crowdin: https://crowdin.net/project/ublock
+If ever you really do want to contribute something, think about the people working hard to maintain the filter lists you are using, which were made available to use by all for free.
***
-It's quite an early version, keep this in mind when you review.
+Documentation:
+https://github.com/gorhill/uBlock#ublock-origin
Project change log:
https://github.com/gorhill/uBlock/releases
+
+Contributors @ Github:
+https://github.com/gorhill/uBlock/graphs/contributors
+
+Contributors @ Crowdin:
+https://crowdin.net/project/ublock
diff --git a/dist/description/description-sq.txt b/dist/description/description-sq.txt
index 77c63c6..a60c524 100644
--- a/dist/description/description-sq.txt
+++ b/dist/description/description-sq.txt
@@ -24,13 +24,13 @@ Po të doni, ka edhe shumë lista të tjera të gatshme:
- Spam404
- Dhe shumë të tjera
-Sigurisht që sa më shumë filtra të aktivizoni, aq më i madh do të jetë impakti te memorja. Edhe me shtimin e dy listave shtesë të Fanboy, hpHosts’s Ad and tracking servers, uBlock përsëri ka impakt më të ulët në memorje sesa bllokuesit e tjerë shumë të njohur.
+Sigurisht që sa më shumë filtra të aktivizoni, aq më i madh do të jetë impakti te memorja. Edhe sikur të shtoni dy listat e tjera të Fanboy, hpHosts’s Ad and tracking servers, uBlock përsëri ka impakt më të ulët te memorja sesa bllokuesit e tjerë shumë të njohur.
Por, kujdes, sepse duke përzgjedhur disa prej këtyre listave, gjasat që faqet të shfaqin probleme do të jenë më të mëdha -- sidomos listat që normalisht përdoren si skeda "hosts".
***
-Pa listat e programuara, ky program nuk vlen për asgjë. Prandaj, po të doni të kontribuoni diçka, mendoni pak për njerëzit që punojnë fort për mirëmbajtjen e listave me filtra që po përdorni, të cilat na ofrohen të gjithëve pa pagesë.
+Pa listat e programuara, ky program nuk ka asnjë vlerë. Prandaj, po të doni të kontribuoni diçka, mendoni pak për njerëzit që punojnë fort për mirëmbajtjen e listave me filtra që po përdorni, të cilat na ofrohen të gjithëve pa pagesë.
***
@@ -43,7 +43,7 @@ Kontributorët @ Crowdin: https://crowdin.net/project/ublock
***
-Kur të bëni vlerësimin e programit, mos harroni se ky është një version paraprak.
+Kur bëni vlerësimin e programit, mos harroni se ky është një version paraprak.
Ditari i projektit:
https://github.com/gorhill/uBlock/releases
diff --git a/doc/benchmarks/vim-test-abp-vs-ublock.png b/doc/benchmarks/vim-test-abp-vs-ublock.png
index bfb8886..0d8abc4 100644
--- a/doc/benchmarks/vim-test-abp-vs-ublock.png
+++ b/doc/benchmarks/vim-test-abp-vs-ublock.png
Binary files differ
diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json
index 7c25ce9..81c3148 100644
--- a/platform/chromium/manifest.json
+++ b/platform/chromium/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "uBlock Origin",
- "version": "1.1.1",
+ "version": "1.3.0",
"default_locale": "en",
"description": "__MSG_extShortDesc__",
diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js
index 79d929e..853accb 100644
--- a/platform/chromium/vapi-background.js
+++ b/platform/chromium/vapi-background.js
@@ -1017,7 +1017,7 @@ vAPI.cloud = (function() {
// 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);
- // Mind chrome.storage.sync.QUOTA_BYTES_PER_ITEM (8192 at time of writing)
+ // Mind chrome.storage.sync.QUOTA_BYTES (128 kB at time of writing)
var maxStorageSize = chrome.storage.sync.QUOTA_BYTES;
var options = {
@@ -1029,8 +1029,8 @@ vAPI.cloud = (function() {
// We "poll" at specific index in order to get a rough idea of how
// large is the stored string.
// This allows reading a single item with only 2 sync operations -- a
- // good thing given chrome.storage.syncMAX_WRITE_OPERATIONS_PER_MINUTE
- // and chrome.storage.syncMAX_WRITE_OPERATIONS_PER_HOUR.
+ // good thing given chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE
+ // and chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR.
var getCoarseChunkCount = function(dataKey, callback) {
var bin = {};
@@ -1085,7 +1085,7 @@ vAPI.cloud = (function() {
bin.size = JSON.stringify(bin).length;
var item = JSON.stringify(bin);
- // Chunkify taking into account QUOTA_BYTES_PER_ITEM:
+ // Chunkify taking into account QUOTA_BYTES_PER_ITEM:
// https://developer.chrome.com/extensions/storage#property-sync
// "The maximum size (in bytes) of each individual item in sync
// "storage, as measured by the JSON stringification of its value
diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js
index d1adfc4..c765d98 100644
--- a/platform/firefox/bootstrap.js
+++ b/platform/firefox/bootstrap.js
@@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
-/* global ADDON_UNINSTALL, APP_SHUTDOWN, APP_STARTUP */
+/* global ADDON_UNINSTALL, APP_SHUTDOWN */
/* exported startup, shutdown, install, uninstall */
'use strict';
@@ -48,7 +48,7 @@ const restartListener = {
/******************************************************************************/
-function startup(data, reason) {
+function startup(data/*, reason*/) {
if ( data !== undefined ) {
version = data.version;
}
@@ -86,7 +86,13 @@ function startup(data, reason) {
);
};
- if ( reason !== APP_STARTUP ) {
+ var ready = false;
+ try {
+ ready = appShell.hiddenDOMWindow &&
+ appShell.hiddenDOMWindow.document;
+ } catch (ex) {
+ }
+ if ( ready ) {
onReady();
return;
}
diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js
index a11720e..37de2b5 100644
--- a/platform/firefox/frameModule.js
+++ b/platform/firefox/frameModule.js
@@ -23,6 +23,7 @@
/******************************************************************************/
+// https://github.com/gorhill/uBlock/issues/800
this.EXPORTED_SYMBOLS = ['contentObserver', 'LocationChangeListener'];
const {interfaces: Ci, utils: Cu} = Components;
@@ -30,6 +31,7 @@ const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
const {XPCOMUtils} = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
const hostName = Services.io.newURI(Components.stack.filename, null, null).host;
+const rpcEmitterName = hostName + ':child-process-message';
//Cu.import('resource://gre/modules/devtools/Console.jsm');
@@ -61,7 +63,7 @@ const getMessageManager = function(win) {
/******************************************************************************/
-const contentObserver = {
+var contentObserver = {
classDescription: 'content-policy for ' + hostName,
classID: Components.ID('{7afbd130-cbaf-46c2-b944-f5d24305f484}'),
contractID: '@' + hostName + '/content-policy;1',
@@ -239,9 +241,25 @@ const contentObserver = {
wantXHRConstructor: false
});
+ if ( Services.cpmm ) {
+ sandbox.rpc = function(details) {
+ var svc = Services;
+ if ( svc === undefined ) { return; }
+ var cpmm = svc.cpmm;
+ if ( !cpmm ) { return; }
+ var r = cpmm.sendSyncMessage(rpcEmitterName, details);
+ if ( Array.isArray(r) ) {
+ return r[0];
+ }
+ };
+ } else {
+ sandbox.rpc = function() {};
+ }
+
sandbox.injectScript = function(script) {
- if ( Services !== undefined ) {
- Services.scriptloader.loadSubScript(script, sandbox);
+ var svc = Services;
+ if ( svc !== undefined ) {
+ svc.scriptloader.loadSubScript(script, sandbox);
} else {
// Sandbox appears void.
// I've seen this happens, need to investigate why.
@@ -258,9 +276,10 @@ const contentObserver = {
sandbox.removeMessageListener();
sandbox.addMessageListener =
sandbox.injectScript =
+ sandbox.outerShutdown =
sandbox.removeMessageListener =
- sandbox.sendAsyncMessage =
- sandbox.outerShutdown = function(){};
+ sandbox.rpc =
+ sandbox.sendAsyncMessage = function(){};
sandbox.vAPI = {};
messager = null;
};
@@ -405,20 +424,26 @@ const contentObserver = {
const locationChangedMessageName = hostName + ':locationChanged';
-const LocationChangeListener = function(docShell) {
+var LocationChangeListener = function(docShell) {
if ( !docShell ) {
return;
}
var requestor = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
var ds = requestor.getInterface(Ci.nsIWebProgress);
+ if ( !ds ) {
+ return;
+ }
var mm = requestor.getInterface(Ci.nsIContentFrameMessageManager);
-
- if ( ds && mm && typeof mm.sendAsyncMessage === 'function' ) {
- this.docShell = ds;
- this.messageManager = mm;
- ds.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
+ if ( !mm ) {
+ return;
+ }
+ if ( typeof mm.sendAsyncMessage !== 'function' ) {
+ return;
}
+ this.docShell = ds;
+ this.messageManager = mm;
+ ds.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
};
LocationChangeListener.prototype.QueryInterface = XPCOMUtils.generateQI([
diff --git a/platform/firefox/frameScript.js b/platform/firefox/frameScript.js
index fdee50c..17cd84f 100644
--- a/platform/firefox/frameScript.js
+++ b/platform/firefox/frameScript.js
@@ -19,13 +19,11 @@
Home: https://github.com/gorhill/uBlock
*/
-/* global addMessageListener, removeMessageListener, docShell */
-
/******************************************************************************/
-var locationChangeListener; // Keep alive while frameScript is alive
+// https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Frame_script_environment
-(function() {
+(function(context) {
'use strict';
@@ -52,25 +50,36 @@ let injectContentScripts = function(win) {
};
let onLoadCompleted = function() {
- removeMessageListener('ublock0-load-completed', onLoadCompleted);
- injectContentScripts(content);
+ context.removeMessageListener('ublock0-load-completed', onLoadCompleted);
+ injectContentScripts(context.content);
};
+context.addMessageListener('ublock0-load-completed', onLoadCompleted);
-addMessageListener('ublock0-load-completed', onLoadCompleted);
+let shutdown = function(ev) {
+ if ( ev.target !== context ) {
+ return;
+ }
+ context.removeMessageListener('ublock0-load-completed', onLoadCompleted);
+ context.removeEventListener('unload', shutdown);
+ context.locationChangeListener = null;
+ LocationChangeListener = null;
+ contentObserver = null;
+};
+context.addEventListener('unload', shutdown);
-if ( docShell ) {
+if ( context.docShell ) {
let Ci = Components.interfaces;
- let wp = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebProgress);
+ let wp = context.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebProgress);
let dw = wp.DOMWindow;
if ( dw === dw.top ) {
- locationChangeListener = new LocationChangeListener(docShell);
+ context.locationChangeListener = new LocationChangeListener(context.docShell);
}
}
/******************************************************************************/
-})();
+})(this);
/******************************************************************************/
diff --git a/platform/firefox/install.rdf b/platform/firefox/install.rdf
index 42ec05a..5f5b7ec 100644
--- a/platform/firefox/install.rdf
+++ b/platform/firefox/install.rdf
@@ -9,6 +9,7 @@
<creator>{author}</creator>
<developer>Deathamns</developer>
<developer>Alex Vallat</developer>
+ <developer>Manuel Reimer</developer>
<type>2</type>
<bootstrap>true</bootstrap>
<multiprocessCompatible>true</multiprocessCompatible>
@@ -38,7 +39,7 @@
<r:Description>
<id>{{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}}</id>
<minVersion>2.21</minVersion>
- <maxVersion>2.37</maxVersion>
+ <maxVersion>2.38</maxVersion>
</r:Description>
</targetApplication>
@@ -50,5 +51,23 @@
<maxVersion>26.*</maxVersion>
</r:Description>
</targetApplication>
+
+ <!-- Conkeror -->
+ <targetApplication>
+ <r:Description>
+ <id>{{a79fe89b-6662-4ff4-8e88-09950ad4dfde}}</id>
+ <minVersion>0.1</minVersion>
+ <maxVersion>9.9</maxVersion>
+ </r:Description>
+ </targetApplication>
+
+ <!-- Thunderbird -->
+ <targetApplication>
+ <r:Description>
+ <id>{{3550f703-e582-4d05-9a08-453d09bdfdc6}}</id>
+ <minVersion>38.3.0</minVersion>
+ <maxVersion>45.0</maxVersion>
+ </r:Description>
+ </targetApplication>
</r:Description>
</r:RDF>
diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js
index a74fd23..0a4b98d 100644
--- a/platform/firefox/vapi-background.js
+++ b/platform/firefox/vapi-background.js
@@ -40,6 +40,7 @@ const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
var vAPI = self.vAPI = self.vAPI || {};
vAPI.firefox = true;
vAPI.fennec = Services.appinfo.ID === '{aa3c5121-dab2-40e2-81ca-7ea25febc110}';
+vAPI.thunderbird = Services.appinfo.ID === '{3550f703-e582-4d05-9a08-453d09bdfdc6}';
/******************************************************************************/
@@ -70,7 +71,7 @@ vAPI.localStorage.setDefaultBool('forceLegacyToolbarButton', false);
var cleanupTasks = [];
// This must be updated manually, every time a new task is added/removed
-var expectedNumberOfCleanups = 7;
+var expectedNumberOfCleanups = 8;
window.addEventListener('unload', function() {
if ( typeof vAPI.app.onShutdown === 'function' ) {
@@ -90,10 +91,11 @@ window.addEventListener('unload', function() {
}
// frameModule needs to be cleared too
+ var frameModuleURL = vAPI.getURL('frameModule.js');
var frameModule = {};
- Cu.import(vAPI.getURL('frameModule.js'), frameModule);
+ Cu.import(frameModuleURL, frameModule);
frameModule.contentObserver.unregister();
- Cu.unload(vAPI.getURL('frameModule.js'));
+ Cu.unload(frameModuleURL);
});
/******************************************************************************/
@@ -522,9 +524,23 @@ vAPI.storage = (function() {
/******************************************************************************/
-var getTabBrowser = function(win) {
- return vAPI.fennec && win.BrowserApp || win.gBrowser || null;
-};
+var getTabBrowser = (function() {
+ if ( vAPI.fennec ) {
+ return function(win) {
+ return win.BrowserApp || null;
+ };
+ }
+
+ if ( vAPI.thunderbird ) {
+ return function(win) {
+ return win.document.getElementById('tabmail') || null;
+ };
+ }
+
+ return function(win) {
+ return win.gBrowser || null;
+ };
+})();
/******************************************************************************/
@@ -557,6 +573,16 @@ vAPI.noTabId = '-1';
vAPI.tabs = {};
+
+/******************************************************************************/
+
+vAPI.tabs.mostRecentWindowId = (function() {
+ if ( vAPI.thunderbird ) {
+ return 'mail:3pane';
+ }
+ return 'navigator:browser';
+})();
+
/******************************************************************************/
vAPI.tabs.registerListeners = function() {
@@ -656,7 +682,7 @@ vAPI.tabs.getAll = function(window) {
/******************************************************************************/
vAPI.tabs.getWindows = function() {
- var winumerator = Services.wm.getEnumerator('navigator:browser');
+ var winumerator = Services.wm.getEnumerator(this.mostRecentWindowId);
var windows = [];
while ( winumerator.hasMoreElements() ) {
@@ -728,11 +754,13 @@ vAPI.tabs.open = function(details) {
}
}
- var win = Services.wm.getMostRecentWindow('navigator:browser');
+ var win = Services.wm.getMostRecentWindow(this.mostRecentWindowId);
var tabBrowser = getTabBrowser(win);
if ( vAPI.fennec ) {
- tabBrowser.addTab(details.url, {selected: details.active !== false});
+ tabBrowser.addTab(details.url, {
+ selected: details.active !== false
+ });
// Note that it's impossible to move tabs on Fennec, so don't bother
return;
}
@@ -749,6 +777,15 @@ vAPI.tabs.open = function(details) {
return;
}
+ if ( vAPI.thunderbird ) {
+ tabBrowser.openTab('contentTab', {
+ contentPage: details.url,
+ background: !details.active
+ });
+ // TODO: Should be possible to move tabs on Thunderbird
+ return;
+ }
+
if ( details.index === -1 ) {
details.index = tabBrowser.browsers.indexOf(tabBrowser.selectedBrowser) + 1;
}
@@ -780,13 +817,16 @@ vAPI.tabs.replace = function(tabId, url) {
/******************************************************************************/
-vAPI.tabs._remove = function(tab, tabBrowser) {
- if ( vAPI.fennec ) {
- tabBrowser.closeTab(tab);
- return;
+vAPI.tabs._remove = (function() {
+ if ( vAPI.fennec || vAPI.thunderbird ) {
+ return function(tab, tabBrowser) {
+ tabBrowser.closeTab(tab);
+ };
}
- tabBrowser.removeTab(tab);
-};
+ return function(tab, tabBrowser) {
+ tabBrowser.removeTab(tab);
+ };
+})();
/******************************************************************************/
@@ -875,6 +915,10 @@ var tabWatcher = (function() {
var tabIdGenerator = 1;
var indexFromBrowser = function(browser) {
+ // TODO: Add support for this
+ if ( vAPI.thunderbird ) {
+ return -1;
+ }
var win = getOwnerWindow(browser);
if ( !win ) {
return -1;
@@ -920,22 +964,36 @@ var tabWatcher = (function() {
return tabbrowser.tabs[i];
};
- var browserFromTarget = function(target) {
- if ( !target ) {
- return null;
- }
+ var browserFromTarget = (function() {
if ( vAPI.fennec ) {
- if ( target.browser ) { // target is a tab
- target = target.browser;
- }
- } else if ( target.linkedPanel ) { // target is a tab
- target = target.linkedBrowser;
+ return function(target) {
+ if ( !target ) { return null; }
+ if ( target.browser ) { // target is a tab
+ target = target.browser;
+ }
+ return target.localName === 'browser' ? target : null;
+ };
}
- if ( target.localName !== 'browser' ) {
- return null;
+ if ( vAPI.thunderbird ) {
+ return function(target) {
+ if ( !target ) { return null; }
+ if ( target.mode ) { // target is object with tab info
+ var browserFunc = target.mode.getBrowser || target.mode.tabType.getBrowser;
+ if ( browserFunc ) {
+ return browserFunc.call(target.mode.tabType, target);
+ }
+ }
+ return target.localName === 'browser' ? target : null;
+ };
}
- return target;
- };
+ return function(target) {
+ if ( !target ) { return null; }
+ if ( target.linkedPanel ) { // target is a tab
+ target = target.linkedBrowser;
+ }
+ return target.localName === 'browser' ? target : null;
+ };
+ })();
var tabIdFromTarget = function(target) {
var browser = browserFromTarget(target);
@@ -965,13 +1023,20 @@ var tabWatcher = (function() {
};
var currentBrowser = function() {
- var win = Services.wm.getMostRecentWindow('navigator:browser');
+ var win = Services.wm.getMostRecentWindow(vAPI.tabs.mostRecentWindowId);
// https://github.com/gorhill/uBlock/issues/399
// getTabBrowser() can return null at browser launch time.
var tabBrowser = getTabBrowser(win);
if ( tabBrowser === null ) {
return null;
}
+ if ( vAPI.thunderbird ) {
+ // Directly at startup the first tab may not be initialized
+ if ( tabBrowser.tabInfo.length === 0 ) {
+ return null;
+ }
+ return tabBrowser.getBrowserForSelectedTab() || null;
+ }
return browserFromTarget(tabBrowser.selectedTab);
};
@@ -986,17 +1051,21 @@ var tabWatcher = (function() {
}
};
- // https://developer.mozilla.org/en-US/docs/Web/Events/TabOpen
- var onOpen = function({target}) {
- var tabId = tabIdFromTarget(target);
- var browser = browserFromTabId(tabId);
- vAPI.tabs.onNavigation({
- frameId: 0,
- tabId: tabId,
- url: browser.currentURI.asciiSpec,
- });
+ var removeTarget = function(target) {
+ onClose({ target: target });
};
+ // https://developer.mozilla.org/en-US/docs/Web/Events/TabOpen
+ //var onOpen = function({target}) {
+ // var tabId = tabIdFromTarget(target);
+ // var browser = browserFromTabId(tabId);
+ // vAPI.tabs.onNavigation({
+ // frameId: 0,
+ // tabId: tabId,
+ // url: browser.currentURI.asciiSpec,
+ // });
+ //};
+
// https://developer.mozilla.org/en-US/docs/Web/Events/TabShow
var onShow = function({target}) {
tabIdFromTarget(target);
@@ -1021,29 +1090,29 @@ var tabWatcher = (function() {
return false;
}
+ if ( typeof vAPI.toolbarButton.attachToNewWindow === 'function' ) {
+ vAPI.toolbarButton.attachToNewWindow(window);
+ }
+
var tabContainer;
if ( tabBrowser.deck ) { // Fennec
tabContainer = tabBrowser.deck;
} else if ( tabBrowser.tabContainer ) { // Firefox
tabContainer = tabBrowser.tabContainer;
vAPI.contextMenu.register(window.document);
- } else {
- return true;
- }
-
- if ( typeof vAPI.toolbarButton.attachToNewWindow === 'function' ) {
- vAPI.toolbarButton.attachToNewWindow(window);
}
// https://github.com/gorhill/uBlock/issues/697
// Ignore `TabShow` events: unfortunately the `pending` attribute is
// not set when a tab is opened as a result of session restore -- it is
// set *after* the event is fired in such case.
- //tabContainer.addEventListener('TabOpen', onOpen);
- tabContainer.addEventListener('TabShow', onShow);
- tabContainer.addEventListener('TabClose', onClose);
- // when new window is opened TabSelect doesn't run on the selected tab?
- tabContainer.addEventListener('TabSelect', onSelect);
+ if ( tabContainer ) {
+ //tabContainer.addEventListener('TabOpen', onOpen);
+ tabContainer.addEventListener('TabShow', onShow);
+ tabContainer.addEventListener('TabClose', onClose);
+ // when new window is opened TabSelect doesn't run on the selected tab?
+ tabContainer.addEventListener('TabSelect', onSelect);
+ }
return true;
};
@@ -1092,7 +1161,9 @@ var tabWatcher = (function() {
// To keep in mind: not all windows are tab containers,
// sometimes the window IS the tab.
var tabs;
- if ( tabBrowser.tabs ) {
+ if ( vAPI.thunderbird ) {
+ tabs = tabBrowser.tabInfo;
+ } else if ( tabBrowser.tabs ) {
tabs = tabBrowser.tabs;
} else if ( tabBrowser.localName === 'browser' ) {
tabs = [tabBrowser];
@@ -1101,8 +1172,10 @@ var tabWatcher = (function() {
}
var browser, URI, tabId;
- for ( var tab of tabs ) {
- browser = tabWatcher.browserFromTarget(tab);
+ var tabindex = tabs.length, tab;
+ while ( tabindex-- ) {
+ tab = tabs[tabindex];
+ browser = browserFromTarget(tab);
if ( browser === null ) {
continue;
}
@@ -1111,7 +1184,6 @@ var tabWatcher = (function() {
if ( URI.schemeIs('chrome') && URI.host === location.host ) {
vAPI.tabs._remove(tab, getTabBrowser(this));
}
- browser = browserFromTarget(tab);
tabId = browserToTabIdMap.get(browser);
if ( tabId !== undefined ) {
removeBrowserEntry(tabId, browser);
@@ -1137,14 +1209,19 @@ var tabWatcher = (function() {
// Initialize map with existing active tabs
var start = function() {
- var tabBrowser, tab;
+ var tabBrowser, tabs, tab;
for ( var win of vAPI.tabs.getWindows() ) {
onWindowLoad.call(win);
tabBrowser = getTabBrowser(win);
if ( tabBrowser === null ) {
continue;
}
- for ( tab of tabBrowser.tabs ) {
+ // `tabBrowser.tabs` may not exist (Thunderbird).
+ tabs = tabBrowser.tabs;
+ if ( !tabs ) {
+ continue;
+ }
+ for ( tab of tabs ) {
if ( vAPI.fennec || !tab.hasAttribute('pending') ) {
tabIdFromTarget(tab);
}
@@ -1173,6 +1250,7 @@ var tabWatcher = (function() {
browserFromTarget: browserFromTarget,
currentBrowser: currentBrowser,
indexFromTarget: indexFromTarget,
+ removeTarget: removeTarget,
start: start,
tabFromBrowser: tabFromBrowser,
tabIdFromTarget: tabIdFromTarget
@@ -1185,7 +1263,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
// If badge is undefined, then setIcon was called from the TabSelect event
var win = badge === undefined
? iconStatus
- : Services.wm.getMostRecentWindow('navigator:browser');
+ : Services.wm.getMostRecentWindow(vAPI.tabs.mostRecentWindowId);
var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab);
var tb = vAPI.toolbarButton;
@@ -1208,7 +1286,7 @@ vAPI.messaging = {
return Cc['@mozilla.org/globalmessagemanager;1']
.getService(Ci.nsIMessageListenerManager);
},
- frameScript: vAPI.getURL('frameScript.js'),
+ frameScriptURL: vAPI.getURL('frameScript.js'),
listeners: {},
defaultHandler: null,
NOOPFUNC: function(){},
@@ -1438,7 +1516,7 @@ vAPI.messaging.setup = function(defaultHandler) {
this.onMessage
);
- this.globalMessageManager.loadFrameScript(this.frameScript, true);
+ this.globalMessageManager.loadFrameScript(this.frameScriptURL, true);
cleanupTasks.push(function() {
var gmm = vAPI.messaging.globalMessageManager;
@@ -1452,7 +1530,7 @@ vAPI.messaging.setup = function(defaultHandler) {
})
);
- gmm.removeDelayedFrameScript(vAPI.messaging.frameScript);
+ gmm.removeDelayedFrameScript(vAPI.messaging.frameScriptURL);
gmm.removeMessageListener(
location.host + ':background',
vAPI.messaging.onMessage
@@ -1472,6 +1550,60 @@ vAPI.messaging.broadcast = function(message) {
};
/******************************************************************************/
+/******************************************************************************/
+
+// Synchronous messaging: Firefox allows this. Chromium does not allow this.
+
+// Sometimes there is no way around synchronous messaging, as long as:
+// - the code at the other end execute fast and return quickly.
+// - it's not abused.
+// Original rationale is <https://github.com/gorhill/uBlock/issues/756>.
+// Synchronous messaging is a good solution for this case because:
+// - It's done only *once* per page load. (Keep in mind there is already a
+// sync message sent for each single network request on a page and it's not
+// an issue, because the code executed is trivial, which is the key -- see
+// shouldLoadListener below).
+// - The code at the other end is fast.
+// Though vAPI.rpcReceiver was brought forth because of this one case, I
+// generalized the concept for whatever future need for synchronous messaging
+// which might arise.
+
+// https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Content_frame_message_manager
+
+vAPI.rpcReceiver = (function() {
+ var calls = Object.create(null);
+ var childProcessMessageName = location.host + ':child-process-message';
+
+ var onChildProcessMessage = function(ev) {
+ var msg = ev.data;
+ if ( !msg ) { return; }
+ var fn = calls[msg.fnName];
+ if ( typeof fn === 'function' ) {
+ return fn(msg);
+ }
+ };
+
+ if ( Services.ppmm ) {
+ Services.ppmm.addMessageListener(
+ childProcessMessageName,
+ onChildProcessMessage
+ );
+ }
+
+ cleanupTasks.push(function() {
+ if ( Services.ppmm ) {
+ Services.ppmm.removeMessageListener(
+ childProcessMessageName,
+ onChildProcessMessage
+ );
+ }
+ });
+
+ return calls;
+})();
+
+/******************************************************************************/
+/******************************************************************************/
var httpObserver = {
classDescription: 'net-channel-event-sinks for ' + location.host,
@@ -1865,7 +1997,11 @@ vAPI.net.registerListeners = function() {
var tabId = tabWatcher.tabIdFromTarget(e.target);
var sourceTabId = null;
- // Popup candidate
+ // Popup candidate: this code path is taken only for when a new top
+ // document loads, i.e. only once per document load. TODO: evaluate for
+ // popup filtering in an asynchrous manner -- it's not really required
+ // to evaluate on the spot. Still, there is currently no harm given
+ // this code path is typically taken only once per page load.
if ( details.openerURL ) {
for ( var browser of tabWatcher.browsers() ) {
var URI = browser.currentURI;
@@ -1899,8 +2035,9 @@ vAPI.net.registerListeners = function() {
}
}
- //console.log('shouldLoadListener:', details.url);
-
+ // We are being called synchronously from the content process, so we
+ // must return ASAP. The code below merely record the details of the
+ // request into a ring buffer for later retrieval by the HTTP observer.
var pendingReq = httpObserver.createPendingRequest(details.url);
pendingReq.frameId = details.frameId;
pendingReq.parentFrameId = details.parentFrameId;
@@ -1918,10 +2055,22 @@ vAPI.net.registerListeners = function() {
var locationChangedListener = function(e) {
var browser = e.target;
+ // I have seen this happens (at startup time)
+ if ( !browser.currentURI ) {
+ return;
+ }
+
// https://github.com/gorhill/uBlock/issues/697
// Dismiss event if the associated tab is pending.
var tab = tabWatcher.tabFromBrowser(browser);
if ( !vAPI.fennec && tab && tab.hasAttribute('pending') ) {
+ // https://github.com/gorhill/uBlock/issues/820
+ // Firefox quirk: it happens the `pending` attribute was not
+ // present for certain tabs at startup -- and this can cause
+ // unwanted [browser <--> tab id] associations internally.
+ // Dispose of these if it is found the `pending` attribute is
+ // set.
+ tabWatcher.removeTarget(tab);
return;
}
@@ -2131,9 +2280,12 @@ vAPI.toolbarButton = {
resizeTimer = null;
var body = iframe.contentDocument.body;
panel.parentNode.style.maxWidth = 'none';
+
+ // https://github.com/gorhill/uMatrix/issues/362
+ panel.parentNode.style.opacity = '1';
+
// https://github.com/chrisaljoudi/uBlock/issues/730
// Voodoo programming: this recipe works
-
var clientHeight = body.clientHeight;
iframe.style.height = toPx(clientHeight);
panel.style.height = toPx(clientHeight + panel.boxObject.height - panel.clientHeight);
@@ -2202,7 +2354,8 @@ vAPI.toolbarButton = {
var addLegacyToolbarButton = function(window) {
var document = window.document;
- var toolbox = document.getElementById('navigator-toolbox') || document.getElementById('mail-toolbox');
+ var toolbox = document.getElementById('navigator-toolbox') ||
+ document.getElementById('mail-toolbox');
if ( !toolbox ) {
return;
}
@@ -2766,6 +2919,12 @@ vAPI.contextMenu.register = function(doc) {
}
var contextMenu = doc.getElementById('contentAreaContextMenu');
+
+ // This can happen (Thunderbird).
+ if ( contextMenu === null ) {
+ return;
+ }
+
var menuitem = doc.createElement('menuitem');
menuitem.setAttribute('id', this.menuItemId);
menuitem.setAttribute('label', this.menuLabel);
diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js
index bd6facd..b8715e8 100644
--- a/platform/firefox/vapi-client.js
+++ b/platform/firefox/vapi-client.js
@@ -31,6 +31,13 @@
/******************************************************************************/
+// Not all sandboxes are given an rpc function, so assign a dummy one if it is
+// missing -- this avoids the need for testing before use.
+
+self.rpc = self.rpc || function(){};
+
+/******************************************************************************/
+
var vAPI = self.vAPI = self.vAPI || {};
vAPI.firefox = true;
vAPI.sessionId = String.fromCharCode(Date.now() % 26 + 97) +
@@ -67,6 +74,30 @@ vAPI.shutdown = (function() {
/******************************************************************************/
+(function() {
+ var hostname = location.hostname;
+ if ( !hostname ) {
+ return;
+ }
+ var filters = self.rpc({
+ fnName: 'getScriptTagFilters',
+ url: location.href,
+ hostname: hostname
+ });
+ if ( typeof filters !== 'string' || filters === '' ) {
+ return;
+ }
+ var reFilters = new RegExp(filters);
+ document.addEventListener('beforescriptexecute', function(ev) {
+ if ( reFilters.test(ev.target.textContent) ) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ });
+})();
+
+/******************************************************************************/
+
vAPI.messaging = {
channels: {},
pending: {},
@@ -254,6 +285,12 @@ MessagingChannel.prototype.send = function(message, callback) {
MessagingChannel.prototype.sendTo = function(message, toTabId, toChannel, callback) {
var messaging = vAPI.messaging;
+ if ( !messaging ) {
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ return;
+ }
// Too large a gap between the last request and the last response means
// the main process is no longer reachable: memory leaks and bad
// performance become a risk -- especially for long-lived, dynamic
diff --git a/platform/opera/manifest.json b/platform/opera/manifest.json
index 6ebbd32..b177b61 100644
--- a/platform/opera/manifest.json
+++ b/platform/opera/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "uBlock Origin",
- "version": "1.1.1",
+ "version": "1.3.0",
"default_locale": "en",
"description": "__MSG_extShortDesc__",
diff --git a/src/1p-filters.html b/src/1p-filters.html
index 0c5d01c..37e8161 100644
--- a/src/1p-filters.html
+++ b/src/1p-filters.html
@@ -13,7 +13,7 @@
<div id="cloudWidget" class="hide" data-cloud-entry="myFiltersPane"></div>
-<p data-i18n="1pFormatHint"></p>
+<p><span data-i18n="1pFormatHint"></span> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Static-filter-syntax" target="_blank">&#xf05a;</a></p>
<p>
<button id="userFiltersApply" class="custom important" type="button" disabled="true" data-i18n="1pApplyChanges"></button>&ensp;
<button id="userFiltersRevert" class="custom" type="button" disabled="true" data-i18n="genericRevert"></button>
diff --git a/src/_locales/ar/messages.json b/src/_locales/ar/messages.json
index 1d9dba5..69462dd 100644
--- a/src/_locales/ar/messages.json
+++ b/src/_locales/ar/messages.json
@@ -236,7 +236,7 @@
"description":"English: Parse and enforce Adblock+ element hiding filters."
},
"3pParseAllABPHideFiltersInfo":{
- "message":"هذا الخيار يفعل تحليل وتطبيق فلاتر <a href=\"https:\/\/adblockplus.org\/en\/faq_internal#elemhide\">Adblock Plus-compatible &ldquo;element hiding&rdquo; filters<\/a> هذي الفلاتر مخصصه للتجميل الصفحه. هذي الفلاتر تخفي بعض صناديق او اعلانات المزعجه التي لا يمنعها محرك الفلاتر تفعيل هذي الخاصيه سوف يجعل الاضافه تستعمل الذاكره اكثر",
+ "message":"<p>هذا الخيار يمكّن من تحليل و تفعيل <a href=\"https:\/\/adblockplus.org\/en\/faq_internal#elemhide\"> فلاتر متطابقة مع فلاتر &rdquo;إخفاء العناصر&ldquo; لتطبيق Adblock Plus<\/a>. هذه الفلاتر جماليّة بالأساس، تهدف إلى إخفاء عناصر في صفحة واب، تعتبر إزعاجا بصريّا، قد لا يتمكّن محرّك فلاتر الرّوابط من كشفها و صدّها.<\/p><p>تفعيل هذا الخيار من شأنه زيادة أثر uBlock₀ على الذّاكرة.<\/p>",
"description":"This option enables the parsing and enforcing of Adblock Plus-compatible 'element hiding' filters. 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.\n\nEnabling this feature increases uBlock Origin's memory footprint."
},
"3pListsOfBlockedHostsHeader":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"دون معلمات",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"بايت",
"description":""
},
"dummy":{
diff --git a/src/_locales/ca/messages.json b/src/_locales/ca/messages.json
index 63e247f..fcc01a5 100644
--- a/src/_locales/ca/messages.json
+++ b/src/_locales/ca/messages.json
@@ -104,7 +104,7 @@
"description":""
},
"popup3pPassiveRulePrompt":{
- "message":"css\/images de tercers",
+ "message":"css\/imatges de tercers",
"description":""
},
"popupInlineScriptRulePrompt":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"sense paràmetres",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
diff --git a/src/_locales/cs/messages.json b/src/_locales/cs/messages.json
index e2c4373..ebe1d36 100644
--- a/src/_locales/cs/messages.json
+++ b/src/_locales/cs/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"bez parametrů",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"bajtů",
"description":""
},
"dummy":{
diff --git a/src/_locales/da/messages.json b/src/_locales/da/messages.json
index 268240e..ca72885 100644
--- a/src/_locales/da/messages.json
+++ b/src/_locales/da/messages.json
@@ -104,7 +104,7 @@
"description":""
},
"popup3pPassiveRulePrompt":{
- "message":"3rd-party css\/images",
+ "message":"Tredjeparts css\/billeder",
"description":""
},
"popupInlineScriptRulePrompt":{
@@ -176,7 +176,7 @@
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{
- "message":"Enable cloud storage support",
+ "message":"Aktiver støtte for sky-lagring",
"description":""
},
"settingsAdvancedUserPrompt":{
@@ -192,7 +192,7 @@
"description":"English: "
},
"settingsWebRTCIPAddressHiddenPrompt":{
- "message":"Prevent WebRTC from leaking local IP addresses",
+ "message":"Forhindre WebRTC i at lække lokale IP-adresser",
"description":"English: "
},
"settingsExperimentalPrompt":{
@@ -436,7 +436,7 @@
"description":"Small header to identify the static filtering section"
},
"loggerStaticFilteringSentence":{
- "message":"{{action}} netværksanmodninger af {{type}} {{br}}som matcher følgende URL {{url}} {{br}}som stammer fra {{origin}},{{br}}{{importance}} der er et matchende undtagelses-filter.",
+ "message":"{{action}} netværksanmodninger af {{type}} {{br}}som matcher følgende URL {{url}} {{br}}og som stammer fra {{origin}},{{br}}{{importance}} der er et matchende undtagelses-filter.",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartBlock":{
@@ -572,11 +572,11 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"uden parametre",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
- "message":"Found in:",
+ "message":"Fundet i:",
"description":"English: List of filter list names follows"
},
"docblockedBack":{
@@ -600,11 +600,11 @@
"description":"English: Permanently"
},
"cloudPush":{
- "message":"Export to cloud storage",
+ "message":"Eksporter til skylagring",
"description":"tooltip"
},
"cloudPull":{
- "message":"Import from cloud storage",
+ "message":"Importer fra skylagring",
"description":"tooltip"
},
"cloudNoData":{
@@ -612,11 +612,11 @@
"description":""
},
"cloudDeviceNamePrompt":{
- "message":"This device name:",
+ "message":"Denne enheds navn:",
"description":"used as a prompt for the user to provide a custom device name"
},
"genericSubmit":{
- "message":"Submit",
+ "message":"Indsend",
"description":"for generic 'submit' buttons"
},
"genericRevert":{
diff --git a/src/_locales/de/messages.json b/src/_locales/de/messages.json
index 3b9b0ec..b646607 100644
--- a/src/_locales/de/messages.json
+++ b/src/_locales/de/messages.json
@@ -164,7 +164,7 @@
"description":"English: Hide placeholders of blocked elements"
},
"settingsIconBadgePrompt":{
- "message":"Zeige die Zahl der blockierten Anfragen auf dem Icon",
+ "message":"Zeige die Zahl der blockierten Anfragen auf dem Symbol",
"description":"English: Show the number of blocked requests on the icon"
},
"settingsContextMenuPrompt":{
@@ -196,7 +196,7 @@
"description":"English: "
},
"settingsExperimentalPrompt":{
- "message":"Aktiviere experimentelle Features (<a href='https:\/\/github.com\/gorhill\/uBlock\/wiki\/Experimental-features'>About<\/a>)",
+ "message":"Aktiviere experimentelle Funktionen (<a href='https:\/\/github.com\/gorhill\/uBlock\/wiki\/Experimental-features'>About<\/a>)",
"description":"English: Enable experimental features"
},
"settingsStorageUsed":{
@@ -296,7 +296,7 @@
"description":"a filter list is 'out of date' (possibly needs to be updated)"
},
"3pLastUpdate":{
- "message":"Letztes Update: {{ago}}",
+ "message":"Letzte Aktualisierung: {{ago}}",
"description":"English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'"
},
"1pFormatHint":{
@@ -348,7 +348,7 @@
"description":"Will discard manually-edited content and exit manual-edit mode"
},
"rulesImport":{
- "message":"Aus Datei importieren...",
+ "message":"Aus Datei importieren …",
"description":""
},
"rulesExport":{
@@ -380,7 +380,7 @@
"description":"English: Export"
},
"whitelistExportFilename":{
- "message":"my-ublock-whitelist_{{datetime}}.txt",
+ "message":"meine-ublock-whitelist_{{datetime}}.txt",
"description":"English: my-ublock-whitelist_{{datetime}}.txt"
},
"whitelistApply":{
@@ -492,7 +492,7 @@
"description":"English: Contributors"
},
"aboutBackupDataButton":{
- "message":"Backup in eine Datei...",
+ "message":"Backup in eine Datei",
"description":"English: Backup to file"
},
"aboutBackupFilename":{
@@ -500,11 +500,11 @@
"description":"English: my-ublock-backup_{{datetime}}.txt"
},
"aboutRestoreDataButton":{
- "message":"Aus einer Datei wiederherstellen...",
+ "message":"Aus einer Datei wiederherstellen ...",
"description":"English: Restore from file..."
},
"aboutResetDataButton":{
- "message":"Fange von ganz vorne an...",
+ "message":"Auf Vorgaben zurücksetzen …",
"description":"English: Reset to default settings..."
},
"aboutRestoreDataConfirm":{
diff --git a/src/_locales/el/messages.json b/src/_locales/el/messages.json
index 18f15bb..4af2dc0 100644
--- a/src/_locales/el/messages.json
+++ b/src/_locales/el/messages.json
@@ -104,7 +104,7 @@
"description":""
},
"popup3pPassiveRulePrompt":{
- "message":"3rd-party css\/images",
+ "message":"Css\/Εικόνες τρίτου μέρους",
"description":""
},
"popupInlineScriptRulePrompt":{
@@ -176,7 +176,7 @@
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{
- "message":"Enable cloud storage support",
+ "message":"Ενεργοποίηση υποστήριξης cloud storage",
"description":""
},
"settingsAdvancedUserPrompt":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"χωρίς παραμέτρους",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -600,11 +600,11 @@
"description":"English: Permanently"
},
"cloudPush":{
- "message":"Export to cloud storage",
+ "message":"Εξαγωγή στο cloud storage",
"description":"tooltip"
},
"cloudPull":{
- "message":"Import from cloud storage",
+ "message":"Εισαγωγή από cloud storage",
"description":"tooltip"
},
"cloudNoData":{
@@ -612,11 +612,11 @@
"description":""
},
"cloudDeviceNamePrompt":{
- "message":"This device name:",
+ "message":"Το όνομα της συσκευής:",
"description":"used as a prompt for the user to provide a custom device name"
},
"genericSubmit":{
- "message":"Submit",
+ "message":"Υποβολή",
"description":"for generic 'submit' buttons"
},
"genericRevert":{
diff --git a/src/_locales/fy/messages.json b/src/_locales/fy/messages.json
index 0960c1b..0d14cf8 100644
--- a/src/_locales/fy/messages.json
+++ b/src/_locales/fy/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"sûnder parameters",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
diff --git a/src/_locales/he/messages.json b/src/_locales/he/messages.json
index 303ac8a..960e436 100644
--- a/src/_locales/he/messages.json
+++ b/src/_locales/he/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"ללא פרמטרים",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"ביתים",
"description":""
},
"dummy":{
diff --git a/src/_locales/hi/messages.json b/src/_locales/hi/messages.json
index c9596fb..af38aca 100644
--- a/src/_locales/hi/messages.json
+++ b/src/_locales/hi/messages.json
@@ -620,11 +620,11 @@
"description":"for generic 'submit' buttons"
},
"genericRevert":{
- "message":"Revert",
+ "message":"पलटदे",
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"बाइट्स",
"description":""
},
"dummy":{
diff --git a/src/_locales/hu/messages.json b/src/_locales/hu/messages.json
index 8e40b03..262e7c1 100644
--- a/src/_locales/hu/messages.json
+++ b/src/_locales/hu/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"paraméterek nélkül",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
diff --git a/src/_locales/id/messages.json b/src/_locales/id/messages.json
index bc5bcb3..6acc82e 100644
--- a/src/_locales/id/messages.json
+++ b/src/_locales/id/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"tanpa parameter",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"byte",
"description":""
},
"dummy":{
diff --git a/src/_locales/it/messages.json b/src/_locales/it/messages.json
index a224459..726381c 100644
--- a/src/_locales/it/messages.json
+++ b/src/_locales/it/messages.json
@@ -516,7 +516,7 @@
"description":"Message to display when an error occurred during restore"
},
"aboutResetDataConfirm":{
- "message":"Verranno cancellate tutte le impostazioni, e uBlock sarà riavviato.\n\nResettare uBlock alle impostazioni di fabbrica?",
+ "message":"Verranno cancellate tutte le impostazioni, e uBlock₀ sarà riavviato.\n\nResettare uBlock alle impostazioni di fabbrica?",
"description":"Message asking user to confirm reset"
},
"errorCantConnectTo":{
@@ -564,7 +564,7 @@
"description":"Firefox-specific: appears as 'uBlock₀ (off)'"
},
"docblockedPrompt1":{
- "message":"uBlock ha impedito alla seguente pagina di caricarsi:",
+ "message":"uBlock₀ ha impedito alla seguente pagina di caricarsi:",
"description":"English: uBlock₀ has prevented the following page from loading:"
},
"docblockedPrompt2":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"senza parametri",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"byte",
"description":""
},
"dummy":{
diff --git a/src/_locales/ja/messages.json b/src/_locales/ja/messages.json
index 01dce44..10f5b08 100644
--- a/src/_locales/ja/messages.json
+++ b/src/_locales/ja/messages.json
@@ -192,7 +192,7 @@
"description":"English: "
},
"settingsWebRTCIPAddressHiddenPrompt":{
- "message":"Prevent WebRTC from leaking local IP addresses",
+ "message":"ローカルIPアドレスの漏洩をもたらすWebRTCを防止する",
"description":"English: "
},
"settingsExperimentalPrompt":{
@@ -436,7 +436,7 @@
"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.",
+ "message":"ネットワークリクエストが {{type}} である場合に、{{br}}URLアドレスが {{url}} と一致し、{{br}}{{origin}} を発信源としている場合は {{action}} をします。{{br}}{{importance}} が存在する場合は例外フィルターと一致させます。",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartBlock":{
@@ -452,7 +452,7 @@
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartAnyType":{
- "message":"any type",
+ "message":"任意の種類",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartOrigin":{
@@ -460,19 +460,19 @@
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartAnyOrigin":{
- "message":"from anywhere",
+ "message":"全体",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartNotImportant":{
- "message":"except when",
+ "message":"例外",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartImportant":{
- "message":"even if",
+ "message":"非例外",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringFinderSentence1":{
- "message":"Static filter {{filter}} found in:",
+ "message":"静的フィルター {{filter}} が見つかりました:",
"description":"Below this sentence, the filter lists in which the filter was found"
},
"aboutChangelog":{
@@ -572,11 +572,11 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"パラメーター未指定",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
- "message":"Found in:",
+ "message":"次のフィルターで見つかりました:",
"description":"English: List of filter list names follows"
},
"docblockedBack":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"バイト",
"description":""
},
"dummy":{
diff --git a/src/_locales/ko/messages.json b/src/_locales/ko/messages.json
index 2c16e04..608057f 100644
--- a/src/_locales/ko/messages.json
+++ b/src/_locales/ko/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"매개 변수 없이",
+ "message":"제외 매개 변수",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
diff --git a/src/_locales/nb/messages.json b/src/_locales/nb/messages.json
index 17f0f99..3474bf2 100644
--- a/src/_locales/nb/messages.json
+++ b/src/_locales/nb/messages.json
@@ -292,7 +292,7 @@
"description":"English: new version available"
},
"3pExternalListObsolete":{
- "message":"utdatert",
+ "message":"utgått på dato",
"description":"a filter list is 'out of date' (possibly needs to be updated)"
},
"3pLastUpdate":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"uten parametere",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
diff --git a/src/_locales/ro/messages.json b/src/_locales/ro/messages.json
index d8a937e..3fe1610 100644
--- a/src/_locales/ro/messages.json
+++ b/src/_locales/ro/messages.json
@@ -16,7 +16,7 @@
"description":"appears as tab name in dashboard"
},
"3pPageName":{
- "message":"Filtre externe",
+ "message":"Filtre terțe",
"description":"appears as tab name in dashboard"
},
"1pPageName":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"fără parametri",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"octeți",
"description":""
},
"dummy":{
diff --git a/src/_locales/sk/messages.json b/src/_locales/sk/messages.json
index 381c3c2..e08a2d3 100644
--- a/src/_locales/sk/messages.json
+++ b/src/_locales/sk/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"bez parametrov",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"bajtov",
"description":""
},
"dummy":{
diff --git a/src/_locales/sl/messages.json b/src/_locales/sl/messages.json
index 1fc5a14..c1087f2 100644
--- a/src/_locales/sl/messages.json
+++ b/src/_locales/sl/messages.json
@@ -104,7 +104,7 @@
"description":""
},
"popup3pPassiveRulePrompt":{
- "message":"3rd-party css\/images",
+ "message":"css\/slike tretje osebe",
"description":""
},
"popupInlineScriptRulePrompt":{
@@ -176,7 +176,7 @@
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{
- "message":"Enable cloud storage support",
+ "message":"Omogoči podporo za shrambo v oblako",
"description":""
},
"settingsAdvancedUserPrompt":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"brez parametrov",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -600,11 +600,11 @@
"description":"English: Permanently"
},
"cloudPush":{
- "message":"Export to cloud storage",
+ "message":"Izvozi iz shrambe oblaka",
"description":"tooltip"
},
"cloudPull":{
- "message":"Import from cloud storage",
+ "message":"Uvozi iz shrambe oblaka",
"description":"tooltip"
},
"cloudNoData":{
@@ -612,11 +612,11 @@
"description":""
},
"cloudDeviceNamePrompt":{
- "message":"This device name:",
+ "message":"Ime te naprave:",
"description":"used as a prompt for the user to provide a custom device name"
},
"genericSubmit":{
- "message":"Submit",
+ "message":"Predloži",
"description":"for generic 'submit' buttons"
},
"genericRevert":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"biti",
"description":""
},
"dummy":{
diff --git a/src/_locales/sq/messages.json b/src/_locales/sq/messages.json
index 4f13dd5..e0db1d5 100644
--- a/src/_locales/sq/messages.json
+++ b/src/_locales/sq/messages.json
@@ -40,7 +40,7 @@
"description":"appears as tab name in dashboard"
},
"popupPowerSwitchInfo":{
- "message":"Klikoj: uBlock bëhet joaktiv\/aktiv te ky uebsajti.\n\nCtrl+klikim: uBlock bëhet joaktiv vetëm te kjo faqja.",
+ "message":"Klikoj: uBlock₀ bëhet joaktiv\/aktiv te ky uebsajti.\n\nCtrl+klikim: uBlock₀ bëhet joaktiv vetëm te kjo faqja.",
"description":"English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page."
},
"popupBlockedRequestPrompt":{
@@ -76,7 +76,7 @@
"description":"English: Go to request log"
},
"popupTipNoPopups":{
- "message":"Bllokoj të gjitha dritaret që hap kjo faqja",
+ "message":"Bllokoj të gjitha dritaret automatike që hap kjo faqja",
"description":"English: Toggle the blocking of all popups for this site"
},
"popupTipNoStrictBlocking":{
@@ -160,7 +160,7 @@
"description":"English: Block element"
},
"settingsCollapseBlockedPrompt":{
- "message":"Fsheh simbolin e elementeve të bllokuara",
+ "message":"Fsheh treguesin e elementeve të bllokuara",
"description":"English: Hide placeholders of blocked elements"
},
"settingsIconBadgePrompt":{
@@ -172,11 +172,11 @@
"description":"English: Make use of context menu where appropriate"
},
"settingsColorBlindPrompt":{
- "message":"Ngjyra të qarta për daltonikët",
+ "message":"Ndryshoj ngjyrat për daltonikët",
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{
- "message":"Enable cloud storage support",
+ "message":"Aktivizoj renë informatike",
"description":""
},
"settingsAdvancedUserPrompt":{
@@ -184,7 +184,7 @@
"description":"English: "
},
"settingsPrefetchingDisabledPrompt":{
- "message":"Çaktivizoj hapjen e parakohshme të faqeve (parandalon lidhjen për kërkesat e bllokuara nga rrjeti)",
+ "message":"Çaktivizoj hapjen e parakohshme të faqeve (duke parandaluar lidhjen me kërkesat e bllokuara)",
"description":"English: "
},
"settingsHyperlinkAuditingDisabledPrompt":{
@@ -252,7 +252,7 @@
"description":"English: Ads"
},
"3pGroupPrivacy":{
- "message":"Sfera private",
+ "message":"Privatësia",
"description":"English: Privacy"
},
"3pGroupMalware":{
@@ -276,7 +276,7 @@
"description":"English: Custom"
},
"3pExternalListsHint":{
- "message":"Një URL për rresht. Rreshtat që kanë si parashtesë &lsquo;!&rsquo; nuk do të merren parasysh, sikundër adresat pa kuptim.",
+ "message":"Një URL për rresht. Nuk do të merren parasysh rreshtat që kanë si parashtesë &lsquo;!&rsquo;, sikundër adresat pa kuptim.",
"description":"English: One URL per line. Lines prefixed with &lsquo;!&rsquo; will be ignored. Invalid URLs will be silently ignored."
},
"3pExternalListsApply":{
@@ -348,11 +348,11 @@
"description":"Will discard manually-edited content and exit manual-edit mode"
},
"rulesImport":{
- "message":"Importoj nga një skedë...",
+ "message":"Importoj nga një skedar...",
"description":""
},
"rulesExport":{
- "message":"Ruaj në skedë",
+ "message":"Ruaj në skedar",
"description":""
},
"rulesDefaultFileName":{
@@ -368,7 +368,7 @@
"description":"English: dynamic rule syntax and full documentation."
},
"whitelistPrompt":{
- "message":"Lista juaj me emrat e hosteve për të cilat uBlock do të jetë joaktiv. Një element për rresht. Emrat e pasaktë nuk do të merren parasysh.",
+ "message":"Lista e hosteve për të cilat uBlock₀ do të jetë joaktiv. Një element për rresht. Nuk do të merren parasysh emrat e pasaktë.",
"description":"English: Your list of host names for which uBlock₀ will be disabled. One host name per line. Invalid host names will be silently ignored."
},
"whitelistImport":{
@@ -436,7 +436,7 @@
"description":"Small header to identify the static filtering section"
},
"loggerStaticFilteringSentence":{
- "message":"{{action}} kërkesat nga rrjeti të {{type}} {{br}}adresa e të cilave korrespondon me {{url}} {{br}}dhe kanë si origjinë {{origin}},{{br}}{{importance}} ka një filtër përjashtues të ngjashëm.",
+ "message":"{{action}} kërkesat e {{type}} {{br}}adresa e të cilave korrespondon me {{url}} {{br}}dhe që kanë si origjinë {{origin}},{{br}}{{importance}} ekziston një filtër përjashtues i ngjashëm.",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartBlock":{
@@ -464,7 +464,7 @@
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartNotImportant":{
- "message":"përveç kur",
+ "message":"përveçse kur",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartImportant":{
@@ -492,7 +492,7 @@
"description":"English: Contributors"
},
"aboutBackupDataButton":{
- "message":"Kopjoj në skedë",
+ "message":"Kopjoj në skedar",
"description":"English: Backup to file"
},
"aboutBackupFilename":{
@@ -500,7 +500,7 @@
"description":"English: my-ublock-backup_{{datetime}}.txt"
},
"aboutRestoreDataButton":{
- "message":"Rindërtoj sipas skedës...",
+ "message":"Rindërtoj sipas skedarit...",
"description":"English: Restore from file..."
},
"aboutResetDataButton":{
@@ -508,7 +508,7 @@
"description":"English: Reset to default settings..."
},
"aboutRestoreDataConfirm":{
- "message":"Të gjithë parametrat do të mbishkruhen me të dhënat e kopjuara më {{time}}, dhe uBlock do të rifillojë.\n\nDo i mbishkruani parametrat aktualë?",
+ "message":"Të gjithë parametrat do të mbishkruhen me të dhënat e kopjuara më {{time}}, dhe uBlock₀ do të hapet përsëri.\n\nDo i mbishkruani parametrat aktualë?",
"description":"Message asking user to confirm restore"
},
"aboutRestoreDataError":{
@@ -516,7 +516,7 @@
"description":"Message to display when an error occurred during restore"
},
"aboutResetDataConfirm":{
- "message":"Të gjithë parametrat do të fshihen dhe uBlock do të rifillojë.\n\nDo ktheni parametrat origjinalë?",
+ "message":"Të gjithë parametrat do të fshihen dhe uBlock₀ do të hapet përsëri.\n\nDo ktheni parametrat origjinalë?",
"description":"Message asking user to confirm reset"
},
"errorCantConnectTo":{
@@ -524,7 +524,7 @@
"description":"English: Network error: unable to connect to {{url}}"
},
"subscriberConfirm":{
- "message":"uBlock: Do e shtoni këtë adresën në listën e filtrave tuaj?\n\nTitulli: \"{{title}}\"\nURL: {{url}}",
+ "message":"uBlock₀: Do e shtoni këtë adresën në listën e filtrave tuaj?\n\nTitulli: \"{{title}}\"\nURL: {{url}}",
"description":"English: The message seen by the user to confirm subscription to a ABP filter list"
},
"elapsedOneMinuteAgo":{
@@ -564,7 +564,7 @@
"description":"Firefox-specific: appears as 'uBlock₀ (off)'"
},
"docblockedPrompt1":{
- "message":"uBlock e pengoi hapjen e faqes:",
+ "message":"uBlock Origin e pengoi hapjen e faqes:",
"description":"English: uBlock₀ has prevented the following page from loading:"
},
"docblockedPrompt2":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"pa parametra",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -600,11 +600,11 @@
"description":"English: Permanently"
},
"cloudPush":{
- "message":"Export to cloud storage",
+ "message":"Eksportoj në renë informatike",
"description":"tooltip"
},
"cloudPull":{
- "message":"Import from cloud storage",
+ "message":"Importoj nga reja informatike",
"description":"tooltip"
},
"cloudNoData":{
@@ -612,11 +612,11 @@
"description":""
},
"cloudDeviceNamePrompt":{
- "message":"This device name:",
+ "message":"Emri i aparatit:",
"description":"used as a prompt for the user to provide a custom device name"
},
"genericSubmit":{
- "message":"Submit",
+ "message":"Parashtroj",
"description":"for generic 'submit' buttons"
},
"genericRevert":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"bajt",
"description":""
},
"dummy":{
diff --git a/src/_locales/sv/messages.json b/src/_locales/sv/messages.json
index a19adc7..a0fbb9f 100644
--- a/src/_locales/sv/messages.json
+++ b/src/_locales/sv/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"utan parametrar",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"byte",
"description":""
},
"dummy":{
diff --git a/src/_locales/te/messages.json b/src/_locales/te/messages.json
index 9613669..d85dbc0 100644
--- a/src/_locales/te/messages.json
+++ b/src/_locales/te/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"పారామితులు లేని",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"బైట్లు",
"description":""
},
"dummy":{
diff --git a/src/_locales/uk/messages.json b/src/_locales/uk/messages.json
index a652df6..9e6f2de 100644
--- a/src/_locales/uk/messages.json
+++ b/src/_locales/uk/messages.json
@@ -176,7 +176,7 @@
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{
- "message":"Увімкнути підтримку хмаринкового сховища",
+ "message":"Увімкнути підтримку хмарного сховища",
"description":""
},
"settingsAdvancedUserPrompt":{
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"without parameters",
+ "message":"без параметрів",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
@@ -600,11 +600,11 @@
"description":"English: Permanently"
},
"cloudPush":{
- "message":"Експортувати до хмаринкового сховища",
+ "message":"Експортувати до хмарного сховища",
"description":"tooltip"
},
"cloudPull":{
- "message":"Імпортувати із хмаринкового сховища",
+ "message":"Імпортувати із хмарного сховища",
"description":"tooltip"
},
"cloudNoData":{
@@ -624,7 +624,7 @@
"description":"for generic 'revert' buttons"
},
"genericBytes":{
- "message":"bytes",
+ "message":"байтів",
"description":""
},
"dummy":{
diff --git a/src/_locales/zh_CN/messages.json b/src/_locales/zh_CN/messages.json
index b938a8e..c1701c9 100644
--- a/src/_locales/zh_CN/messages.json
+++ b/src/_locales/zh_CN/messages.json
@@ -572,7 +572,7 @@
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
- "message":"没有参数",
+ "message":"无参数",
"description":"label to be used for the parameter-less URL"
},
"docblockedFoundIn":{
diff --git a/src/background.html b/src/background.html
index 5852880..43863d2 100644
--- a/src/background.html
+++ b/src/background.html
@@ -30,6 +30,7 @@
<script src="js/traffic.js"></script>
<script src="js/contextmenu.js"></script>
<script src="js/reverselookup.js"></script>
+<script src="js/rpcreceiver.js"></script>
<script src="js/start.js"></script>
</body>
</html>
diff --git a/src/css/dashboard-common.css b/src/css/dashboard-common.css
index b291720..b23e0ca 100644
--- a/src/css/dashboard-common.css
+++ b/src/css/dashboard-common.css
@@ -15,6 +15,12 @@ h3 {
a {
text-decoration: none;
}
+a.info {
+ color: #ccc;
+ }
+a.info:hover {
+ color: #444;
+ }
button {
padding: 0.33em;
}
diff --git a/src/css/popup.css b/src/css/popup.css
index 50b0f0e..cef5934 100644
--- a/src/css/popup.css
+++ b/src/css/popup.css
@@ -3,6 +3,7 @@ body {
border: 0;
float: left;
margin: 0;
+ opacity: 1;
overflow: hidden;
padding: 0;
white-space: nowrap;
diff --git a/src/css/settings.css b/src/css/settings.css
index 48ba84a..308334f 100644
--- a/src/css/settings.css
+++ b/src/css/settings.css
@@ -11,12 +11,6 @@ ul#userSettings,
ul#userSettings ul {
list-style-type: none;
}
-ul#userSettings a.info {
- color: #ccc;
- }
-ul#userSettings a.info:hover {
- color: #444;
- }
ul#userSettings .subgroup {
margin-top: 1em;
}
diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js
index a3e81b8..b5fca1b 100644
--- a/src/js/3p-filters.js
+++ b/src/js/3p-filters.js
@@ -371,8 +371,22 @@ var onPurgeClicked = function() {
if ( !href ) {
return;
}
+
messager.send({ what: 'purgeCache', path: href });
button.remove();
+
+ // If the cached version is purged, the installed version must be assumed
+ // to be obsolete.
+ var entry = listDetails.current && listDetails.current[href];
+ if ( entry && entry.off !== true ) {
+ if ( typeof entry.homeURL !== 'string' || entry.homeURL === '' ) {
+ li.descendants('span.status.new').css('display', '');
+ } else {
+ li.descendants('span.status.obsolete').css('display', '');
+ }
+ needUpdate = true;
+ }
+
if ( li.descendants('input').first().prop('checked') ) {
cacheWasPurged = true;
renderWidgets();
diff --git a/src/js/assets.js b/src/js/assets.js
index 8549cd8..08616c5 100644
--- a/src/js/assets.js
+++ b/src/js/assets.js
@@ -61,6 +61,7 @@ var lastRepoMetaTimestamp = 0;
var lastRepoMetaIsRemote = false;
var refreshRepoMetaPeriod = 5 * oneHour;
var errorCantConnectTo = vAPI.i18n('errorCantConnectTo');
+var xhrTimeout = vAPI.localStorage.getItem('xhrTimeout') || 30000;
var exports = {
autoUpdate: true,
@@ -275,8 +276,14 @@ var cachedAssetsManager = (function() {
exports.remove(/./);
};
+ exports.exists = function(path) {
+ return entries !== null && entries.hasOwnProperty(path);
+ };
+
exports.onRemovedListener = null;
+ getEntries(function(){});
+
return exports;
})();
@@ -285,6 +292,10 @@ var cachedAssetsManager = (function() {
var getTextFileFromURL = function(url, onLoad, onError) {
// console.log('µBlock.assets/getTextFileFromURL("%s"):', url);
+ if ( typeof onError !== 'function' ) {
+ onError = onLoad;
+ }
+
// https://github.com/gorhill/uMatrix/issues/15
var onResponseReceived = function() {
this.onload = this.onerror = this.ontimeout = null;
@@ -318,7 +329,7 @@ var getTextFileFromURL = function(url, onLoad, onError) {
var xhr = new XMLHttpRequest();
try {
xhr.open('get', url, true);
- xhr.timeout = 30000;
+ xhr.timeout = xhrTimeout;
xhr.onload = onResponseReceived;
xhr.onerror = onErrorReceived;
xhr.ontimeout = onErrorReceived;
@@ -376,11 +387,16 @@ var getRepoMetadata = function(callback) {
lastRepoMetaTimestamp = Date.now();
lastRepoMetaIsRemote = exports.remoteFetchBarrier === 0;
+ var defaultChecksums;
var localChecksums;
var repoChecksums;
var checksumsReceived = function() {
- if ( localChecksums === undefined || repoChecksums === undefined ) {
+ if (
+ defaultChecksums === undefined ||
+ localChecksums === undefined ||
+ repoChecksums === undefined
+ ) {
return;
}
// Remove from cache assets which no longer exist in the repo
@@ -392,6 +408,17 @@ var getRepoMetadata = function(callback) {
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,
@@ -450,41 +477,64 @@ var getRepoMetadata = function(callback) {
return out.join('\n');
};
- var parseChecksums = function(text, which) {
- var entries = repoMetadata.entries;
+ var parseChecksums = function(text, eachFn) {
var lines = text.split(/\n+/);
var i = lines.length;
- var fields, assetPath;
+ var fields;
while ( i-- ) {
fields = lines[i].trim().split(/\s+/);
if ( fields.length !== 2 ) {
continue;
}
- assetPath = fields[1];
- if ( entries[assetPath] === undefined ) {
- entries[assetPath] = new AssetEntry();
- }
- entries[assetPath][which + 'Checksum'] = fields[0];
+ eachFn(fields[1], fields[0]);
}
};
var onLocalChecksumsLoaded = function(details) {
+ var entries = repoMetadata.entries;
+ var processChecksum = function(path, checksum) {
+ if ( entries.hasOwnProperty(path) === false ) {
+ entries[path] = new AssetEntry();
+ }
+ entries[path].localChecksum = checksum;
+ };
if ( (localChecksums = validateChecksums(details)) ) {
- parseChecksums(localChecksums, 'local');
+ 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, 'repo');
+ parseChecksums(repoChecksums, processChecksum);
}
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);
};
@@ -1166,8 +1216,14 @@ exports.purge = function(pattern, before) {
cachedAssetsManager.remove(pattern, before);
};
+exports.purgeCacheableAsset = function(pattern, before) {
+ cachedAssetsManager.remove(pattern, before);
+ lastRepoMetaTimestamp = 0;
+};
+
exports.purgeAll = function(callback) {
cachedAssetsManager.removeAll(callback);
+ lastRepoMetaTimestamp = 0;
};
/******************************************************************************/
diff --git a/src/js/background.js b/src/js/background.js
index 1aa5d54..8ff3b2b 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -57,7 +57,7 @@ return {
collapseBlocked: true,
colorBlindFriendly: false,
contextMenuEnabled: true,
- dynamicFilteringEnabled: false,
+ dynamicFilteringEnabled: true,
experimentalEnabled: false,
externalLists: defaultExternalLists,
firewallPaneMinimized: true,
diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js
index 776862f..f2986b8 100644
--- a/src/js/contentscript-end.js
+++ b/src/js/contentscript-end.js
@@ -478,7 +478,7 @@ var uBlockCollapser = (function() {
// https://www.chromestatus.com/features/4668884095336448
// "Multiple shadow roots is being deprecated."
if ( shadow !== null ) {
- if ( shadow.className !== sessionId ) {
+ if ( shadow.className !== sessionId ) {
elem.style.setProperty('display', 'none', 'important');
}
continue;
@@ -486,9 +486,13 @@ var uBlockCollapser = (function() {
// https://github.com/gorhill/uBlock/pull/555
// Not all nodes can be shadowed:
// https://github.com/w3c/webcomponents/issues/102
+ // https://github.com/gorhill/uBlock/issues/762
+ // Remove display style that might get in the way of the shadow
+ // node doing its magic.
try {
shadow = elem.createShadowRoot();
shadow.className = sessionId;
+ elem.style.removeProperty('display');
} catch (ex) {
elem.style.setProperty('display', 'none', 'important');
}
@@ -921,11 +925,15 @@ var uBlockCollapser = (function() {
return;
}
var onMouseClick = function(ev) {
+ var elem = ev.target;
+ while ( elem !== null && elem.localName !== 'a' ) {
+ elem = elem.parentElement;
+ }
messager.send({
what: 'mouseClick',
x: ev.clientX,
y: ev.clientY,
- url: ev.target && ev.target.localName === 'a' ? ev.target.href : ''
+ url: elem !== null ? elem.href : ''
});
};
diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js
index ac7d924..a625085 100644
--- a/src/js/contentscript-start.js
+++ b/src/js/contentscript-start.js
@@ -195,9 +195,13 @@ var hideElements = function(selectors) {
// https://github.com/gorhill/uBlock/pull/555
// Not all nodes can be shadowed:
// https://github.com/w3c/webcomponents/issues/102
+ // https://github.com/gorhill/uBlock/issues/762
+ // Remove display style that might get in the way of the shadow
+ // node doing its magic.
try {
shadow = elem.createShadowRoot();
shadow.className = sessionId;
+ elem.style.removeProperty('display');
} catch (ex) {
elem.style.setProperty('display', 'none', 'important');
}
diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js
index 9a5d23f..3ef45d4 100644
--- a/src/js/cosmetic-filtering.js
+++ b/src/js/cosmetic-filtering.js
@@ -230,20 +230,19 @@ FilterEntity.fromSelfie = function(s) {
/******************************************************************************/
var FilterParser = function() {
- this.prefix = '';
- this.suffix = '';
+ this.prefix = this.suffix = this.style = '';
this.unhide = 0;
this.hostnames = [];
this.invalid = false;
this.cosmetic = true;
- this.reParser = /^\s*([^#]*)(##|#@#)(.+)\s*$/;
+ this.reParser = /^([^#]*?)(##|#@#)(.+)$/;
+ this.reScriptContains = /^script:contains\(.+?\)$/;
};
/******************************************************************************/
FilterParser.prototype.reset = function() {
- this.prefix = '';
- this.suffix = '';
+ this.prefix = this.suffix = this.style = '';
this.unhide = 0;
this.hostnames.length = 0;
this.invalid = false;
@@ -262,10 +261,20 @@ FilterParser.prototype.parse = function(s) {
this.cosmetic = false;
return this;
}
-
- // Remember original string
- this.prefix = matches[1];
- this.suffix = matches[3];
+ this.prefix = matches[1].trim();
+ this.unhide = matches[2].charAt(1) === '@' ? 1 : 0;
+ this.suffix = matches[3].trim();
+
+ // Cosmetic filters with explicit style properties can apply only:
+ // - to specific cosmetic filters (those which apply to a specific site)
+ // - to block cosmetic filters (not exception cosmetic filters)
+ if ( this.suffix.slice(-1) === '}' ) {
+ // Not supported for now: this code will ensure some backward
+ // compatibility for when cosmetic filters with explicit style
+ // properties start to be in use.
+ this.invalid = true;
+ return this;
+ }
// 2014-05-23:
// https://github.com/gorhill/httpswitchboard/issues/260
@@ -283,10 +292,31 @@ FilterParser.prototype.parse = function(s) {
this.suffix = this.suffix.slice(1);
}
- this.unhide = matches[2].charAt(1) === '@' ? 1 : 0;
if ( this.prefix !== '' ) {
this.hostnames = this.prefix.split(/\s*,\s*/);
}
+
+ // Script tag filters: pre-process them so that can be used with minimal
+ // overhead in the content script.
+ // Examples:
+ // focus.de##script:contains(/uabInject/)
+ // focus.de##script:contains(uabInject)
+ if ( this.suffix.charAt(0) === 's' && this.reScriptContains.test(this.suffix) ) {
+ // Currently supported only as non-generic selector. Also, exception
+ // script tag filter makes no sense, ignore.
+ if ( this.hostnames.length === 0 || this.unhide === 1 ) {
+ this.invalid = true;
+ return this;
+ }
+ var suffix = this.suffix;
+ this.suffix = 'script//:';
+ if ( suffix.charAt(16) !== '/' || suffix.slice(-2) !== '/)' ) {
+ this.suffix += suffix.slice(16, -1).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ } else {
+ this.suffix += suffix.slice(17, -2).replace(/\\/g, '\\');
+ }
+ }
+
return this;
};
@@ -554,6 +584,8 @@ FilterContainer.prototype.reset = function() {
// hostname, entity-based filters
this.hostnameFilters = {};
this.entityFilters = {};
+ this.scriptTagFilters = {};
+ this.scriptTagFilterCount = 0;
};
/******************************************************************************/
@@ -576,11 +608,14 @@ FilterContainer.prototype.isValidSelector = (function() {
try {
// https://github.com/gorhill/uBlock/issues/693
div.matches(s + ',\n#foo');
+ return true;
} catch (e) {
- console.error('uBlock> invalid cosmetic filter:', s);
- return false;
}
- return true;
+ if ( s.lastIndexOf('script//:', 0) === 0 ) {
+ return true;
+ }
+ console.error('uBlock> invalid cosmetic filter:', s);
+ return false;
};
})();
@@ -790,6 +825,11 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) {
// h ir twitter.com .promoted-tweet
if ( fields[0] === 'h' ) {
+ // Special filter: script tags. Not a real CSS selector.
+ if ( fields[3].lastIndexOf('script//:', 0) === 0 ) {
+ this.createScriptTagFilter(fields[2], fields[3].slice(9));
+ continue;
+ }
filter = new FilterHostname(fields[3], fields[2]);
bucket = this.hostnameFilters[fields[1]];
if ( bucket === undefined ) {
@@ -821,6 +861,11 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) {
// entity selector
if ( fields[0] === 'e' ) {
+ // Special filter: script tags. Not a real CSS selector.
+ if ( fields[2].lastIndexOf('script//:', 0) === 0 ) {
+ this.createScriptTagFilter(fields[1], fields[2].slice(9));
+ continue;
+ }
bucket = this.entityFilters[fields[1]];
if ( bucket === undefined ) {
this.entityFilters[fields[1]] = [fields[2]];
@@ -886,6 +931,49 @@ FilterContainer.prototype.skipCompiledContent = function(text, lineBeg) {
/******************************************************************************/
+FilterContainer.prototype.createScriptTagFilter = function(hostname, s) {
+ if ( this.scriptTagFilters.hasOwnProperty(hostname) ) {
+ this.scriptTagFilters[hostname] += '|' + s;
+ } else {
+ this.scriptTagFilters[hostname] = s;
+ }
+ this.scriptTagFilterCount += 1;
+};
+
+/******************************************************************************/
+
+FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) {
+ if ( this.scriptTagFilterCount === 0 ) {
+ return;
+ }
+ var out = [], hn = hostname, pos;
+ for (;;) {
+ if ( this.scriptTagFilters.hasOwnProperty(hn) ) {
+ out.push(this.scriptTagFilters[hn]);
+ }
+ if ( hn === domain ) {
+ break;
+ }
+ pos = hn.indexOf('.');
+ if ( pos === -1 ) {
+ break;
+ }
+ hn = hn.slice(pos + 1);
+ }
+ pos = domain.indexOf('.');
+ if ( pos !== -1 ) {
+ hn = domain.slice(0, pos);
+ if ( this.scriptTagFilters.hasOwnProperty(hn) ) {
+ out.push(this.scriptTagFilters[hn]);
+ }
+ }
+ if ( out.length !== 0 ) {
+ return out.join('|');
+ }
+};
+
+/******************************************************************************/
+
FilterContainer.prototype.freeze = function() {
this.duplicateBuster = {};
@@ -939,7 +1027,9 @@ FilterContainer.prototype.toSelfie = function() {
highMediumGenericHideCount: this.highMediumGenericHideCount,
highHighGenericHide: this.highHighGenericHide,
highHighGenericHideCount: this.highHighGenericHideCount,
- genericDonthide: this.genericDonthide
+ genericDonthide: this.genericDonthide,
+ scriptTagFilters: this.scriptTagFilters,
+ scriptTagFilterCount: this.scriptTagFilterCount
};
};
@@ -1000,6 +1090,8 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
this.highHighGenericHide = selfie.highHighGenericHide;
this.highHighGenericHideCount = selfie.highHighGenericHideCount;
this.genericDonthide = selfie.genericDonthide;
+ this.scriptTagFilters = selfie.scriptTagFilters;
+ this.scriptTagFilterCount = selfie.scriptTagFilterCount;
this.frozen = true;
};
diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js
index 5b1c469..c64954a 100644
--- a/src/js/document-blocked.js
+++ b/src/js/document-blocked.js
@@ -193,12 +193,9 @@ uDom.nodeFromId('why').textContent = details.fs;
};
var renderParams = function(parentNode, rawURL) {
- var url = null;
- try {
- url = new URL(rawURL);
- } catch(ex) {
- }
- if ( url === null || url.search.length === 0 ) {
+ var a = document.createElement('a');
+ a.href = rawURL;
+ if ( a.search.length === 0 ) {
return false;
}
@@ -209,7 +206,7 @@ uDom.nodeFromId('why').textContent = details.fs;
);
parentNode.appendChild(li);
- var params = url.search.slice(1).split('&');
+ var params = a.search.slice(1).split('&');
var param, name, value, ul;
for ( var i = 0; i < params.length; i++ ) {
param = params[i];
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 2841fef..6076f8d 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -815,7 +815,7 @@ var onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'purgeCache':
- µb.assets.purge(request.path);
+ µb.assets.purgeCacheableAsset(request.path);
break;
default:
diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js
index b82f9ea..946001c 100644
--- a/src/js/reverselookup-worker.js
+++ b/src/js/reverselookup-worker.js
@@ -31,25 +31,35 @@ var listEntries = Object.create(null);
// Helpers
-var rescape = function(s) {
+var reEscape = function(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
+var reSpecialChars = /[\*\^\t\v\n]/;
+
/******************************************************************************/
var fromNetFilter = function(details) {
var lists = [];
- var entry, pos;
+ var compiledFilter = details.compiledFilter;
+ var entry, content, pos, c;
for ( var path in listEntries ) {
entry = listEntries[path];
if ( entry === undefined ) {
continue;
}
- pos = entry.content.indexOf(details.compiledFilter);
+ content = entry.content;
+ pos = content.indexOf(compiledFilter);
if ( pos === -1 ) {
continue;
}
+ // https://github.com/gorhill/uBlock/issues/835
+ // We need an exact match.
+ c = content.charAt(pos + compiledFilter.length);
+ if ( c !== '' && reSpecialChars.test(c) === false ) {
+ continue;
+ }
lists.push({
title: entry.title,
supportURL: entry.supportURL
@@ -103,16 +113,16 @@ var fromCosmeticFilter = function(details) {
var matches = rePlainSelector.exec(filter);
if ( matches ) {
if ( matches[0] === filter ) { // simple CSS selector
- reStr = rescape('c\vlg\v' + filter);
+ reStr = reEscape('c\vlg\v' + filter);
} else { // complex CSS selector
- reStr = rescape('c\vlg+\v' + matches[0] + '\v' + filter);
+ reStr = reEscape('c\vlg+\v' + matches[0] + '\v' + filter);
}
} else if ( reHighLow.test(filter) ) { // [alt] or [title]
- reStr = rescape('c\vhlg0\v' + filter);
+ reStr = reEscape('c\vhlg0\v' + filter);
} else if ( reHighMedium.test(filter) ) { // [href^="..."]
- reStr = rescape('c\vhmg0\v') + '[a-z.-]+' + rescape('\v') + '[a-z]*' + rescape(filter);
+ reStr = reEscape('c\vhmg0\v') + '[a-z.-]+' + reEscape('\v') + '[a-z]*' + reEscape(filter);
} else { // all else
- reStr = rescape('c\vhhg0\v' + filter);
+ reStr = reEscape('c\vhhg0\v' + filter);
}
candidates[details.rawFilter] = new RegExp(reStr + '(?:\\n|$)');
@@ -125,9 +135,9 @@ var fromCosmeticFilter = function(details) {
if ( hostname !== '' ) {
for ( ;; ) {
candidates[hostname + '##' + filter] = new RegExp(
- rescape('c\vh\v') +
+ reEscape('c\vh\v') +
'\\w+' +
- rescape('\v' + hostname + '\v' + filter) +
+ reEscape('\v' + hostname + '\v' + filter) +
'(?:\\n|$)'
);
// If there is no valid domain, there won't be any other
@@ -152,7 +162,7 @@ var fromCosmeticFilter = function(details) {
if ( pos !== -1 ) {
var entity = domain.slice(0, pos);
candidates[entity + '.*##' + filter] = new RegExp(
- rescape('c\ve\v' + entity + '\v' + filter) +
+ reEscape('c\ve\v' + entity + '\v' + filter) +
'(?:\\n|$)'
);
}
diff --git a/src/js/rpcreceiver.js b/src/js/rpcreceiver.js
new file mode 100644
index 0000000..995fd76
--- /dev/null
+++ b/src/js/rpcreceiver.js
@@ -0,0 +1,53 @@
+/*******************************************************************************
+
+ uBlock Origin - a browser extension to block requests.
+ Copyright (C) 2015 Raymond Hill
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://github.com/gorhill/uBlock
+*/
+
+/* global vAPI, µBlock */
+
+/******************************************************************************/
+
+(function() {
+
+'use strict';
+
+/******************************************************************************/
+
+if ( typeof vAPI.rpcReceiver !== 'object' ) {
+ return;
+}
+
+/******************************************************************************/
+
+vAPI.rpcReceiver.getScriptTagFilters = function(details) {
+ var µb = µBlock;
+ var cfe = µb.cosmeticFilteringEngine;
+ if ( !cfe ) { return; }
+ var hostname = details.hostname;
+ return cfe.retrieveScriptTagRegex(
+ µb.URI.domainFromHostname(hostname) || hostname,
+ hostname
+ );
+};
+
+/******************************************************************************/
+
+})();
+
+/******************************************************************************/
diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js
index 92cc7af..6bbf13a 100644
--- a/src/js/static-net-filtering.js
+++ b/src/js/static-net-filtering.js
@@ -1454,7 +1454,10 @@ FilterParser.prototype.parseOptions = function(s) {
this.parseOptParty(false, not);
continue;
}
- if ( opt === 'elemhide' ) {
+ // https://issues.adblockplus.org/ticket/616
+ // `generichide` concept already supported, just a matter of
+ // adding support for the new keyword.
+ if ( opt === 'elemhide' || opt === 'generichide' ) {
if ( this.action === AllowAction ) {
this.parseOptType('elemhide', false);
this.action = BlockAction;
diff --git a/src/popup.html b/src/popup.html
index f3c8c70..62f0b07 100644
--- a/src/popup.html
+++ b/src/popup.html
@@ -10,7 +10,7 @@
</head>
<body>
-<a href="dashboard.html" target="_blank" id="gotoPrefs" title="popupTipDashboard"><span id="appname"> </span> <span id="version"> </span></a>
+<a href="dashboard.html" target="_blank" id="gotoPrefs" data-i18n-tip="popupTipDashboard" data-tip-position="under"><span id="appname"> </span> <span id="version"> </span></a>
<div id="panes">
<div>
<p id="switch" data-i18n-tip="popupPowerSwitchInfo" data-tip-position="under"><span class="fa">&#xf011;</span></p>
diff --git a/src/settings.html b/src/settings.html
index 25bd7d6..3c35353 100644
--- a/src/settings.html
+++ b/src/settings.html
@@ -15,12 +15,12 @@
<li><input id="icon-badge" type="checkbox"><label data-i18n="settingsIconBadgePrompt" for="icon-badge"></label>
<li><input id="context-menu-enabled" type="checkbox"><label data-i18n="settingsContextMenuPrompt" for="context-menu-enabled"></label>
<li><input id="color-blind-friendly" type="checkbox"><label data-i18n="settingsColorBlindPrompt" for="color-blind-friendly"></label>
- <li><input id="cloud-storage-enabled" type="checkbox"><label data-i18n="settingsCloudStorageEnabledPrompt" for="cloud-storage-enabled"></label>
+ <li><input id="cloud-storage-enabled" type="checkbox"><label data-i18n="settingsCloudStorageEnabledPrompt" for="cloud-storage-enabled"></label> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Cloud-storage" target="_blank">&#xf05a;</a>
<li><input id="advanced-user-enabled" type="checkbox"><label data-i18n="settingsAdvancedUserPrompt" for="advanced-user-enabled"></label>
<li class="subgroup"><span data-i18n="3pGroupPrivacy"></span><ul>
<li><input id="prefetching-disabled" type="checkbox"><label data-i18n="settingsPrefetchingDisabledPrompt" for="prefetching-disabled"></label> <a class="fa info" href="https://wikipedia.org/wiki/Link_prefetching#Issues_and_criticisms" target="_blank">&#xf05a;</a>
<li><input id="hyperlink-auditing-disabled" type="checkbox"><label data-i18n="settingsHyperlinkAuditingDisabledPrompt" for="hyperlink-auditing-disabled"></label> <a class="fa info" href="http://www.wilderssecurity.com/threads/hyperlink-auditing-aka-a-ping-and-beacon-aka-navigator-sendbeacon.364904/" target="_blank">&#xf05a;</a>
- <li><input id="webrtc-ipaddress-hidden" type="checkbox"><label data-i18n="settingsWebRTCIPAddressHiddenPrompt" for="webrtc-ipaddress-hidden"></label> <a class="fa info" href="https://www.w3.org/wiki/Privacy/IPAddresses" target="_blank">&#xf05a;</a>
+ <li><input id="webrtc-ipaddress-hidden" type="checkbox"><label data-i18n="settingsWebRTCIPAddressHiddenPrompt" for="webrtc-ipaddress-hidden"></label> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Prevent-WebRTC-from-leaking-local-IP-address" target="_blank">&#xf05a;</a>
</ul>
</ul>