Compare commits

..

No commits in common. 'master' and '0.9.3.6' have entirely different histories.

@ -1,29 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Browser (please complete the following information):**
- OS: [e.g. MacOS, Linux]
- Browser [e.g. Firefox]
- Version [e.g. 80.0.1]
**Additional context**
Add any other context about the problem here.

@ -1,17 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

2
.gitignore vendored

@ -1 +1 @@
dist/build/
dist/

@ -1,20 +1,12 @@
{
"browser": true,
"devel": true,
"eqeqeq": true,
"esversion": 8,
"esnext": true,
"globalstrict": true,
"globals": {
"browser": false,
"self": false,
"chrome": false,
"log": false,
"webext": false,
"vAPI": false,
"µMatrix": false
},
"laxbreak": true,
"strict": "global",
"undef": true,
"unused": true,
"validthis": true
"chrome": false,
"safari": false,
"Components": false // global variable in Firefox
}
}

@ -3,13 +3,12 @@ env:
matrix:
- BROWSER=chromium EXT=zip
- BROWSER=firefox EXT=xpi
script: "./tools/make-${BROWSER}.sh ${TRAVIS_TAG}"
script: ./tools/make-${BROWSER}.sh all
deploy:
provider: releases
prerelease: true
api_key:
secure: eQgPAHH6PKu2dLK+NafxwLl66t0cyW5x5NZFquOwsNMal5nsfof7lyXj2F0Q0vUpGeI21MOipBI8UGv5oXPoiXnr0fhEbEBz65C9vypK61WkDCQVGVeZVNGQwSXUm6gD2EzpPgTCIs52+7dKCDJ3stXzdimOiOTYs4WMNKKarFM=
file: dist/build/uMatrix_${TRAVIS_TAG}.${BROWSER}.${EXT}
file: dist/build/uMatrix.${BROWSER}.${EXT}
skip_cleanup: true
on:
repo: gorhill/uMatrix

@ -1,4 +1,24 @@
### Submitting issues
Submit on <https://github.com/geekprojects/nuTensor/issues>.
**The issue tracker is for provable issues only:** You will have to make the case that the issue is really with uMatrix and not something else on your side. To make a case means to provide detailed steps so that anybody can reproduce the issue. Be sure to rule out that the issue is not caused by something specific on your side.
For **support/discussions**, there is [Mozilla Discourse](https://discourse.mozilla-community.org/t/support-umatrix/5131).
Issues opened which are not actual issues with the code will be closed as _invalid_ without further comment.
### Important
1. When you file an issue, your **responsibility** is to provide **ALL** the exact steps needed for me to reproduce an issue.
1. Ideally, never should I have to _guess_ how to reproduce an issue.
- Hence this is why very detailed steps must be very carefully written down **the first time** the issue is filed.
- Never assume an important step is "obvious".
1. Every single step, in order, must be provided, with **ALL** relevant details.
1. Screenshots are nice, but use common sense: I can't cut and paste important text information from screenshots.
- Regarding screenshots: common sense. Too much of a thing can easily end up as noise.
1. Open source quality software comes from contributors carefully **crafting** code: conversely, issues must also be carefully **crafted**.
- In other words: you benefit from the carefully crafted code, return the favor by **carefully** crafting issues/bug reports.
1. If your mindset is that your time is more precious than that of my time, refrain from filing issues.
### DO NOT
- Add noise to issues by adding `:+1:` and other such pointless comments which add no substance.

@ -1,23 +1,36 @@
## nuTensor
## uMatrix<br>[<img src="https://travis-ci.org/gorhill/uMatrix.svg?branch=master" height="16">](https://travis-ci.org/gorhill/uMatrix)
Definitely for advanced users.
This is a fork of the now-discontinued [uMatrix](https://github.com/gorhill/uMatrix).
My focus will be on keeping it working on Firefox for now. I'll do my best to keep it working, but sadly no guarantees. Help will always be welcome!
Keep Github issues for bugs. User support is [Mozilla's add-on-support](https://discourse.mozilla-community.org/t/support-umatrix/5131).
***
Forked and refactored from [HTTP Switchboard](https://github.com/gorhill/httpswitchboard).
Forked and refactored from [uMatrix](https://github.com/gorhill/uMatrix).
Install [manually](https://github.com/gorhill/uMatrix/blob/master/doc/README.md) the [latest release](https://github.com/gorhill/uMatrix/releases), or install from:
- [Firefox AMO](https://addons.mozilla.org/firefox/addon/umatrix/)
- [Chrome store](https://chrome.google.com/webstore/detail/µmatrix/ogfcmafjalglgifnmanfmnieipoejdcf)
- [Opera store](https://addons.opera.com/en-gb/extensions/details/umatrix/)
You may contribute with translation work:
- For in-app strings, on Crowdin: [uMatrix on Crowdin](https://crowdin.com/project/umatrix).
- For [description](https://github.com/gorhill/uMatrix/tree/master/doc/description) (to be used in AMO, Chrome store, etc.), submit a pull request. [Reference description is here](https://github.com/gorhill/uMatrix/blob/master/doc/description/description.txt) (feel free to improve as you wish, I am not a writer).
[HTTP Switchboard's documentation](https://github.com/gorhill/httpswitchboard/wiki) is still relevant, except for [uMatrix's differences with HTTP Switchboard](https://github.com/gorhill/uMatrix/wiki/Changes-from-HTTP-Switchboard).
You may contribute with documentation: [uMatrix's wiki](https://github.com/gorhill/uMatrix/wiki).
## Warnings
#### Regarding broken sites
nuTensor does not guarantee that sites will work fine: it is for advanced users who can figure how to un-break sites, because essentially nuTensor is a firewall which works in relaxed block-all/allow-exceptionally mode out of the box: it is not unexpected that sites will break.
uMatrix does not guarantee that sites will work fine: it is for advanced users who can figure how to un-break sites, because essentially uMatrix is a firewall which works in relaxed block-all/allow-exceptionally mode out of the box: it is not unexpected that sites will break.
**So this means do not file issues to report broken sites when the sites are broken because uMatrix does its job as expected.** I will close any such issue without further comment.
I expect there will be community driven efforts for users to help each others. If uMatrix had a home, I would probably set up a forum, but I do not plan for such thing, I really just want to code, not manage web sites. If you need help to un-break a site when using uMatrix, you can try [Wilders Security](http://www.wilderssecurity.com/threads/umatrix-the-http-switchboard-successor.369601/), where you are likely to receive help if needed, whether by me or other users.
**So this means do not file issues to report broken sites when the sites are broken because nuTensor does its job as expected.** I will close any such issue without further comment.
uMatrix can be set to work in [allow-all/block-exceptionally](https://github.com/gorhill/httpswitchboard/wiki/How-to-use-HTTP-Switchboard:-Two-opposing-views#the-allow-allblock-exceptionally-approach) mode with a single click on the `all` cell in the global scope `*`, if you prefer to work this way. This will of course break less sites, but you would then lose all the benefits which comes with block-all/allow-exceptionally mode -- though you will still benefit from the 62,000+ blacklisted hostnames by default.
**Using nuTensor logger is key to un-break sites:** the logger will show you all that nuTensor does internally.
## License

@ -1,88 +0,0 @@
{
"assets.json": {
"content": "internal",
"updateAfter": 13,
"contentURL": [
"https://raw.githubusercontent.com/geekprojects/nuTensor/master/assets/assets.json",
"assets/assets.json"
]
},
"public_suffix_list.dat": {
"content": "internal",
"updateAfter": 19,
"contentURL": [
"https://publicsuffix.org/list/public_suffix_list.dat",
"assets/thirdparties/publicsuffix.org/list/public_suffix_list.dat",
"assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat"
]
},
"malware-0": {
"content": "filters",
"title": "Malware Domain List",
"contentURL": [
"https://www.malwaredomainlist.com/hostslist/hosts.txt",
"assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt"
]
},
"malware-1": {
"content": "filters",
"title": "Malware domains",
"contentURL": [
"https://mirror.cedia.org.ec/malwaredomains/justdomains",
"https://mirror1.malwaredomains.com/files/justdomains",
"assets/thirdparties/mirror1.malwaredomains.com/files/justdomains",
"assets/thirdparties/mirror1.malwaredomains.com/files/justdomains.txt"
],
"supportURL": "http://www.malwaredomains.com/"
},
"dpollock-0": {
"content": "filters",
"updateAfter": 11,
"title": "Dan Pollocks hosts file",
"contentURL": [
"https://someonewhocares.org/hosts/hosts",
"assets/thirdparties/someonewhocares.org/hosts/hosts.txt"
],
"supportURL": "https://someonewhocares.org/hosts/"
},
"hphosts": {
"content": "filters",
"updateAfter": 11,
"title": "hpHosts Ad and tracking servers",
"contentURL": [
"https://hosts-file.net/.%5Cad_servers.txt",
"assets/thirdparties/hosts-file.net/ad_servers.txt"
],
"supportURL": "https://hosts-file.net/"
},
"mvps-0": {
"content": "filters",
"updateAfter": 11,
"title": "MVPS HOSTS",
"contentURL": [
"https://winhelp2002.mvps.org/hosts.txt",
"assets/thirdparties/winhelp2002.mvps.org/hosts.txt"
],
"supportURL": "https://winhelp2002.mvps.org/"
},
"plowe-0": {
"content": "filters",
"updateAfter": 13,
"title": "Peter Lowes Ad and tracking server list",
"contentURL": [
"https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext",
"assets/thirdparties/pgl.yoyo.org/as/serverlist",
"assets/thirdparties/pgl.yoyo.org/as/serverlist.txt"
],
"supportURL": "https://pgl.yoyo.org/adservers/"
},
"recipes_en-0": {
"content": "recipes",
"title": "Ruleset recipes for English websites",
"contentURL": [
"https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/recipes/recipes_en.txt",
"assets/umatrix/recipes_en.txt"
],
"lang": "en"
}
}

@ -0,0 +1,13 @@
9f9b2c35acd233d92b67c61ddd4848a5 assets/umatrix/hosts-files.json
188ce926323d816ae9d7d5ebbb567862 assets/umatrix/blacklist.txt
3f870be0cde75b2560f945c0d5a22908 assets/thirdparties/mirror1.malwaredomains.com/files/immortal_domains.txt
7f0443f3dcc9abfd47cfbc95ce824ddb assets/thirdparties/mirror1.malwaredomains.com/files/README.md
b6d676582288285c639336fd73b64da8 assets/thirdparties/mirror1.malwaredomains.com/files/justdomains
8d2e70bb096f8c9f9eced97c41c5a201 assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat
0a9b9fc36673e38ce3d13006c6e8f22a assets/thirdparties/someonewhocares.org/hosts/hosts
9ed51ad85086c002ac9fb61b04a3aff3 assets/thirdparties/winhelp2002.mvps.org/hosts.txt
042419405031f0fcfac3735bf4f05e21 assets/thirdparties/www.malwaredomainlist.com/hostslist/README.md
a6e04103353f982fb0bf16b362518239 assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt
1105acc610ae213f425996d7bd41b49d assets/thirdparties/hosts-file.net/ad-servers
6b0d134e221bf4bd4ac67e8b1c8d54d0 assets/thirdparties/pgl.yoyo.org/as/serverlist
5b8e13b618c68293430913029118781a assets/thirdparties/pgl.yoyo.org/as/README.md

File diff suppressed because it is too large Load Diff

@ -0,0 +1,3 @@
<http://www.malwaredomains.com/?page_id=1508>:
"This malware block lists provided here are for free for noncommercial use as part of the fight against malware."

@ -0,0 +1,4 @@
<http://pgl.yoyo.org/as/index.php>:
Site does encourage use of the list, and nowhere could I find terms and
conditions to use the list. Assuming it can be used freely.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,3 @@
<http://www.malwaredomainlist.com/>:
"Our list can be used for free by anyone. Feel free to use it."

@ -0,0 +1,61 @@
# Blacklist maintained by HTTP Switchboard
# For those domain names which are not found in other blacklists
2mdn.net # "2mdn.net is a domain used by Doubleclick which is an advertising company..."
aad73c550c.se # Related to adrotator.se which is itself blacklisted
acxiom-online.com # Wikipedia: "Acxiom Corporation is a marketing technology and services company".
adextent.com # "We are an advertising technology company - we build technologies that improve ads performance"
adgear.com # "AdGear is an online advertising technologies company based in Montreal, Canada"
adnxs.com # "Adnxs.com is run by AppNexus, a company that provides technology, data and analytics to help companies buy and sell online display advertising" (Ref.: http://www.theguardian.com/technology/2012/apr/23/adnxs-tracking-trackers-cookies-web-monitoring)
adobetag.com # "Adobe Announces Adobe Tag Manager for the Online Marketing Suite"
aimatch.com # "Ad Server, SAS® Intelligent Advertising for Publishers"
analytics.edgesuite.net
atedra.com # "Atedra est un réseau de publicité Internet francophone au Canada"
axf8.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
betrad.com # "Evidon: Home | Online Marketing Intelligence, Web Analytics, Privacy" (which also publishes "Ghostery" add-on..)
bizographics.com # "Business Audience Marketing"
bkrtx.com
# "Imagine Having The Power To Turn Abandoning Visitors Into Customers"
# "The BounceX software is tracking all the cursor movements of every visitor in real-time" (yikes!)
bounceexchange.com
clicktale.com # "See absolutely everything your visitors do on your webpage ... See their every mouse move, click and keystroke"
clicktale.net # Redirect to `clicktale.com`
crosspixel.net # (cookies, localStorage) "leading provider of high performance audience data and information for the real-time advertising industry"
crsspxl.com # Related to crosspixel.net
datarating.com # see https://github.com/gorhill/httpswitchboard/issues/343
displaymarketplace.com
erovinmo.com # No info whatsoever from site itself can be found = naughty corner. Ironically spotted at "http://www.technologyreview.com/news/519336/bruce-schneier-nsa-spying-is-making-us-less-safe/" (also: http://www.mywot.com/en/scorecard/erovinmo.com)
exelator.com # "domain used by eXelate Media which is an advertising company that is part of a network of sites, cookies, and other technologies used to track you" (Ref.: http://www.donottrackplus.com/trackers/exelator.com.php)
everestjs.net # related to `everesttech.net`
everesttech.net # "search engine marketing (SEM) solutions", pixel image on the page, looks like tracking to me. Spotted @ `http://www.homedepot.ca/` (search worked fine when blocking this hostname)
eyereturn.com # "eyeReturn Marketing is the only end-to-end digital advertising platform in the market"
gigya.com # "The tools you need to connect with consumers, harness rich data, and make marketing relevant"
inmuiads.info #
janrainbackplane.com # "Easily visualize, segment and update customer profiles to enable true personalized marketing"
krxd.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
lijit.com # "We provide online advertising services, audience analytics"
llnwd.net # http://en.wikipedia.org/wiki/Limelight_Networks
lduhtrp.net
mathtag.com # "domain used by MediaMath to place cookies, on behalf of its customers, on the computers of visitors to our selected customer's websites and who may view our customer's display advertisements"
mxpnl.com #
moatads.com # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
mookie1.com # "Specializing in online digital advertising, search marketing"
msads.net # Sounds like ads, and no home web page...
omtrdc.net # Redirect to Omniture
outbrain.com # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
panoramtech.net # As seen in a screenshot at http://arstechnica.com/security/2014/01/malware-vendors-buy-chrome-extensions-to-send-adware-filled-updates/
parsely.com # http://en.wikipedia.org/wiki/Parse.ly
peer39.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
pub2srv.com # "This url is used by ad network Propeller Ads Media for ad serving"
servebom.com # no home page, seen as 'tracking.servebom.com': good enough for this list
# These have "tracking" in domain name...
tracking.tomsguide.com
tracking.tomshardware.com
tracking.tomshardware.co.uk
wunderloop.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
yceml.net

@ -0,0 +1,30 @@
{
"mirror1.malwaredomains.com/files/immortal_domains.txt": {
"title": "Long-lived malware domains",
"homeURL": "http://malwaredomains.lehigh.edu/files/immortal_domains.txt"
},
"mirror1.malwaredomains.com/files/justdomains": {
"title": "Malware domains",
"homeURL": "https://mirror.cedia.org.ec/malwaredomains/justdomains"
},
"pgl.yoyo.org/as/serverlist": {
"title": "Peter Lowes Ad server list",
"homeURL": "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&mimetype=plaintext"
},
"www.malwaredomainlist.com/hostslist/hosts.txt": {
"title": "Malware Domain List",
"homeURL": "https://www.malwaredomainlist.com/hostslist/hosts.txt"
},
"hosts-file.net/ad-servers": {
"title": "hpHostss Ad and tracking servers",
"homeURL": "http://hosts-file.net/ad_servers.txt"
},
"someonewhocares.org/hosts/hosts": {
"title": "Dan Pollocks hosts file",
"homeURL": "http://someonewhocares.org/hosts/hosts"
},
"winhelp2002.mvps.org/hosts.txt": {
"title": "MVPS HOSTS",
"homeURL": "http://winhelp2002.mvps.org/hosts.txt"
}
}

@ -1,192 +0,0 @@
#!/usr/bin/env python3
import datetime
import json
import jwt
import os
import re
import requests
import shutil
import subprocess
import sys
import tempfile
import time
import zipfile
from distutils.version import StrictVersion
from string import Template
# - Download target (raw) uMatrix.chromium.zip from GitHub
# - This is referred to as "raw" package
# - This will fail if not a dev build
# - Upload uMatrix.chromium.zip to Chrome store
# - Publish uMatrix.chromium.zip to Chrome store
# Find path to project root
projdir = os.path.split(os.path.abspath(__file__))[0]
while not os.path.isdir(os.path.join(projdir, '.git')):
projdir = os.path.normpath(os.path.join(projdir, '..'))
# We need a version string to work with
if len(sys.argv) >= 2 and sys.argv[1]:
version = sys.argv[1]
else:
version = input('Github release version: ')
version.strip()
if not re.search('^\d+\.\d+\.\d+(b|rc)\d+$', version):
print('Error: Invalid version string.')
exit(1)
cs_extension_id = 'eckgcipdkhcfghnmincccnhpdmnbefki'
tmpdir = tempfile.TemporaryDirectory()
raw_zip_filename = 'uMatrix_'+ version + '.chromium.zip'
raw_zip_filepath = os.path.join(tmpdir.name, raw_zip_filename)
github_owner = 'gorhill'
github_repo = 'uMatrix'
# Load/save auth secrets
# The build directory is excluded from git
ubo_secrets = dict()
ubo_secrets_filename = os.path.join(projdir, 'dist', 'build', 'ubo_secrets')
if os.path.isfile(ubo_secrets_filename):
with open(ubo_secrets_filename) as f:
ubo_secrets = json.load(f)
def input_secret(prompt, token):
if token in ubo_secrets:
prompt += ''
prompt += ': '
value = input(prompt).strip()
if len(value) == 0:
if token not in ubo_secrets:
print('Token error:', token)
exit(1)
value = ubo_secrets[token]
elif token not in ubo_secrets or value != ubo_secrets[token]:
ubo_secrets[token] = value
exists = os.path.isfile(ubo_secrets_filename)
with open(ubo_secrets_filename, 'w') as f:
json.dump(ubo_secrets, f, indent=2)
if not exists:
os.chmod(ubo_secrets_filename, 0o600)
return value
# GitHub API token
github_token = input_secret('Github token', 'github_token')
github_auth = 'token ' + github_token
#
# Get metadata from GitHub about the release
#
# https://developer.github.com/v3/repos/releases/#get-a-single-release
print('Downloading release info from GitHub...')
release_info_url = 'https://api.github.com/repos/{0}/{1}/releases/tags/{2}'.format(github_owner, github_repo, version)
headers = { 'Authorization': github_auth, }
response = requests.get(release_info_url, headers=headers)
if response.status_code != 200:
print('Error: Release not found: {0}'.format(response.status_code))
exit(1)
release_info = response.json()
#
# Extract URL to raw package from metadata
#
# Find url for uMatrix.chromium.zip
raw_zip_url = ''
for asset in release_info['assets']:
if asset['name'] == raw_zip_filename:
raw_zip_url = asset['url']
if len(raw_zip_url) == 0:
print('Error: Release asset URL not found')
exit(1)
#
# Download raw package from GitHub
#
# https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
print('Downloading raw zip package from GitHub...')
headers = {
'Authorization': github_auth,
'Accept': 'application/octet-stream',
}
response = requests.get(raw_zip_url, headers=headers)
# Redirections are transparently handled:
# http://docs.python-requests.org/en/master/user/quickstart/#redirection-and-history
if response.status_code != 200:
print('Error: Downloading raw package failed -- server error {0}'.format(response.status_code))
exit(1)
with open(raw_zip_filepath, 'wb') as f:
f.write(response.content)
print('Downloaded raw package saved as {0}'.format(raw_zip_filepath))
#
# Upload to Chrome store
#
# Auth tokens
cs_id = input_secret('Chrome store id', 'cs_id')
cs_secret = input_secret('Chrome store secret', 'cs_secret')
cs_refresh = input_secret('Chrome store refresh token', 'cs_refresh')
print('Uploading to Chrome store...')
with open(raw_zip_filepath, 'rb') as f:
print('Generating access token...')
auth_url = 'https://accounts.google.com/o/oauth2/token'
auth_payload = {
'client_id': cs_id,
'client_secret': cs_secret,
'grant_type': 'refresh_token',
'refresh_token': cs_refresh,
}
auth_response = requests.post(auth_url, data=auth_payload)
if auth_response.status_code != 200:
print('Error: Auth failed -- server error {0}'.format(auth_response.status_code))
print(auth_response.text)
exit(1)
response_dict = auth_response.json()
if 'access_token' not in response_dict:
print('Error: Auth failed -- no access token')
exit(1)
# Prepare access token
cs_auth = 'Bearer ' + response_dict['access_token']
headers = {
'Authorization': cs_auth,
'x-goog-api-version': '2',
}
# Upload
print('Uploading package...')
upload_url = 'https://www.googleapis.com/upload/chromewebstore/v1.1/items/{0}'.format(cs_extension_id)
upload_response = requests.put(upload_url, headers=headers, data=f)
f.close()
if upload_response.status_code != 200:
print('Upload failed -- server error {0}'.format(upload_response.status_code))
print(upload_response.text)
exit(1)
response_dict = upload_response.json();
if 'uploadState' not in response_dict or response_dict['uploadState'] != 'SUCCESS':
print('Upload failed -- server error {0}'.format(response_dict['uploadState']))
exit(1)
print('Upload succeeded.')
# Publish
print('Publishing package...')
publish_url = 'https://www.googleapis.com/chromewebstore/v1.1/items/{0}/publish'.format(cs_extension_id)
headers = {
'Authorization': cs_auth,
'x-goog-api-version': '2',
'Content-Length': '0',
}
publish_response = requests.post(publish_url, headers=headers)
if publish_response.status_code != 200:
print('Error: Chrome store publishing failed -- server error {0}'.format(publish_response.status_code))
exit(1)
response_dict = publish_response.json();
if 'status' not in response_dict or response_dict['status'][0] != 'OK':
print('Publishing failed -- server error {0}'.format(response_dict['status']))
exit(1)
print('Publishing succeeded.')
print('All done.')

@ -1,321 +0,0 @@
#!/usr/bin/env python3
import datetime
import json
import jwt
import os
import re
import requests
import shutil
import subprocess
import sys
import tempfile
import time
import zipfile
from distutils.version import LooseVersion
from string import Template
# - Download target (raw) nuTensor.firefox.xpi from GitHub
# - This is referred to as "raw" package
# - This will fail if not a dev build
# - Modify raw package to make it self-hosted
# - This is referred to as "unsigned" package
# - Ask AMO to sign nuTensor.firefox.xpi
# - Generate JWT to be used for communication with server
# - Upload unsigned package to AMO
# - Wait for a valid download URL for signed package
# - Download signed package as nuTensor.firefox.signed.xpi
# - This is referred to as "signed" package
# - Upload nuTensor.firefox.signed.xpi to GitHub
# - Remove nuTensor.firefox.xpi from GitHub
# - Modify updates.json to point to new version
# - Commit changes to repo
# Find path to project root
projdir = os.path.split(os.path.abspath(__file__))[0]
while not os.path.isdir(os.path.join(projdir, '.git')):
projdir = os.path.normpath(os.path.join(projdir, '..'))
# Check that found project root is valid
version_filepath = os.path.join(projdir, 'dist', 'version')
if not os.path.isfile(version_filepath):
print('Version file not found.')
exit(1)
# We need a version string to work with
if len(sys.argv) >= 2 and sys.argv[1]:
tag_version = sys.argv[1]
else:
tag_version = input('Github release version: ')
tag_version.strip()
match = re.search('^(\d+\.\d+\.\d+)(?:(b|rc)(\d+))?$', tag_version)
if not match:
print('Error: Invalid version string.')
exit(1)
ext_version = match.group(1);
if match.group(2):
revision = int(match.group(3))
if match.group(2) == 'rc':
revision += 100;
ext_version += '.' + str(revision)
extension_id = 'nuTensor@geekprojects.com'
tmpdir = tempfile.TemporaryDirectory()
raw_xpi_filename = 'nuTensor_' + tag_version + '.firefox.xpi'
raw_xpi_filepath = os.path.join(tmpdir.name, raw_xpi_filename)
unsigned_xpi_filepath = os.path.join(tmpdir.name, 'nuTensor.firefox.unsigned.xpi')
signed_xpi_filename = 'nuTensor_' + tag_version + '.firefox.signed.xpi'
signed_xpi_filepath = os.path.join(tmpdir.name, signed_xpi_filename)
github_owner = 'geekprojects'
github_repo = 'nuTensor'
# Load/save auth secrets
# The build directory is excluded from git
ubo_secrets = dict()
ubo_secrets_filename = os.path.join(projdir, 'dist', 'build', 'ubo_secrets')
if os.path.isfile(ubo_secrets_filename):
with open(ubo_secrets_filename) as f:
ubo_secrets = json.load(f)
def input_secret(prompt, token):
if token in ubo_secrets:
prompt += ''
prompt += ': '
value = input(prompt).strip()
if len(value) == 0:
if token not in ubo_secrets:
print('Token error:', token)
exit(1)
value = ubo_secrets[token]
elif token not in ubo_secrets or value != ubo_secrets[token]:
ubo_secrets[token] = value
exists = os.path.isfile(ubo_secrets_filename)
with open(ubo_secrets_filename, 'w') as f:
json.dump(ubo_secrets, f, indent=2)
if not exists:
os.chmod(ubo_secrets_filename, 0o600)
return value
# GitHub API token
github_token = input_secret('Github token', 'github_token')
github_auth = 'token ' + github_token
#
# Get metadata from GitHub about the release
#
# https://developer.github.com/v3/repos/releases/#get-a-single-release
print('Downloading release info from GitHub...')
release_info_url = 'https://api.github.com/repos/{0}/{1}/releases/tags/{2}'.format(github_owner, github_repo, tag_version)
headers = { 'Authorization': github_auth, }
response = requests.get(release_info_url, headers=headers)
if response.status_code != 200:
print('Error: Release not found: {0}'.format(response.status_code))
exit(1)
release_info = response.json()
#
# Extract URL to raw package from metadata
#
# Find url for nuTensor.firefox.xpi
raw_xpi_url = ''
for asset in release_info['assets']:
if asset['name'] == signed_xpi_filename:
print('Error: Found existing signed self-hosted package.')
exit(1)
if asset['name'] == raw_xpi_filename:
raw_xpi_url = asset['url']
if len(raw_xpi_url) == 0:
print('Error: Release asset URL not found')
exit(1)
#
# Download raw package from GitHub
#
# https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
print('Downloading raw xpi package from GitHub...')
headers = {
'Authorization': github_auth,
'Accept': 'application/octet-stream',
}
response = requests.get(raw_xpi_url, headers=headers)
# Redirections are transparently handled:
# http://docs.python-requests.org/en/master/user/quickstart/#redirection-and-history
if response.status_code != 200:
print('Error: Downloading raw package failed -- server error {0}'.format(response.status_code))
exit(1)
with open(raw_xpi_filepath, 'wb') as f:
f.write(response.content)
print('Downloaded raw package saved as {0}'.format(raw_xpi_filepath))
#
# Convert the package to a self-hosted one: add `update_url` to the manifest
#
print('Converting raw xpi package into self-hosted xpi package...')
with zipfile.ZipFile(raw_xpi_filepath, 'r') as zipin:
with zipfile.ZipFile(unsigned_xpi_filepath, 'w') as zipout:
for item in zipin.infolist():
data = zipin.read(item.filename)
if item.filename == 'manifest.json':
manifest = json.loads(bytes.decode(data))
manifest['browser_specific_settings']['gecko']['update_url'] = 'https://raw.githubusercontent.com/{0}/{1}/master/dist/firefox/updates.json'.format(github_owner, github_repo)
data = json.dumps(manifest, indent=2, separators=(',', ': '), sort_keys=True).encode()
zipout.writestr(item, data)
#
# Ask AMO to sign the self-hosted package
# - https://developer.mozilla.org/en-US/Add-ons/Distribution#Distributing_your_add-on
# - https://pyjwt.readthedocs.io/en/latest/usage.html
# - https://addons-server.readthedocs.io/en/latest/topics/api/auth.html
# - https://addons-server.readthedocs.io/en/latest/topics/api/signing.html
#
amo_api_key = ''
amo_secret = ''
def get_jwt_auth():
global amo_api_key
if amo_api_key == '':
amo_api_key = input_secret('AMO API key', 'amo_api_key')
global amo_secret
if amo_secret == '':
amo_secret = input_secret('AMO API secret', 'amo_secret')
amo_nonce = os.urandom(8).hex()
jwt_payload = {
'iss': amo_api_key,
'jti': amo_nonce,
'iat': datetime.datetime.utcnow(),
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=15),
}
return 'JWT ' + jwt.encode(jwt_payload, amo_secret).decode()
print('Ask AMO to sign self-hosted xpi package...')
with open(unsigned_xpi_filepath, 'rb') as f:
# https://blog.mozilla.org/addons/2019/11/11/security-improvements-in-amo-upload-tools/
# "We recommend allowing up to 15 minutes."
interval = 60 # check every 60 seconds
countdown = 15 * 60 / interval # for at most 15 minutes
headers = { 'Authorization': get_jwt_auth(), }
data = { 'channel': 'unlisted' }
files = { 'upload': f, }
signing_url = 'https://addons.mozilla.org/api/v3/addons/{0}/versions/{1}/'.format(extension_id, ext_version)
print('Submitting package to be signed...')
response = requests.put(signing_url, headers=headers, data=data, files=files)
if response.status_code != 202:
print('Error: Creating new version failed -- server error {0}'.format(response.status_code))
print(response.text)
exit(1)
print('Request for signing self-hosted xpi package succeeded.')
signing_request_response = response.json();
f.close()
print('Waiting for AMO to process the request to sign the self-hosted xpi package...')
# Wait for signed package to be ready
signing_check_url = signing_request_response['url']
while True:
sys.stdout.write('.')
sys.stdout.flush()
time.sleep(interval)
countdown -= 1
if countdown <= 0:
print('Error: AMO signing timed out')
exit(1)
headers = { 'Authorization': get_jwt_auth(), }
response = requests.get(signing_check_url, headers=headers)
if response.status_code != 200:
print('Error: AMO signing failed -- server error {0}'.format(response.status_code))
print(response.text)
exit(1)
signing_check_response = response.json()
if not signing_check_response['processed']:
continue
if not signing_check_response['valid']:
print('Error: AMO validation failed')
print(response.text)
exit(1)
if not signing_check_response['files'] or len(signing_check_response['files']) == 0:
continue
if not signing_check_response['files'][0]['signed']:
continue
if not signing_check_response['files'][0]['download_url']:
print('Error: AMO signing failed')
print(response.text)
exit(1)
print('\r')
print('Self-hosted xpi package successfully signed.')
download_url = signing_check_response['files'][0]['download_url']
print('Downloading signed self-hosted xpi package from {0}...'.format(download_url))
response = requests.get(download_url, headers=headers)
if response.status_code != 200:
print('Error: Download signed package failed -- server error {0}'.format(response.status_code))
print(response.text)
exit(1)
with open(signed_xpi_filepath, 'wb') as f:
f.write(response.content)
f.close()
print('Signed self-hosted xpi package downloaded.')
break
#
# Upload signed package to GitHub
#
# https://developer.github.com/v3/repos/releases/#upload-a-release-asset
print('Uploading signed self-hosted xpi package to GitHub...')
with open(signed_xpi_filepath, 'rb') as f:
url = release_info['upload_url'].replace('{?name,label}', '?name=' + signed_xpi_filename)
headers = {
'Authorization': github_auth,
'Content-Type': 'application/zip',
}
response = requests.post(url, headers=headers, data=f.read())
if response.status_code != 201:
print('Error: Upload signed package failed -- server error: {0}'.format(response.status_code))
exit(1)
#
# Remove raw package from GitHub
#
# https://developer.github.com/v3/repos/releases/#delete-a-release-asset
print('Remove raw xpi package from GitHub...')
headers = { 'Authorization': github_auth, }
response = requests.delete(raw_xpi_url, headers=headers)
if response.status_code != 204:
print('Error: Deletion of raw package failed -- server error: {0}'.format(response.status_code))
#
# Update updates.json to point to new package -- but only if just-signed
# package is higher version than current one.
#
print('Update GitHub to point to newly signed self-hosted xpi package...')
updates_json_filepath = os.path.join(projdir, 'dist', 'firefox', 'updates.json')
with open(updates_json_filepath) as f:
updates_json = json.load(f)
f.close()
previous_version = updates_json['addons'][extension_id]['updates'][0]['version']
if LooseVersion(ext_version) > LooseVersion(previous_version):
with open(os.path.join(projdir, 'dist', 'firefox', 'updates.template.json')) as f:
template_json = Template(f.read())
f.close()
updates_json = template_json.substitute(ext_version=ext_version, tag_version=tag_version)
with open(updates_json_filepath, 'w') as f:
f.write(updates_json)
f.close()
# Automatically git add/commit if needed.
# - Stage the changed file
r = subprocess.run(['git', 'status', '-s', updates_json_filepath], stdout=subprocess.PIPE)
rout = bytes.decode(r.stdout).strip()
if len(rout) >= 2 and rout[1] == 'M':
subprocess.run(['git', 'add', updates_json_filepath])
# - Commit the staged file
r = subprocess.run(['git', 'status', '-s', updates_json_filepath], stdout=subprocess.PIPE)
rout = bytes.decode(r.stdout).strip()
if len(rout) >= 2 and rout[0] == 'M':
subprocess.run(['git', 'commit', '-m', 'Make Firefox dev build auto-update', updates_json_filepath])
subprocess.run(['git', 'push', 'origin', 'HEAD'])
print('All done.')

@ -1,14 +0,0 @@
{
"addons": {
"nuTensor@geekprojects.com": {
"updates": [
{
"version": "1.4.1.6",
"browser_specific_settings": { "gecko": { "strict_min_version": "56" } },
"update_info_url": "https://github.com/geekprojects/nuTensor/releases/tag/1.4.1b6",
"update_link": "https://github.com/geekprojects/nuTensor/releases/download/1.4.1b6/nuTensor_1.4.1b6.firefox.signed.xpi"
}
]
}
}
}

@ -1,14 +0,0 @@
{
"addons": {
"nuTensor@geekprojects.com": {
"updates": [
{
"version": "$ext_version",
"browser_specific_settings": { "gecko": { "strict_min_version": "56" } },
"update_info_url": "https://github.com/geekprojects/nuTensor/releases/tag/$tag_version",
"update_link": "https://github.com/geekprojects/nuTensor/releases/download/$tag_version/nuTensor_$tag_version.firefox.signed.xpi"
}
]
}
}
}

1
dist/version vendored

@ -1 +0,0 @@
1.5.0.0

@ -1,39 +0,0 @@
## Building nuTensor
### Requirements
Tools:
* bash
* python 3
You will need both this nuTensor and the nuAssets repositories. These should both be placed in the same directory:
```
git clone https://github.com/geekprojects/nuAssets.git
git clone https://github.com/geekprojects/nuTensor.git
cd nuTensor
```
### Packaging
You can now run the scripts that package everything up.
These are bash scripts. They have only been tested on Linux and MacOS.
#### For Firefox
```
tools/make-firefox.sh all
```
#### For Chrome/Chromium (Not yet tested)
```
tools/make-chromium.sh all
```
#### For Opera (Not yet tested)
```
tools/make-opera.sh
```
The installation package should now be found in dist/build/
### Installing
Follow the instructions in [README.md](README.md) to install it.

@ -1,8 +1,8 @@
### This is uMatrix's manifesto
### This is µMatrix's manifesto
1. The **user decides** what web content is acceptable or not in their browser.
That is all.
The purpose of _uMatrix_ is to give the user the means for informed
The purpose of _µMatrix_ is to give the user the means for informed
consent and informed dissent.

@ -2,16 +2,16 @@
#### Chromium
- Download and unzip `nuTensor.chromium.zip` ([latest release desirable](https://github.com/geekprojects/nuTensor/releases)).
- Rename the unzipped directory to `nuTensor`
- When you later update manually, replace the **content** of the `nuTensor` folder with the **content** of the latest zipped version.
- Download and unzip `uMatrix.chromium.zip` ([latest release desirable](https://github.com/gorhill/uMatrix/releases)).
- Rename the unzipped directory to `umatrix`
- When you later update manually, replace the **content** of the `umatrix` folder with the **content** of the latest zipped version.
- This will ensure that all the extension settings will be preserved
- As long as the extension loads **from same folder path from which it was originally installed**, all your settings will be preserved.
- Go to chromium/chrome *Extensions*.
- Click to check *Developer mode*.
- Click *Load unpacked extension...*.
- In the file selector dialog:
- Select the directory `nuTensor` which was created above.
- Select the directory `umatrix` which was created above.
- Click *Open*.
The extension will now be available in your chromium/chromium-based browser.
@ -22,16 +22,16 @@ Remember that you have to update manually also. For some users, updating manuall
#### Firefox
Although nuTensor is a restartless addon, I found that **installing** a newer version over an older one often will glitch the installation. These steps always worked fine:
Although uMatrix is a restartless addon, I found that **installing** a newer version over an older one often will glitch the installation. These steps always worked fine:
- Download `nuTensor.firefox.xpi` ([latest release desirable](https://github.com/geekprojects/nuTensor/releases)).
- Uninstall current nuTensor if already installed
- Download `uMatrix.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uMatrix/releases)).
- Uninstall current uMatrix if already installed
- Quit Firefox completely
- Launch Firefox
- Drag and drop the previously downloaded `nuTensor.firefox.xpi` into Firefox
- Drag and drop the previously downloaded `uMatrix.firefox.xpi` into Firefox
nuTensor settings are kept intact even after you uninstall the addon.
uMatrix settings are kept intact even after you uninstall the addon.
On Linux, the settings are saved in a SQlite file located at `~/.mozilla/firefox/[profile name]/extension-data/nuTensor.sqlite`.
On Linux, the settings are saved in a SQlite file located at `~/.mozilla/firefox/[profile name]/extension-data/umatrix.sqlite`.
On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\nuTensor.sqlite`.
On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\umatrix.sqlite`.

@ -1,81 +1,93 @@
uMatrix格子状のマトリクスで管理するファイヤウォール。様々なプライバシー強化ツールを兼ね備えます。
µMatrix: A point-and-click matrix-based firewall, with many privacy-enhancing tools. For advanced users.
* 上級ユーザー向けです *
µMatrix put you in full control of where your browser is allowed to connect, what type of data it is allowed to download, and what it is allowed to execute. Nobody else decides for you: You choose. You are in full control of your privacy.
uMatrix を使うと、ブラウザーがどこと通信して良いのか、どの種類のデータをダウンロードするのか、何を実行して良いのかを決定することができます。誰か他の人があなたの代わりに決めてくれることはありません。自分のプライバシーを自分で制御するのです。
Out of the box, µMatrix works in relax block-all/allow-exceptionally mode, meaning web sites which require 3rd-party scripts are likely to be "broken". With two clicks, µMatrix can be set to work in allow-all/block-exceptionally mode, which generally will not break web sites. See https://github.com/gorhill/httpswitchboard/wiki/How-to-use-HTTP-Switchboard:-Two-opposing-views for more details on this topic.
初期設定では、uMatrix は安心できる「すべてブロック・必要なら許可」の設定になっています。つまり、サードパーティーのスクリプトを必要とするウェブサイトは“壊れる”ことが多いでしょう。2クリックで、「すべて許可・必要ならブロック」の設定に変えることもでき、この場合、ウェブサイトを壊すことはほとんどありません。より詳しくは https://github.com/gorhill/httpswitchboard/wiki/How-to-use-HTTP-Switchboard:-Two-opposing-views を見て下さい。
Regarding the myth that "Chromium-based browsers can't reliably block javascript", see: https://github.com/gorhill/httpswitchboard/wiki/Blocking-javascript-execution-reliably-in-Chromium-based-browsers. Summary: Yes, javascript can be blocked reliably in Chromium.
* あなたが選んだブロック/許可の結果による“すべての”リクエストの失敗/実施の状況を確認できます。
* See ALL the remote connections, failed or attempted, depending on whether they were blocked or allowed (you decide).
* ワンクリックで、接続先やデータの種類に応じてリクエストを許可/ブロックすることができます。ブロックされたリクエストがブラウザーから外に出ることは決してありません。
* A single-click to whitelist/blacklist one or multiple classes of requests according to the destination and type of data (a blocked request will NEVER leave your browser).
* 効率的なブラックリストCookie はブラウザーの外には出ず、JavaScript もプラグインも実行されず、追跡ピクセルがダウンロードされることもありません。
* Efficient blacklisting: cookies won't leave your browser, javascript won't execute, plugins won't play, tracking pixels won't download, etc.
* リストにないとブロックしてくれない(そして多くの漏れがあるであろう)ブラックリストだけに頼る必要はありません。自分で制御できるのです。
* You do not have to solely rely on just one particular curated blacklist (arguably with many missing entries) outside which nothing else can be blocked: You are in full control.
* 使い勝手の良さuMatrix では、ウェブページが発したリクエストを、マトリクスをクリックすることで簡単に許可/ブロックすることができます。
* Ease of use: µMatrix lets you easily whitelist/blacklist net requests which originate from within a web page according to a point-and-click matrix:
- ドメイン名(左の列)
* 具体的なドメインから
* 汎用的なドメインまで 選ぶことができます
- domain names (left column)
* from very specific
* to very generic
- リクエストの種類(先頭の行)
* Cookie
* CSS 関連のリソース(スタイルシートと Web フォント)
* 画像
* メディア
* スクリプト
* XHR(スクリプトにより生成されるリクエスト)
* フレーム
* その他
- type of requests (top row)
* cookies
* CSS-related resources (stylesheets and web fonts)
* images
* plugins
* scripts
* XHR (requests made by scripts)
* frames
* others
ワンクリックで、特定のセル、特定のドメイン、グループ化されたドメイン、特定のリクエストの種類、そしてマトリクス全体を許可/ブロックできます。
You can blacklist/whitelist a single cell, an entire row, a group of rows, an entire column, or the whole matrix with just one click.
uMatrix のフィルタリングエンジンは、どのセルが許可/ブロックされているかによって、優先順位に基づくロジックでリクエストを処理します。これにより、例えば、あるページ全体をワンクリックで許可しておけば、このページに新しいリクエストが見つかるたびに許可を追加する必要がなくなります。
µMatrix's filtering engine uses precedence logic to evaluate what is blocked/allowed according to which cells are blacklisted/whitelisted. For example, this allows you to whitelist a whole page with one click, without having to repeatedly whitelist whatever new data appear on the page.
それぞれのルールには有効範囲があります。例えば、www.facebook.com にアクセスしていない時だけ facebook.com と facebook.net をブロックすることができます。これにより、Facebook が、あなたのブラウジング習慣からあなたの人物像を作成することを防ぎます。
All rules are scoped. For example, you can block `facebook.com` and `facebook.net` everywhere except when visiting a page on `www.facebook.com`. This way Facebook won't be able to build a profile of your browsing habits.
この拡張機能の最終目標は、ユーザーが自身のプライバシーに気を配れるよう、可能な限り簡潔に、ウェブサイトを包括的あるいは部分的に許可/ブロックできるようにすることです。
The goal of this extension is to make the allowing or blocking of web sites, wholly or partly, as straightforward as possible, so as to encourage users to care about their privacy.
この拡張機能には、合計 62,000 件以上のホスト名からなるサードパーティーのホストファイルが含まれます。これらのファイルは好みに応じてオンオフすることができます。
The extension comes with 3rd-party hosts files totaling over 58,000 distinct hostnames (lists can be selectively disabled/enabled according to your choice).
まとめると、あなたはネットをブラウジングする方法を次のように選ぶことができます。
Ultimately, you can choose however you browse the net:
* はじめはすべてブロック、必要に応じて許可していく(初期設定)
* Blacklist all by default, and whitelist as needed (default mode).
* はじめはすべて許可、必要に応じてブロックしていく
* Whitelist all by default, and blacklist as needed.
どちらの方法でも、組み込みのホストファイルのおかげで、トラッカーやマルウェアサイトなどに対する基礎的な保護を得ることができます。もしくはこれらをすべて無効にすることもできます。
Either way, you still benefit from the preset blacklists so that at least you get basic protection from trackers, malware sites, etc. Or you can disable all of these preset blacklists.
決めるのはあなたです。
Your choice.
ドキュメント: https://github.com/gorhill/uMatrix/wiki
Randomly assembled documentation: https://github.com/gorhill/uMatrix/wiki
=====
この拡張機能のもう一つの使い道は、知識なしでも、ブラウザーの中でウェブページが何をしているか理解することです。あなたはウェブページがどこと通信するのかをすべて見た上で決断し、ウェブページ中の特定の種類の通信を制限できます。
FEEDBACK:
拡張機能アイコンに表示される数値は、ウェブページによっていくつのリクエストが試みられたかの総数(許可/ブロック合わせて)を示します。
For any question/issue you might have, use the "Send Feedback" button on the right, in order for me to be able to answer readily. I can't answer directly to reviews, but I will be more than happy to answer you directly in the feedback section.
マトリクスの適切なセルをクリックして、許可/ブロック/グレーのいずれにするかを決めます。“グレー”では、マトリクス内のより優先度の高いセルからブロック/許可の状態を引き継ぎます。
=====
赤いセル は事実上ブロックされている状態であり、リクエストは接続先に届きません。
* 濃い赤:そのドメイン名とリクエスト種類の組み合わせは、明示的にブロックされています。
* 淡い赤:セルが“グレー”状態のため、ブロック状態が引き継がれました。
BUGS, ISSUES, SUGEGSTIONS:
緑のセル は事実上許可されている状態であり、リクエストは接続先に届きます。
* 濃い緑:そのドメイン名とリクエスト種類の組み合わせは、明示的に許可されています。
* 淡い緑:セルが“グレー”状態のため、許可が引き継がれました。
https://github.com/gorhill/uMatrix/issues
マトリクスの一番左上にある「すべて」のセルはデフォルトのグローバル設定を示し、「すべてを許可」と「すべてをブロック」のどちらをデフォルトにするかを選ぶことができます。人によっては、すべてを許可した上で必要に応じてブロックすることを好むでしょう。私の個人的な設定はもちろんその逆、すべてをブロックした上で必要なものを許可します。
You are very welcomed to contribute your views on open issues and suggestions, various arguments for/against help me in deciding what is needed to improve the extension.
この拡張機能は、例えば画像の読み込みをすべてブロックすることで、ブラウジングスピードを上げるのにも役立ちます。
Ease of use is the primary goal. I've seen users give up on Firefox's NoScript because it gets too much in the way according to them, so rather than blame these users for poor security habits, I prefer to blame developers and this project is a tentative to address the issues which cause some users to give up on basic security.
=====
This extension is also useful to understand what the web page in your browser is doing, often without your knowledge. You have full ability to see and decide with whom a web page communicates, and to restrict these communications to specific classes of objects within the web page.
The number which appear in the extension icon correspond to the total number of distinct requests attempted (successfully or not depending on whether these were allowed or blocked) by the web page.
Simply click on the appropriate entry in the matrix in order to white-, black- or graylist a component. Graylisting means the blocked or allowed status will be inherited from another cell with higher precedence in the matrix.
Red square = effectively blacklisted, i.e. requests are prevented from reaching their intended destination:
* Dark red square: the domain name and/or type of request is specifically blacklisted.
* Faded red square: the blacklist status is inherited because the entry is graylisted.
バグ・問題報告: https://github.com/gorhill/uMatrix/issues
Green square = effectively whitelisted, i.e. requests are allowed to reach their intended destination:
* Dark green square: the domain name and/or type of request is specifically whitelisted.
* Faded green square: the whitelist status is inherited because the entry is graylisted.
The top-left cell in the matrix, the "all" cell, represents the default global setting, which allows you to choose whether allowing or blocking everything is the default behavior. Some prefer to allow everything while blocking exceptionally. My personal preference is of course the reverse, blocking everything and allowing exceptionally.
This extension is also useful if you wish to speed up your browsing, by globally blocking all requests for images as an example.
=====
ソースコード: https://github.com/gorhill/uMatrix (GPLv3)
SOURCE CODE: https://github.com/gorhill/uMatrix (GPLv3)
更新履歴: https://github.com/gorhill/uMatrix/releases
CHANGE LOG: https://github.com/gorhill/uMatrix/releases

@ -10,7 +10,7 @@ Out of the box, uMatrix works in relax block-all/allow-exceptionally mode, meani
* A single-click to whitelist/blacklist one or multiple classes of requests according to the destination and type of data (a blocked request will NEVER leave your browser).
* Efficient blacklisting: cookies won't leave your browser, JavaScript won't execute, media won't play, tracking pixels won't download, etc.
* Efficient blacklisting: cookies won't leave your browser, JavaScript won't execute, plugins won't play, tracking pixels won't download, etc.
* You do not have to solely rely on just one particular curated blacklist (arguably with many missing entries) outside which nothing else can be blocked: You are in full control.
@ -22,11 +22,11 @@ Out of the box, uMatrix works in relax block-all/allow-exceptionally mode, meani
- type of requests (top row)
* cookies
* css (stylesheets and web fonts)
* CSS-related resources (stylesheets and web fonts)
* images
* media
* plugins
* scripts
* xhr (requests made by scripts)
* XHR (requests made by scripts)
* frames
* others
@ -56,7 +56,7 @@ Randomly assembled documentation: https://github.com/gorhill/uMatrix/wiki
This extension is also useful to understand what the web page in your browser is doing, often without your knowledge. You have full ability to see and decide with whom a web page communicates, and to restrict these communications to specific classes of objects within the web page.
The number which appears in the extension icon correspond to the total number of distinct requests blocked by the extension.
The number which appear in the extension icon correspond to the total number of distinct requests attempted (successfully or not depending on whether these were allowed or blocked) by the web page.
Simply click on the appropriate entry in the matrix in order to white-, black- or graylist a component. Graylisting means the blocked or allowed status will be inherited from another cell with higher precedence in the matrix.
@ -74,7 +74,7 @@ This extension is also useful if you wish to speed up your browsing, by globally
=====
BUGS, ISSUES: https://github.com/uBlockOrigin/uMatrix-issues/issues
BUGS, ISSUES: https://github.com/gorhill/uMatrix/issues
SOURCE CODE: https://github.com/gorhill/uMatrix (GPLv3)

@ -1,8 +1,8 @@
{
"manifest_version": 2,
"name": "nuTensor",
"short_name": "nuTensor",
"version": "1.3.3.8",
"name": "uMatrix",
"short_name": "uMatrix",
"version": "0.9.3.6",
"description": "__MSG_extShortDesc__",
"icons": {
"16": "img/icon_16.png",
@ -12,36 +12,64 @@
"default_icon": {
"19": "img/browsericons/icon19-19.png"
},
"default_title": "nuTensor",
"default_title": "uMatrix",
"default_popup": "popup.html"
},
"author": "Raymond Hill",
"background": {
"page": "background.html"
},
"commands": {
"revert-all": {
"description": "__MSG_commandRevertAll__",
"suggested_key": {
"default": "Alt+Q",
"mac": "Command+Shift+Q"
}
},
"whitelist-all": {
"description": "__MSG_commandWhitelistAll__",
"suggested_key": {
"default": "Alt+A",
"mac": "Command+Shift+A"
}
},
"whitelist-page-domain": {
"description": "__MSG_commandWhitelistPageDomain__",
"suggested_key": {
"default": "Alt+W",
"mac": "Command+Shift+W"
}
},
"open-dashboard": {
"description": "__MSG_commandOpenDashboard__",
"suggested_key": {
"default": "Alt+S",
"mac": "Command+Shift+S"
}
}
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["/js/vapi.js", "/js/vapi-client.js", "/js/contentscript-start.js"],
"js": ["js/vapi-client.js", "js/contentscript-start.js"],
"run_at": "document_start",
"all_frames": true
},
{
"matches": ["http://*/*", "https://*/*"],
"js": ["/js/contentscript.js"],
"js": ["js/contentscript-end.js"],
"run_at": "document_end",
"all_frames": true
}
],
"default_locale": "en",
"homepage_url": "https://github.com/geekprojects/nuTensor",
"minimum_chrome_version": "45.0",
"options_ui": {
"page": "dashboard.html",
"open_in_tab": true
},
"homepage_url": "https://github.com/gorhill/uMatrix/wiki",
"minimum_chrome_version": "22.0",
"options_page": "dashboard.html",
"permissions": [
"browsingData",
"contentSettings",
"cookies",
"privacy",
"storage",
@ -50,6 +78,7 @@
"webNavigation",
"webRequest",
"webRequestBlocking",
"<all_urls>"
"http://*/*",
"https://*/*"
]
}

@ -0,0 +1,9 @@
<!DOCTYPE html>
<head>
<script src="js/vapi-client.js"></script>
<script src="js/options_ui.js"></script>
<title></title>
</head>
<body>
</body>
</html>

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to block requests.
Copyright (C) 2019-present Raymond Hill
µBlock - a browser extension to block requests.
Copyright (C) 2015 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,16 +19,27 @@
Home: https://github.com/gorhill/uBlock
*/
/******************************************************************************/
(function() {
/******************************************************************************/
'use strict';
self.log = (function() {
const noopFunc = function() {};
const info = function(s) { console.log(`[uMatrix] ${s}`); };
return {
get verbosity( ) { return; },
set verbosity(level) {
this.info = console.info = level === 'info' ? info : noopFunc;
},
info: noopFunc,
};
var messager = vAPI.messaging.channel('_open');
messager.send({
what: 'gotoURL',
details: {
url: 'dashboard.html',
select: true,
index: -1
}
});
window.close();
/******************************************************************************/
})();
/******************************************************************************/

File diff suppressed because it is too large Load Diff

@ -1,308 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
// For non-background page
'use strict';
/******************************************************************************/
// Direct messaging connection ability
(( ) => {
// >>>>>>>> start of private namespace
if (
typeof vAPI !== 'object' ||
vAPI.messaging instanceof Object === false ||
vAPI.MessagingConnection instanceof Function
) {
return;
}
const listeners = new Set();
const connections = new Map();
vAPI.MessagingConnection = class {
constructor(handler, details) {
this.messaging = vAPI.messaging;
this.handler = handler;
this.id = details.id;
this.to = details.to;
this.toToken = details.toToken;
this.from = details.from;
this.fromToken = details.fromToken;
this.checkTimer = undefined;
// On Firefox it appears ports are not automatically disconnected
// when navigating to another page.
const ctor = vAPI.MessagingConnection;
if ( ctor.pagehide !== undefined ) { return; }
ctor.pagehide = ( ) => {
for ( const connection of connections.values() ) {
connection.disconnect();
connection.handler(
connection.toDetails('connectionBroken')
);
}
};
window.addEventListener('pagehide', ctor.pagehide);
}
toDetails(what, payload) {
return {
what: what,
id: this.id,
from: this.from,
fromToken: this.fromToken,
to: this.to,
toToken: this.toToken,
payload: payload
};
}
disconnect() {
if ( this.checkTimer !== undefined ) {
clearTimeout(this.checkTimer);
this.checkTimer = undefined;
}
connections.delete(this.id);
const port = this.messaging.getPort();
if ( port === null ) { return; }
port.postMessage({
channel: 'vapi',
msg: this.toDetails('connectionBroken'),
});
}
checkAsync() {
if ( this.checkTimer !== undefined ) {
clearTimeout(this.checkTimer);
}
this.checkTimer = vAPI.setTimeout(
( ) => { this.check(); },
499
);
}
check() {
this.checkTimer = undefined;
if ( connections.has(this.id) === false ) { return; }
const port = this.messaging.getPort();
if ( port === null ) { return; }
port.postMessage({
channel: 'vapi',
msg: this.toDetails('connectionCheck'),
});
this.checkAsync();
}
receive(details) {
switch ( details.what ) {
case 'connectionAccepted':
this.toToken = details.toToken;
this.handler(details);
this.checkAsync();
break;
case 'connectionBroken':
connections.delete(this.id);
this.handler(details);
break;
case 'connectionMessage':
this.handler(details);
this.checkAsync();
break;
case 'connectionCheck':
const port = this.messaging.getPort();
if ( port === null ) { return; }
if ( connections.has(this.id) ) {
this.checkAsync();
} else {
details.what = 'connectionBroken';
port.postMessage({ channel: 'vapi', msg: details });
}
break;
case 'connectionRefused':
connections.delete(this.id);
this.handler(details);
break;
}
}
send(payload) {
const port = this.messaging.getPort();
if ( port === null ) { return; }
port.postMessage({
channel: 'vapi',
msg: this.toDetails('connectionMessage', payload),
});
}
static addListener(listener) {
listeners.add(listener);
}
static async connectTo(from, to, handler) {
const port = vAPI.messaging.getPort();
if ( port === null ) { return; }
const connection = new vAPI.MessagingConnection(handler, {
id: `${from}-${to}-${vAPI.sessionId}`,
to: to,
from: from,
fromToken: port.name
});
connections.set(connection.id, connection);
port.postMessage({
channel: 'vapi',
msg: {
what: 'connectionRequested',
id: connection.id,
from: from,
fromToken: port.name,
to: to,
}
});
return connection.id;
}
static disconnectFrom(connectionId) {
const connection = connections.get(connectionId);
if ( connection === undefined ) { return; }
connection.disconnect();
}
static sendTo(connectionId, payload) {
const connection = connections.get(connectionId);
if ( connection === undefined ) { return; }
connection.send(payload);
}
static canDestroyPort() {
return listeners.length === 0 && connections.size === 0;
}
static mustDestroyPort() {
if ( connections.size === 0 ) { return; }
for ( const connection of connections.values() ) {
connection.receive({ what: 'connectionBroken' });
}
connections.clear();
}
static canProcessMessage(details) {
if ( details.channel !== 'vapi' ) { return; }
switch ( details.msg.what ) {
case 'connectionAccepted':
case 'connectionBroken':
case 'connectionCheck':
case 'connectionMessage':
case 'connectionRefused': {
const connection = connections.get(details.msg.id);
if ( connection === undefined ) { break; }
connection.receive(details.msg);
return true;
}
case 'connectionRequested':
if ( listeners.length === 0 ) { return; }
const port = vAPI.messaging.getPort();
if ( port === null ) { break; }
let listener, result;
for ( listener of listeners ) {
result = listener(details.msg);
if ( result !== undefined ) { break; }
}
if ( result === undefined ) { break; }
if ( result === true ) {
details.msg.what = 'connectionAccepted';
details.msg.toToken = port.name;
const connection = new vAPI.MessagingConnection(
listener,
details.msg
);
connections.set(connection.id, connection);
} else {
details.msg.what = 'connectionRefused';
}
port.postMessage(details);
return true;
default:
break;
}
}
};
vAPI.messaging.extensions.push(vAPI.MessagingConnection);
// <<<<<<<< end of private namespace
})();
/******************************************************************************/
// Broadcast listening ability
(( ) => {
// >>>>>>>> start of private namespace
if (
typeof vAPI !== 'object' ||
vAPI.messaging instanceof Object === false ||
vAPI.broadcastListener instanceof Object
) {
return;
}
const listeners = new Set();
vAPI.broadcastListener = {
add: function(listener) {
listeners.add(listener);
vAPI.messaging.getPort();
},
remove: function(listener) {
listeners.delete(listener);
},
canDestroyPort() {
return listeners.size === 0;
},
mustDestroyPort() {
listeners.clear();
},
canProcessMessage(details) {
if ( details.broadcast === false ) { return; }
for ( const listener of listeners ) {
listener(details.msg);
}
},
};
vAPI.messaging.extensions.push(vAPI.broadcastListener);
// <<<<<<<< end of private namespace
})();
/******************************************************************************/
/*******************************************************************************
DO NOT:
- Remove the following code
- Add code beyond the following code
Reason:
- https://github.com/gorhill/uBlock/pull/3721
- uBO never uses the return value from injected content scripts
**/
void 0;

@ -1,8 +1,7 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2015 The uBlock Origin authors
Copyright (C) 2014-present Raymond Hill
µBlock - a browser extension to block requests.
Copyright (C) 2014 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,249 +19,173 @@
Home: https://github.com/gorhill/uBlock
*/
// For non-background page
// For non background pages
/******************************************************************************/
(function(self) {
'use strict';
/******************************************************************************/
var vAPI = self.vAPI = self.vAPI || {};
var chrome = self.chrome;
// https://github.com/chrisaljoudi/uBlock/issues/456
// Skip if already injected.
// Already injected?
if ( vAPI.vapiClientInjected ) {
//console.debug('vapi-client.js already injected: skipping.');
return;
}
vAPI.vapiClientInjected = true;
// >>>>>>>> start of HUGE-IF-BLOCK
if (
typeof vAPI === 'object' &&
vAPI.randomToken instanceof Function === false
) {
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
Math.random().toString(36).slice(2);
vAPI.chrome = true;
/******************************************************************************/
/******************************************************************************/
vAPI.randomToken = function() {
const now = Date.now();
return String.fromCharCode(now % 26 + 97) +
Math.floor((1 + Math.random()) * now).toString(36);
};
vAPI.sessionId = vAPI.randomToken();
vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);
vAPI.shutdown = (function() {
var jobs = [];
/******************************************************************************/
var add = function(job) {
jobs.push(job);
};
vAPI.shutdown = {
jobs: [],
add: function(job) {
this.jobs.push(job);
},
exec: function() {
// Shutdown asynchronously, to ensure shutdown jobs are called from
// the top context.
self.requestIdleCallback(( ) => {
const jobs = this.jobs.slice();
this.jobs.length = 0;
while ( jobs.length !== 0 ) {
(jobs.pop())();
}
});
},
remove: function(job) {
let pos;
while ( (pos = this.jobs.indexOf(job)) !== -1 ) {
this.jobs.splice(pos, 1);
var exec = function() {
//console.debug('Shutting down...');
var job;
while ( job = jobs.pop() ) {
job();
}
}
};
};
return {
add: add,
exec: exec
};
})();
/******************************************************************************/
vAPI.messaging = {
port: null,
portTimer: null,
portTimerDelay: 10000,
extended: undefined,
extensions: [],
msgIdGenerator: 1,
pending: new Map(),
shuttingDown: false,
shutdown: function() {
this.shuttingDown = true;
this.destroyPort();
},
var messagingConnector = function(response) {
if ( !response ) {
return;
}
// https://github.com/uBlockOrigin/uBlock-issues/issues/403
// Spurious disconnection can happen, so do not consider such events
// as world-ending, i.e. stay around. Except for embedded frames.
var channels = vAPI.messaging.channels;
var channel, listener;
disconnectListener: function() {
this.port = null;
if ( window !== window.top ) {
vAPI.shutdown.exec();
}
},
disconnectListenerBound: null,
messageListener: function(details) {
if ( details instanceof Object === false ) { return; }
// Response to specific message previously sent
if ( details.msgId !== undefined ) {
const resolver = this.pending.get(details.msgId);
if ( resolver !== undefined ) {
this.pending.delete(details.msgId);
resolver(details.msg);
return;
if ( response.broadcast === true && !response.channelName ) {
for ( channel in channels ) {
if ( channels.hasOwnProperty(channel) === false ) {
continue;
}
listener = channels[channel].listener;
if ( typeof listener === 'function' ) {
listener(response.msg);
}
}
return;
}
// Unhandled messages
this.extensions.every(ext => ext.canProcessMessage(details) !== true);
},
messageListenerBound: null,
canDestroyPort: function() {
return this.pending.size === 0 &&
(
this.extensions.length === 0 ||
this.extensions.every(e => e.canDestroyPort())
);
},
if ( response.requestId ) {
listener = vAPI.messaging.listeners[response.requestId];
delete vAPI.messaging.listeners[response.requestId];
delete response.requestId;
}
mustDestroyPort: function() {
if ( this.extensions.length === 0 ) { return; }
this.extensions.forEach(e => e.mustDestroyPort());
this.extensions.length = 0;
},
if ( !listener ) {
channel = channels[response.channelName];
listener = channel && channel.listener;
}
portPoller: function() {
this.portTimer = null;
if ( this.port !== null && this.canDestroyPort() ) {
return this.destroyPort();
}
this.portTimer = vAPI.setTimeout(this.portPollerBound, this.portTimerDelay);
this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000);
},
portPollerBound: null,
if ( typeof listener === 'function' ) {
listener(response.msg);
}
};
destroyPort: function() {
if ( this.portTimer !== null ) {
clearTimeout(this.portTimer);
this.portTimer = null;
}
const port = this.port;
if ( port !== null ) {
port.disconnect();
port.onMessage.removeListener(this.messageListenerBound);
port.onDisconnect.removeListener(this.disconnectListenerBound);
this.port = null;
}
this.mustDestroyPort();
// service pending callbacks
if ( this.pending.size !== 0 ) {
const pending = this.pending;
this.pending = new Map();
for ( const resolver of pending.values() ) {
resolver();
}
}
},
/******************************************************************************/
createPort: function() {
if ( this.shuttingDown ) { return null; }
if ( this.messageListenerBound === null ) {
this.messageListenerBound = this.messageListener.bind(this);
this.disconnectListenerBound = this.disconnectListener.bind(this);
this.portPollerBound = this.portPoller.bind(this);
}
try {
this.port = browser.runtime.connect({name: vAPI.sessionId}) || null;
} catch (ex) {
this.port = null;
}
// Not having a valid port at this point means the main process is
// not available: no point keeping the content scripts alive.
if ( this.port === null ) {
vAPI.shutdown.exec();
return null;
}
this.port.onMessage.addListener(this.messageListenerBound);
this.port.onDisconnect.addListener(this.disconnectListenerBound);
this.portTimerDelay = 10000;
if ( this.portTimer === null ) {
this.portTimer = vAPI.setTimeout(
this.portPollerBound,
this.portTimerDelay
);
}
return this.port;
},
vAPI.messaging = {
port: null,
channels: {},
listeners: {},
requestId: 1,
getPort: function() {
return this.port !== null ? this.port : this.createPort();
setup: function() {
this.port = chrome.runtime.connect({name: vAPI.sessionId});
this.port.onMessage.addListener(messagingConnector);
},
send: function(channel, msg) {
// 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
// pages. Guard against this.
if ( this.pending.size > 50 ) {
vAPI.shutdown.exec();
}
const port = this.getPort();
if ( port === null ) {
return Promise.resolve();
close: function() {
if ( this.port === null ) {
return;
}
const msgId = this.msgIdGenerator++;
const promise = new Promise(resolve => {
this.pending.set(msgId, resolve);
});
port.postMessage({ channel, msgId, msg });
return promise;
this.port.disconnect();
this.port.onMessage.removeListener(messagingConnector);
this.port = null;
this.channels = {};
this.listeners = {};
},
// Dynamically extend capabilities.
extend: function() {
if ( this.extended === undefined ) {
this.extended = vAPI.messaging.send('vapi', {
what: 'extendClient'
}).then(( ) => {
return self.vAPI instanceof Object &&
this.extensions.length !== 0;
}).catch(( ) => {
});
channel: function(channelName, callback) {
if ( !channelName ) {
return;
}
return this.extended;
},
};
vAPI.shutdown.add(( ) => {
vAPI.messaging.shutdown();
window.vAPI = undefined;
});
this.channels[channelName] = {
channelName: channelName,
listener: typeof callback === 'function' ? callback : null,
send: function(message, callback) {
if ( vAPI.messaging.port === null ) {
vAPI.messaging.setup();
}
message = {
channelName: this.channelName,
msg: message
};
if ( callback ) {
message.requestId = vAPI.messaging.requestId++;
vAPI.messaging.listeners[message.requestId] = callback;
}
vAPI.messaging.port.postMessage(message);
},
close: function() {
delete vAPI.messaging.channels[this.channelName];
if ( Object.keys(vAPI.messaging.channels).length === 0 ) {
vAPI.messaging.close();
}
}
};
return this.channels[channelName];
}
};
/******************************************************************************/
/******************************************************************************/
// No need to have vAPI client linger around after shutdown if
// we are not a top window (because element picker can still
// be injected in top window).
if ( window !== window.top ) {
vAPI.shutdown.add(function() {
vAPI = null;
});
}
// <<<<<<<< end of HUGE-IF-BLOCK
/******************************************************************************/
/*******************************************************************************
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
setTimeout(function() { callback(); }, delay);
};
DO NOT:
- Remove the following code
- Add code beyond the following code
Reason:
- https://github.com/gorhill/uBlock/pull/3721
- uBO never uses the return value from injected content scripts
/******************************************************************************/
**/
})(this);
void 0;
/******************************************************************************/

@ -1,7 +1,7 @@
/*******************************************************************************
uMatrix - a browser extension to black/white list requests.
Copyright (C) 2014-present The uMatrix/uBlock Origin authors
µMatrix - a browser extension to block requests.
Copyright (C) 2014 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -21,200 +21,64 @@
// For background page or non-background pages
'use strict';
/* global self */
/******************************************************************************/
/******************************************************************************/
vAPI.T0 = Date.now();
/******************************************************************************/
(function() {
vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self);
'use strict';
/******************************************************************************/
vAPI.webextFlavor = {
major: 0,
soup: new Set()
};
var vAPI = self.vAPI = self.vAPI || {};
var chrome = self.chrome;
(( ) => {
const ua = navigator.userAgent;
const flavor = vAPI.webextFlavor;
const soup = flavor.soup;
const dispatch = function() {
window.dispatchEvent(new CustomEvent('webextFlavor'));
};
/******************************************************************************/
// This is always true.
soup.add('ublock').add('webext');
// http://www.w3.org/International/questions/qa-scripts#directions
// Whether this is a dev build.
if ( /^\d+\.\d+\.\d+\D/.test(browser.runtime.getManifest().version) ) {
soup.add('devbuild');
}
var setScriptDirection = function(language) {
document.body.setAttribute(
'dir',
['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1 ? 'rtl' : 'ltr'
);
};
if ( /\bMobile\b/.test(ua) ) {
soup.add('mobile');
}
/******************************************************************************/
// Asynchronous
if (
browser instanceof Object &&
typeof browser.runtime.getBrowserInfo === 'function'
) {
browser.runtime.getBrowserInfo().then(info => {
flavor.major = parseInt(info.version, 10) || 60;
soup.add(info.vendor.toLowerCase())
.add(info.name.toLowerCase());
if ( soup.has('firefox') && flavor.major < 57 ) {
soup.delete('html_filtering');
}
dispatch();
});
if ( browser.runtime.getURL('').startsWith('moz-extension://') ) {
soup.add('mozilla')
.add('firefox')
.add('user_stylesheet')
.add('html_filtering');
flavor.major = 60;
}
vAPI.download = function(details) {
if ( !details.url ) {
return;
}
// Synchronous -- order of tests is important
let match;
if ( (match = /\bEdge\/(\d+)/.exec(ua)) !== null ) {
flavor.major = parseInt(match[1], 10) || 0;
soup.add('microsoft').add('edge');
} else if ( (match = /\bOPR\/(\d+)/.exec(ua)) !== null ) {
const reEx = /\bChrom(?:e|ium)\/([\d.]+)/;
if ( reEx.test(ua) ) { match = reEx.exec(ua); }
flavor.major = parseInt(match[1], 10) || 0;
soup.add('opera').add('chromium');
} else if ( (match = /\bChromium\/(\d+)/.exec(ua)) !== null ) {
flavor.major = parseInt(match[1], 10) || 0;
soup.add('chromium');
} else if ( (match = /\bChrome\/(\d+)/.exec(ua)) !== null ) {
flavor.major = parseInt(match[1], 10) || 0;
soup.add('google').add('chromium');
} else if ( (match = /\bSafari\/(\d+)/.exec(ua)) !== null ) {
flavor.major = parseInt(match[1], 10) || 0;
soup.add('apple').add('safari');
}
// https://github.com/gorhill/uBlock/issues/3588
if ( soup.has('chromium') && flavor.major >= 66 ) {
soup.add('user_stylesheet');
}
// Don't starve potential listeners
vAPI.setTimeout(dispatch, 97);
})();
/******************************************************************************/
{
const punycode = self.punycode;
const reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
const reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
const reHostFromNakedAuthority = /^[0-9a-z._-]+[0-9a-z]$/i;
const reHostFromAuthority = /^(?:[^@]*@)?([^:]+)(?::\d*)?$/;
const reIPv6FromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]+\])(?::\d*)?$/i;
const reMustNormalizeHostname = /[^0-9a-z._-]/;
vAPI.hostnameFromURI = function(uri) {
let matches = reCommonHostnameFromURL.exec(uri);
if ( matches !== null ) { return matches[1]; }
matches = reAuthorityFromURI.exec(uri);
if ( matches === null ) { return ''; }
const authority = matches[1].slice(2);
if ( reHostFromNakedAuthority.test(authority) ) {
return authority.toLowerCase();
}
matches = reHostFromAuthority.exec(authority);
if ( matches === null ) {
matches = reIPv6FromAuthority.exec(authority);
if ( matches === null ) { return ''; }
}
let hostname = matches[1];
while ( hostname.endsWith('.') ) {
hostname = hostname.slice(0, -1);
}
if ( reMustNormalizeHostname.test(hostname) ) {
hostname = punycode.toASCII(hostname.toLowerCase());
}
return hostname;
};
const reHostnameFromNetworkURL =
/^(?:http|ws|ftp)s?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
vAPI.hostnameFromNetworkURL = function(url) {
const matches = reHostnameFromNetworkURL.exec(url);
return matches !== null ? matches[1] : '';
};
const psl = self.publicSuffixList;
const reIPAddressNaive = /^\d+\.\d+\.\d+\.\d+$|^\[[\da-zA-Z:]+\]$/;
vAPI.domainFromHostname = function(hostname) {
return reIPAddressNaive.test(hostname)
? hostname
: psl.getDomain(hostname);
};
vAPI.domainFromURI = function(uri) {
return uri !== ''
? vAPI.domainFromHostname(vAPI.hostnameFromURI(uri))
: '';
};
}
/******************************************************************************/
vAPI.download = function(details) {
if ( !details.url ) { return; }
const a = document.createElement('a');
var a = document.createElement('a');
a.href = details.url;
a.setAttribute('download', details.filename || '');
a.setAttribute('type', 'text/plain');
a.dispatchEvent(new MouseEvent('click'));
};
/******************************************************************************/
vAPI.getURL = browser.runtime.getURL;
vAPI.insertHTML = function(node, html) {
node.innerHTML = html;
};
/******************************************************************************/
vAPI.i18n = browser.i18n.getMessage;
// http://www.w3.org/International/questions/qa-scripts#directions
document.body.setAttribute(
'dir',
['ar', 'he', 'fa', 'ps', 'ur'].indexOf(vAPI.i18n('@@ui_locale')) !== -1
? 'rtl'
: 'ltr'
);
vAPI.getURL = chrome.runtime.getURL;
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/3057
// - webNavigation.onCreatedNavigationTarget become broken on Firefox when we
// try to make the popup panel close itself using the original
// `window.open('', '_self').close()`.
vAPI.i18n = chrome.i18n.getMessage;
vAPI.closePopup = function() {
if ( vAPI.webextFlavor.soup.has('firefox') ) {
window.close();
return;
}
setScriptDirection(vAPI.i18n('@@ui_locale'));
/******************************************************************************/
// TODO: try to figure why this was used instead of a plain window.close().
// https://github.com/gorhill/uBlock/commit/b301ac031e0c2e9a99cb6f8953319d44e22f33d2#diff-bc664f26b9c453e0d43a9379e8135c6a
window.open('', '_self').close();
vAPI.closePopup = function() {
window.open('','_self').close();
};
/******************************************************************************/
@ -224,56 +88,14 @@ vAPI.closePopup = function() {
// This storage is optional, but it is nice to have, for a more polished user
// experience.
// https://github.com/gorhill/uBlock/issues/2824
// Use a dummy localStorage if for some reasons it's not available.
// https://github.com/gorhill/uMatrix/issues/840
// Always use a wrapper to seamlessly handle exceptions
vAPI.localStorage = {
clear: function() {
try {
window.localStorage.clear();
} catch(ex) {
}
},
getItem: function(key) {
try {
return window.localStorage.getItem(key);
} catch(ex) {
}
return null;
},
removeItem: function(key) {
try {
window.localStorage.removeItem(key);
} catch(ex) {
}
},
setItem: function(key, value) {
try {
window.localStorage.setItem(key, value);
} catch(ex) {
}
}
};
vAPI.localStorage = window.localStorage;
/******************************************************************************/
/*******************************************************************************
vAPI.setTimeout = vAPI.setTimeout || window.setTimeout.bind(window);
DO NOT:
- Remove the following code
- Add code beyond the following code
Reason:
- https://github.com/gorhill/uBlock/pull/3721
- uBO never uses the return value from injected content scripts
/******************************************************************************/
**/
})();
void 0;
/******************************************************************************/

@ -1,178 +0,0 @@
/*******************************************************************************
uMatrix - a browser extension to block requests.
Copyright (C) 2017-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uMatrix
*/
// For background page
'use strict';
/******************************************************************************/
(( ) => {
// https://github.com/uBlockOrigin/uBlock-issues/issues/407
if ( vAPI.webextFlavor.soup.has('chromium') === false ) { return; }
const extToTypeMap = new Map([
['eot','font'],['otf','font'],['svg','font'],['ttf','font'],['woff','font'],['woff2','font'],
['mp3','media'],['mp4','media'],['webm','media'],
['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image']
]);
const headerValue = (headers, name) => {
let i = headers.length;
while ( i-- ) {
if ( headers[i].name.toLowerCase() === name ) {
return headers[i].value.trim();
}
}
return '';
};
const parsedURL = new URL('https://www.example.org/');
// Extend base class to normalize as per platform.
vAPI.Net = class extends vAPI.Net {
constructor() {
super();
this.suspendedTabIds = new Set();
}
normalizeDetails(details) {
// Chromium 63+ supports the `initiator` property, which contains
// the URL of the origin from which the network request was made.
if (
typeof details.initiator === 'string' &&
details.initiator !== 'null'
) {
details.documentUrl = details.initiator;
}
let type = details.type;
if ( type === 'imageset' ) {
details.type = 'image';
return;
}
// The rest of the function code is to normalize type
if ( type !== 'other' ) { return; }
// Try to map known "extension" part of URL to request type.
parsedURL.href = details.url;
const path = parsedURL.pathname,
pos = path.indexOf('.', path.length - 6);
if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) {
details.type = type;
return;
}
// Try to extract type from response headers if present.
if ( details.responseHeaders ) {
type = headerValue(details.responseHeaders, 'content-type');
if ( type.startsWith('font/') ) {
details.type = 'font';
return;
}
if ( type.startsWith('image/') ) {
details.type = 'image';
return;
}
if ( type.startsWith('audio/') || type.startsWith('video/') ) {
details.type = 'media';
return;
}
}
}
// https://www.reddit.com/r/uBlockOrigin/comments/9vcrk3/
// Some types can be mapped from 'other', thus include 'other' if and
// only if the caller is interested in at least one of those types.
denormalizeTypes(types) {
if ( types.length === 0 ) {
return Array.from(this.validTypes);
}
const out = new Set();
for ( const type of types ) {
if ( this.validTypes.has(type) ) {
out.add(type);
}
}
if ( out.has('other') === false ) {
for ( const type of extToTypeMap.values() ) {
if ( out.has(type) ) {
out.add('other');
break;
}
}
}
return Array.from(out);
}
suspendOneRequest(details) {
this.suspendedTabIds.add(details.tabId);
return { cancel: true };
}
unsuspendAllRequests() {
for ( const tabId of this.suspendedTabIds ) {
vAPI.tabs.reload(tabId);
}
this.suspendedTabIds.clear();
}
};
})();
/******************************************************************************/
// https://github.com/uBlockOrigin/uBlock-issues/issues/548
// Use `X-DNS-Prefetch-Control` to workaround Chromium's disregard of the
// setting "Predict network actions to improve page load performance".
vAPI.prefetching = (( ) => {
// https://github.com/uBlockOrigin/uBlock-issues/issues/407
if ( vAPI.webextFlavor.soup.has('chromium') === false ) { return; }
let listening = false;
const onHeadersReceived = function(details) {
details.responseHeaders.push({
name: 'X-DNS-Prefetch-Control',
value: 'off'
});
return { responseHeaders: details.responseHeaders };
};
return state => {
const wr = chrome.webRequest;
if ( state && listening ) {
wr.onHeadersReceived.removeListener(onHeadersReceived);
listening = false;
} else if ( !state && !listening ) {
wr.onHeadersReceived.addListener(
onHeadersReceived,
{
urls: [ 'http://*/*', 'https://*/*' ],
types: [ 'main_frame', 'sub_frame' ]
},
[ 'blocking', 'responseHeaders' ]
);
listening = true;
}
};
})();
/******************************************************************************/

@ -1,86 +0,0 @@
/*******************************************************************************
uMatrix - a browser extension to block requests.
Copyright (C) 2017-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
'use strict';
/* global HTMLDocument, XMLDocument */
// For background page, auxiliary pages, and content scripts.
/******************************************************************************/
if ( self.browser instanceof Object ) {
self.chrome = self.browser;
} else {
self.browser = self.chrome;
}
/******************************************************************************/
// https://bugzilla.mozilla.org/show_bug.cgi?id=1408996#c9
var vAPI = self.vAPI; // jshint ignore:line
// https://github.com/chrisaljoudi/uBlock/issues/464
// https://github.com/chrisaljoudi/uBlock/issues/1528
// A XMLDocument can be a valid HTML document.
// https://github.com/gorhill/uBlock/issues/1124
// Looks like `contentType` is on track to be standardized:
// https://dom.spec.whatwg.org/#concept-document-content-type
// https://forums.lanik.us/viewtopic.php?f=64&t=31522
// Skip text/plain documents.
if (
(
document instanceof HTMLDocument ||
document instanceof XMLDocument &&
document.createElement('div') instanceof HTMLDivElement
) &&
(
/^image\/|^text\/plain/.test(document.contentType || '') === false
) &&
(
self.vAPI instanceof Object === false || vAPI.nuTensor !== true
)
) {
vAPI = self.vAPI = { nuTensor: true };
}
/*******************************************************************************
DO NOT:
- Remove the following code
- Add code beyond the following code
Reason:
- https://github.com/gorhill/uBlock/pull/3721
- uMatrix never uses the return value from injected content scripts
**/
void 0;

@ -1,176 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
'use strict';
// `webext` is a promisified api of `chrome`. Entries are added as
// the promisification of uBO progress.
const webext = (( ) => { // jshint ignore:line
// >>>>> start of private scope
const noopFunc = ( ) => { };
const promisifyNoFail = function(thisArg, fnName, outFn = r => r) {
const fn = thisArg[fnName];
return function() {
return new Promise(resolve => {
fn.call(thisArg, ...arguments, function() {
if ( chrome.runtime.lastError instanceof Object ) {
void chrome.runtime.lastError.message;
}
resolve(outFn(...arguments));
});
});
};
};
const promisify = function(thisArg, fnName) {
const fn = thisArg[fnName];
return function() {
return new Promise((resolve, reject) => {
fn.call(thisArg, ...arguments, function() {
const lastError = chrome.runtime.lastError;
if ( lastError instanceof Object ) {
return reject(lastError.message);
}
resolve(...arguments);
});
});
};
};
const webext = {
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/browserAction
browserAction: {
setBadgeBackgroundColor: promisifyNoFail(chrome.browserAction, 'setBadgeBackgroundColor'),
setBadgeText: promisifyNoFail(chrome.browserAction, 'setBadgeText'),
setBadgeTextColor: noopFunc,
setIcon: promisifyNoFail(chrome.browserAction, 'setIcon'),
setTitle: promisifyNoFail(chrome.browserAction, 'setTitle'),
},
cookies: {
getAll: promisifyNoFail(chrome.cookies, 'getAll'),
remove: promisifyNoFail(chrome.cookies, 'remove'),
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/menus
/*
menus: {
create: function() {
return chrome.contextMenus.create(...arguments, ( ) => {
void chrome.runtime.lastError;
});
},
onClicked: chrome.contextMenus.onClicked,
remove: promisifyNoFail(chrome.contextMenus, 'remove'),
},
*/
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy
privacy: {
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage
storage: {
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local
local: {
clear: promisify(chrome.storage.local, 'clear'),
get: promisify(chrome.storage.local, 'get'),
getBytesInUse: promisify(chrome.storage.local, 'getBytesInUse'),
remove: promisify(chrome.storage.local, 'remove'),
set: promisify(chrome.storage.local, 'set'),
},
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs
tabs: {
get: promisifyNoFail(chrome.tabs, 'get', tab => tab instanceof Object ? tab : null),
executeScript: promisifyNoFail(chrome.tabs, 'executeScript'),
insertCSS: promisifyNoFail(chrome.tabs, 'insertCSS'),
query: promisifyNoFail(chrome.tabs, 'query', tabs => Array.isArray(tabs) ? tabs : []),
reload: promisifyNoFail(chrome.tabs, 'reload'),
remove: promisifyNoFail(chrome.tabs, 'remove'),
update: promisifyNoFail(chrome.tabs, 'update', tab => tab instanceof Object ? tab : null),
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webNavigation
webNavigation: {
getFrame: promisify(chrome.webNavigation, 'getFrame'),
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/windows
windows: {
get: promisifyNoFail(chrome.windows, 'get', win => win instanceof Object ? win : null),
create: promisifyNoFail(chrome.windows, 'create', win => win instanceof Object ? win : null),
update: promisifyNoFail(chrome.windows, 'update', win => win instanceof Object ? win : null),
},
};
// browser.privacy entries
{
const settings = [
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/network
[ 'network', 'networkPredictionEnabled' ],
[ 'network', 'webRTCIPHandlingPolicy' ],
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/websites
[ 'websites', 'hyperlinkAuditingEnabled' ],
];
for ( const [ category, setting ] of settings ) {
let categoryEntry = webext.privacy[category];
if ( categoryEntry instanceof Object === false ) {
categoryEntry = webext.privacy[category] = {};
}
const settingEntry = categoryEntry[setting] = {};
const thisArg = chrome.privacy[category][setting];
settingEntry.clear = promisifyNoFail(thisArg, 'clear');
settingEntry.get = promisifyNoFail(thisArg, 'get');
settingEntry.set = promisifyNoFail(thisArg, 'set');
}
}
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/managed
if ( chrome.storage.managed instanceof Object ) {
webext.storage.managed = {
get: promisify(chrome.storage.managed, 'get'),
};
}
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync
if ( chrome.storage.sync instanceof Object ) {
webext.storage.sync = {
QUOTA_BYTES: chrome.storage.sync.QUOTA_BYTES,
QUOTA_BYTES_PER_ITEM: chrome.storage.sync.QUOTA_BYTES_PER_ITEM,
MAX_ITEMS: chrome.storage.sync.MAX_ITEMS,
MAX_WRITE_OPERATIONS_PER_HOUR: chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR,
MAX_WRITE_OPERATIONS_PER_MINUTE: chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE,
clear: promisify(chrome.storage.sync, 'clear'),
get: promisify(chrome.storage.sync, 'get'),
getBytesInUse: promisify(chrome.storage.sync, 'getBytesInUse'),
remove: promisify(chrome.storage.sync, 'remove'),
set: promisify(chrome.storage.sync, 'set'),
};
}
// https://bugs.chromium.org/p/chromium/issues/detail?id=608854
if ( chrome.tabs.removeCSS instanceof Function ) {
webext.tabs.removeCSS = promisifyNoFail(chrome.tabs, 'removeCSS');
}
return webext;
// <<<<< end of private scope
})();

@ -0,0 +1,162 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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/uMatrix
*/
/* global ADDON_UNINSTALL, APP_SHUTDOWN */
/* exported startup, shutdown, install, uninstall */
'use strict';
/******************************************************************************/
// Accessing the context of the background page:
// var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=umatrix]').contentWindow;
let bgProcess;
let version;
const hostName = 'umatrix';
const restartListener = {
get messageManager() {
return Components.classes['@mozilla.org/parentprocessmessagemanager;1']
.getService(Components.interfaces.nsIMessageListenerManager);
},
receiveMessage: function() {
shutdown();
startup();
}
};
/******************************************************************************/
function startup(data/*, reason*/) {
if ( data !== undefined ) {
version = data.version;
}
let appShell = Components.classes['@mozilla.org/appshell/appShellService;1']
.getService(Components.interfaces.nsIAppShellService);
let onReady = function(e) {
if ( e ) {
this.removeEventListener(e.type, onReady);
}
let hiddenDoc = appShell.hiddenDOMWindow.document;
// https://github.com/gorhill/uBlock/issues/10
// Fixed by github.com/AlexVallat:
// https://github.com/chrisaljoudi/uBlock/issues/1149
// https://github.com/AlexVallat/uBlock/commit/e762a29d308caa46578cdc34a9be92c4ad5ecdd0
if ( hiddenDoc.readyState === 'loading' ) {
hiddenDoc.addEventListener('DOMContentLoaded', onReady);
return;
}
bgProcess = hiddenDoc.documentElement.appendChild(
hiddenDoc.createElementNS('http://www.w3.org/1999/xhtml', 'iframe')
);
bgProcess.setAttribute(
'src',
'chrome://' + hostName + '/content/background.html#' + version
);
restartListener.messageManager.addMessageListener(
hostName + '-restart',
restartListener
);
};
var ready = false;
try {
ready = appShell.hiddenDOMWindow &&
appShell.hiddenDOMWindow.document;
} catch (ex) {
}
if ( ready ) {
onReady();
return;
}
let ww = Components.classes['@mozilla.org/embedcomp/window-watcher;1']
.getService(Components.interfaces.nsIWindowWatcher);
ww.registerNotification({
observe: function(win, topic) {
if ( topic !== 'domwindowopened' ) {
return;
}
try {
void appShell.hiddenDOMWindow;
} catch (ex) {
return;
}
ww.unregisterNotification(this);
win.addEventListener('DOMContentLoaded', onReady);
}
});
}
/******************************************************************************/
function shutdown(data, reason) {
if ( reason === APP_SHUTDOWN ) {
return;
}
bgProcess.parentNode.removeChild(bgProcess);
if ( data === undefined ) {
return;
}
// Remove the restartObserver only when the extension is being disabled
restartListener.messageManager.removeMessageListener(
hostName + '-restart',
restartListener
);
}
/******************************************************************************/
function install() {
// https://bugzil.la/719376
Components.classes['@mozilla.org/intl/stringbundle;1']
.getService(Components.interfaces.nsIStringBundleService)
.flushBundles();
}
/******************************************************************************/
function uninstall(data, aReason) {
if ( aReason !== ADDON_UNINSTALL ) {
return;
}
// To cleanup vAPI.localStorage in vapi-common.js, aka
// "extensions.umatrix.*" in `about:config`.
Components.utils.import('resource://gre/modules/Services.jsm', null)
.Services.prefs
.getBranch('extensions.' + hostName + '.')
.deleteBranch('');
}
/******************************************************************************/

@ -0,0 +1 @@
content umatrix ./

@ -0,0 +1,46 @@
#umatrix-legacy-button {
list-style-image: url('../img/browsericons/icon19-19.png');
}
#umatrix-legacy-button.off {
list-style-image: url('../img/browsericons/icon19-off.png');
}
toolbar[iconsize="small"] #umatrix-legacy-button {
list-style-image: url('../img/browsericons/icon19-19.png');
}
toolbar[iconsize="small"] #umatrix-legacy-button.off {
list-style-image: url('../img/browsericons/icon19-off.png');
}
#umatrix-legacy-button[badge]::before {
background: #000;
color: #fff;
content: attr(badge);
font: bold 10px sans-serif;
margin-top: -2px;
padding: 0 2px;
position: fixed;
}
/* This hack required because if the before content changes it de-pops the
popup (without firing any events). So just hide it instead. Note, can't
actually *hide* it, or the same thing happens.
**/
#umatrix-legacy-button[badge=""]::before {
padding: 0;
}
/* Override off state when in palette */
toolbarpaletteitem #umatrix-legacy-button.off {
list-style-image: url('../img/browsericons/icon19-12.png');
}
/* Override badge when in palette */
toolbarpaletteitem #umatrix-legacy-button[badge]::before {
content: none;
}
/* Prevent pale moon from showing the arrow underneath the button */
/* https://github.com/chrisaljoudi/uBlock/issues/1449#issuecomment-112112761 */
#umatrix-legacy-button .toolbarbutton-menu-dropmarker {
display: none;
-moz-box-orient: horizontal;
}

@ -0,0 +1,348 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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/uMatrix
*/
/* global Components */
'use strict';
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/800#issuecomment-146580443
this.EXPORTED_SYMBOLS = ['contentObserver', 'LocationChangeListener'];
const {interfaces: Ci, utils: Cu} = Components;
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;
// Cu.import('resource://gre/modules/Console.jsm');
/******************************************************************************/
const getMessageManager = function(win) {
let iface = win
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDocShell)
.sameTypeRootTreeItem
.QueryInterface(Ci.nsIDocShell)
.QueryInterface(Ci.nsIInterfaceRequestor);
try {
return iface.getInterface(Ci.nsIContentFrameMessageManager);
} catch (ex) {
// This can throw. It appears `shouldLoad` can be called *after* a
// tab has been closed. For example, a case where this happens all
// the time (FF38):
// - Open twitter.com (assuming you have an account and are logged in)
// - Close twitter.com
// There will be an exception raised when `shouldLoad` is called
// to process a XMLHttpRequest with URL `https://twitter.com/i/jot`
// fired from `https://twitter.com/`, *after* the tab is closed.
// In such case, `win` is `about:blank`.
}
return null;
};
/******************************************************************************/
var contentObserver = {
classDescription: 'content-policy for ' + hostName,
classID: Components.ID('{c84283d4-9975-41b7-b1a4-f106af56b51d}'),
contractID: '@' + hostName + '/content-policy;1',
ACCEPT: Ci.nsIContentPolicy.ACCEPT,
MAIN_FRAME: Ci.nsIContentPolicy.TYPE_DOCUMENT,
contentBaseURI: 'chrome://' + hostName + '/content/js/',
cpMessageName: hostName + ':shouldLoad',
uniqueSandboxId: 1,
modernFirefox: Services.appinfo.ID === '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}' &&
Services.vc.compare(Services.appinfo.platformVersion, '44') > 0,
get componentRegistrar() {
return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
},
get categoryManager() {
return Components.classes['@mozilla.org/categorymanager;1']
.getService(Ci.nsICategoryManager);
},
QueryInterface: XPCOMUtils.generateQI([
Ci.nsIFactory,
Ci.nsIObserver,
Ci.nsIContentPolicy,
Ci.nsISupportsWeakReference
]),
createInstance: function(outer, iid) {
if ( outer ) {
throw Components.results.NS_ERROR_NO_AGGREGATION;
}
return this.QueryInterface(iid);
},
register: function() {
Services.obs.addObserver(this, 'document-element-inserted', true);
if ( !this.modernFirefox ) {
this.componentRegistrar.registerFactory(
this.classID,
this.classDescription,
this.contractID,
this
);
this.categoryManager.addCategoryEntry(
'content-policy',
this.contractID,
this.contractID,
false,
true
);
}
},
unregister: function() {
Services.obs.removeObserver(this, 'document-element-inserted');
if ( !this.modernFirefox ) {
this.componentRegistrar.unregisterFactory(
this.classID,
this
);
this.categoryManager.deleteCategoryEntry(
'content-policy',
this.contractID,
false
);
}
},
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy
// https://bugzil.la/612921
shouldLoad: function(type, location, origin, context) {
if ( Services === undefined || !context ) {
return this.ACCEPT;
}
if ( !location.schemeIs('http') && !location.schemeIs('https') ) {
return this.ACCEPT;
}
var contextWindow;
if ( type === this.MAIN_FRAME ) {
contextWindow = context.contentWindow || context;
} else if ( type === this.SUB_FRAME ) {
contextWindow = context.contentWindow;
} else {
contextWindow = (context.ownerDocument || context).defaultView;
}
// The context for the toolbar popup is an iframe element here,
// so check context.top instead of context
if ( !contextWindow.top || !contextWindow.location ) {
return this.ACCEPT;
}
let messageManager = getMessageManager(contextWindow);
if ( messageManager === null ) {
return this.ACCEPT;
}
let details = {
rawType: type,
url: location.asciiSpec
};
if ( typeof messageManager.sendRpcMessage === 'function' ) {
// https://bugzil.la/1092216
messageManager.sendRpcMessage(this.cpMessageName, details);
} else {
// Compatibility for older versions
messageManager.sendSyncMessage(this.cpMessageName, details);
}
return this.ACCEPT;
},
initContentScripts: function(win, sandbox) {
let messager = getMessageManager(win);
let sandboxId = hostName + ':sb:' + this.uniqueSandboxId++;
if ( sandbox ) {
let sandboxName = [
win.location.href.slice(0, 100),
win.document.title.slice(0, 100)
].join(' | ');
// https://github.com/gorhill/uMatrix/issues/325
// "Pass sameZoneAs to sandbox constructor to make GCs cheaper"
sandbox = Cu.Sandbox([win], {
sameZoneAs: win.top,
sandboxName: sandboxId + '[' + sandboxName + ']',
sandboxPrototype: win,
wantComponents: false,
wantXHRConstructor: false
});
sandbox.injectScript = function(script) {
Services.scriptloader.loadSubScript(script, sandbox);
};
}
else {
sandbox = win;
}
sandbox._sandboxId_ = sandboxId;
sandbox.sendAsyncMessage = messager.sendAsyncMessage;
sandbox.addMessageListener = function(callback) {
if ( sandbox._messageListener_ ) {
sandbox.removeMessageListener();
}
sandbox._messageListener_ = function(message) {
callback(message.data);
};
messager.addMessageListener(
sandbox._sandboxId_,
sandbox._messageListener_
);
messager.addMessageListener(
hostName + ':broadcast',
sandbox._messageListener_
);
};
sandbox.removeMessageListener = function() {
try {
messager.removeMessageListener(
sandbox._sandboxId_,
sandbox._messageListener_
);
messager.removeMessageListener(
hostName + ':broadcast',
sandbox._messageListener_
);
} catch (ex) {
// It throws sometimes, mostly when the popup closes
}
sandbox._messageListener_ = null;
};
return sandbox;
},
observe: function(doc) {
let win = doc.defaultView;
if ( !win ) {
return;
}
let loc = win.location;
if ( !loc ) {
return;
}
// https://github.com/gorhill/uBlock/issues/260
// TODO: We may have to skip more types, for now let's be
// conservative, i.e. let's not test against `text/html`.
if ( doc.contentType.lastIndexOf('image/', 0) === 0 ) {
return;
}
if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) {
if ( loc.protocol === 'chrome:' && loc.host === hostName ) {
this.initContentScripts(win);
}
// What about data: and about:blank?
return;
}
let lss = Services.scriptloader.loadSubScript;
let sandbox = this.initContentScripts(win, true);
// Can throw with attempts at injecting into non-HTML document.
// Example: https://a.pomf.se/avonjf.webm
try {
lss(this.contentBaseURI + 'vapi-client.js', sandbox);
lss(this.contentBaseURI + 'contentscript-start.js', sandbox);
} catch (ex) {
return; // don't further try to inject anything
}
let docReady = (e) => {
let doc = e.target;
doc.removeEventListener(e.type, docReady, true);
lss(this.contentBaseURI + 'contentscript-end.js', sandbox);
};
if ( doc.readyState === 'loading') {
doc.addEventListener('DOMContentLoaded', docReady, true);
} else {
docReady({ target: doc, type: 'DOMContentLoaded' });
}
}
};
/******************************************************************************/
const locationChangedMessageName = hostName + ':locationChanged';
var LocationChangeListener = function(docShell) {
if ( !docShell ) {
return;
}
var requestor = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
var ds = requestor.getInterface(Ci.nsIWebProgress);
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);
}
};
LocationChangeListener.prototype.QueryInterface = XPCOMUtils.generateQI([
'nsIWebProgressListener',
'nsISupportsWeakReference'
]);
LocationChangeListener.prototype.onLocationChange = function(webProgress, request, location, flags) {
if ( !webProgress.isTopLevel ) {
return;
}
this.messageManager.sendAsyncMessage(locationChangedMessageName, {
url: location.asciiSpec,
flags: flags,
});
};
/******************************************************************************/
contentObserver.register();
/******************************************************************************/

@ -0,0 +1,72 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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/uMatrix
*/
/******************************************************************************/
var locationChangeListener; // Keep alive while frameScript is alive
(function() {
'use strict';
/******************************************************************************/
let {contentObserver, LocationChangeListener} = Components.utils.import(
Components.stack.filename.replace('Script', 'Module'),
null
);
let injectContentScripts = function(win) {
if ( !win || !win.document ) {
return;
}
contentObserver.observe(win.document);
if ( win.frames && win.frames.length ) {
let i = win.frames.length;
while ( i-- ) {
injectContentScripts(win.frames[i]);
}
}
};
let onLoadCompleted = function() {
removeMessageListener('umatrix-load-completed', onLoadCompleted);
injectContentScripts(content);
};
addMessageListener('umatrix-load-completed', onLoadCompleted);
if ( docShell ) {
let Ci = Components.interfaces;
let wp = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
let dw = wp.DOMWindow;
if ( dw === dw.top ) {
locationChangeListener = new LocationChangeListener(docShell);
}
}
/******************************************************************************/
})();
/******************************************************************************/

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<r:RDF xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.mozilla.org/2004/em-rdf#">
<r:Description about="urn:mozilla:install-manifest">
<id>uMatrix@raymondhill.net</id>
<version>{version}</version>
<name>{name}</name>
<description>{description}</description>
<homepageURL>{homepage}</homepageURL>
<creator>{author}</creator>
<developer>Deathamns</developer>
<developer>Alex Vallat</developer>
<type>2</type>
<bootstrap>true</bootstrap>
<multiprocessCompatible>true</multiprocessCompatible>
<optionsType>2</optionsType>
{localized}
<!-- Firefox -->
<targetApplication>
<r:Description>
<id>{{ec8030f7-c20a-464f-9b0e-13a3a9e97384}}</id>
<minVersion>24.0</minVersion>
<maxVersion>46.0</maxVersion>
</r:Description>
</targetApplication>
<!-- SeaMonkey -->
<targetApplication>
<r:Description>
<id>{{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}}</id>
<minVersion>2.21</minVersion>
<maxVersion>2.39.*</maxVersion>
</r:Description>
</targetApplication>
<!-- Pale Moon -->
<targetApplication>
<r:Description>
<id>{{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}}</id>
<minVersion>25.0</minVersion>
<maxVersion>26.*</maxVersion>
</r:Description>
</targetApplication>
</r:Description>
</r:RDF>

@ -1,69 +0,0 @@
{
"browser_specific_settings": {
"gecko": {
"id": "nuTensor@geekprojects.com",
"strict_min_version": "60.0"
}
},
"author": "Raymond Hill",
"background": {
"page": "background.html"
},
"browser_action": {
"browser_style": false,
"default_icon": {
"19": "img/browsericons/icon19-off.png"
},
"default_title": "nuTensor",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["/js/vapi.js", "/js/vapi-client.js", "/js/contentscript-start.js"],
"run_at": "document_start",
"all_frames": true
},
{
"matches": ["http://*/*", "https://*/*"],
"js": ["/js/contentscript.js"],
"run_at": "document_end",
"all_frames": true
}
],
"default_locale": "en",
"description": "__MSG_extShortDesc__",
"icons": {
"16": "img/icon_16.png",
"128": "img/icon_128.png"
},
"manifest_version": 2,
"name": "nuTensor",
"options_ui": {
"page":"dashboard.html",
"open_in_tab": true
},
"permissions": [
"browsingData",
"cookies",
"dns",
"privacy",
"storage",
"tabs",
"webNavigation",
"webRequest",
"webRequestBlocking",
"<all_urls>"
],
"short_name": "nuTensor",
"sidebar_action": {
"default_title": "__MSG_loggerPageName__",
"default_panel": "logger-ui.html",
"default_icon": {
"16": "img/icon_16.png",
"128": "img/icon_128.png"
},
"open_at_install": false
},
"version": "0.9.9"
}

@ -0,0 +1,9 @@
<?xml version="1.0" ?>
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<setting type="control">
<vbox>
<button id="showDashboardButton"/>
<button id="showLoggerButton"/>
</vbox>
</setting>
</vbox>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,211 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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/uMatrix
*/
/* jshint esnext: true */
/* global addMessageListener, removeMessageListener, sendAsyncMessage */
// For non background pages
/******************************************************************************/
(function(self) {
'use strict';
/******************************************************************************/
var vAPI = self.vAPI = self.vAPI || {};
vAPI.firefox = true;
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
Math.random().toString(36).slice(2);
/******************************************************************************/
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
return setTimeout(function() { callback(); }, delay);
};
/******************************************************************************/
vAPI.shutdown = (function() {
var jobs = [];
var add = function(job) {
jobs.push(job);
};
var exec = function() {
//console.debug('Shutting down...');
var job;
while ( (job = jobs.pop()) ) {
job();
}
};
return {
add: add,
exec: exec
};
})();
/******************************************************************************/
var messagingConnector = function(response) {
if ( !response ) {
return;
}
var channels = vAPI.messaging.channels;
var channel, listener;
if ( response.broadcast && !response.channelName ) {
for ( channel in channels ) {
if ( channels.hasOwnProperty(channel) === false ) {
continue;
}
listener = channels[channel].listener;
if ( typeof listener === 'function' ) {
listener(response.msg);
}
}
return;
}
if ( response.requestId ) {
listener = vAPI.messaging.listeners[response.requestId];
delete vAPI.messaging.listeners[response.requestId];
delete response.requestId;
}
if ( !listener ) {
channel = channels[response.channelName];
listener = channel && channel.listener;
}
if ( typeof listener === 'function' ) {
listener(response.msg);
}
};
/******************************************************************************/
vAPI.messaging = {
channels: {},
listeners: {},
requestId: 1,
setup: function() {
this.connector = function(msg) {
messagingConnector(JSON.parse(msg));
};
addMessageListener(this.connector);
this.channels.vAPI = {
listener: function(msg) {
if ( typeof msg.cmd === 'string' && msg.cmd === 'injectScript' ) {
var details = msg.details;
if ( !details.allFrames && window !== window.top ) {
return;
}
self.injectScript(details.file);
}
}
};
},
close: function() {
if ( !this.connector ) {
return;
}
removeMessageListener();
this.connector = null;
this.channels = {};
this.listeners = {};
},
channel: function(channelName, callback) {
if ( !channelName ) {
return;
}
this.channels[channelName] = {
channelName: channelName,
listener: typeof callback === 'function' ? callback : null,
send: function(message, callback) {
if ( !vAPI.messaging.connector ) {
vAPI.messaging.setup();
}
message = {
channelName: self._sandboxId_ + '|' + this.channelName,
msg: message
};
if ( callback ) {
message.requestId = vAPI.messaging.requestId++;
vAPI.messaging.listeners[message.requestId] = callback;
}
sendAsyncMessage('umatrix:background', message);
},
close: function() {
delete vAPI.messaging.channels[this.channelName];
}
};
return this.channels[channelName];
},
toggleListener: function({type, persisted}) {
if ( !vAPI.messaging.connector ) {
return;
}
if ( type === 'pagehide' ) {
removeMessageListener();
return;
}
if ( persisted ) {
addMessageListener(vAPI.messaging.connector);
}
}
};
window.addEventListener('pagehide', vAPI.messaging.toggleListener, true);
window.addEventListener('pageshow', vAPI.messaging.toggleListener, true);
/******************************************************************************/
// No need to have vAPI client linger around after shutdown if
// we are not a top window (because element picker can still
// be injected in top window).
if ( window !== window.top ) {
// Can anything be done?
}
/******************************************************************************/
})(this);
/******************************************************************************/

@ -0,0 +1,186 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 The µBlock authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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/uMatrix
*/
/* global sendAsyncMessage */
// For background page or non-background pages
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
const {Services} = Components.utils.import(
'resource://gre/modules/Services.jsm',
null
);
var vAPI = self.vAPI = self.vAPI || {};
/******************************************************************************/
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, extra) {
return setTimeout(function(a) { callback(a); }, delay, extra);
};
/******************************************************************************/
// http://www.w3.org/International/questions/qa-scripts#directions
var setScriptDirection = function(language) {
document.body.setAttribute(
'dir',
['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1 ? 'rtl' : 'ltr'
);
};
/******************************************************************************/
vAPI.download = function(details) {
if ( !details.url ) {
return;
}
var a = document.createElement('a');
a.href = details.url;
a.setAttribute('download', details.filename || '');
a.dispatchEvent(new MouseEvent('click'));
};
/******************************************************************************/
vAPI.insertHTML = (function() {
const parser = Components.classes['@mozilla.org/parserutils;1']
.getService(Components.interfaces.nsIParserUtils);
// https://github.com/gorhill/uBlock/issues/845
// Apparently dashboard pages execute with `about:blank` principal.
return function(node, html) {
while ( node.firstChild ) {
node.removeChild(node.firstChild);
}
node.appendChild(parser.parseFragment(
html,
parser.SanitizerAllowStyle,
false,
Services.io.newURI('about:blank', null, null),
document.documentElement
));
};
})();
/******************************************************************************/
vAPI.getURL = function(path) {
return 'chrome://' + location.host + '/content/' + path.replace(/^\/+/, '');
};
/******************************************************************************/
vAPI.i18n = (function() {
var stringBundle = Services.strings.createBundle(
'chrome://' + location.host + '/locale/messages.properties'
);
return function(s) {
try {
return stringBundle.GetStringFromName(s);
} catch (ex) {
return '';
}
};
})();
setScriptDirection(navigator.language);
/******************************************************************************/
vAPI.closePopup = function() {
sendAsyncMessage(location.host + ':closePopup');
};
/******************************************************************************/
// A localStorage-like object which should be accessible from the
// background page or auxiliary pages.
// This storage is optional, but it is nice to have, for a more polished user
// experience.
vAPI.localStorage = {
pbName: '',
pb: null,
str: Components.classes['@mozilla.org/supports-string;1']
.createInstance(Components.interfaces.nsISupportsString),
init: function(pbName) {
this.pbName = pbName;
this.pb = Services.prefs.getBranch(pbName);
},
getItem: function(key) {
try {
return this.pb.getComplexValue(
key,
Components.interfaces.nsISupportsString
).data;
} catch (ex) {
return null;
}
},
setItem: function(key, value) {
this.str.data = value;
this.pb.setComplexValue(
key,
Components.interfaces.nsISupportsString,
this.str
);
},
getBool: function(key) {
try {
return this.pb.getBoolPref(key);
} catch (ex) {
return null;
}
},
setBool: function(key, value) {
this.pb.setBoolPref(key, value);
},
setDefaultBool: function(key, defaultValue) {
Services.prefs.getDefaultBranch(this.pbName).setBoolPref(key, defaultValue);
},
removeItem: function(key) {
this.pb.clearUserPref(key);
},
clear: function() {
this.pb.deleteBranch('');
}
};
vAPI.localStorage.init('extensions.' + location.host + '.');
/******************************************************************************/
})();
/******************************************************************************/

@ -0,0 +1 @@
/* Firefox: no platform-specific code */

@ -1,263 +0,0 @@
/*******************************************************************************
uMatrix - a browser extension to block requests.
Copyright (C) 2017-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uMatrix
*/
// For background page
'use strict';
/******************************************************************************/
(( ) => {
// https://github.com/uBlockOrigin/uBlock-issues/issues/407
if ( vAPI.webextFlavor.soup.has('firefox') === false ) { return; }
// https://github.com/gorhill/uBlock/issues/2950
// Firefox 56 does not normalize URLs to ASCII, uBO must do this itself.
// https://bugzilla.mozilla.org/show_bug.cgi?id=945240
const evalMustPunycode = ( ) => {
return vAPI.webextFlavor.soup.has('firefox') &&
vAPI.webextFlavor.major < 57;
};
let mustPunycode = evalMustPunycode();
// The real actual webextFlavor value may not be set in stone, so listen
// for possible future changes.
window.addEventListener('webextFlavor', ( ) => {
mustPunycode = evalMustPunycode();
}, { once: true });
const punycode = self.punycode;
const reAsciiHostname = /^https?:\/\/[0-9a-z_.:@-]+[/?#]/;
const parsedURL = new URL('about:blank');
// Related issues:
// - https://github.com/gorhill/uBlock/issues/1327
// - https://github.com/uBlockOrigin/uBlock-issues/issues/128
// - https://bugzilla.mozilla.org/show_bug.cgi?id=1503721
// Extend base class to normalize as per platform.
vAPI.Net = class extends vAPI.Net {
constructor() {
super();
this.pendingRequests = [];
this.cnames = new Map([ [ '', '' ] ]);
this.cnameIgnoreList = null;
this.cnameIgnore1stParty = true;
this.cnameIgnoreExceptions = true;
this.cnameIgnoreRootDocument = true;
this.cnameMaxTTL = 60;
this.cnameReplayFullURL = false;
this.cnameTimer = undefined;
this.canRevealCNAME = browser.dns instanceof Object;
}
setOptions(options) {
super.setOptions(options);
this.cnameIgnoreList = this.regexFromStrList(options.cnameIgnoreList);
this.cnameIgnore1stParty = options.cnameIgnore1stParty !== false;
this.cnameIgnoreExceptions = options.cnameIgnoreExceptions !== false;
this.cnameIgnoreRootDocument = options.cnameIgnoreRootDocument !== false;
this.cnameMaxTTL = options.cnameMaxTTL || 120;
this.cnameReplayFullURL = options.cnameReplayFullURL === true;
this.cnames.clear(); this.cnames.set('', '');
}
normalizeDetails(details) {
if ( mustPunycode && !reAsciiHostname.test(details.url) ) {
parsedURL.href = details.url;
details.url = details.url.replace(
parsedURL.hostname,
punycode.toASCII(parsedURL.hostname)
);
}
const type = details.type;
if ( type === 'imageset' ) {
details.type = 'image';
return;
}
// https://github.com/uBlockOrigin/uBlock-issues/issues/345
// Re-categorize an embedded object as a `sub_frame` if its
// content type is that of a HTML document.
if ( type === 'object' && Array.isArray(details.responseHeaders) ) {
for ( const header of details.responseHeaders ) {
if ( header.name.toLowerCase() === 'content-type' ) {
if ( header.value.startsWith('text/html') ) {
details.type = 'sub_frame';
}
break;
}
}
}
}
denormalizeTypes(types) {
if ( types.length === 0 ) {
return Array.from(this.validTypes);
}
const out = new Set();
for ( const type of types ) {
if ( this.validTypes.has(type) ) {
out.add(type);
}
if ( type === 'image' && this.validTypes.has('imageset') ) {
out.add('imageset');
}
if ( type === 'sub_frame' ) {
out.add('object');
}
}
return Array.from(out);
}
processCanonicalName(hn, cn, details) {
const hnBeg = details.url.indexOf(hn);
if ( hnBeg === -1 ) { return; }
const oldURL = details.url;
let newURL = oldURL.slice(0, hnBeg) + cn;
const hnEnd = hnBeg + hn.length;
if ( this.cnameReplayFullURL ) {
newURL += oldURL.slice(hnEnd);
} else {
const pathBeg = oldURL.indexOf('/', hnEnd);
if ( pathBeg !== -1 ) {
newURL += oldURL.slice(hnEnd, pathBeg + 1);
}
}
details.url = newURL;
details.aliasURL = oldURL;
return super.onBeforeSuspendableRequest(details);
}
recordCanonicalName(hn, record) {
let cname =
typeof record.canonicalName === 'string' &&
record.canonicalName !== hn
? record.canonicalName
: '';
if (
cname !== '' &&
this.cnameIgnore1stParty &&
vAPI.domainFromHostname(cname) === vAPI.domainFromHostname(hn)
) {
cname = '';
}
if (
cname !== '' &&
this.cnameIgnoreList !== null &&
this.cnameIgnoreList.test(cname)
) {
cname = '';
}
this.cnames.set(hn, cname);
if ( this.cnameTimer === undefined ) {
this.cnameTimer = self.setTimeout(
( ) => {
this.cnameTimer = undefined;
this.cnames.clear(); this.cnames.set('', '');
},
this.cnameMaxTTL * 60000
);
}
return cname;
}
regexFromStrList(list) {
if (
typeof list !== 'string' ||
list.length === 0 ||
list === 'unset' ||
browser.dns instanceof Object === false
) {
return null;
}
if ( list === '*' ) {
return /^./;
}
return new RegExp(
'(?:^|\.)(?:' +
list.trim()
.split(/\s+/)
.map(a => a.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
.join('|') +
')$'
);
}
onBeforeSuspendableRequest(details) {
const r = super.onBeforeSuspendableRequest(details);
if ( this.canRevealCNAME === false ) { return r; }
if ( r !== undefined ) {
if ( r.cancel === false ) { return; }
if (
r.cancel === true ||
r.redirectUrl !== undefined ||
this.cnameIgnoreExceptions
) {
return r;
}
}
if (
details.type === 'main_frame' &&
this.cnameIgnoreRootDocument
) {
return;
}
const hn = vAPI.hostnameFromNetworkURL(details.url);
const cname = this.cnames.get(hn);
if ( cname === '' ) { return; }
if ( cname !== undefined ) {
return this.processCanonicalName(hn, cname, details);
}
return browser.dns.resolve(hn, [ 'canonical_name' ]).then(
rec => {
const cname = this.recordCanonicalName(hn, rec);
if ( cname === '' ) { return; }
return this.processCanonicalName(hn, cname, details);
},
( ) => {
this.cnames.set(hn, '');
}
);
}
suspendOneRequest(details) {
const pending = {
details: Object.assign({}, details),
resolve: undefined,
promise: undefined
};
pending.promise = new Promise(resolve => {
pending.resolve = resolve;
});
this.pendingRequests.push(pending);
return pending.promise;
}
unsuspendAllRequests() {
const pendingRequests = this.pendingRequests;
this.pendingRequests = [];
for ( const entry of pendingRequests ) {
entry.resolve(this.onBeforeSuspendableRequest(entry.details));
}
}
canSuspend() {
return true;
}
};
})();
/******************************************************************************/

@ -1,24 +0,0 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2019-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
'use strict';
const webext = browser; // jshint ignore:line

@ -1,10 +0,0 @@
{
"sidebar_action": {
"default_title": "__MSG_loggerPageName__",
"default_panel": "logger-ui.html",
"default_icon": {
"16": "img/icon_16.png",
"128": "img/icon_128.png"
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,20 +1,16 @@
{
"extName": {
"message": "nuTensor",
"message": "uMatrix",
"description": ""
},
"extShortDesc": {
"message": "Point & click to forbid/allow any class of requests made by your browser. A fork of Raymond Hill's uMatrix.",
"message": "Point & click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.",
"description": "this will be used as short description in web stores: MUST BE 132 characters OR LESS"
},
"dashboardPageName": {
"message": "nuTensor — Dashboard",
"message": "uMatrix — Dashboard",
"description": ""
},
"loggerPageName": {
"message": "nuTensor — Logger",
"description": "Title for the logger window"
},
"settingsPageName": {
"message": "Settings",
"description": "a tab in dashboard"
@ -32,11 +28,7 @@
"description": "a tab in dashboard"
},
"ubiquitousRulesPageName" : {
"message": "Assets",
"description": "a tab in dashboard"
},
"rawSettingsPageName": {
"message": "More",
"message": "Hosts files",
"description": "a tab in dashboard"
},
"aboutPageName": {
@ -61,10 +53,6 @@
"message": "image",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"mediaPrettyName": {
"message": "media",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"pluginPrettyName": {
"message": "plugin",
"description": "HAS TO FIT IN MATRIX HEADER!"
@ -73,8 +61,8 @@
"message": "script",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"fetchPrettyName": {
"message": "fetch",
"xhrPrettyName": {
"message": "XHR",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"framePrettyName": {
@ -91,28 +79,20 @@
"message": "No net traffic seen for this tab so far.",
"description": ""
},
"matrixLocalScopeTip" : {
"message": "Select a local scope to see/create rules which apply only in that scope",
"description": "Tool tip for the local scope button"
},
"matrixGlobalScopeTip" : {
"message": "Select the global scope to see/create rules which apply everywhere",
"description": "Tool tip for the global scope button"
},
"matrixMtxButtonTip" : {
"message": "Disable/enable matrix filtering for this scope",
"message": "Disable/enable matrix filtering for this scope.",
"description": "Tool tip for matrix button"
},
"matrixPersistButtonTip" : {
"message": "Save all temporary changes for this scope",
"message": "Save all temporary changes for this scope.",
"description": "Tool tip for the persist button"
},
"matrixRevertButtonTip" : {
"message": "Revert temporary changes for this scope",
"message": "Revert temporary changes for this scope.",
"description": "Tool tip for the revert local permission button"
},
"matrixReloadButton" : {
"message": "Reload the page. \nPress Shift to bypass the browser cache.",
"message": "Reload the page.",
"description": "Tool tip for the reload button"
},
"matrix1stPartyLabel" : {
@ -124,23 +104,15 @@
"description": "Appears in the metadata row of bottom-most group: **mind the limited width**"
},
"matrixSwitchNoMixedContent" : {
"message": "Forbid mixed content",
"message": "Strict HTTPS",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchNoWorker" : {
"message": "Forbid web workers",
"matrixSwitchUASpoof" : {
"message": "User agent spoofing",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchReferrerSpoof" : {
"message": "Spoof <code>Referer</code> header",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchNoscriptSpoof" : {
"message": "Spoof <code><noscript></code> tags",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchRevealCname" : {
"message": "Reveal canonical names",
"message": "Referrer spoofing",
"description": "A menu entry in the matrix popup"
},
"matrixRevertAllEntry" : {
@ -155,21 +127,9 @@
"message": "Go to dashboard",
"description": "A menu entry in the matrix popup"
},
"matrixNoTabFound" : {
"message": "No web page found",
"description": "Displays in place of matrix when no data is found for the current page"
},
"matrixRecipeImportTip" : {
"message": "Import rules",
"description": "Used as a tooltip for the recipe import button"
},
"matrixRecipeSaveTip" : {
"message": "Save rules",
"description": "Used as a tooltip for the recipe padlock button"
},
"statsPageTitle" : {
"message": "nuTensor &ndash; Statistics",
"message": "uMatrix &ndash; Statistics",
"description": ""
},
"statsPageGenericStats" : {
@ -185,7 +145,7 @@
"description": ""
},
"statsPageHyperlinkAuditingFoiled" : {
"message": "<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}",
"message": "<a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}",
"description": ""
},
"statsPageCookiesRemoved" : {
@ -277,180 +237,38 @@
"description": "First part of Remember the last [n] HTTP requests per page"
},
"statsPageLogSizePrompt2" : {
"message": "HTTP requests <b>per page</b>",
"message": "HTTP requests <b>per page</b>.",
"description": "Second part of Remember the last [n] HTTP requests per page"
},
"statsPageLogSizeHelp" : {
"message": "<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>nuTensor</i>).</p>",
"message": "<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>uMatrix</i>).</p>",
"description": "To help user understand the purpose of the log size value"
},
"statsPageRefresh" : {
"message": "Refresh",
"description": ""
},
"logAll":{
"message":"All",
"description":"Appears in the logger's tab selector"
},
"logBehindTheScene":{
"message":"Tabless",
"description":"Pretty name for behind-the-scene network requests"
},
"loggerCurrentTab":{
"message":"Current tab",
"description":"Appears in the logger's tab selector"
},
"loggerReloadTip":{
"message":"Reload the tab content",
"description":"Tooltip for the reload button in the logger page"
},
"loggerFilterInputPlaceholder" : {
"message": "filter expression(s)",
"description": "Appears in the input filed where filter expressions are entered"
},
"loggerEntryCookieDeleted" : {
"message": "cookie deleted: {{value}}",
"description": "An entry for when a cookie is deleted"
},
"loggerEntryDeleteCookieError" : {
"message": "failed to delete cookie: {{value}}",
"description": "An entry for when the browser cache is cleared"
},
"loggerEntryBrowserCacheCleared" : {
"message": "browser cache cleared",
"description": "An entry for when a cookie can't be deleted"
},
"loggerEntryAssetUpdated" : {
"message": "asset updated: {{value}}",
"description": "An entry for when an asset was updated"
},
"loggerRowFiltererButtonTip":{
"message":"Toggle logger filtering",
"description":"Tooltip for the row filterer button in the logger page"
},
"logFilterPrompt":{
"message":"filter logger content",
"description": "Placeholder string for logger output filtering input field"
},
"loggerPopupPanelTip":{
"message":"Toggle the popup panel",
"description":"Tooltip for the popup panel button in the logger page"
},
"loggerInfoTip":{
"message":"uBlock Origin wiki: The logger",
"description":"Tooltip for the top-right info label in the logger page"
},
"loggerClearTip":{
"message":"Clear logger",
"description":"Tooltip for the eraser in the logger page; used to blank the content of the logger"
},
"loggerPauseTip":{
"message":"Pause logger (discard all incoming data)",
"description":"Tooltip for the pause button in the logger page"
},
"loggerUnpauseTip":{
"message":"Unpause logger",
"description":"Tooltip for the play button in the logger page"
},
"loggerRowFiltererBuiltinTip":{
"message":"Logger filtering options",
"description":"Tooltip for the button to bring up logger output filtering options"
},
"loggerRowFiltererBuiltinNot":{
"message":"Not",
"description":"A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltinBlocked":{
"message":"blocked",
"description":"A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltinInfo":{
"message":"info",
"description":"A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltin1p":{
"message":"1st-party",
"description":"A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltin3p":{
"message":"3rd-party",
"description":"A keyword in the built-in row filtering expression"
},
"loggerEntryDetailsHeader":{
"message":"Details",
"description":"Small header to identify the 'Details' pane for a specific logger entry"
},
"loggerEntryDetailsContext":{
"message":"Context",
"description":"Label to identify a context field (typically a hostname)"
},
"loggerEntryDetailsPartyness":{
"message":"Partyness",
"description":"Label to identify a field providing partyness information"
},
"loggerEntryDetailsType":{
"message":"Type",
"description":"Label to identify the type of an entry"
},
"loggerEntryDetailsURL":{
"message":"URL",
"description":"Label to identify the URL of an entry"
},
"loggerEntryRuleHeader":{
"message":"Rule",
"description":"Small header to identify the 'Rule' pane for a specific logger entry"
},
"loggerSettingDiscardPrompt":{
"message":"Logger entries which do not fulfill all three conditions below will be automatically discarded:",
"description":"Logger setting: A sentence to describe the purpose of the settings below"
},
"loggerSettingPerEntryMaxAge":{
"message":"Preserve entries from the last {{input}} minutes",
"description":"A logger setting"
},
"loggerSettingPerTabMaxLoads":{
"message":"Preserve at most {{input}} page loads per tab",
"description":"A logger setting"
},
"loggerSettingPerTabMaxEntries":{
"message":"Preserve at most {{input}} entries per tab",
"description":"A logger setting"
},
"loggerSettingPerEntryLineCount":{
"message":"Use {{input}} lines per entry in vertically expanded mode",
"description":"A logger setting"
},
"loggerExportFormatList":{
"message":"List",
"description":"Label for radio-button to pick export format"
},
"loggerExportFormatTable":{
"message":"Table",
"description":"Label for radio-button to pick export format"
},
"loggerExportEncodePlain":{
"message":"Plain",
"description":"Label for radio-button to pick export text format"
},
"loggerExportEncodeMarkdown":{
"message":"Markdown",
"description":"Label for radio-button to pick export text format"
},
"settingsPageTitle" : {
"message": "nuTensor &ndash; Settings",
"message": "uMatrix &ndash; Settings",
"description": ""
},
"settingsMatrixDisplayHeader" : {
"message": "Matrix",
"description": "header for matrix settings used in Settings page"
"message": "Appearance",
"description": ""
},
"settingsMatrixDisplayTextSizePrompt" : {
"message": "Text size:",
"description": ""
},
"settingsIconBadgeEnabled":{
"message":"Show the number of blocked resources on the icon",
"description":""
"settingsMatrixDisplayTextSizeNormal" : {
"message": "Normal",
"description": ""
},
"settingsMatrixDisplayTextSizeLarge" : {
"message": "Large",
"description": ""
},
"settingsMatrixDisplayColorBlind" : {
"message": "Color-blind friendly",
@ -460,53 +278,57 @@
"message": "Convenience",
"description": "English: Convenience"
},
"settingsDefaultScopeLevel" : {
"message": "Default scope level:",
"description": "Label for default scope level selector in Settings pane"
"settingsMatrixAutoReloadPrompt" : {
"message": "When the matrix is closed, smart reload these tabs:",
"description": ""
},
"settingsMatrixAutoReloadNone" : {
"message": "None",
"description": ""
},
"settingsMatrixAutoReloadCurrent" : {
"message": "Current",
"description": ""
},
"settingsDefaultScopeLevel0" : {
"message": "Global",
"description": "Scope will be global"
"settingsMatrixAutoReloadAll" : {
"message": "All",
"description": ""
},
"settingsDefaultScopeLevel1" : {
"message": "Domain",
"description": "Scope will be base domain"
"settingsMatrixAutoReloadInfo" : {
"message": "Whenever you make changes in the matrix which can affect the display and/or behavior of one or more pages, <i>uMatrix</i> will reload affected pages automatically when you close the matrix.",
"description": ""
},
"settingsDefaultScopeLevel2" : {
"message": "Site",
"description": "Scope will be full hostname of site"
"settingsSubframeColor" : {
"message": "Blocked frames:&ensp;Color",
"description": "English: Blocked frames:&ensp;Color"
},
"settingsCollapseBlocked" : {
"message": "Hide placeholder of blocked elements",
"description": "A setting in the dashboard's Settings pane"
"settingsSubframeOpacity" : {
"message": "Opacity",
"description": "English: Opacity"
},
"settingsCollapseBlacklisted" : {
"message": "Hide placeholder of blacklisted elements",
"description": "A setting in the dashboard's Settings pane: 'blacklisted' means 'for which there is a specific block rule', 'specific' means 'a rule for which the destination hostname is not `*`'"
"settingsIconBadgeEnabled":{
"message":"Show the number of distinct requests on the icon",
"description":"English: Show the number of distinct requests on the icon"
},
"settingsNoscriptTagsSpoofed" : {
"message": "Spoof <code><noscript></code> tags when 1st-party scripts are blocked",
"description": "This appears in the Settings pane in the dashboard"
"settingsCollapseBlocked" : {
"message": "Collapse placeholder of blocked elements",
"description": "English: Collapse placeholder of blocked elements"
},
"settingsCloudStorageEnabled" : {
"message": "Enable cloud storage support",
"description": ""
},
"settingsMatrixNoTooltips" : {
"message": "Disable tooltips",
"description": ""
},
"privacyPageTitle" : {
"message": "nuTensor &ndash; Privacy",
"message": "uMatrix &ndash; Privacy",
"description": ""
},
"privacyDeleteBlockedCookiesPrompt" : {
"message": "Delete blocked cookies",
"message": "Delete blocked cookies.",
"description": ""
},
"privacyDeleteBlockedCookiesHelp" : {
"message": "<p>Blacklisted cookies are not prevented by <i>nuTensor</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>nuTensor</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>nuTensor</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>",
"message": "<p>Blacklisted cookies are not prevented by <i>uMatrix</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>uMatrix</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>uMatrix</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>",
"description": ""
},
"privacyDeleteNonBlockedSessionCookiesPrompt1" : {
@ -514,7 +336,7 @@
"description": "First part of 'Delete non-blocked session cookies [n] minutes after the last time they have been used'"
},
"privacyDeleteNonBlockedSessionCookiesPrompt2" : {
"message": " minutes after the last time they have been used",
"message": " minutes after the last time they have been used.",
"description": "Second part of 'Delete non-blocked session cookies [n] minutes after the last time they have been used'"
},
"privacyDeleteNonBlockedSessionCookiesHelp" : {
@ -534,23 +356,23 @@
"description": "First part of 'Clear browser cache every [n] minutes'"
},
"privacyClearCachePrompt2" : {
"message": "minutes",
"message": "minutes.",
"description": "Second part of 'Clear browser cache every [n] minutes'"
},
"privacyClearCacheHelp" : {
"message": "<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>nuTensor</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>",
"message": "<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup style='font-size:smaller'>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>uMatrix</i> do it for you, at the interval you wish.</p><p style='font-size:smaller'>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a><br>[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>",
"description": ""
},
"privacyProcessRefererPrompt" : {
"message": "Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests",
"message": "Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests.",
"description": ""
},
"privacyProcessRefererHelp" : {
"message": "From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>nuTensor</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.",
"message": "<p>From Wikipedia:</p><blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote><p>If this setting is checked, <i>uMatrix</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.",
"description": ""
},
"privacyNoMixedContentPrompt" : {
"message": "Strict HTTPS: forbid mixed content",
"message": "Strict HTTPS: forbid mixed content.",
"description": ""
},
"privacyNoMixedContentHelp" : {
@ -558,13 +380,33 @@
"description": ""
},
"privacyProcessHyperlinkAuditingPrompt" : {
"message": "Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts",
"message": "Block all <a href='http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing'>hyperlink auditing</a> attempts.",
"description": ""
},
"privacyProcessHyperlinkAuditingHelp" : {
"message": "<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>",
"description": ""
},
"privacySpoofUserAgentPrompt1" : {
"message": "Spoof <a href='https://en.wikipedia.org/wiki/User_agent'>User-Agent</a> string by randomly picking a new one below every",
"description": "First part of UA-spoofing prompt"
},
"privacySpoofUserAgentPrompt2" : {
"message": "minutes.",
"description": "Second part"
},
"privacySpoofUserAgentHelp" : {
"message": "<a href='https://www.eff.org/deeplinks/2010/01/tracking-by-user-agent'>According to the <i>Electronic Frontier Foundation</i></a>: &ldquo;[...] your browser sends a &lsquo;User Agent&rsquo; header to the website saying precisely which operating system and web browser you are using. This information could help distinguish Internet users from one another because these versions differ, often considerably, from person to person. [...] <b>the User Agent string becomes a real privacy problem</b>.&rdquo;<p>This option allows you to address the privacy issue raised by the EFF. Please note that <a href='https://code.google.com/p/chromium/issues/detail?id=129353'>your actual User Agent string can be leaked</a> through WebSockets.</p><p>You can supply your own list of user agent strings. One string per line. Blank lines and lines prefixed with &lsquo;#&rsquo; will be ignored.</p>",
"description": ""
},
"privacyBehindTheSceneHeader" : {
"message": "Behind-the-scene requests",
"description": ""
},
"privacyProcessBehindTheSceneHelp" : {
"message": "",
"description": ""
},
"userRulesPermanentHeader": {
@ -613,10 +455,6 @@
},
"assetsHostsSection" : {
"message": "Hosts files",
"description": "header to identify the hosts files section"
},
"hostsFilesPrompt" : {
"message": "All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.",
"description": ""
@ -638,7 +476,7 @@
"description": ""
},
"hostsFilesAutoUpdatePrompt":{
"message":"Auto-update assets",
"message":"Auto-update hosts files.",
"description":""
},
"hostsFilesUpdateNow":{
@ -650,7 +488,7 @@
"description":""
},
"hostsFilesExternalListsHint":{
"message":"Import external assets here: \nOne URL per line; invalid URLs will be silently ignored.",
"message":"One URL per line. Lines prefixed with &lsquo;#&rsquo; will be ignored. Invalid URLs will be silently ignored.",
"description":""
},
"hostsFilesExternalListsParse":{
@ -669,34 +507,10 @@
"message":"outdated",
"description":""
},
"assetsRecipesSection" : {
"message": "Ruleset recipes",
"description": "header to identify the ruleset files section"
},
"assetsRecipesSummary" : {
"message": "Ruleset recipes are imported from the popup panel <em>on demand</em>, i.e. <b>only</b> through user interaction.",
"description": ""
},
"assetsImportLabel" : {
"message": "Import...",
"description": ""
},
"assetsInlineHostsLabel": {
"message": "My hosts",
"description": ""
},
"assetsInlineRecipesLabel": {
"message": "My recipes",
"description": ""
},
"rawSettingsWarning" : {
"message": "Warning! Change these raw configuration settings at your own risk.",
"description": ""
},
"aboutChangelog" : {
"message": "<a href='https://github.com/geekprojects/nuTensor/releases'>Change log</a>",
"message": "<a href='https://github.com/gorhill/uMatrix/releases'>Change log</a>",
"description": ""
},
"aboutStorageUsed" : {
@ -704,11 +518,11 @@
"description": ""
},
"aboutDoc" : {
"message": "<a href='https://github.com/geekprojects/nuTensor/wiki'>Documentation</a>",
"message": "<a href='https://github.com/gorhill/uMatrix/wiki'>Documentation</a>",
"description": ""
},
"aboutPermissions" : {
"message": "<a href='https://github.com/geekprojects/nuTensor/wiki/Permissions'>Permissions</a>",
"message": "<a href='https://github.com/gorhill/httpswitchboard/wiki/Permissions'>Permissions</a>",
"description": ""
},
"aboutCode" : {
@ -716,8 +530,8 @@
"description": ""
},
"aboutIssues" : {
"message": "Issue tracker",
"description": "Text for a link to official issue tracker"
"message": "Bugs and issues",
"description": ""
},
"aboutContributors":{
"message":"Contributors",
@ -740,7 +554,7 @@
"description": ""
},
"aboutBackupButton" : {
"message": "Back up to file...",
"message": "Backup to file...",
"description": ""
},
"aboutBackupFilename" : {
@ -752,7 +566,7 @@
"description": ""
},
"aboutRestoreConfirm" : {
"message": "All your settings will be overwritten using data backed up on {{time}}, and nuTensor will restart.\n\nOverwrite all existing settings using backed up data?",
"message": "All your settings will be overwritten using data backed up on {{time}}, and uMatrix will restart.\n\nOverwrite all existing settings using backed up data?",
"description": "Message asking user to confirm restore"
},
"aboutRestoreError" : {
@ -772,18 +586,43 @@
"description": "Message asking user to confirm reset"
},
"loggerFilterInputPlaceholder" : {
"message": "filter expression(s)",
"description": "Appears in the input filed where filter expressions are entered"
},
"loggerMaxEntriesTip" : {
"message": "Maximum number of entries",
"description": "Appears as a tooltip when hovering the input field"
},
"loggerEntryUserAgentSpoofing" : {
"message": "spoofing user agent with: {{value}}",
"description": "An entry for when a new user agent string is selected"
},
"loggerEntryCookieDeleted" : {
"message": "cookie deleted: {{value}}",
"description": "An entry for when a cookie is deleted"
},
"loggerEntryDeleteCookieError" : {
"message": "failed to delete cookie: {{value}}",
"description": "An entry for when the browser cache is cleared"
},
"loggerEntryBrowserCacheCleared" : {
"message": "browser cache cleared",
"description": "An entry for when a cookie can't be deleted"
},
"loggerEntryAssetUpdated" : {
"message": "asset updated: {{value}}",
"description": "An entry for when an asset was updated"
},
"mainBlockedPrompt1": {
"message": "nuTensor has prevented the following page from loading:",
"description": "English: nuTensor has prevented the following page from loading:"
"message": "uMatrix has prevented the following page from loading:",
"description": "English: uMatrix has prevented the following page from loading:"
},
"mainBlockedPrompt2": {
"message": "Because of the following rule",
"description": "English: Because of the following rule"
},
"mainBlockedNoParamsPrompt": {
"message": "without parameters",
"description": "label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png"
},
"mainBlockedBack" : {
"message": "Go back",
"description": "English: Go back"
@ -872,14 +711,5 @@
"errorCantConnectTo":{
"message":"Network error: Unable to connect to {{url}}",
"description":""
},
"genericApplyChanges": {
"message": "Apply changes",
"description": "for generic 'Apply changes' buttons"
},
"genericCopyToClipboard":{
"message":"Copy to clipboard",
"description":"Label for buttons used to copy something to the clipboard"
}
}

@ -1,866 +0,0 @@
{
"extName": {
"message": "nuTensor",
"description": ""
},
"extShortDesc": {
"message": "Point & click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.",
"description": "this will be used as short description in web stores: MUST BE 132 characters OR LESS"
},
"dashboardPageName": {
"message": "nuTensor — Panelo",
"description": ""
},
"loggerPageName": {
"message": "nuTensor — Logger",
"description": "Title for the logger window"
},
"settingsPageName": {
"message": "Agordoj",
"description": "a tab in dashboard"
},
"privacyPageName": {
"message": "Privateco",
"description": "a tab in dashboard"
},
"statsPageName": {
"message": "Statistics",
"description": "a tab in dashboard"
},
"userRulesPageName": {
"message": "Miaj reguloj",
"description": "a tab in dashboard"
},
"ubiquitousRulesPageName": {
"message": "Assets",
"description": "a tab in dashboard"
},
"rawSettingsPageName": {
"message": "More",
"description": "a tab in dashboard"
},
"aboutPageName": {
"message": "Pri",
"description": "a tab in dashboard"
},
"allPrettyName": {
"message": "all",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"cookiePrettyName": {
"message": "kuketo",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"cssPrettyName": {
"message": "css",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"imagePrettyName": {
"message": "bildo",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"mediaPrettyName": {
"message": "media",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"pluginPrettyName": {
"message": "kromaĵo",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"scriptPrettyName": {
"message": "skripto",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"fetchPrettyName": {
"message": "fetch",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"framePrettyName": {
"message": "kadro",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"otherPrettyName": {
"message": "alia",
"description": "HAS TO FIT IN MATRIX HEADER!"
},
"matrixNoNetTrafficPrompt": {
"message": "No net traffic seen for this tab so far.",
"description": ""
},
"matrixLocalScopeTip": {
"message": "Select a local scope to see/create rules which apply only in that scope",
"description": "Tool tip for the local scope button"
},
"matrixGlobalScopeTip": {
"message": "Select the global scope to see/create rules which apply everywhere",
"description": "Tool tip for the global scope button"
},
"matrixMtxButtonTip": {
"message": "Disable/enable matrix filtering for this scope",
"description": "Tool tip for matrix button"
},
"matrixPersistButtonTip": {
"message": "Save all temporary changes for this scope",
"description": "Tool tip for the persist button"
},
"matrixRevertButtonTip": {
"message": "Revert temporary changes for this scope",
"description": "Tool tip for the revert local permission button"
},
"matrixReloadButton": {
"message": "Reŝargi la paĝon.",
"description": "Tool tip for the reload button"
},
"matrix1stPartyLabel": {
"message": "1st-party",
"description": "1st-party"
},
"matrixBlacklistedHostnames": {
"message": "{{count}} blacklisted hostname(s)",
"description": "Appears in the metadata row of bottom-most group: **mind the limited width**"
},
"matrixSwitchNoMixedContent": {
"message": "Forbid mixed content",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchNoWorker": {
"message": "Forbid web workers",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchReferrerSpoof": {
"message": "Spoof <code>Referer</code> header",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchNoscriptSpoof": {
"message": "Spoof <code><noscript></code> tags",
"description": "A menu entry in the matrix popup"
},
"matrixSwitchRevealCname": {
"message": "Reveal canonical names",
"description": "A menu entry in the matrix popup"
},
"matrixRevertAllEntry": {
"message": "Revert all temporary changes",
"description": "A menu entry in the matrix popup"
},
"matrixLoggerMenuEntry": {
"message": "Iri al protokolilo",
"description": "A menu entry in the matrix popup"
},
"matrixDashboardMenuEntry": {
"message": "Iri al panelo",
"description": "A menu entry in the matrix popup"
},
"matrixNoTabFound": {
"message": "No web page found",
"description": "Displays in place of matrix when no data is found for the current page"
},
"matrixRecipeImportTip": {
"message": "Import rules",
"description": "Used as a tooltip for the recipe import button"
},
"matrixRecipeSaveTip": {
"message": "Save rules",
"description": "Used as a tooltip for the recipe padlock button"
},
"statsPageTitle": {
"message": "nuTensor &ndash; Statistics",
"description": ""
},
"statsPageGenericStats": {
"message": "Generic statistics",
"description": ""
},
"statsPageCookieHeadersFoiled": {
"message": "<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}",
"description": ""
},
"statsPageRefererHeadersFoiled": {
"message": "<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}",
"description": ""
},
"statsPageHyperlinkAuditingFoiled": {
"message": "<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}",
"description": ""
},
"statsPageCookiesRemoved": {
"message": "Lokaj kuketoj forigitaj: {{count}}",
"description": ""
},
"statsPageLocalStoragesCleared": {
"message": "<a href='http://diveintohtml5.info/storage.html'>Lokaj konservadoj</a> malplenigitaj: {{count}}",
"description": ""
},
"statsPageBrowserCacheCleared": {
"message": "Browser caches cleared: {{count}}",
"description": ""
},
"statsPageDetailedStats": {
"message": "Detailed statistics",
"description": ""
},
"statsPageDetailedAllPages": {
"message": "All",
"description": ""
},
"statsPageDetailedBehindTheScenePage": {
"message": "Behind the scene",
"description": ""
},
"statsPageOverview": {
"message": "Superrigardo",
"description": ""
},
"statsPageRequests": {
"message": "Petoj",
"description": "header for the stat type column"
},
"statsPageAllowed": {
"message": "Allowed",
"description": "header for the allowed requests column"
},
"statsPageBlocked": {
"message": "Blocked",
"description": "header for the blocked requests column"
},
"statsPageAll": {
"message": "All",
"description": ""
},
"statsPagePages": {
"message": "Paĝoj",
"description": ""
},
"statsPageCookies": {
"message": "Kuketoj",
"description": ""
},
"statsPageCSS": {
"message": "CSS",
"description": ""
},
"statsPageImages": {
"message": "Bildoj",
"description": ""
},
"statsPagePlugins": {
"message": "Kromaĵoj",
"description": ""
},
"statsPageScripts": {
"message": "Skriptoj",
"description": ""
},
"statsPageXHRs": {
"message": "XHR-oj",
"description": ""
},
"statsPageFrames": {
"message": "Kadroj",
"description": ""
},
"statsPageOthers": {
"message": "Aliaj",
"description": ""
},
"statsPageDetailed": {
"message": "Protokolilo",
"description": ""
},
"statsPageLogSizePrompt1": {
"message": "Remember the last",
"description": "First part of Remember the last [n] HTTP requests per page"
},
"statsPageLogSizePrompt2": {
"message": "HTTP requests <b>per page</b>",
"description": "Second part of Remember the last [n] HTTP requests per page"
},
"statsPageLogSizeHelp": {
"message": "<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter &ldquo;<code>0</code>&rdquo; to turn off detailed logging (and consequently reduce the memory footprint of <i>nuTensor</i>).</p>",
"description": "To help user understand the purpose of the log size value"
},
"statsPageRefresh": {
"message": "Aktualigi",
"description": ""
},
"logAll": {
"message": "All",
"description": "Appears in the logger's tab selector"
},
"logBehindTheScene": {
"message": "Tabless",
"description": "Pretty name for behind-the-scene network requests"
},
"loggerCurrentTab": {
"message": "Current tab",
"description": "Appears in the logger's tab selector"
},
"loggerReloadTip": {
"message": "Reload the tab content",
"description": "Tooltip for the reload button in the logger page"
},
"loggerFilterInputPlaceholder": {
"message": "filter expression(s)",
"description": "Appears in the input filed where filter expressions are entered"
},
"loggerEntryCookieDeleted": {
"message": "kuketo forigita: {{value}}",
"description": "An entry for when a cookie is deleted"
},
"loggerEntryDeleteCookieError": {
"message": "failed to delete cookie: {{value}}",
"description": "An entry for when the browser cache is cleared"
},
"loggerEntryBrowserCacheCleared": {
"message": "browser cache cleared",
"description": "An entry for when a cookie can't be deleted"
},
"loggerEntryAssetUpdated": {
"message": "asset updated: {{value}}",
"description": "An entry for when an asset was updated"
},
"loggerRowFiltererButtonTip": {
"message": "Toggle logger filtering",
"description": "Tooltip for the row filterer button in the logger page"
},
"logFilterPrompt": {
"message": "filter logger content",
"description": "Placeholder string for logger output filtering input field"
},
"loggerPopupPanelTip": {
"message": "Toggle the popup panel",
"description": "Tooltip for the popup panel button in the logger page"
},
"loggerInfoTip": {
"message": "uBlock Origin wiki: The logger",
"description": "Tooltip for the top-right info label in the logger page"
},
"loggerClearTip": {
"message": "Clear logger",
"description": "Tooltip for the eraser in the logger page; used to blank the content of the logger"
},
"loggerPauseTip": {
"message": "Pause logger (discard all incoming data)",
"description": "Tooltip for the pause button in the logger page"
},
"loggerUnpauseTip": {
"message": "Unpause logger",
"description": "Tooltip for the play button in the logger page"
},
"loggerRowFiltererBuiltinTip": {
"message": "Logger filtering options",
"description": "Tooltip for the button to bring up logger output filtering options"
},
"loggerRowFiltererBuiltinNot": {
"message": "Not",
"description": "A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltinBlocked": {
"message": "blocked",
"description": "A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltinInfo": {
"message": "info",
"description": "A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltin1p": {
"message": "1st-party",
"description": "A keyword in the built-in row filtering expression"
},
"loggerRowFiltererBuiltin3p": {
"message": "3rd-party",
"description": "A keyword in the built-in row filtering expression"
},
"loggerEntryDetailsHeader": {
"message": "Details",
"description": "Small header to identify the 'Details' pane for a specific logger entry"
},
"loggerEntryDetailsContext": {
"message": "Context",
"description": "Label to identify a context field (typically a hostname)"
},
"loggerEntryDetailsPartyness": {
"message": "Partyness",
"description": "Label to identify a field providing partyness information"
},
"loggerEntryDetailsType": {
"message": "Type",
"description": "Label to identify the type of an entry"
},
"loggerEntryDetailsURL": {
"message": "URL",
"description": "Label to identify the URL of an entry"
},
"loggerEntryRuleHeader": {
"message": "Rule",
"description": "Small header to identify the 'Rule' pane for a specific logger entry"
},
"loggerSettingDiscardPrompt": {
"message": "Logger entries which do not fulfill all three conditions below will be automatically discarded:",
"description": "Logger setting: A sentence to describe the purpose of the settings below"
},
"loggerSettingPerEntryMaxAge": {
"message": "Preserve entries from the last {{input}} minutes",
"description": "A logger setting"
},
"loggerSettingPerTabMaxLoads": {
"message": "Preserve at most {{input}} page loads per tab",
"description": "A logger setting"
},
"loggerSettingPerTabMaxEntries": {
"message": "Preserve at most {{input}} entries per tab",
"description": "A logger setting"
},
"loggerSettingPerEntryLineCount": {
"message": "Use {{input}} lines per entry in vertically expanded mode",
"description": "A logger setting"
},
"loggerExportFormatList": {
"message": "List",
"description": "Label for radio-button to pick export format"
},
"loggerExportFormatTable": {
"message": "Table",
"description": "Label for radio-button to pick export format"
},
"loggerExportEncodePlain": {
"message": "Plain",
"description": "Label for radio-button to pick export text format"
},
"loggerExportEncodeMarkdown": {
"message": "Markdown",
"description": "Label for radio-button to pick export text format"
},
"settingsPageTitle": {
"message": "nuTensor &ndash; Agordoj",
"description": ""
},
"settingsMatrixDisplayHeader": {
"message": "Apero",
"description": "header for matrix settings used in Settings page"
},
"settingsMatrixDisplayTextSizePrompt": {
"message": "Tekstogrando:",
"description": ""
},
"settingsIconBadgeEnabled": {
"message": "Show the number of blocked resources on the icon",
"description": ""
},
"settingsMatrixDisplayColorBlind": {
"message": "Afabla por kolorblinduloj",
"description": ""
},
"settingsMatrixConvenienceHeader": {
"message": "Komforto",
"description": "English: Convenience"
},
"settingsDefaultScopeLevel": {
"message": "Default scope level:",
"description": "Label for default scope level selector in Settings pane"
},
"settingsDefaultScopeLevel0": {
"message": "Global",
"description": "Scope will be global"
},
"settingsDefaultScopeLevel1": {
"message": "Domain",
"description": "Scope will be base domain"
},
"settingsDefaultScopeLevel2": {
"message": "Site",
"description": "Scope will be full hostname of site"
},
"settingsCollapseBlocked": {
"message": "Hide placeholder of blocked elements",
"description": "A setting in the dashboard's Settings pane"
},
"settingsCollapseBlacklisted": {
"message": "Hide placeholder of blacklisted elements",
"description": "A setting in the dashboard's Settings pane: 'blacklisted' means 'for which there is a specific block rule', 'specific' means 'a rule for which the destination hostname is not `*`'"
},
"settingsNoscriptTagsSpoofed": {
"message": "Spoof <code><noscript></code> tags when 1st-party scripts are blocked",
"description": "This appears in the Settings pane in the dashboard"
},
"settingsCloudStorageEnabled": {
"message": "Ŝalti subtenon por nuba konservado",
"description": ""
},
"settingsMatrixNoTooltips": {
"message": "Disable tooltips",
"description": ""
},
"privacyPageTitle": {
"message": "nuTensor &ndash; Privateco",
"description": ""
},
"privacyDeleteBlockedCookiesPrompt": {
"message": "Delete blocked cookies",
"description": ""
},
"privacyDeleteBlockedCookiesHelp": {
"message": "<p>Blacklisted cookies are not prevented by <i>nuTensor</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>nuTensor</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>nuTensor</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>",
"description": ""
},
"privacyDeleteNonBlockedSessionCookiesPrompt1": {
"message": "Delete non-blocked session cookies ",
"description": "First part of 'Delete non-blocked session cookies [n] minutes after the last time they have been used'"
},
"privacyDeleteNonBlockedSessionCookiesPrompt2": {
"message": " minutes after the last time they have been used",
"description": "Second part of 'Delete non-blocked session cookies [n] minutes after the last time they have been used'"
},
"privacyDeleteNonBlockedSessionCookiesHelp": {
"message": "<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: &ldquo;A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.&rdquo;</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>",
"description": ""
},
"privacyDeleteBlockedLocalStoragePrompt": {
"message": "Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames",
"description": ""
},
"privacyDeleteBlockedLocalStorageHelp": {
"message": "TODO",
"description": ""
},
"privacyClearCachePrompt1": {
"message": "Clear browser cache every",
"description": "First part of 'Clear browser cache every [n] minutes'"
},
"privacyClearCachePrompt2": {
"message": "minutoj.",
"description": "Second part of 'Clear browser cache every [n] minutes'"
},
"privacyClearCacheHelp": {
"message": "<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>nuTensor</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>&ldquo;Preventing Web Tracking via the Browser Cache&rdquo;</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>&ldquo;Cookieless cookies&rdquo;</a></p>",
"description": ""
},
"privacyProcessRefererPrompt": {
"message": "Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests",
"description": ""
},
"privacyProcessRefererHelp": {
"message": "From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>nuTensor</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.",
"description": ""
},
"privacyNoMixedContentPrompt": {
"message": "Strict HTTPS: forbid mixed content",
"description": ""
},
"privacyNoMixedContentHelp": {
"message": "<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>",
"description": ""
},
"privacyProcessHyperlinkAuditingPrompt": {
"message": "Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts",
"description": ""
},
"privacyProcessHyperlinkAuditingHelp": {
"message": "<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>",
"description": ""
},
"userRulesPermanentHeader": {
"message": "Daŭraj reguloj",
"description": ""
},
"userRulesTemporaryHeader": {
"message": "Nedaŭraj reguloj",
"description": ""
},
"userRulesRevert": {
"message": "Malfari",
"description": "Will remove all temporary rules"
},
"userRulesCommit": {
"message": "Konservi",
"description": "Will save all temporary rules"
},
"userRulesEdit": {
"message": "Redakti",
"description": "Will enable manual-edit mode (textarea)"
},
"userRulesEditSave": {
"message": "Konservi",
"description": "Will save manually-edited content and exit manual-edit mode"
},
"userRulesEditDicard": {
"message": "Ignori",
"description": "Will discard manually-edited content and exit manual-edit mode"
},
"userRulesImport": {
"message": "Importi el dosiero...",
"description": ""
},
"userRulesExport": {
"message": "Eksporti al dosiero...",
"description": ""
},
"userRulesFormatHint": {
"message": "See this page for rule syntax.",
"description": ""
},
"userRulesDefaultFileName": {
"message": "miaj-umatrix-reguloj.txt",
"description": "default file name to use"
},
"assetsHostsSection": {
"message": "Hosts files",
"description": "header to identify the hosts files section"
},
"hostsFilesPrompt": {
"message": "All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.",
"description": ""
},
"hostsFilesStats": {
"message": "{{blockedHostnameCount}} distinct blocked hostnames from:",
"description": ""
},
"hostsFilesPerFileStats": {
"message": "{{used}} used out of {{total}}",
"description": ""
},
"hostsFilesLastUpdate": {
"message": "Lasta ĝisdatigo: {{ago}}",
"description": "English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'"
},
"hostsFilesApplyChanges": {
"message": "Apliki ŝanĝojn",
"description": ""
},
"hostsFilesAutoUpdatePrompt": {
"message": "Auto-update assets",
"description": ""
},
"hostsFilesUpdateNow": {
"message": "Ĝisdatigi nun",
"description": ""
},
"hostsFilesPurgeAll": {
"message": "Malplenigi ĉiujn kaŝmemorojn",
"description": ""
},
"hostsFilesExternalListsHint": {
"message": "Import external assets here: \nOne URL per line; invalid URLs will be silently ignored.",
"description": ""
},
"hostsFilesExternalListsParse": {
"message": "Analizi",
"description": ""
},
"hostsFilesExternalListPurge": {
"message": "malplenigi kaŝmemoron",
"description": ""
},
"hostsFilesExternalListNew": {
"message": "nova versio disponebla",
"description": ""
},
"hostsFilesExternalListObsolete": {
"message": "neĝisdata",
"description": ""
},
"assetsRecipesSection": {
"message": "Ruleset recipes",
"description": "header to identify the ruleset files section"
},
"assetsRecipesSummary": {
"message": "Ruleset recipes are imported from the popup panel <em>on demand</em>, i.e. <b>only</b> through user interaction.",
"description": ""
},
"assetsImportLabel": {
"message": "Import...",
"description": ""
},
"assetsInlineHostsLabel": {
"message": "My hosts",
"description": ""
},
"assetsInlineRecipesLabel": {
"message": "My recipes",
"description": ""
},
"rawSettingsWarning": {
"message": "Warning! Change these raw configuration settings at your own risk.",
"description": ""
},
"aboutChangelog": {
"message": "<a href='https://github.com/geekprojects/nuTensor/releases'>Ŝanĝoprotokolo</a>",
"description": ""
},
"aboutStorageUsed": {
"message": "Storage used: {{storageUsed}} bytes",
"description": ""
},
"aboutDoc": {
"message": "<a href='https://github.com/geekprojects/nuTensor/wiki'>Documentation</a>",
"description": ""
},
"aboutPermissions": {
"message": "<a href='https://github.com/geekprojects/nuTensor/wiki/Permissions'>Permissions</a>",
"description": ""
},
"aboutCode": {
"message": "Fontkodo (GPLv3)",
"description": ""
},
"aboutIssues": {
"message": "Cimoj kaj problemoj",
"description": "Text for a link to official issue tracker"
},
"aboutContributors": {
"message": "Kontribuantoj",
"description": "English: Contributors"
},
"aboutCodeContributors": {
"message": "Kode:",
"description": ""
},
"aboutIssueContributors": {
"message": "Problemoj:",
"description": ""
},
"aboutTranslationContributors": {
"message": "Tradukoj:",
"description": ""
},
"aboutUserDataHeader": {
"message": "Viaj datumoj",
"description": ""
},
"aboutBackupButton": {
"message": "Savkopii al dosiero...",
"description": ""
},
"aboutBackupFilename": {
"message": "mia-umatrix-savkopio.txt",
"description": "default filename to use"
},
"aboutRestoreButton": {
"message": "Restore from file...",
"description": ""
},
"aboutRestoreConfirm": {
"message": "All your settings will be overwritten using data backed up on {{time}}, and nuTensor will restart.\n\nOverwrite all existing settings using backed up data?",
"description": "Message asking user to confirm restore"
},
"aboutRestoreError": {
"message": "The data could not be read or is invalid",
"description": ""
},
"aboutOr": {
"message": "... aŭ ...",
"description": "English: ... or ..."
},
"aboutResetButton": {
"message": "Reset to default settings",
"description": "English: Reset to default settings"
},
"aboutResetConfirm": {
"message": "Caution! this will remove all your custom settings. Are you sure you want to proceed?",
"description": "Message asking user to confirm reset"
},
"mainBlockedPrompt1": {
"message": "nuTensor has prevented the following page from loading:",
"description": "English: nuTensor has prevented the following page from loading:"
},
"mainBlockedPrompt2": {
"message": "Because of the following rule",
"description": "English: Because of the following rule"
},
"mainBlockedNoParamsPrompt": {
"message": "without parameters",
"description": "label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png"
},
"mainBlockedBack": {
"message": "Reen",
"description": "English: Go back"
},
"mainBlockedClose": {
"message": "Fermi",
"description": "English: Close"
},
"commandRevertAll": {
"message": "Revert all temporary changes",
"description": ""
},
"commandWhitelistPageDomain": {
"message": "Temporarily whitelist page domain",
"description": ""
},
"commandWhitelistAll": {
"message": "Temporarily whitelist all",
"description": ""
},
"commandOpenDashboard": {
"message": "Malfermi la panelo",
"description": ""
},
"elapsedOneMinuteAgo": {
"message": "antaŭ 1 minuto",
"description": "English: a minute ago"
},
"elapsedManyMinutesAgo": {
"message": "antaŭ {{value}} minutoj",
"description": "English: {{value}} minutes ago"
},
"elapsedOneHourAgo": {
"message": "antaŭ 1 horo",
"description": "English: an hour ago"
},
"elapsedManyHoursAgo": {
"message": "antaŭ {{value}} horoj",
"description": "English: {{value}} hours ago"
},
"elapsedOneDayAgo": {
"message": "antaŭ 1 tago",
"description": "English: a day ago"
},
"elapsedManyDaysAgo": {
"message": "antaŭ {{value}} tagoj",
"description": "English: {{value}} days ago"
},
"showDashboardButton": {
"message": "Panelo",
"description": "Appears in Firefox's add-on preferences"
},
"showLoggerButton": {
"message": "Protokolilo",
"description": "Appears in Firefox's add-on preferences"
},
"cloudPush": {
"message": "Eksporti al nuba konservado",
"description": "tooltip"
},
"cloudPull": {
"message": "Importi el nuba konservado",
"description": "tooltip"
},
"cloudNoData": {
"message": "...\n...",
"description": ""
},
"cloudDeviceNamePrompt": {
"message": "Nomo de ĉi tiu aparato:",
"description": "used as a prompt for the user to provide a custom device name"
},
"genericSubmit": {
"message": "Sendi",
"description": "for generic 'submit' buttons"
},
"genericRevert": {
"message": "Malfari",
"description": "for generic 'revert' buttons"
},
"errorCantConnectTo": {
"message": "Reteraro: {{url}} ne konekteblas",
"description": ""
},
"genericApplyChanges": {
"message": "Apply changes",
"description": "for generic 'Apply changes' buttons"
},
"genericCopyToClipboard": {
"message": "Copy to clipboard",
"description": "Label for buttons used to copy something to the clipboard"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save