first commit
@ -0,0 +1,7 @@
|
|||||||
|
# µMatrix for Chromium
|
||||||
|
|
||||||
|
[Under development: unusable]
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
<a href="https://github.com/gorhill/umatrix/blob/master/LICENSE.txt">GPLv3</a>.
|
@ -0,0 +1,101 @@
|
|||||||
|
HTTP Switchboard (FOSS) 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.
|
||||||
|
|
||||||
|
HTTP Switchboard filtering engine is composed of two specialized filtering engines: The matrix filtering engine (firewall-like filters), and the ABP filtering engine (Adblock Plus-compatible filters).
|
||||||
|
|
||||||
|
[IMPORTANT: Out of the box, HTTP Switchboard works in block-all/allow-exceptionally mode, meaning web sites which require scripts are likely to be "broken". With two clicks, HTTP Switchboard 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.]
|
||||||
|
|
||||||
|
[IMPORTANT: 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]
|
||||||
|
|
||||||
|
* 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).
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
* Ease of use: HTTP Switchboard 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
|
||||||
|
|
||||||
|
- type of requests (top row)
|
||||||
|
* cookies
|
||||||
|
* stylesheets (and web fonts)
|
||||||
|
* images
|
||||||
|
* objects
|
||||||
|
* 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.
|
||||||
|
|
||||||
|
HTTP Switchboard's matrix 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.
|
||||||
|
|
||||||
|
You can also create scopes for your whitelist/blacklist rules. For example, this allows you to whitelist `facebook.com` ONLY when visiting Facebook web site.
|
||||||
|
|
||||||
|
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 not discourage those users who give up easily on good security and privacy habits.
|
||||||
|
|
||||||
|
As of April 2014, the extension comes with preset blacklists totaling over 55,000 distinct hostnames (each list can be disabled/enabled according to your choice, and there are more preset blacklists which you can activate if you wish so.)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Randomly assembled documentation: https://github.com/gorhill/httpswitchboard/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.
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
This is pre-version 1.0, more work is intended. Bugs/issues/suggestions are addressed as quickly as possible. See: https://github.com/gorhill/httpswitchboard/issues?state=open
|
||||||
|
|
||||||
|
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 behind the scene. 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) behind the scene.
|
||||||
|
|
||||||
|
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 entry 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.
|
||||||
|
|
||||||
|
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 ("all") 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 blocking all requests for images as an example.
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
HTTP Switchboard is the fruit of a personal project, there no company of any kind involved, therefore no agenda other than giving users the tools to be in complete control of their browser. I appreciate the thought, but I do not want donation, now or in the future. If you **REALLY** want to give something in return, then my wish would be that you direct your donation to an organisation genuinely dedicated to defend basic principles of democracy. Examples: Freedom of the Press Foundation (https://pressfreedomfoundation.org/), EFF (https://www.eff.org/), Wikileaks (https://wikileaks.org/), or whatever non-for-profit organisation fits the "genuinely dedicated to defend basic principles of democracy" profile in your home country.
|
||||||
|
|
||||||
|
Contributors: https://github.com/gorhill/httpswitchboard/graphs/contributors
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
Source code is GPLv3, available at https://github.com/gorhill/httpswitchboard
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
Latest changes:
|
||||||
|
|
||||||
|
0.9.0.0
|
||||||
|
2014-04-27
|
||||||
|
https://github.com/gorhill/httpswitchboard/wiki/Change-log#0900
|
@ -0,0 +1,101 @@
|
|||||||
|
HTTP Switchboard (Gratuit et Open Source) vous donne les PLEINS POUVOIRS sur votre navigateur et sur les endroits vers lesquels ce dernier peut se connecter, ce qu'il peut télécharger, et ce qu'il peut exécuter. Personne d'autre ne décidera pour vous : VOUS décidez. Vous avez toutes les cartes en main pour contrôler votre vie privée en ligne.
|
||||||
|
|
||||||
|
Le moteur de filtrage d'HTTPSB (abréviation d'HTTP Switchboard) comprend deux types de filtrage spécialisé : Le moteur de filtrage matriciel (utilisant des filtres similaires à ceux d'un parefeu), et le moteur de filtrage type ABP (employant des filtres compatibles Adblock Plus).
|
||||||
|
|
||||||
|
[IMPORTANT : Par défaut, HTTPSB fonctionne en mode Blocage total/Permissions exceptionnelles, ce qui veut dire que les sites Web utilisant des scripts peuvent dysfonctionner. En seulement deux clics, l'extension peut être configurée pour travailler en mode Tout autoriser/Blocage exceptionnel, ce qui en général n'empêchera pas des sites Web de fonctionner. Pour plus d'information à ce sujet, consultez cette page Web en Anglais : https://github.com/gorhill/httpswitchboard/wiki/How-to-use-HTTP-Switchboard:-Two-opposing-views ]
|
||||||
|
|
||||||
|
[IMPORTANT : À propos du soi-disant fait que "les navigateurs à moteurs Chromium ne peuvent bloquer le code JavaScript en toute fiabilité", consultez cette page Web en Anglais : https://github.com/gorhill/httpswitchboard/wiki/Blocking-javascript-execution-reliably-in-Chromium-based-browsers]
|
||||||
|
|
||||||
|
Caractéristiques d'HTTP Switchboard :
|
||||||
|
|
||||||
|
* Consultez TOUTES les tentatives de connexions distantes, échouées ou réussies, selon qu'elles soient bloquées ou autorisées(c'est vous qui en décidez)
|
||||||
|
|
||||||
|
* En un clic, vous pouvez mettre en liste blanche/noire une ou plusieurs sortes de requêtes d'après la destination et le type des données (sachant qu'une requête bloquée ne "sortira" JAMAIS de votre navigateur)
|
||||||
|
|
||||||
|
* Mise en liste noire efficace : Les cookies ne sortiront pas de votre navigateur, le code JavaScript ne sera pas exécuté, les plugins ne démarreront pas, les pixels de pistage ne seront pas téléchargés, etc
|
||||||
|
|
||||||
|
* Attention à ne pas simplement compter que sur une seule liste noire prédéfinie pour vous protéger, pensez à utiliser les multiples listes prédéfinies pour renforcer le moteur de filtrage type ADB
|
||||||
|
|
||||||
|
* Facilité d'utilisation : HTTPSB vous laisse facilement mettre en liste blance/noire des requêtes réseau provenant d'une page Web d'après une matrice qui vous permet de manipuler en quelques clics de souris :
|
||||||
|
|
||||||
|
- Les noms de domaine (colonne de gauche)
|
||||||
|
* De très spécifique
|
||||||
|
* À très générique
|
||||||
|
|
||||||
|
- Les types de requête (première ligne)
|
||||||
|
* cookies
|
||||||
|
* stylesheets (connu sous l'abréviation CSS) (et polices Web)
|
||||||
|
* images
|
||||||
|
* objects (objets)
|
||||||
|
* scripts
|
||||||
|
* XHR (requêtes effectuées par des scripts)
|
||||||
|
* frames
|
||||||
|
* others (autres)
|
||||||
|
|
||||||
|
Vous pouvez en un clic mettre en liste noire/blanche une simple cellule, une ligne entière, un ensemble de lignes, une colonne entière, ou la matrice toute entière.
|
||||||
|
|
||||||
|
Le moteur de filtrage matriciel d'HTTP Switchboard utilise une logique de priorité pour évaluer ce qui doit être bloqué/autorisé d'après l'état des cellules (liste noire/blanche). Par exemple, cela vous permet de mettre en liste blanche tout une page Web en un clic, sans avoir à mettre en liste blanche de manière répétitive toute nouvelle donnée apparaissant sur la page.
|
||||||
|
|
||||||
|
Vous pouvez aussi créer des règles de différentes portées pour vos règles blanches/noires. Par exemple, cela vous permet de mettre en liste blanche "twitter.com" UNIQUEMENT si vous visitez le site Web de Twitter.
|
||||||
|
|
||||||
|
Le but de cette extension est de rendre l'autorisation/le refus de sites Web, en totalité ou partiellement, aussi direct(e) que possible, pour ne pas décourager les utilisateurs qui abandonnent facilement les bonnes habitudes en matière de sécurité et de confidentialité.
|
||||||
|
|
||||||
|
Au mois d'Avril 2014, HTTPSB fournit des listes prédéfinies de blocage totalisant plus de 55 000 noms de domaine différents, sachant que chacune de ces listes peut être désactivée/activé selon vos choix, et qu'il y a des listes prédéfinies supplémentaires qui peuvent aussi être activées si vous le souhaitez.
|
||||||
|
|
||||||
|
Enfin, vous pouvez choisir comment surfer sur le Net :
|
||||||
|
|
||||||
|
* Tout mettre en liste noire, et accorder des permissions exceptionnelles si besoin est (il s'agit du mode par défaut)
|
||||||
|
|
||||||
|
* Tout mettre en liste blanche, et bloquer exceptionnellement si nécessaire (non recommandé)
|
||||||
|
|
||||||
|
Sitôt l'installation de l'extension terminée, vous aurez le choix entre différents modes de filtrage matriciel, une fois le mode choisi, il ne tient qu'à vous de le personnaliser et de l'accorder à vos besoins !
|
||||||
|
|
||||||
|
De toute manière, vous bénéficierez toujours des listes de règles prédéfinis pour avoir ne serait-ce qu'une protection basique contre les pisteurs, les sites Web malveillants, etc. Ou vous pouvez désactiver tout cela (non recommandé) !
|
||||||
|
|
||||||
|
Encore une fois, c'est votre choix.
|
||||||
|
|
||||||
|
Retrouvez ici en langue anglaise de la documentation rassemblée aléatoirement sur l'extension : https://github.com/gorhill/httpswitchboard/wiki
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
RETOURS : Pour n'importe quel doute/souci que vous pourriez avoir, n'hésitez pas à me contacter. Je ne suis pas en mesure de répondre directement aux avis des utilisateurs sur Chrome/Opera Web Store, mais je serais ravi de vous répondre directement.
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
Il s'agit d'une préversion qui nécessite encore un peu de travail, bien que proche de la version 1.0. Les anomalies/problèmes/suggestions sont traités le plus rapidement possible. Rendez-vous ici (en Anglais) : https://github.com/gorhill/httpswitchboard/issues?state=open
|
||||||
|
|
||||||
|
Vous êtes plus que bienvenue pour apporter vos points de vue sur les problèmes et suggestions discutés, et argumenter en sa faveur/défaveur peut m'aider à me décider de ce qui doit être fait pour améliorer l'extension.
|
||||||
|
|
||||||
|
La facilité d'utilisation est l'objectif principal. J'ai déjà vu des utilisateurs jeter l'éponge au sujet de l'extension NoScript à cause du fait qu'elle leur a paru trop contraignante, alors plutôt que de les blâmer pour de piètres habitudes de sécurité, je préfère en vouloir aux développeurs; et ce projet est une tentative de rectifier ces erreurs qui ont entrainé l'abandon des bonnes habitudes de sécurité chez certaines personnes.
|
||||||
|
|
||||||
|
HTTP Switchboard est également utile pour comprendre ce qu'effectue en coulisses une page Web dans votre navigateur. Vous pouvez parfaitement voir et décider avec quoi une page Web communique, et restreindre ces différentes communications.
|
||||||
|
|
||||||
|
Le nombre qui apparait sur l'icone de l'extension correpond au nombre total de requêtes distinctes tentées (et réussies ou non selon qu'elles ont été autorisées/refusées) en coulisses.
|
||||||
|
|
||||||
|
Cliquez simplement à l'endroit approprié dans la matrice pour mettre en liste blanche/noire/grise un élément. La liste grise signifie que leur statut d'autorisation/de refus est héritier depuis une entrée parent dans la matrice.
|
||||||
|
|
||||||
|
Rectangle rouge = Mise en liste noire, c'est-à-dire que le moteur de filtrage matriciel empêche les requêtes d'atteindre la destination qu'elles convoitaient :
|
||||||
|
* Rectangle rouge vif : Le nom de domaine et/ou le type de requête est tout particulièrement mis en liste noire
|
||||||
|
* Rectangle rouge pâle : Le statut de liste blanche est tributaire de l'élément parent (placé sur liste grise)
|
||||||
|
|
||||||
|
Rectangle vert = Mise en liste blanche, c'est-à-dire que les requêtes sont autorisées à atteindre la destination qu'elles convoitaient :
|
||||||
|
* Rectangle vert vif : Le nom de domaine et/ou le type de requête est tout particulièrement mis en liste blanche
|
||||||
|
* Rectangle vert pâle : Le statut de liste blanche est tributaire de l'élément parent (placé sur liste grise)
|
||||||
|
|
||||||
|
La cellule en haut à gauche de la matrice ("all") représente le paramètre global par défaut, qui vous permet de choisir ce qui est autorisé ou bloqué entièrement dans le comportement par défaut. D'aucuns préfèrent tout autoriser et bloquer exceptionnellement. Personnellement je préfère l'inverse, tout bloquer et autoriser exceptionnellement.
|
||||||
|
|
||||||
|
Cette extension est aussi utile si vous souhaitez accélérer votre navigation, en bloquant toutes les requêtes d'images par exemple.
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
HTTP Switchboard est le fruit d'un projet personnel, il n'y a aucune entreprise impliquée, ainsi je n'ai pas de cahier des charges à tenir, seulement à donner aux utilisateurs les outils pour pouvoir tout contrôler de leurs navigateurs. J'apprécie le geste, mais je ne veux pas de dons, que ce soit maintenant ou à l'avenir. Si vous tenez VRAIMENT à donner quelque chose en retour, alors je souhaiterais que vous fassiez vos dons à un organisme authentiquement dédié à la défense de principes basiques de la démocratie, comme par exemple, la Fondation pour la Liberté de la Presse (https://pressfreedomfoundation.org/), l'Electronic Frontier Foundation (https://www.eff.org/), Wikileaks (https://wikileaks.org/), ou tout organisme à but non-lucratif répond au critère de "défendeur des valeurs de la démocratie" et qui se trouve dans votre pays.
|
||||||
|
|
||||||
|
Contributeurs: https://github.com/gorhill/httpswitchboard/graphs/contributors
|
||||||
|
|
||||||
|
=====
|
||||||
|
|
||||||
|
Le Code Source est sous licence GPLv3, et est disponible à l'adresse suivante : https://github.com/gorhill/httpswitchboard
|
||||||
|
|
||||||
|
----------
|
||||||
|
|
||||||
|
Consultez le Journal des changements à l'adresse suivante (en Anglais) : https://github.com/gorhill/httpswitchboard/wiki/Change-log
|
@ -0,0 +1,8 @@
|
|||||||
|
### 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 _µMatrix_ is to give the user the means for informed
|
||||||
|
consent and informed dissent.
|
@ -0,0 +1,11 @@
|
|||||||
|
## INSTALL
|
||||||
|
|
||||||
|
- Download and unzip `httpswitchboard_{version}.zip` (latest version desirable).
|
||||||
|
- Go to chromium/chrome *Extensions*.
|
||||||
|
- Click to check *Developer mode*.
|
||||||
|
- Click *Load unpacked extension...*.
|
||||||
|
- In the file selector dialog:
|
||||||
|
- Select the directory `httpswitchboard` which was created when you unzipped `httpswitchboard.zip`.
|
||||||
|
- Click *Open*.
|
||||||
|
|
||||||
|
The extension will now be available in your chromium/chrome browser.
|
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 798 B |
After Width: | Height: | Size: 6.0 KiB |
@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "__MSG_extName__",
|
||||||
|
"short_name": "µMatrix",
|
||||||
|
"version": "0.8.0.0",
|
||||||
|
"description": "__MSG_extShortDesc__",
|
||||||
|
"icons": {
|
||||||
|
"16": "icon_16.png",
|
||||||
|
"128": "icon_128.png"
|
||||||
|
},
|
||||||
|
"browser_action": {
|
||||||
|
"default_icon": {
|
||||||
|
"19": "img/browsericons/icon19.png"
|
||||||
|
},
|
||||||
|
"default_title": "__MSG_extName__",
|
||||||
|
"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/contentscript-start.js"],
|
||||||
|
"run_at": "document_start",
|
||||||
|
"all_frames": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matches": ["http://*/*", "https://*/*"],
|
||||||
|
"js": ["js/contentscript-end.js"],
|
||||||
|
"run_at": "document_end",
|
||||||
|
"all_frames": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_locale": "en",
|
||||||
|
"homepage_url": "https://github.com/gorhill/uMatrix/wiki",
|
||||||
|
"minimum_chrome_version": "22.0",
|
||||||
|
"options_page": "dashboard.html",
|
||||||
|
"permissions": [
|
||||||
|
"browsingData",
|
||||||
|
"contentSettings",
|
||||||
|
"cookies",
|
||||||
|
"downloads",
|
||||||
|
"storage",
|
||||||
|
"tabs",
|
||||||
|
"unlimitedStorage",
|
||||||
|
"webNavigation",
|
||||||
|
"webRequest",
|
||||||
|
"webRequestBlocking",
|
||||||
|
"http://*/*",
|
||||||
|
"https://*/*"
|
||||||
|
],
|
||||||
|
"web_accessible_resources": [
|
||||||
|
"css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf",
|
||||||
|
"css/noop.css"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>µMatrix — About</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||||
|
<style>
|
||||||
|
table th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
table td:first-child {
|
||||||
|
padding-right: 2em;
|
||||||
|
}
|
||||||
|
#allLocalAssetsUpdated {
|
||||||
|
margin-left: 1em;
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
#assetList table tr.unchanged {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
#assetList table tr td:nth-of-type(2) {
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
#assetList table tr.unchanged td:nth-of-type(2) {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#assetList.ooo > table {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#assetList.oox > table,
|
||||||
|
#assetList.oxx > table,
|
||||||
|
#assetList.xxx > table {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#assetList > p {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#assetList.ooo > p.ooo {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#assetList.oxx > p.oxx {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#assetList.xxx > p.xxx {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2>µMatrix <span id="aboutVersion"></span></h2>
|
||||||
|
<div>
|
||||||
|
<span data-i18n="aboutChangelog"></span><br>
|
||||||
|
<span id="aboutStorageUsed"></span><br>
|
||||||
|
<span data-i18n="aboutDoc"></span><br>
|
||||||
|
<span data-i18n="aboutPermissions"></span><br>
|
||||||
|
<span data-i18n="aboutCode"></span><br>
|
||||||
|
<span data-i18n="aboutCredits"></span><br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 data-i18n="aboutUserDataHeader"></h2>
|
||||||
|
<div>
|
||||||
|
<p><button type="button" id="backupUserDataButton" data-i18n="aboutUserDataBackupButton"></button>
|
||||||
|
<button type="button" id="restoreUserDataButton" data-i18n="aboutUserDataRestoreButton"></button>
|
||||||
|
<p style="margin-left: 2em;" data-i18n="aboutUserDataOr">
|
||||||
|
<p><button type="button" id="resetUserDataButton" data-i18n="aboutUserDataResetButton"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 data-i18n="aboutExtensionDataHeader"></h2>
|
||||||
|
<div>
|
||||||
|
<p class="para" data-i18n="aboutAssetsUpdatePrompt"></p>
|
||||||
|
<div id="assetList">
|
||||||
|
<!--
|
||||||
|
Let's define 'abc' as bit 0 to 2
|
||||||
|
Where bit can be '0' or '1'
|
||||||
|
Bit 0: list => o=absent, x=present
|
||||||
|
Bit 1: list => o=clean, x=dirty
|
||||||
|
Bit 2: update => o=idle, x=updating
|
||||||
|
Therefore:
|
||||||
|
List visible: oox oxx xox
|
||||||
|
Etc.
|
||||||
|
-->
|
||||||
|
<table class="ooo">
|
||||||
|
<tr><th data-i18n="aboutAssetsUpdateColPath"><th data-i18n="aboutAssetsUpdateColStatus">
|
||||||
|
</table>
|
||||||
|
<p class="ooo" style="color:red" data-i18n="aboutAssetsUpdateGetListError"></p>
|
||||||
|
<p class="oxx"><button type="button" id="aboutAssetsUpdateButton" data-i18n="aboutAssetsUpdateButton"></button></p>
|
||||||
|
<p class="xxx"><button type="button" disabled data-i18n="aboutAssetsUpdatingButton"></button></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="lib/jquery-2.min.js"></script>
|
||||||
|
<script src="js/i18n.js"></script>
|
||||||
|
<script src="js/dashboard-common.js"></script>
|
||||||
|
<script src="js/messaging-client.js"></script>
|
||||||
|
<script src="js/about.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,24 @@
|
|||||||
|
f92fb201840fcb83afd1125791433229 assets/httpsb/preset-recipes-1st.yaml
|
||||||
|
fcc093771d88df709220ec1013fad5f9 assets/httpsb/ubiquitous-block-lists.json
|
||||||
|
66c4469349db6c19f88df92540fd0840 assets/httpsb/setup/httpsb-noscript-like-setup.txt
|
||||||
|
e9f66671bb58917577127e483f2187e7 assets/httpsb/setup/httpsb-requestpolicy-like-setup.txt
|
||||||
|
cb1f8dc3b5aa5fabe113a0e4e7068893 assets/httpsb/setup/httpsb-block-all-narrowly-allow-all-setup.txt
|
||||||
|
8181bd0c532d7f0a38cb743f1a7d4143 assets/httpsb/setup/httpsb-just-block-ads-setup.txt
|
||||||
|
b869255b8152f363dee21e8427e27c34 assets/httpsb/setup/httpsb-block-nothing-report-everything-setup.txt
|
||||||
|
229b68f9695fb1a597b29f344a185939 assets/httpsb/setup/httpsb-allow-all-block-exceptionally-setup.txt
|
||||||
|
ccd4abb2f8714009b147edd34e6a52d6 assets/httpsb/setup/httpsb-factory-setup.txt
|
||||||
|
188ce926323d816ae9d7d5ebbb567862 assets/httpsb/blacklist.txt
|
||||||
|
847b801091b64d0bfb1dcd2f7c6fe204 assets/httpsb/block-facebook.txt
|
||||||
|
fb55d70ecc242116235092e9c21df24e assets/httpsb/preset-recipes-3rd.yaml
|
||||||
|
050657115a3356a0d1bfccfed5d11ace assets/thirdparties/mirror1.malwaredomains.com/files/immortal_domains.txt
|
||||||
|
7f0443f3dcc9abfd47cfbc95ce824ddb assets/thirdparties/mirror1.malwaredomains.com/files/README.md
|
||||||
|
e6c01d808e91ae6fad9b0d869f3dabd6 assets/thirdparties/mirror1.malwaredomains.com/files/justdomains
|
||||||
|
f6bba27911e9b379bcb638e42f0bb740 assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat
|
||||||
|
d0892d1e1b6a3c70f43895bdc386e9fb assets/thirdparties/someonewhocares.org/hosts/hosts
|
||||||
|
11fd3342c899e461f047606c94008951 assets/thirdparties/winhelp2002.mvps.org/hosts.txt
|
||||||
|
042419405031f0fcfac3735bf4f05e21 assets/thirdparties/www.malwaredomainlist.com/hostslist/README.md
|
||||||
|
255d695c75b187d64a8ab1c7047b8dec assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt
|
||||||
|
4d6a139c60b2dc910f976ccf314beab6 assets/thirdparties/hosts-file.net/hosts.txt
|
||||||
|
d510b028e14eddb722c1fd0027475bf1 assets/thirdparties/hosts-file.net/ad-servers
|
||||||
|
11697e3b7c4a579ed80c8a5de71e64ae assets/thirdparties/pgl.yoyo.org/as/serverlist
|
||||||
|
5b8e13b618c68293430913029118781a assets/thirdparties/pgl.yoyo.org/as/README.md
|
@ -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.
|
@ -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,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# This script assumes a linux environment
|
||||||
|
|
||||||
|
TEMPFILE=/tmp/umatrix-asset
|
||||||
|
|
||||||
|
echo "*** HTTP Switchboard: updating remote assets..."
|
||||||
|
|
||||||
|
THIRDPARTY_REMOTEURLS=(
|
||||||
|
'http://mirror1.malwaredomains.com/files/immortal_domains.txt'
|
||||||
|
'http://mirror1.malwaredomains.com/files/justdomains'
|
||||||
|
'http://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&showintro=1&startdate%5Bday%5D=&startdate%5Bmonth%5D=&startdate%5Byear%5D=&mimetype=plaintext'
|
||||||
|
'http://www.malwaredomainlist.com/hostslist/hosts.txt'
|
||||||
|
'http://hosts-file.net/.%5Cad_servers.txt'
|
||||||
|
'http://someonewhocares.org/hosts/hosts'
|
||||||
|
'http://winhelp2002.mvps.org/hosts.txt'
|
||||||
|
'http://publicsuffix.org/list/effective_tld_names.dat'
|
||||||
|
)
|
||||||
|
|
||||||
|
THIRDPARTY_LOCALURLS=(
|
||||||
|
'thirdparties/mirror1.malwaredomains.com/files/immortal_domains.txt'
|
||||||
|
'thirdparties/mirror1.malwaredomains.com/files/justdomains'
|
||||||
|
'thirdparties/pgl.yoyo.org/as/serverlist'
|
||||||
|
'thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt'
|
||||||
|
'thirdparties/hosts-file.net/ad-servers'
|
||||||
|
'thirdparties/someonewhocares.org/hosts/hosts'
|
||||||
|
'thirdparties/winhelp2002.mvps.org/hosts.txt'
|
||||||
|
'thirdparties/publicsuffix.org/list/effective_tld_names.dat'
|
||||||
|
)
|
||||||
|
|
||||||
|
ENTRY_INDEX=0
|
||||||
|
for THIRDPARTY_REMOTEURL in ${THIRDPARTY_REMOTEURLS[@]}; do
|
||||||
|
THIRDPARTY_LOCALURL=${THIRDPARTY_LOCALURLS[ENTRY_INDEX]}
|
||||||
|
echo "*** Downloading" $THIRDPARTY_REMOTEURL
|
||||||
|
if wget -q -T 30 -O $TEMPFILE -- $THIRDPARTY_REMOTEURL; then
|
||||||
|
if [ -s $TEMPFILE ]; then
|
||||||
|
if ! cmp -s $TEMPFILE $THIRDPARTY_LOCALURL; then
|
||||||
|
echo " New version found: $THIRDPARTY_LOCALURL"
|
||||||
|
if [ "$1" != "dry" ]; then
|
||||||
|
mv $TEMPFILE $THIRDPARTY_LOCALURL
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
let ENTRY_INDEX+=1
|
||||||
|
done
|
||||||
|
|
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# This script assumes a linux environment
|
||||||
|
|
||||||
|
echo "*** HTTP Switchboard: generating checksums.txt file..."
|
||||||
|
pushd ..
|
||||||
|
truncate -s 0 assets/checksums.txt
|
||||||
|
LIST="$(find assets/httpsb assets/thirdparties -type f)"
|
||||||
|
for ENTRY in $LIST; do
|
||||||
|
echo `md5sum $ENTRY` >> assets/checksums.txt
|
||||||
|
done
|
||||||
|
popd
|
||||||
|
|
||||||
|
echo "*** HTTP Switchboard: checksums updated."
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# This script assumes a linux environment
|
||||||
|
|
||||||
|
echo "*** HTTP Switchboard: git adding changed assets..."
|
||||||
|
git add --update --ignore-removal --ignore-errors ./*
|
||||||
|
echo "*** HTTP Switchboard: git committing assets..."
|
||||||
|
git commit -m 'update of third-party assets'
|
||||||
|
echo "*** HTTP Switchboard: git pushing assets to remote master..."
|
||||||
|
git push origin master
|
||||||
|
|
||||||
|
echo "*** HTTP Switchboard: git done."
|
||||||
|
|
@ -0,0 +1,36 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>µMatrix</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="lib/punycode.min.js"></script>
|
||||||
|
<script src="lib/publicsuffixlist.min.js"></script>
|
||||||
|
<script src="lib/yamd5.min.js"></script>
|
||||||
|
<script src="js/types.js"></script>
|
||||||
|
<script src="js/background.js"></script>
|
||||||
|
<script src="js/xal.js"></script>
|
||||||
|
<script src="js/usersettings.js"></script>
|
||||||
|
<script src="js/async.js"></script>
|
||||||
|
<script src="js/liquid-dict.js"></script>
|
||||||
|
<script src="js/matrix.js"></script>
|
||||||
|
<script src="js/utils.js"></script>
|
||||||
|
<script src="js/assets.js"></script>
|
||||||
|
<script src="js/asset-updater.js"></script>
|
||||||
|
<script src="js/httpsb.js"></script>
|
||||||
|
<script src="js/reqstats.js"></script>
|
||||||
|
<script src="js/cookies.js"></script>
|
||||||
|
<script src="js/messaging.js"></script>
|
||||||
|
<script src="js/profiler.js"></script>
|
||||||
|
<script src="js/storage.js"></script>
|
||||||
|
<script src="js/pagestats.js"></script>
|
||||||
|
<script src="js/tab.js"></script>
|
||||||
|
<script src="js/traffic.js"></script>
|
||||||
|
<script src="js/uritools.js"></script>
|
||||||
|
<script src="js/useragent.js"></script>
|
||||||
|
<script src="js/messaging-handlers.js"></script>
|
||||||
|
<script src="js/start.js"></script>
|
||||||
|
<script src="js/commands.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,73 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: 'httpsb';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'httpsb';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-Bold.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'httpsb';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-Light.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'httpsb';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: bold;
|
||||||
|
src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-BoldItalic.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'httpsb';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100;
|
||||||
|
src: local('httpsb'), url(fonts/Roboto_Condensed/RobotoCondensed-Light.ttf) format('truetype');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'FontAwesome';
|
||||||
|
src: url('fonts/fontawesome-webfont.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
.fa {
|
||||||
|
font-family: FontAwesome;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1;
|
||||||
|
vertical-align: baseline;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* http://stackoverflow.com/questions/2011142/how-to-change-the-style-of-title-attribute-inside-the-anchor-tag?answertab=votes */
|
||||||
|
*[data-tip] {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
*[data-tip]:hover:after {
|
||||||
|
position: absolute;
|
||||||
|
content: attr(data-tip);
|
||||||
|
padding: 4px 6px;
|
||||||
|
font: 11px sans-serif;
|
||||||
|
line-height: 140%;
|
||||||
|
text-align: left;
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
|
top: 110%;
|
||||||
|
white-space: pre;
|
||||||
|
z-index: 20;
|
||||||
|
border: 1px solid gray;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 1px 1px 3px gray;
|
||||||
|
}
|
||||||
|
.tip-anchor-left[data-tip]:hover:after {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.tip-anchor-right[data-tip]:hover:after {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0.5em 5em 0.5em;
|
||||||
|
font: 15px httpsb,sans-serif;
|
||||||
|
}
|
||||||
|
h2, h3 {
|
||||||
|
margin: 1em 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
h2:nth-of-type(1) {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
h2 + * {
|
||||||
|
margin: 0 0 0 1em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.para {
|
||||||
|
width: 40em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.whatisthis {
|
||||||
|
margin: 0 0 0 8px;
|
||||||
|
border: 0;
|
||||||
|
padding: 0 0 4px 0;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background: url('/img/help16.png') no-repeat right bottom 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
.whatisthis:hover {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
.whatisthis-expandable {
|
||||||
|
margin: 0.5em 0 1em 1.25em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: none;
|
||||||
|
border: 1px dotted black;
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
}
|
||||||
|
.whatisthis-expandable > p {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.whatisthis-expandable > p:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.whatisthis-expandable.whatisthis-expanded {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.warn {
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px;
|
||||||
|
background-color: #FEDAE0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
@ -0,0 +1,590 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
font: 13px httpsb,sans-serif;
|
||||||
|
background-color: white;
|
||||||
|
min-width: 32em;
|
||||||
|
min-height: 15em;
|
||||||
|
}
|
||||||
|
*:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paneHead {
|
||||||
|
padding: 0;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
height: 5.5em;
|
||||||
|
left:0;
|
||||||
|
right: 0;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.paneContent {
|
||||||
|
padding-top: 5.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
.toolbar.alignLeft {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.toolbar.alignRight {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
body .toolbar button {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 4px;
|
||||||
|
font: inherit;
|
||||||
|
background-color: white;
|
||||||
|
opacity: 0.9;
|
||||||
|
cursor: pointer;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
body .toolbar button:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
body .toolbar button.disabled {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
body .toolbar button.switch.disabled {
|
||||||
|
color: #a00;
|
||||||
|
}
|
||||||
|
body .toolbar button.fa {
|
||||||
|
font: 1.75em 'FontAwesome';
|
||||||
|
min-width: 1.1em;
|
||||||
|
}
|
||||||
|
body.tScopeGlobal .toolbar button.scopeRel:not(.disabled) {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
body.tScopeLocal .toolbar button.scopeRel:not(.disabled) {
|
||||||
|
color: #24c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 3px 0 0 0;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 50;
|
||||||
|
font-size: 110%;
|
||||||
|
display: none;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
.dropdown-menu > ul {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.25em;
|
||||||
|
background-color: white;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
.dropdown-menu > ul > li.dropdown-menu-entry {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 4px 0.5em;
|
||||||
|
color: black;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.dropdown-menu > ul > li.dropdown-menu-entry:hover {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
.dropdown-menu > ul > li.dropdown-menu-entry-divider {
|
||||||
|
margin: 0.5em 0;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
.dropdown-menu > ul > li.dropdown-menu-entry > .fa {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
font-size: 110%;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
.dropdown-menu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.dropdown-menu.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.dropdown-menu-capture {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
opacity: 0.1;
|
||||||
|
display: none;
|
||||||
|
z-index: 40;
|
||||||
|
}
|
||||||
|
.dropdown-menu.show ~ .dropdown-menu-capture {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body #buttonRevertAll {
|
||||||
|
position: relative;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
body #buttonRevertAll > span {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 60%;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
body #buttonRevertAll > span:nth-of-type(1) {
|
||||||
|
left: 3px;
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
body #buttonRevertAll > span:nth-of-type(2) {
|
||||||
|
right: 3px;
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
body #buttonRevertAll > span:nth-of-type(3) {
|
||||||
|
right: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
}
|
||||||
|
body #buttonRevertAll > span:nth-of-type(4) {
|
||||||
|
left: 3px;
|
||||||
|
bottom: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
button > span.badge {
|
||||||
|
padding: 1px 1px;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 40%;
|
||||||
|
position: absolute;
|
||||||
|
right: 1px;
|
||||||
|
bottom: 1px;
|
||||||
|
color: #000;
|
||||||
|
background-color: rgba(240,240,240,0.75)
|
||||||
|
}
|
||||||
|
button.disabled > span.badge {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#buttonPresets + .dropdown-menu {
|
||||||
|
position: fixed;
|
||||||
|
left: 10vw;
|
||||||
|
width: 80vw;
|
||||||
|
}
|
||||||
|
.presetInfo {
|
||||||
|
margin: 0.25em 0.5em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.presetEntry {
|
||||||
|
margin: 0.25em 0.25em;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
.presetEntry .fa {
|
||||||
|
margin-right: 0.25em;
|
||||||
|
font-size: 110%;
|
||||||
|
}
|
||||||
|
.presetEntry:hover {
|
||||||
|
background-color: #80e2ff;
|
||||||
|
}
|
||||||
|
#presetMore > *:first-child {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
text-align: center;
|
||||||
|
color: #888;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
#presetMore > *:first-child + div {
|
||||||
|
margin: 0.25em 0 0 0;
|
||||||
|
padding: 0.25em 0 0 0;
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#presetMore > *:first-child + div.show {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#presetMore > *:first-child + div > * {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
#presetMoreRecipe {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
width: 75%;
|
||||||
|
height: 4em;
|
||||||
|
overflow: hidden;
|
||||||
|
resize: none;
|
||||||
|
font-size: 10px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
#presetMoreRecipe.bad {
|
||||||
|
border: 1px solid #fcc;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
#presetMoreWrite.bad {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I think this is obsolete */
|
||||||
|
.dropdown-menu > li > a > i {
|
||||||
|
padding: 0 6px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .toolbar button#scopeCell {
|
||||||
|
margin: 0;
|
||||||
|
border: 1px dotted rgba(0,0,0,0.2);
|
||||||
|
padding: 6px 1px 3px 1px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
width: 16em;
|
||||||
|
height: 1.5em;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 100%;
|
||||||
|
color: white;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: -1px -1px;
|
||||||
|
}
|
||||||
|
body #scopeCell + .dropdown-menu {
|
||||||
|
padding: 6px 1px 3px 1px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
text-align: right;
|
||||||
|
width: 16em;
|
||||||
|
}
|
||||||
|
body.tScopeGlobal #scopeCell {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
body.tScopeLocal #scopeCell {
|
||||||
|
background-color: #24c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.matrix {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.matRow {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.matCell {
|
||||||
|
margin: 1px 1px 0 0;
|
||||||
|
border: 1px dotted rgba(0,0,0,0.2);
|
||||||
|
padding: 6px 1px 3px 1px;
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: content-box;
|
||||||
|
width: 2.4em;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 110%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paneHead #matHead {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 3px;
|
||||||
|
}
|
||||||
|
.paneHead .matCell:nth-child(2) {
|
||||||
|
letter-spacing: -0.3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paneContent .matrix .matRow > .matCell:first-child {
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
.paneContent .matrix .matRow > .matCell:first-child > b {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matrix .matRow > .matCell:first-child {
|
||||||
|
width: 16em;
|
||||||
|
text-align: start;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
.matrix .matRow.l2 > .matCell:first-child {
|
||||||
|
margin-left: 1px;
|
||||||
|
width: calc(16em - 1px);
|
||||||
|
}
|
||||||
|
.matrix .matRow > .matCell:hover {
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
.matrix .matGroup .matSection {
|
||||||
|
margin: 2px 0 0 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
/* background-color: rgba(0,0,0,0.05); */
|
||||||
|
}
|
||||||
|
.matrix .matGroup .matSection:first-child {
|
||||||
|
margin-top: 2px;
|
||||||
|
border-top: 1px dotted #ccc;
|
||||||
|
padding-top: 1px;
|
||||||
|
}
|
||||||
|
.matrix .matGroup.g0 .matSection:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
.matrix .matGroup.g3 .matSection:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
/* Collapsing of domains */
|
||||||
|
.matrix .matSection .matRow.meta {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.matrix .matSection.collapsible.collapsed .matRow.meta {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.matrix .matSection.collapsible.collapsed .matRow.l1:not(.meta) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.matrix .matSection.collapsible.collapsed .matRow.l2.collapsible {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Collapsing of blacklisted */
|
||||||
|
.matrix .g3Meta {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
height: 6px;
|
||||||
|
background: url('/img/matrix-group-hide.png') no-repeat center top,
|
||||||
|
url('/img/matrix-group-hline.png') repeat-x center top 3px;
|
||||||
|
opacity: 0.2;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.matrix .g3Meta:hover {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.matrix .g3Meta.g3Collapsed {
|
||||||
|
background: url('/img/matrix-group-show.png') no-repeat center top,
|
||||||
|
url('/img/matrix-group-hline.png') repeat-x center top 3px;
|
||||||
|
}
|
||||||
|
.matrix .g3Meta.g3Collapsed ~ .matSection {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
body.powerOff .matrix .g3Meta.g3Collapsed ~ .matSection {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.matrix .g3Meta ~ .matRow.ro {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.matrix .g3Meta.g3Collapsed ~ .matRow.ro {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body.powerOff .matrix .g3Meta.g3Collapsed ~ .matRow.ro {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.matrix .matGroup .g3Meta + *,.matrix .matGroup .g3Meta + * + * {
|
||||||
|
margin-top: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cell coloring */
|
||||||
|
.rd {
|
||||||
|
color: white;
|
||||||
|
background-color: #c00;
|
||||||
|
}
|
||||||
|
.gd {
|
||||||
|
color: white;
|
||||||
|
background-color: #080;
|
||||||
|
}
|
||||||
|
.ri,
|
||||||
|
.rg {
|
||||||
|
border-color: #debaba;
|
||||||
|
color: black;
|
||||||
|
background-color: #f8d0d0;
|
||||||
|
}
|
||||||
|
.gi,
|
||||||
|
.gg {
|
||||||
|
border-color: #bad6ba;
|
||||||
|
color: black;
|
||||||
|
background-color: #d0f0d0;
|
||||||
|
}
|
||||||
|
.matCell.rdp {
|
||||||
|
background-image: url('/img/permanent-black-small.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: -1px -1px;
|
||||||
|
}
|
||||||
|
.matCell.gdp {
|
||||||
|
background-image: url('/img/permanent-white-small.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: -1px -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cell coloring for color blind-friendly (hopefully) */
|
||||||
|
body.colorblind .rd {
|
||||||
|
color: white;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
body.colorblind .gd {
|
||||||
|
border-color: #aaa;
|
||||||
|
color: black;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
body.colorblind .ri,
|
||||||
|
body.colorblind .rg {
|
||||||
|
border-color: #333;
|
||||||
|
color: white;
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
body.colorblind .gi,
|
||||||
|
body.colorblind .gg {
|
||||||
|
border-color: #aaa;
|
||||||
|
color: black;
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
body.colorblind .matCell.rdp {
|
||||||
|
background-image: url('/img/permanent-black-small-cb.png');
|
||||||
|
}
|
||||||
|
body.colorblind .matCell.gdp {
|
||||||
|
background-image: url('/img/permanent-white-small-cb.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.matRow.rw .matCell {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
body.powerOff .matRow.rw .matCell {
|
||||||
|
cursor: auto;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#cellHotspots {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
#whitelist, #blacklist {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 50%;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
#whitelist {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
#blacklist {
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
body.powerOff #whitelist, body.powerOff #blacklist {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.rw .matCell.ri #whitelist:hover,
|
||||||
|
.rw .matCell.rg #whitelist:hover {
|
||||||
|
background-color: #080;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
body.colorblind .rw .matCell.ri #whitelist:hover,
|
||||||
|
body.colorblind .rw .matCell.rg #whitelist:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.rw .matCell.gi #whitelist:hover,
|
||||||
|
.rw .matCell.gg #whitelist:hover {
|
||||||
|
background-color: #080;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
body.colorblind .rw .matCell.gi #whitelist:hover,
|
||||||
|
body.colorblind .rw .matCell.gg #whitelist:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.matCell.rdt #whitelist:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.matCell.gdt #whitelist:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.rw .matCell.ri #blacklist:hover,
|
||||||
|
.rw .matCell.rg #blacklist:hover {
|
||||||
|
background-color: #c00;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
body.colorblind .rw .matCell.ri #blacklist:hover,
|
||||||
|
body.colorblind .rw .matCell.rg #blacklist:hover {
|
||||||
|
background-color: #000;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.rw .matCell.gi #blacklist:hover,
|
||||||
|
.rw .matCell.gg #blacklist:hover {
|
||||||
|
background-color: #c00;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
body.colorblind .rw .matCell.gi #blacklist:hover,
|
||||||
|
body.colorblind .rw .matCell.gg #blacklist:hover {
|
||||||
|
background-color: #000;
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.matCell.rd #blacklist:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.matCell.gd #blacklist:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
#domainOnly {
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid gray;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 1px;
|
||||||
|
position: absolute;
|
||||||
|
font-size: 1.1em;
|
||||||
|
left: 20%;
|
||||||
|
bottom: -20%;
|
||||||
|
visibility: hidden;
|
||||||
|
color: black;
|
||||||
|
background-color: white;
|
||||||
|
opacity: 0.25;
|
||||||
|
z-index: 10000;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.matSection #domainOnly .fa:before {
|
||||||
|
content: '\f106';
|
||||||
|
}
|
||||||
|
.matSection.collapsed #domainOnly .fa:before {
|
||||||
|
content: '\f107';
|
||||||
|
}
|
||||||
|
.matSection.collapsible .matRow.l1 .matCell:nth-of-type(1):hover #domainOnly {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
#matHead #domainOnly .fa:before {
|
||||||
|
content: '\f106';
|
||||||
|
}
|
||||||
|
#matHead.collapsed #domainOnly .fa:before {
|
||||||
|
content: '\f107';
|
||||||
|
}
|
||||||
|
#matHead.collapsible .matRow .matCell:nth-of-type(1):hover #domainOnly {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
#domainOnly:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
@ -0,0 +1,228 @@
|
|||||||
|
#navi-bar {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
padding: 0 0.5em 0.5em 0.5em;
|
||||||
|
position: fixed;
|
||||||
|
background-color: white;
|
||||||
|
width: 100%;
|
||||||
|
height: 7em;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
#navi-bar + div {
|
||||||
|
padding: 8em 0.5em 1em 0.5em;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
table td {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 4px 0;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
table td h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 99%;
|
||||||
|
height: 20em;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
margin: 0.5em 0.25em 0 0;
|
||||||
|
border: 1px solid rgba(0,0,0,0.2);
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 0.25em 0.75em;
|
||||||
|
font: inherit;
|
||||||
|
background-color: #eee;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button .fa {
|
||||||
|
margin-right: 0.5em;
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
|
||||||
|
#recipeDecode, #recipeImport, #recipeEncode, #recipeExport {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
#recipeImport, #recipeExport {
|
||||||
|
padding-top: 32px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
#recipeDecode, #recipeEncode {
|
||||||
|
margin: 0 4px;
|
||||||
|
padding-top: 28px;
|
||||||
|
display: inline-block;
|
||||||
|
height: 28px;
|
||||||
|
}
|
||||||
|
#recipeDecode {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
background: url('/img/decode.png') no-repeat center top;
|
||||||
|
}
|
||||||
|
#recipeEncode {
|
||||||
|
margin-top: 1em;
|
||||||
|
background: url('/img/encode.png') no-repeat center top;
|
||||||
|
}
|
||||||
|
#recipeImport {
|
||||||
|
background: url('/img/import.png') no-repeat center top;
|
||||||
|
}
|
||||||
|
#recipeExport {
|
||||||
|
background: url('/img/export.png') no-repeat center top;
|
||||||
|
}
|
||||||
|
#recipeDecode:hover, #recipeImport:hover, #recipeEncode:hover, #recipeExport:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.recipe {
|
||||||
|
margin: 2px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 1px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: smaller;
|
||||||
|
line-height: 110%;
|
||||||
|
color: #888;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
white-space: pre;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.scopes ul {
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1em;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.scopes > ul {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope {
|
||||||
|
margin: 4px;
|
||||||
|
border: 1px dotted #ccc;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope:hover {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.todelete {
|
||||||
|
background-color: #fee;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > div:first-child {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > div:first-child .scopeName {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > div:first-child .state {
|
||||||
|
padding: 3px 3px 0 3px;
|
||||||
|
float: right;
|
||||||
|
font-size: 120%;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > div:first-child .state::before {
|
||||||
|
content: '\f09c';
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.permanent > div:first-child .state::before {
|
||||||
|
content: '\f023';
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.todelete > div:first-child .state::before {
|
||||||
|
content: '\f00d';
|
||||||
|
opacity: 1;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
#global.scopes > ul > li.scope > div:first-child {
|
||||||
|
color: #000;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
#global.scopes > ul > li.scope > ul > li > ul {
|
||||||
|
min-width: 12em;
|
||||||
|
}
|
||||||
|
#perdomain.scopes > ul > li.scope > *:first-child {
|
||||||
|
color: #24c;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
#persite.scopes > ul > li.scope > *:first-child {
|
||||||
|
color: #48c;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
#global.scopes > ul > li.scope > ul > li {
|
||||||
|
margin-left: 2em;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
#global.scopes > ul > li.scope > ul > li:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > .recipe {
|
||||||
|
margin: 1em 2px 2px 2px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 1px;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 110%;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
width: 20em;
|
||||||
|
height: 2em;
|
||||||
|
color: #888;
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
white-space: pre;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope .recipe:hover {
|
||||||
|
opacity: 1.0;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li.white {
|
||||||
|
color: #080;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li.black {
|
||||||
|
color: #c00;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li.gray {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li > ul > li {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li > ul > li:hover {
|
||||||
|
background-color: #eef;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li > ul > li > span.state {
|
||||||
|
padding: 4px 4px 0 4px;
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.permanent > ul > li > ul > li > span.state {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.permanent > ul > li > ul > li > span.state::before {
|
||||||
|
content: '\f09c';
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.permanent > ul > li > ul > li.permanent > span.state::before {
|
||||||
|
content: '\f023';
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.todelete > ul > li > ul > li.rule {
|
||||||
|
background-color: #fee;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li > ul > li.rule.todelete {
|
||||||
|
background-color: #fee;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope > ul > li > ul > li.rule.todelete > span.state::before {
|
||||||
|
content: '\f00d';
|
||||||
|
opacity: 1;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.scopes > ul > li.scope.todelete > ul > li > ul > li.rule > span.state::before {
|
||||||
|
content: '\f00d';
|
||||||
|
opacity: 1;
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.bad {
|
||||||
|
background-color: #fdd;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>µMatrix — Dashboard</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
font: 15px httpsb,sans-serif;
|
||||||
|
position: relative;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#dashboard-nav {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 50px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
#dashboard-nav-widgets {
|
||||||
|
margin: 0;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
padding: 4px 0 3px 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
#dashboard-nav-widgets span {
|
||||||
|
padding: 0 0.5em;
|
||||||
|
font-size: larger;
|
||||||
|
}
|
||||||
|
.tabButton {
|
||||||
|
margin: 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-top-left-radius: 3px;
|
||||||
|
border-top-right-radius: 3px;
|
||||||
|
padding: 4px;
|
||||||
|
color: black;
|
||||||
|
background-color: #eee;
|
||||||
|
font: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.tabButton:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
.tabButton:active,.tabButton:visited {
|
||||||
|
color: inherited;
|
||||||
|
}
|
||||||
|
.tabButton.selected {
|
||||||
|
border-bottom: 1px solid white;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
overflow: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link href='css/common.css' rel='stylesheet' type='text/css'>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="dashboard-nav">
|
||||||
|
<div id="dashboard-nav-widgets">
|
||||||
|
<span>µMatrix</span>
|
||||||
|
<a class="tabButton" id="settings" href="#settings" data-dashboard-panel-url="settings.html" data-i18n="settingsPageName"></a>
|
||||||
|
<a class="tabButton" id="privacy" href="#privacy" data-dashboard-panel-url="privacy.html" data-i18n="privacyPageName"></a>
|
||||||
|
<a class="tabButton" id="ubiquitous-rules" href="#ubiquitous-rules" data-dashboard-panel-url="ubiquitous-rules.html" data-i18n="ubiquitousRulesPageName"></a>
|
||||||
|
<a class="tabButton" id="statistics" href="#statistics" data-dashboard-panel-url="info.html" data-i18n="statsPageName"></a>
|
||||||
|
<a class="tabButton" id="about" href="#about" data-dashboard-panel-url="about.html" data-i18n="aboutPageName"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<iframe src=""></iframe>
|
||||||
|
|
||||||
|
<script src="lib/jquery-2.min.js"></script>
|
||||||
|
<script src="js/i18n.js"></script>
|
||||||
|
<script src="js/dashboard.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
After Width: | Height: | Size: 817 B |
After Width: | Height: | Size: 830 B |
After Width: | Height: | Size: 840 B |
After Width: | Height: | Size: 814 B |
After Width: | Height: | Size: 829 B |
After Width: | Height: | Size: 825 B |
After Width: | Height: | Size: 808 B |
After Width: | Height: | Size: 822 B |
After Width: | Height: | Size: 808 B |
After Width: | Height: | Size: 792 B |
After Width: | Height: | Size: 761 B |
After Width: | Height: | Size: 750 B |
After Width: | Height: | Size: 836 B |
After Width: | Height: | Size: 836 B |
After Width: | Height: | Size: 827 B |
After Width: | Height: | Size: 837 B |
After Width: | Height: | Size: 833 B |
After Width: | Height: | Size: 823 B |
After Width: | Height: | Size: 818 B |
After Width: | Height: | Size: 830 B |
After Width: | Height: | Size: 777 B |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 479 B |
After Width: | Height: | Size: 479 B |
After Width: | Height: | Size: 587 B |
After Width: | Height: | Size: 371 B |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 608 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 178 B |
After Width: | Height: | Size: 257 B |
After Width: | Height: | Size: 217 B |
After Width: | Height: | Size: 243 B |
After Width: | Height: | Size: 280 B |
After Width: | Height: | Size: 238 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 230 B |
After Width: | Height: | Size: 246 B |
After Width: | Height: | Size: 319 B |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 331 B |
@ -0,0 +1,173 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<title>µMatrix — Info</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||||
|
<style>
|
||||||
|
select {
|
||||||
|
max-width: 20em;
|
||||||
|
}
|
||||||
|
#stats div div,#lists div div {
|
||||||
|
padding: 0 1em 0 0;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#requests-filters {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
#requests-filters label {
|
||||||
|
padding-right: 1em
|
||||||
|
}
|
||||||
|
#requests-log {
|
||||||
|
margin: 0;
|
||||||
|
border: 1px inset #eee;
|
||||||
|
padding: 0;
|
||||||
|
font: 11px monospace;
|
||||||
|
background-color: white;
|
||||||
|
overflow: scroll;
|
||||||
|
width: calc(100% - 1.5em);
|
||||||
|
}
|
||||||
|
#requests-log table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
#requests-log tr {
|
||||||
|
margin: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: #070;
|
||||||
|
}
|
||||||
|
#requests-log tr.ro {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
#requests-log tr:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
#requests-log tr.blocked-true {
|
||||||
|
color: #c00;
|
||||||
|
}
|
||||||
|
#requests-log tr:first-child {
|
||||||
|
font-weight: bold;
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
#requests-log tr > td {
|
||||||
|
padding: 1px 0.75em 1px 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
#requests-log tr > td:nth-of-type(2) {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.type-main_frame {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
tr.unused {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h2 data-i18n="statsPageGenericStats" id="generic-stats"></h2>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li id="statsPageCookieHeadersFoiled">
|
||||||
|
<li id="statsPageRefererHeadersFoiled">
|
||||||
|
<li id="statsPageHyperlinkAuditingFoiled">
|
||||||
|
<li id="statsPageCookiesRemoved">
|
||||||
|
<li id="statsPageLocalStoragesCleared">
|
||||||
|
<li id="statsPageBrowserCacheCleared">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 data-i18n="statsPageDetailedStats" id="detailed-stats"></h2>
|
||||||
|
<div>
|
||||||
|
<select id="selectPageUrls">
|
||||||
|
<option value="all" data-i18n="statsPageDetailedAllPages"></option>
|
||||||
|
<option id="selectPageUrlTemplate" value="http://chromium-behind-the-scene/" data-i18n="statsPageDetailedBehindTheScenePage"></option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<h3 data-i18n="statsPageOverview"></h3>
|
||||||
|
<div id="stats">
|
||||||
|
<div style="white-space:nowrap;">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span data-i18n="statsPageRequests"></span><br>
|
||||||
|
All:<br>
|
||||||
|
Pages:<br>
|
||||||
|
<a href="http://en.wikipedia.org/wiki/Http_cookie">Cookies</a>:<br>
|
||||||
|
<a href="https://en.wikipedia.org/wiki/Cascading_Style_Sheets">CSS</a>:<br>
|
||||||
|
Images:<br>
|
||||||
|
<span title="i.e. Flash, ActiveX, Java applets, etc.">Plugins</span>:<br>
|
||||||
|
<a href="http://en.wikipedia.org/wiki/HTML_scripting">Scripts</a>:<br>
|
||||||
|
<a href="http://en.wikipedia.org/wiki/XMLHttpRequest">XMLHttpRequests</a>:<br>
|
||||||
|
<a href="http://en.wikipedia.org/wiki/Framing_(World_Wide_Web)">Frames</a>:<br>
|
||||||
|
<a title="i.e. HTML5 audio, HTML5 video, HTML5 canvas, etc." href="https://github.com/gorhill/httpswitchboard/wiki/In-the-top-right-of-the-matrix,-what-is-%22other%22%3F">Others</a>:<br>
|
||||||
|
</div>
|
||||||
|
<div style="color:#c00">
|
||||||
|
<span data-i18n="statsPageBlocked"></span><br>
|
||||||
|
<span id="blockedAllCount"></span><br>
|
||||||
|
<span id="blockedMainFrameCount"></span><br>
|
||||||
|
<span id="blockedCookieCount"></span><br>
|
||||||
|
<span id="blockedStylesheetCount"></span><br>
|
||||||
|
<span id="blockedImageCount"></span><br>
|
||||||
|
<span id="blockedObjectCount"></span><br>
|
||||||
|
<span id="blockedScriptCount"></span><br>
|
||||||
|
<span id="blockedXHRCount"></span><br>
|
||||||
|
<span id="blockedSubFrameCount"></span><br>
|
||||||
|
<span id="blockedOtherCount"></span><br>
|
||||||
|
</div>
|
||||||
|
<div style="color:#070">
|
||||||
|
<span data-i18n="statsPageAllowed"></span><br>
|
||||||
|
<span id="allowedAllCount"></span><br>
|
||||||
|
<span id="allowedMainFrameCount"></span><br>
|
||||||
|
<span id="allowedCookieCount"></span><br>
|
||||||
|
<span id="allowedStylesheetCount"></span><br>
|
||||||
|
<span id="allowedImageCount"></span><br>
|
||||||
|
<span id="allowedObjectCount"></span><br>
|
||||||
|
<span id="allowedScriptCount"></span><br>
|
||||||
|
<span id="allowedXHRCount"></span><br>
|
||||||
|
<span id="allowedSubFrameCount"></span><br>
|
||||||
|
<span id="allowedOtherCount"></span><br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<h3 data-i18n="statsPageDetailed"></h3>
|
||||||
|
<div id="requests">
|
||||||
|
<div><span data-i18n="statsPageLogSizePrompt1"></span> <input id="max-logged-requests" type="text" value="50" size="3"> <span data-i18n="statsPageLogSizePrompt2"></span>
|
||||||
|
<button class="whatisthis"></button>
|
||||||
|
<p class="whatisthis-expandable para" data-i18n="statsPageLogSizeHelp"></p>
|
||||||
|
<p id="requests-filters"><button id="refresh-requests" data-i18n="statsPageRefresh"></button> Show:
|
||||||
|
<input id="show-main_frame" type="checkbox" checked value="1"><label for="show-main_frame">Pages</label>
|
||||||
|
<input id="show-blocked" type="checkbox" checked value="1"><label for="show-blocked"><span style="color:#c00" data-i18n="statsPageBlocked">Blocked</span></label>
|
||||||
|
<input id="show-allowed" type="checkbox" checked value="1"><label for="show-allowed"><span style="color:#070" data-i18n="statsPageAllowed">Allowed</span></label>
|
||||||
|
<input id="show-cookie" type="checkbox" checked value="1"><label for="show-cookie">Cookies</label>
|
||||||
|
<input id="show-image" type="checkbox" checked value="1"><label for="show-image">Images</label>
|
||||||
|
<input id="show-stylesheet" type="checkbox" checked value="1"><label for="show-stylesheet">CSS</label>
|
||||||
|
<input id="show-object" type="checkbox" checked value="1"><label for="show-object">Objects</label>
|
||||||
|
<input id="show-script" type="checkbox" checked value="1"><label for="show-script">Scripts</label>
|
||||||
|
<input id="show-xmlhttprequest" type="checkbox" checked value="1"><label for="show-xmlhttprequest">XHRs</label>
|
||||||
|
<input id="show-sub_frame" type="checkbox" checked value="1"><label for="show-sub_frame">Frames</label>
|
||||||
|
<input id="show-other" type="checkbox" checked value="1"><label for="show-other">Others</label>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div id="requests-log" style="overflow-y:hidden">
|
||||||
|
<table id="requestsTable">
|
||||||
|
<tr class="ro"><td>when<td>what<td><td class="fa"><td>where</tr>
|
||||||
|
<tr class="ro" id="requestRowTemplate"><td><td><td><a href="" style="display:none"><a></a><td class="fa"><td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div> <!-- end of detailed stats -->
|
||||||
|
|
||||||
|
<script src="lib/jquery-2.min.js"></script>
|
||||||
|
<script src="js/i18n.js"></script>
|
||||||
|
<script src="js/dashboard-common.js"></script>
|
||||||
|
<script src="js/messaging-client.js"></script>
|
||||||
|
<script src="js/info.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,287 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2014 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global chrome, $ */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var updateList = {};
|
||||||
|
var assetListSwitches = ['o', 'o', 'o'];
|
||||||
|
var commitHistoryURLPrefix = 'https://github.com/gorhill/httpswitchboard/commits/master/';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var backupUserDataToFile = function() {
|
||||||
|
var allUserData = {
|
||||||
|
timeStamp: Date.now(),
|
||||||
|
version: '',
|
||||||
|
userSettings: {},
|
||||||
|
scopes: '',
|
||||||
|
remoteBlacklists: {},
|
||||||
|
ubiquitousBlacklist: '',
|
||||||
|
ubiquitousWhitelist: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
var userWhitelistReady = function(details) {
|
||||||
|
allUserData.ubiquitousWhitelist = details.content;
|
||||||
|
chrome.downloads.download({
|
||||||
|
'url': 'data:text/plain,' + encodeURIComponent(JSON.stringify(allUserData)),
|
||||||
|
'filename': 'umatrix-alluserdata-backup.txt',
|
||||||
|
'saveAs': true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var userBlacklistReady = function(details) {
|
||||||
|
allUserData.ubiquitousBlacklist = details.content;
|
||||||
|
messaging.ask({ what: 'readUserUbiquitousAllowRules' }, userWhitelistReady);
|
||||||
|
};
|
||||||
|
|
||||||
|
var ruleDataReady = function(store) {
|
||||||
|
allUserData.version = store.version;
|
||||||
|
allUserData.scopes = store.scopes;
|
||||||
|
allUserData.remoteBlacklists = store.remoteBlacklists;
|
||||||
|
messaging.ask({ what: 'readUserUbiquitousBlockRules' }, userBlacklistReady);
|
||||||
|
};
|
||||||
|
|
||||||
|
var userSettingsReady = function(store) {
|
||||||
|
allUserData.userSettings = store;
|
||||||
|
chrome.storage.local.get(['version', 'scopes', 'remoteBlacklists'], ruleDataReady);
|
||||||
|
};
|
||||||
|
|
||||||
|
messaging.ask({ what: 'readUserSettings' }, userSettingsReady);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var restoreUserDataFromFile = function() {
|
||||||
|
var input = $('<input />').attr({
|
||||||
|
type: 'file',
|
||||||
|
accept: 'text/plain'
|
||||||
|
});
|
||||||
|
|
||||||
|
var restartCountdown = 4;
|
||||||
|
var doCountdown = function() {
|
||||||
|
restartCountdown -= 1;
|
||||||
|
if ( restartCountdown > 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chrome.runtime.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
var restoreBackup = function(data) {
|
||||||
|
chrome.storage.local.set(data.userSettings, doCountdown);
|
||||||
|
var store = {
|
||||||
|
'version': data.version,
|
||||||
|
'scopes': data.scopes
|
||||||
|
};
|
||||||
|
// This case may happen if data was backed up without the user having
|
||||||
|
// changed default selection of lists.
|
||||||
|
if ( data.remoteBlacklists !== undefined ) {
|
||||||
|
store.remoteBlacklists = data.remoteBlacklists;
|
||||||
|
}
|
||||||
|
chrome.storage.local.set(store, doCountdown);
|
||||||
|
messaging.ask({
|
||||||
|
what: 'writeUserUbiquitousBlockRules',
|
||||||
|
content: data.ubiquitousBlacklist
|
||||||
|
},
|
||||||
|
doCountdown
|
||||||
|
);
|
||||||
|
messaging.ask({
|
||||||
|
what: 'writeUserUbiquitousAllowRules',
|
||||||
|
content: data.ubiquitousWhitelist
|
||||||
|
},
|
||||||
|
doCountdown
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
var validateBackup = function(s) {
|
||||||
|
var data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(s);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
data = undefined;
|
||||||
|
}
|
||||||
|
if ( typeof data !== 'object' ||
|
||||||
|
typeof data.timeStamp !== 'number' ||
|
||||||
|
typeof data.version !== 'string' ||
|
||||||
|
typeof data.userSettings !== 'object' ||
|
||||||
|
typeof data.scopes !== 'string' ||
|
||||||
|
typeof data.ubiquitousBlacklist !== 'string' ||
|
||||||
|
typeof data.ubiquitousWhitelist !== 'string' ) {
|
||||||
|
alert('File content is not valid backed up data.');
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
var fileReaderOnLoadHandler = function() {
|
||||||
|
var data = validateBackup(this.result);
|
||||||
|
if ( !data ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var time = new Date(data.timeStamp);
|
||||||
|
var msg = chrome.i18n
|
||||||
|
.getMessage('aboutUserDataRestoreConfirm')
|
||||||
|
.replace('{{time}}', time.toLocaleString());
|
||||||
|
var proceed = window.confirm(msg);
|
||||||
|
if ( proceed ) {
|
||||||
|
restoreBackup(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var filePickerOnChangeHandler = function() {
|
||||||
|
$(this).off('change', filePickerOnChangeHandler);
|
||||||
|
var file = this.files[0];
|
||||||
|
if ( !file ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( file.type.indexOf('text') !== 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var fr = new FileReader();
|
||||||
|
fr.onload = fileReaderOnLoadHandler;
|
||||||
|
fr.readAsText(file);
|
||||||
|
input.off('change', filePickerOnChangeHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
input.on('change', filePickerOnChangeHandler);
|
||||||
|
input.trigger('click');
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var resetUserData = function() {
|
||||||
|
messaging.tell({
|
||||||
|
what: 'gotoExtensionURL',
|
||||||
|
url: 'setup.html'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var setAssetListClassBit = function(bit, state) {
|
||||||
|
assetListSwitches[assetListSwitches.length-1-bit] = !state ? 'o' : 'x';
|
||||||
|
$('#assetList')
|
||||||
|
.removeClass()
|
||||||
|
.addClass(assetListSwitches.join(''));
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var renderAssetList = function(details) {
|
||||||
|
var dirty = false;
|
||||||
|
var paths = Object.keys(details.list).sort();
|
||||||
|
if ( paths.length > 0 ) {
|
||||||
|
$('#assetList .assetEntry').remove();
|
||||||
|
var assetTable = $('#assetList table');
|
||||||
|
var i = 0;
|
||||||
|
var path, status, html;
|
||||||
|
while ( path = paths[i++] ) {
|
||||||
|
status = details.list[path].status;
|
||||||
|
dirty = dirty || status !== 'Unchanged';
|
||||||
|
html = [];
|
||||||
|
html.push('<tr class="assetEntry ' + status.toLowerCase().replace(/ +/g, '-') + '">');
|
||||||
|
html.push('<td>');
|
||||||
|
html.push('<a href="' + commitHistoryURLPrefix + path + '">');
|
||||||
|
html.push(path.replace(/^(assets\/[^/]+\/)(.+)$/, '$1<b>$2</b>'));
|
||||||
|
html.push('</a>');
|
||||||
|
html.push('<td>');
|
||||||
|
html.push(chrome.i18n.getMessage('aboutAssetsUpdateStatus' + status));
|
||||||
|
assetTable.append(html.join(''));
|
||||||
|
}
|
||||||
|
$('#assetList a').attr('target', '_blank');
|
||||||
|
updateList = details.list;
|
||||||
|
}
|
||||||
|
setAssetListClassBit(0, paths.length !== 0);
|
||||||
|
setAssetListClassBit(1, dirty);
|
||||||
|
setAssetListClassBit(2, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var updateAssets = function() {
|
||||||
|
setAssetListClassBit(2, true);
|
||||||
|
var onDone = function(details) {
|
||||||
|
if ( details.changedCount !== 0 ) {
|
||||||
|
messaging.tell({ what: 'loadUpdatableAssets' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
messaging.ask({ what: 'launchAssetUpdater', list: updateList }, onDone);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var updateAssetsList = function() {
|
||||||
|
messaging.ask({ what: 'getAssetUpdaterList' }, renderAssetList);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Updating all assets could be done from elsewhere and if so the
|
||||||
|
// list here needs to be updated.
|
||||||
|
|
||||||
|
var onAnnounce = function(msg) {
|
||||||
|
switch ( msg.what ) {
|
||||||
|
case 'allLocalAssetsUpdated':
|
||||||
|
updateAssetsList();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
messaging.start('about.js');
|
||||||
|
messaging.listen(onAnnounce);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
$('#aboutVersion').html(chrome.runtime.getManifest().version);
|
||||||
|
var renderStats = function(details) {
|
||||||
|
var template = chrome.i18n.getMessage('aboutStorageUsed');
|
||||||
|
var percent = 0;
|
||||||
|
if ( details.storageQuota ) {
|
||||||
|
percent = (details.storageUsed / details.storageQuota * 100).toFixed(1);
|
||||||
|
}
|
||||||
|
$('#aboutStorageUsed').html(template.replace('{{storageUsed}}', percent));
|
||||||
|
};
|
||||||
|
messaging.ask({ what: 'getSomeStats' }, renderStats);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
$('#aboutAssetsUpdateButton').on('click', updateAssets);
|
||||||
|
$('#backupUserDataButton').on('click', backupUserDataToFile);
|
||||||
|
$('#restoreUserDataButton').on('click', restoreUserDataFromFile);
|
||||||
|
$('#resetUserDataButton').on('click', resetUserData);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
updateAssetsList();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,224 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2013 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global chrome, µMatrix */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Asset update manager
|
||||||
|
|
||||||
|
µMatrix.assetUpdater = (function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var getUpdateList = function(callback) {
|
||||||
|
var localChecksumsText = '';
|
||||||
|
var remoteChecksumsText = '';
|
||||||
|
|
||||||
|
var compareChecksums = function() {
|
||||||
|
var parseChecksumsText = function(text) {
|
||||||
|
var result = {};
|
||||||
|
var lines = text.split(/\n+/);
|
||||||
|
var i = lines.length;
|
||||||
|
var fields;
|
||||||
|
while ( i-- ) {
|
||||||
|
fields = lines[i].trim().split(/\s+/);
|
||||||
|
if ( fields.length !== 2 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result[fields[1]] = fields[0];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
if ( remoteChecksumsText === 'Error' || localChecksumsText === 'Error' ) {
|
||||||
|
remoteChecksumsText = localChecksumsText = '';
|
||||||
|
}
|
||||||
|
var localAssetChecksums = parseChecksumsText(localChecksumsText);
|
||||||
|
var remoteAssetChecksums = parseChecksumsText(remoteChecksumsText);
|
||||||
|
|
||||||
|
var toUpdate = {};
|
||||||
|
var path;
|
||||||
|
for ( path in remoteAssetChecksums ) {
|
||||||
|
if ( !remoteAssetChecksums.hasOwnProperty(path) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( localAssetChecksums[path] === undefined ) {
|
||||||
|
toUpdate[path] = {
|
||||||
|
status: 'Added',
|
||||||
|
remoteChecksum: remoteAssetChecksums[path],
|
||||||
|
localChecksum: ''
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( localAssetChecksums[path] === remoteAssetChecksums[path] ) {
|
||||||
|
toUpdate[path] = {
|
||||||
|
status: 'Unchanged',
|
||||||
|
remoteChecksum: remoteAssetChecksums[path],
|
||||||
|
localChecksum: localAssetChecksums[path]
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
toUpdate[path] = {
|
||||||
|
status: 'Changed',
|
||||||
|
remoteChecksum: remoteAssetChecksums[path],
|
||||||
|
localChecksum: localAssetChecksums[path]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for ( path in localAssetChecksums ) {
|
||||||
|
if ( !localAssetChecksums.hasOwnProperty(path) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( remoteAssetChecksums[path] === undefined ) {
|
||||||
|
toUpdate[path] = {
|
||||||
|
status: 'Removed',
|
||||||
|
remoteChecksum: '',
|
||||||
|
localChecksum: localAssetChecksums[path]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback({ 'list': toUpdate });
|
||||||
|
};
|
||||||
|
|
||||||
|
var validateChecksums = function(details) {
|
||||||
|
if ( details.error || details.content === '' ) {
|
||||||
|
return 'Error';
|
||||||
|
}
|
||||||
|
if ( /^(?:[0-9a-f]{32}\s+\S+(\s+|$))+/.test(details.content) ) {
|
||||||
|
return details.content;
|
||||||
|
}
|
||||||
|
return 'Error';
|
||||||
|
};
|
||||||
|
|
||||||
|
var onLocalChecksumsLoaded = function(details) {
|
||||||
|
localChecksumsText = validateChecksums(details);
|
||||||
|
if ( remoteChecksumsText !== '' ) {
|
||||||
|
compareChecksums();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var onRemoteChecksumsLoaded = function(details) {
|
||||||
|
remoteChecksumsText = validateChecksums(details);
|
||||||
|
if ( localChecksumsText !== '' ) {
|
||||||
|
compareChecksums();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
µMatrix.assets.getRemote('assets/checksums.txt', onRemoteChecksumsLoaded);
|
||||||
|
µMatrix.assets.get('assets/checksums.txt', onLocalChecksumsLoaded);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// If `list` is null, it will be fetched internally.
|
||||||
|
|
||||||
|
var update = function(list, callback) {
|
||||||
|
var assetChangedCount = 0;
|
||||||
|
var assetProcessedCount;
|
||||||
|
var updatedAssetChecksums = [];
|
||||||
|
|
||||||
|
var onCompleted = function() {
|
||||||
|
var details = {
|
||||||
|
what: 'allLocalAssetsUpdated',
|
||||||
|
changedCount: assetChangedCount
|
||||||
|
};
|
||||||
|
callback(details);
|
||||||
|
µMatrix.messaging.announce(details);
|
||||||
|
};
|
||||||
|
|
||||||
|
var doCountdown = function() {
|
||||||
|
assetProcessedCount -= 1;
|
||||||
|
if ( assetProcessedCount > 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
µMatrix.assets.put(
|
||||||
|
'assets/checksums.txt',
|
||||||
|
updatedAssetChecksums.join('\n'),
|
||||||
|
onCompleted
|
||||||
|
);
|
||||||
|
chrome.storage.local.set({ 'assetsUpdateTimestamp': Date.now() });
|
||||||
|
};
|
||||||
|
|
||||||
|
var assetUpdated = function(details) {
|
||||||
|
var path = details.path;
|
||||||
|
var entry = list[path];
|
||||||
|
if ( details.error ) {
|
||||||
|
updatedAssetChecksums.push(entry.localChecksum + ' ' + path);
|
||||||
|
} else {
|
||||||
|
updatedAssetChecksums.push(entry.remoteChecksum + ' ' + path);
|
||||||
|
assetChangedCount += 1;
|
||||||
|
}
|
||||||
|
doCountdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
var processList = function() {
|
||||||
|
assetProcessedCount = Object.keys(list).length;
|
||||||
|
if ( assetProcessedCount === 0 ) {
|
||||||
|
onCompleted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var entry;
|
||||||
|
var details = { path: '', md5: '' };
|
||||||
|
for ( var path in list ) {
|
||||||
|
if ( list.hasOwnProperty(path) === false ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry = list[path];
|
||||||
|
if ( entry.status === 'Added' || entry.status === 'Changed' ) {
|
||||||
|
details.path = path;
|
||||||
|
details.md5 = entry.remoteChecksum;
|
||||||
|
µMatrix.assets.update(details, assetUpdated);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( entry.status === 'Unchanged' ) {
|
||||||
|
updatedAssetChecksums.push(entry.localChecksum + ' ' + path);
|
||||||
|
}
|
||||||
|
doCountdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var listLoaded = function(details) {
|
||||||
|
list = details.list;
|
||||||
|
processList();
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( list ) {
|
||||||
|
processList();
|
||||||
|
} else {
|
||||||
|
getUpdateList(listLoaded);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Export API
|
||||||
|
|
||||||
|
return {
|
||||||
|
'getList': getUpdateList,
|
||||||
|
'update': update
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
@ -0,0 +1,354 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2013 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global chrome, µMatrix */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Low-level asset files manager
|
||||||
|
|
||||||
|
µMatrix.assets = (function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var remoteRoot = µMatrix.projectServerRoot;
|
||||||
|
var nullFunc = function() {};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cachedAssetsManager = (function() {
|
||||||
|
var entries = null;
|
||||||
|
var exports = {};
|
||||||
|
var cachedAssetPathPrefix = 'cached_asset_content://';
|
||||||
|
|
||||||
|
var getEntries = function(callback) {
|
||||||
|
if ( entries !== null ) {
|
||||||
|
callback(entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var onLoaded = function(bin) {
|
||||||
|
if ( chrome.runtime.lastError ) {
|
||||||
|
console.error(
|
||||||
|
'assets.js > cachedAssetsManager> getEntries():',
|
||||||
|
chrome.runtime.lastError.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
entries = bin.cached_asset_entries || {};
|
||||||
|
callback(entries);
|
||||||
|
};
|
||||||
|
chrome.storage.local.get('cached_asset_entries', onLoaded);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.load = function(path, cbSuccess, cbError) {
|
||||||
|
cbSuccess = cbSuccess || nullFunc;
|
||||||
|
cbError = cbError || nullFunc;
|
||||||
|
var details = {
|
||||||
|
'path': path,
|
||||||
|
'content': ''
|
||||||
|
};
|
||||||
|
var cachedContentPath = cachedAssetPathPrefix + path;
|
||||||
|
var onLoaded = function(bin) {
|
||||||
|
if ( chrome.runtime.lastError ) {
|
||||||
|
console.error(
|
||||||
|
'assets.js > cachedAssetsManager.load():',
|
||||||
|
chrome.runtime.lastError.message
|
||||||
|
);
|
||||||
|
details.error = 'Error: ' + chrome.runtime.lastError.message;
|
||||||
|
cbError(details);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
details.content = bin[cachedContentPath];
|
||||||
|
cbSuccess(details);
|
||||||
|
};
|
||||||
|
var onEntries = function(entries) {
|
||||||
|
if ( entries[path] === undefined ) {
|
||||||
|
details.error = 'Error: not found'
|
||||||
|
cbError(details);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chrome.storage.local.get(cachedContentPath, onLoaded);
|
||||||
|
};
|
||||||
|
getEntries(onEntries);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.save = function(path, content, cbSuccess, cbError) {
|
||||||
|
cbSuccess = cbSuccess || nullFunc;
|
||||||
|
cbError = cbError || nullFunc;
|
||||||
|
var cachedContentPath = cachedAssetPathPrefix + path;
|
||||||
|
var bin = {};
|
||||||
|
bin[cachedContentPath] = content;
|
||||||
|
var onSaved = function() {
|
||||||
|
if ( chrome.runtime.lastError ) {
|
||||||
|
console.error(
|
||||||
|
'assets.js > cachedAssetsManager.save():',
|
||||||
|
chrome.runtime.lastError.message
|
||||||
|
);
|
||||||
|
cbError(chrome.runtime.lastError.message);
|
||||||
|
} else {
|
||||||
|
cbSuccess();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var onEntries = function(entries) {
|
||||||
|
if ( entries[path] === undefined ) {
|
||||||
|
entries[path] = true;
|
||||||
|
bin.cached_asset_entries = entries;
|
||||||
|
}
|
||||||
|
chrome.storage.local.set(bin, onSaved);
|
||||||
|
};
|
||||||
|
getEntries(onEntries);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.remove = function(pattern) {
|
||||||
|
var onEntries = function(entries) {
|
||||||
|
var mustSave = false;
|
||||||
|
var pathstoRemove = [];
|
||||||
|
var paths = Object.keys(entries);
|
||||||
|
var i = paths.length;
|
||||||
|
var path;
|
||||||
|
while ( i-- ) {
|
||||||
|
if ( typeof pattern === 'string' && path !== pattern ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( pattern instanceof RegExp && !pattern.test(path) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pathstoRemove.push(cachedAssetPathPrefix + path);
|
||||||
|
delete entries[path];
|
||||||
|
mustSave = true;
|
||||||
|
}
|
||||||
|
if ( mustSave ) {
|
||||||
|
chrome.storage.local.remove(pathstoRemove);
|
||||||
|
chrome.storage.local.set({ 'cached_asset_entries': entries });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
getEntries(onEntries);
|
||||||
|
};
|
||||||
|
|
||||||
|
return exports;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var getTextFileFromURL = function(url, onLoad, onError) {
|
||||||
|
// console.log('assets.js > getTextFileFromURL("%s"):', url);
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.responseType = 'text';
|
||||||
|
xhr.onload = onLoad;
|
||||||
|
xhr.onerror = onError;
|
||||||
|
xhr.ontimeout = onError;
|
||||||
|
xhr.open('get', url, true);
|
||||||
|
xhr.send();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Flush cached non-user assets if these are from a prior version.
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/212
|
||||||
|
|
||||||
|
var cacheSynchronized = false;
|
||||||
|
|
||||||
|
var synchronizeCache = function() {
|
||||||
|
if ( cacheSynchronized ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cacheSynchronized = true;
|
||||||
|
|
||||||
|
var onLastVersionRead = function(store) {
|
||||||
|
var currentVersion = chrome.runtime.getManifest().version;
|
||||||
|
var lastVersion = store.extensionLastVersion || '0.0.0.0';
|
||||||
|
if ( currentVersion === lastVersion ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chrome.storage.local.set({ 'extensionLastVersion': currentVersion });
|
||||||
|
cachedAssetsManager.remove(/assets\/(umatrix|thirdparties)\//);
|
||||||
|
};
|
||||||
|
|
||||||
|
chrome.storage.local.get('extensionLastVersion', onLastVersionRead);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var readLocalFile = function(path, callback) {
|
||||||
|
var reportBack = function(content, err) {
|
||||||
|
var details = {
|
||||||
|
'path': path,
|
||||||
|
'content': content,
|
||||||
|
'error': err
|
||||||
|
};
|
||||||
|
callback(details);
|
||||||
|
};
|
||||||
|
|
||||||
|
var onLocalFileLoaded = function() {
|
||||||
|
// console.log('assets.js > onLocalFileLoaded()');
|
||||||
|
reportBack(this.responseText);
|
||||||
|
this.onload = this.onerror = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var onLocalFileError = function(ev) {
|
||||||
|
console.error('assets.js > readLocalFile() / onLocalFileError("%s")', path);
|
||||||
|
reportBack('', 'Error');
|
||||||
|
this.onload = this.onerror = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var onCacheFileLoaded = function(details) {
|
||||||
|
// console.log('assets.js > readLocalFile() / onCacheFileLoaded()');
|
||||||
|
reportBack(details.content);
|
||||||
|
};
|
||||||
|
|
||||||
|
var onCacheFileError = function(details) {
|
||||||
|
// This handler may be called under normal circumstances: it appears
|
||||||
|
// the entry may still be present even after the file was removed.
|
||||||
|
console.error('assets.js > readLocalFile() / onCacheFileError("%s")', details.path);
|
||||||
|
getTextFileFromURL(chrome.runtime.getURL(details.path), onLocalFileLoaded, onLocalFileError);
|
||||||
|
};
|
||||||
|
|
||||||
|
cachedAssetsManager.load(path, onCacheFileLoaded, onCacheFileError);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var readRemoteFile = function(path, callback) {
|
||||||
|
var reportBack = function(content, err) {
|
||||||
|
var details = {
|
||||||
|
'path': path,
|
||||||
|
'content': content,
|
||||||
|
'error': err
|
||||||
|
};
|
||||||
|
callback(details);
|
||||||
|
};
|
||||||
|
|
||||||
|
var onRemoteFileLoaded = function() {
|
||||||
|
// console.log('assets.js > readRemoteFile() / onRemoteFileLoaded()');
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/263
|
||||||
|
if ( this.status === 200 ) {
|
||||||
|
reportBack(this.responseText);
|
||||||
|
} else {
|
||||||
|
reportBack('', 'Error ' + this.statusText);
|
||||||
|
}
|
||||||
|
this.onload = this.onerror = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var onRemoteFileError = function(ev) {
|
||||||
|
console.error('assets.js > readRemoteFile() / onRemoteFileError("%s")', path);
|
||||||
|
reportBack('', 'Error');
|
||||||
|
this.onload = this.onerror = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 'umatrix=...' is to skip browser cache
|
||||||
|
getTextFileFromURL(
|
||||||
|
remoteRoot + path + '?umatrix=' + Date.now(),
|
||||||
|
onRemoteFileLoaded,
|
||||||
|
onRemoteFileError
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var writeLocalFile = function(path, content, callback) {
|
||||||
|
var reportBack = function(err) {
|
||||||
|
var details = {
|
||||||
|
'path': path,
|
||||||
|
'content': content,
|
||||||
|
'error': err
|
||||||
|
};
|
||||||
|
callback(details);
|
||||||
|
};
|
||||||
|
|
||||||
|
var onFileWriteSuccess = function() {
|
||||||
|
console.log('assets.js > writeLocalFile() / onFileWriteSuccess("%s")', path);
|
||||||
|
reportBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
var onFileWriteError = function(err) {
|
||||||
|
console.error('assets.js > writeLocalFile() / onFileWriteError("%s"):', path, err);
|
||||||
|
reportBack(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
cachedAssetsManager.save(path, content, onFileWriteSuccess, onFileWriteError);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var updateFromRemote = function(details, callback) {
|
||||||
|
// 'umatrix=...' is to skip browser cache
|
||||||
|
var remoteURL = remoteRoot + details.path + '?umatrix=' + Date.now();
|
||||||
|
var targetPath = details.path;
|
||||||
|
var targetMd5 = details.md5 || '';
|
||||||
|
|
||||||
|
var reportBackError = function() {
|
||||||
|
callback({
|
||||||
|
'path': targetPath,
|
||||||
|
'error': 'Error'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var onRemoteFileLoaded = function() {
|
||||||
|
this.onload = this.onerror = null;
|
||||||
|
if ( typeof this.responseText !== 'string' ) {
|
||||||
|
console.error('assets.js > updateFromRemote("%s") / onRemoteFileLoaded(): no response', remoteURL);
|
||||||
|
reportBackError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( YaMD5.hashStr(this.responseText) !== targetMd5 ) {
|
||||||
|
console.error('assets.js > updateFromRemote("%s") / onRemoteFileLoaded(): bad md5 checksum', remoteURL);
|
||||||
|
reportBackError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// console.debug('assets.js > updateFromRemote("%s") / onRemoteFileLoaded()', remoteURL);
|
||||||
|
writeLocalFile(targetPath, this.responseText, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
var onRemoteFileError = function(ev) {
|
||||||
|
this.onload = this.onerror = null;
|
||||||
|
console.error('assets.js > updateFromRemote() / onRemoteFileError("%s"):', remoteURL, this.statusText);
|
||||||
|
reportBackError();
|
||||||
|
};
|
||||||
|
|
||||||
|
getTextFileFromURL(
|
||||||
|
remoteURL,
|
||||||
|
onRemoteFileLoaded,
|
||||||
|
onRemoteFileError
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Flush cached assets if cache content is from an older version: the extension
|
||||||
|
// always ships with the most up-to-date assets.
|
||||||
|
|
||||||
|
synchronizeCache();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Export API
|
||||||
|
|
||||||
|
return {
|
||||||
|
'get': readLocalFile,
|
||||||
|
'getRemote': readRemoteFile,
|
||||||
|
'put': writeLocalFile,
|
||||||
|
'update': updateFromRemote
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
@ -0,0 +1,175 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2013 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global chrome, µMatrix */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Async job queue module
|
||||||
|
|
||||||
|
µMatrix.asyncJobs = (function() {
|
||||||
|
|
||||||
|
var processJobs = function() {
|
||||||
|
asyncJobManager.process();
|
||||||
|
};
|
||||||
|
|
||||||
|
var AsyncJobEntry = function(name) {
|
||||||
|
this.name = name;
|
||||||
|
this.data = null;
|
||||||
|
this.callback = null;
|
||||||
|
this.when = 0;
|
||||||
|
this.period = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
AsyncJobEntry.prototype.destroy = function() {
|
||||||
|
this.name = '';
|
||||||
|
this.data = null;
|
||||||
|
this.callback = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var AsyncJobManager = function() {
|
||||||
|
this.timeResolution = 200;
|
||||||
|
this.jobs = {};
|
||||||
|
this.jobCount = 0;
|
||||||
|
this.jobJunkyard = [];
|
||||||
|
this.timerId = null;
|
||||||
|
this.timerWhen = Number.MAX_VALUE;
|
||||||
|
};
|
||||||
|
|
||||||
|
AsyncJobManager.prototype.restartTimer = function() {
|
||||||
|
var when = Number.MAX_VALUE;
|
||||||
|
var jobs = this.jobs, job;
|
||||||
|
for ( var jobName in jobs ) {
|
||||||
|
job = jobs[jobName];
|
||||||
|
if ( job instanceof AsyncJobEntry ) {
|
||||||
|
if ( job.when < when ) {
|
||||||
|
when = job.when;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Quantize time value
|
||||||
|
when = Math.floor((when + this.timeResolution - 1) / this.timeResolution) * this.timeResolution;
|
||||||
|
|
||||||
|
if ( when < this.timerWhen ) {
|
||||||
|
clearTimeout(this.timerId);
|
||||||
|
this.timerWhen = when;
|
||||||
|
this.timerId = setTimeout(processJobs, Math.max(when - Date.now(), 10));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AsyncJobManager.prototype.add = function(name, data, callback, delay, recurrent) {
|
||||||
|
var job = this.jobs[name];
|
||||||
|
if ( !job ) {
|
||||||
|
job = this.jobJunkyard.pop();
|
||||||
|
if ( !job ) {
|
||||||
|
job = new AsyncJobEntry(name);
|
||||||
|
} else {
|
||||||
|
job.name = name;
|
||||||
|
}
|
||||||
|
this.jobs[name] = job;
|
||||||
|
this.jobCount++;
|
||||||
|
}
|
||||||
|
job.data = data;
|
||||||
|
job.callback = callback;
|
||||||
|
job.when = Date.now() + delay;
|
||||||
|
job.period = recurrent ? delay : 0;
|
||||||
|
this.restartTimer();
|
||||||
|
};
|
||||||
|
|
||||||
|
AsyncJobManager.prototype.process = function() {
|
||||||
|
this.timerId = null;
|
||||||
|
this.timerWhen = Number.MAX_VALUE;
|
||||||
|
var now = Date.now();
|
||||||
|
var job;
|
||||||
|
for ( var jobName in this.jobs ) {
|
||||||
|
if ( this.jobs.hasOwnProperty(jobName) === false ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
job = this.jobs[jobName];
|
||||||
|
if ( job.when > now ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
job.callback(job.data);
|
||||||
|
if ( job.period ) {
|
||||||
|
job.when = now + job.period;
|
||||||
|
} else {
|
||||||
|
delete this.jobs[jobName];
|
||||||
|
job.destroy();
|
||||||
|
this.jobCount--;
|
||||||
|
this.jobJunkyard.push(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.restartTimer();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only one instance
|
||||||
|
var asyncJobManager = new AsyncJobManager();
|
||||||
|
|
||||||
|
// Publish
|
||||||
|
return asyncJobManager;
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Update visual of extension icon.
|
||||||
|
// A time out is used to coalesce adjacent requests to update badge.
|
||||||
|
|
||||||
|
µMatrix.updateBadge = function(pageUrl) {
|
||||||
|
var updateBadgeCallback = function(pageUrl) {
|
||||||
|
var µm = µMatrix;
|
||||||
|
if ( pageUrl === µm.behindTheSceneURL ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tabId = µm.tabIdFromPageUrl(pageUrl);
|
||||||
|
if ( !tabId ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pageStats = µm.pageStatsFromTabId(tabId);
|
||||||
|
if ( pageStats ) {
|
||||||
|
pageStats.updateBadge(tabId);
|
||||||
|
} else {
|
||||||
|
chrome.browserAction.setIcon({ tabId: tabId, path: 'img/browsericons/icon19.png' });
|
||||||
|
chrome.browserAction.setBadgeText({ tabId: tabId, text: '?' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.asyncJobs.add('updateBadge ' + pageUrl, pageUrl, updateBadgeCallback, 250);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Notify whoever care that url stats have changed (they need to
|
||||||
|
// rebuild their matrix).
|
||||||
|
|
||||||
|
µMatrix.urlStatsChanged = function(pageUrl) {
|
||||||
|
// rhill 2013-11-17: No point in sending this message if the popup menu
|
||||||
|
// does not exist. I suspect this could be related to
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/58
|
||||||
|
var urlStatsChangedCallback = function(pageUrl) {
|
||||||
|
µMatrix.messaging.tell('popup.js', {
|
||||||
|
what: 'urlStatsChanged',
|
||||||
|
pageURL: pageUrl
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.asyncJobs.add('urlStatsChanged-' + pageUrl, pageUrl, urlStatsChangedCallback, 1000);
|
||||||
|
};
|
@ -0,0 +1,141 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2014 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global chrome */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var µMatrix = (function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var defaultUserAgentStrings = [
|
||||||
|
'# http://www.useragentstring.com/pages/Chrome/',
|
||||||
|
'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36',
|
||||||
|
'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36',
|
||||||
|
'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.67 Safari/537.36',
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36',
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
|
||||||
|
];
|
||||||
|
|
||||||
|
var getDefaultUserAgentStrings = function() {
|
||||||
|
return defaultUserAgentStrings.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
manifest: chrome.runtime.getManifest(),
|
||||||
|
|
||||||
|
userSettings: {
|
||||||
|
autoWhitelistPageDomain: false,
|
||||||
|
clearBrowserCache: true,
|
||||||
|
clearBrowserCacheAfter: 60,
|
||||||
|
colorBlindFriendly: false,
|
||||||
|
deleteCookies: false,
|
||||||
|
deleteUnusedSessionCookies: false,
|
||||||
|
deleteUnusedSessionCookiesAfter: 60,
|
||||||
|
deleteLocalStorage: false,
|
||||||
|
displayTextSize: '13px',
|
||||||
|
maxLoggedRequests: 50,
|
||||||
|
popupHideBlacklisted: false,
|
||||||
|
popupCollapseDomains: false,
|
||||||
|
popupCollapseSpecificDomains: {},
|
||||||
|
processBehindTheSceneRequests: false,
|
||||||
|
processHyperlinkAuditing: true,
|
||||||
|
processReferer: false,
|
||||||
|
smartAutoReload: 'all',
|
||||||
|
spoofUserAgent: false,
|
||||||
|
spoofUserAgentEvery: 5,
|
||||||
|
spoofUserAgentWith: getDefaultUserAgentStrings(),
|
||||||
|
statsFilters: {},
|
||||||
|
strictBlocking: true,
|
||||||
|
subframeColor: '#cc0000',
|
||||||
|
subframeOpacity: 100
|
||||||
|
},
|
||||||
|
|
||||||
|
clearBrowserCacheCycle: 0,
|
||||||
|
updateAssetsEvery: 5 * 24 * 60 * 60 * 1000,
|
||||||
|
projectServerRoot: 'https://raw.githubusercontent.com/gorhill/umatrix/master/',
|
||||||
|
|
||||||
|
// list of remote blacklist locations
|
||||||
|
remoteBlacklists: {
|
||||||
|
// uMatrix
|
||||||
|
'assets/umatrix/blacklist.txt': { title: 'uMatrix' },
|
||||||
|
|
||||||
|
// 3rd-party lists now fetched dynamically
|
||||||
|
},
|
||||||
|
|
||||||
|
// urls stats are kept on the back burner while waiting to be reactivated
|
||||||
|
// in a tab or another.
|
||||||
|
pageStats: {},
|
||||||
|
|
||||||
|
// A map of redirects, to allow reverse lookup of redirects from landing
|
||||||
|
// page, so that redirection can be reported to the user.
|
||||||
|
redirectRequests: {},
|
||||||
|
|
||||||
|
// tabs are used to redirect stats collection to a specific url stats
|
||||||
|
// structure.
|
||||||
|
pageUrlToTabId: {},
|
||||||
|
tabIdToPageUrl: {},
|
||||||
|
|
||||||
|
// page url => permission scope
|
||||||
|
tMatrix: null,
|
||||||
|
pMatrix: null,
|
||||||
|
|
||||||
|
// Current entries from ubiquitous lists --
|
||||||
|
// just hostnames, '*/' is implied, this saves significantly on memory.
|
||||||
|
ubiquitousBlacklist: null,
|
||||||
|
ubiquitousWhitelist: null,
|
||||||
|
|
||||||
|
// various stats
|
||||||
|
requestStats: new WebRequestStats(),
|
||||||
|
cookieRemovedCounter: 0,
|
||||||
|
localStorageRemovedCounter: 0,
|
||||||
|
cookieHeaderFoiledCounter: 0,
|
||||||
|
refererHeaderFoiledCounter: 0,
|
||||||
|
hyperlinkAuditingFoiledCounter: 0,
|
||||||
|
browserCacheClearedCounter: 0,
|
||||||
|
storageQuota: chrome.storage.local.QUOTA_BYTES,
|
||||||
|
storageUsed: 0,
|
||||||
|
userAgentReplaceStr: '',
|
||||||
|
userAgentReplaceStrBirth: 0,
|
||||||
|
|
||||||
|
// record what chromium is doing behind the scene
|
||||||
|
behindTheSceneURL: 'http://chromium-behind-the-scene/',
|
||||||
|
behindTheSceneTabId: 0x7FFFFFFF,
|
||||||
|
behindTheSceneMaxReq: 250,
|
||||||
|
behindTheSceneScope: 'chromium-behind-the-scene',
|
||||||
|
|
||||||
|
// Commonly encountered strings
|
||||||
|
chromeExtensionURLPrefix: 'chrome-extension://',
|
||||||
|
noopCSSURL: chrome.runtime.getURL('css/noop.css'),
|
||||||
|
fontCSSURL: chrome.runtime.getURL('css/fonts/Roboto_Condensed/RobotoCondensed-Regular.ttf'),
|
||||||
|
|
||||||
|
// so that I don't have to care for last comma
|
||||||
|
dummy: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
@ -0,0 +1,85 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2014 Contributors to HTTP Switchboard
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var whitelistPageDomain = function(tabs) {
|
||||||
|
if ( tabs.length === 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tab = tabs[0];
|
||||||
|
if ( !tab.url ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var µm = µMatrix;
|
||||||
|
if ( µm.autoWhitelist1stPartyTemporarily(tab.url) ) {
|
||||||
|
µm.smartReloadTab(tab.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var whitelistAll = function(tabs) {
|
||||||
|
if ( tabs.length === 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tab = tabs[0];
|
||||||
|
if ( !tab.url ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var µm = µMatrix;
|
||||||
|
if ( µm.autoWhitelistAllTemporarily(tab.url) ) {
|
||||||
|
µm.smartReloadTab(tab.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var onCommand = function(command) {
|
||||||
|
switch ( command ) {
|
||||||
|
case 'revert-all':
|
||||||
|
µMatrix.revertAllRules();
|
||||||
|
break;
|
||||||
|
case 'whitelist-page-domain':
|
||||||
|
chrome.tabs.query({ active: true }, whitelistPageDomain);
|
||||||
|
break;
|
||||||
|
case 'whitelist-all':
|
||||||
|
chrome.tabs.query({ active: true }, whitelistAll);
|
||||||
|
break;
|
||||||
|
case 'open-dashboard':
|
||||||
|
µMatrix.utils.gotoExtensionURL('dashboard.html');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
chrome.commands.onCommand.addListener(onCommand);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
@ -0,0 +1,358 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2014 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint multistr: true */
|
||||||
|
/* global chrome */
|
||||||
|
|
||||||
|
// Injected into content pages
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/345
|
||||||
|
|
||||||
|
var messaging = (function(name){
|
||||||
|
var port = null;
|
||||||
|
var requestId = 1;
|
||||||
|
var requestIdToCallbackMap = {};
|
||||||
|
var listenCallback = null;
|
||||||
|
|
||||||
|
var onPortMessage = function(details) {
|
||||||
|
if ( typeof details.id !== 'number' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Announcement?
|
||||||
|
if ( details.id < 0 ) {
|
||||||
|
if ( listenCallback ) {
|
||||||
|
listenCallback(details.msg);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var callback = requestIdToCallbackMap[details.id];
|
||||||
|
if ( !callback ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Must be removed before calling client to be sure to not execute
|
||||||
|
// callback again if the client stops the messaging service.
|
||||||
|
delete requestIdToCallbackMap[details.id];
|
||||||
|
callback(details.msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
var start = function(name) {
|
||||||
|
port = chrome.runtime.connect({ name: name });
|
||||||
|
port.onMessage.addListener(onPortMessage);
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/193
|
||||||
|
port.onDisconnect.addListener(stop);
|
||||||
|
};
|
||||||
|
|
||||||
|
var stop = function() {
|
||||||
|
listenCallback = null;
|
||||||
|
port.disconnect();
|
||||||
|
port = null;
|
||||||
|
flushCallbacks();
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( typeof name === 'string' && name !== '' ) {
|
||||||
|
start(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ask = function(msg, callback) {
|
||||||
|
if ( port === null ) {
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( callback === undefined ) {
|
||||||
|
tell(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var id = requestId++;
|
||||||
|
port.postMessage({ id: id, msg: msg });
|
||||||
|
requestIdToCallbackMap[id] = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
var tell = function(msg) {
|
||||||
|
if ( port !== null ) {
|
||||||
|
port.postMessage({ id: 0, msg: msg });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var listen = function(callback) {
|
||||||
|
listenCallback = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
var flushCallbacks = function() {
|
||||||
|
var callback;
|
||||||
|
for ( var id in requestIdToCallbackMap ) {
|
||||||
|
if ( requestIdToCallbackMap.hasOwnProperty(id) === false ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
callback = requestIdToCallbackMap[id];
|
||||||
|
if ( !callback ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Must be removed before calling client to be sure to not execute
|
||||||
|
// callback again if the client stops the messaging service.
|
||||||
|
delete requestIdToCallbackMap[id];
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: start,
|
||||||
|
stop: stop,
|
||||||
|
ask: ask,
|
||||||
|
tell: tell,
|
||||||
|
listen: listen
|
||||||
|
};
|
||||||
|
})('contentscript-end.js');
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// This is to be executed only once: putting this code in its own closure
|
||||||
|
// means the code will be flushed from memory once executed.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/*------------[ Unrendered Noscript (because CSP) Workaround ]----------------*/
|
||||||
|
|
||||||
|
var checkScriptBlacklistedHandler = function(response) {
|
||||||
|
if ( !response.scriptBlacklisted ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var scripts = document.querySelectorAll('noscript');
|
||||||
|
var i = scripts.length;
|
||||||
|
var realNoscript, fakeNoscript;
|
||||||
|
while ( i-- ) {
|
||||||
|
realNoscript = scripts[i];
|
||||||
|
fakeNoscript = document.createElement('div');
|
||||||
|
fakeNoscript.innerHTML = '<!-- uMatrix NOSCRIPT tag replacement: see <https://github.com/gorhill/httpswitchboard/issues/177> -->\n' + realNoscript.textContent;
|
||||||
|
realNoscript.parentNode.replaceChild(fakeNoscript, realNoscript);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
messaging.ask({
|
||||||
|
what: 'checkScriptBlacklisted',
|
||||||
|
url: window.location.href
|
||||||
|
},
|
||||||
|
checkScriptBlacklistedHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var localStorageHandler = function(mustRemove) {
|
||||||
|
if ( mustRemove ) {
|
||||||
|
window.localStorage.clear();
|
||||||
|
// console.debug('HTTP Switchboard > found and removed non-empty localStorage');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check with extension whether local storage must be emptied
|
||||||
|
// rhill 2014-03-28: we need an exception handler in case 3rd-party access
|
||||||
|
// to site data is disabled.
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/215
|
||||||
|
try {
|
||||||
|
if ( window.localStorage && window.localStorage.length ) {
|
||||||
|
messaging.ask({
|
||||||
|
what: 'contentScriptHasLocalStorage',
|
||||||
|
url: window.location.href
|
||||||
|
},
|
||||||
|
localStorageHandler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: indexedDB
|
||||||
|
if ( window.indexedDB && !!window.indexedDB.webkitGetDatabaseNames ) {
|
||||||
|
// var db = window.indexedDB.webkitGetDatabaseNames().onsuccess = function(sender) {
|
||||||
|
// console.debug('webkitGetDatabaseNames(): result=%o', sender.target.result);
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Web SQL
|
||||||
|
if ( window.openDatabase ) {
|
||||||
|
// Sad:
|
||||||
|
// "There is no way to enumerate or delete the databases available for an origin from this API."
|
||||||
|
// Ref.: http://www.w3.org/TR/webdatabase/#databases
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var nodesAddedHandler = function(nodeList, summary) {
|
||||||
|
var i = 0;
|
||||||
|
var node, src, text;
|
||||||
|
while ( node = nodeList.item(i++) ) {
|
||||||
|
if ( !node.tagName ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( node.tagName.toUpperCase() ) {
|
||||||
|
|
||||||
|
case 'SCRIPT':
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/252
|
||||||
|
// Do not count µMatrix's own script tags, they are not required
|
||||||
|
// to "unbreak" a web page
|
||||||
|
if ( node.id && node.id.indexOf('uMatrix-') === 0 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
text = node.textContent.trim();
|
||||||
|
if ( text !== '' ) {
|
||||||
|
summary.scriptSources['{inline_script}'] = true;
|
||||||
|
summary.mustReport = true;
|
||||||
|
}
|
||||||
|
src = (node.src || '').trim();
|
||||||
|
if ( src !== '' ) {
|
||||||
|
summary.scriptSources[src] = true;
|
||||||
|
summary.mustReport = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'A':
|
||||||
|
if ( node.href.indexOf('javascript:') === 0 ) {
|
||||||
|
summary.scriptSources['{inline_script}'] = true;
|
||||||
|
summary.mustReport = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'OBJECT':
|
||||||
|
src = (node.data || '').trim();
|
||||||
|
if ( src !== '' ) {
|
||||||
|
summary.pluginSources[src] = true;
|
||||||
|
summary.mustReport = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'EMBED':
|
||||||
|
src = (node.src || '').trim();
|
||||||
|
if ( src !== '' ) {
|
||||||
|
summary.pluginSources[src] = true;
|
||||||
|
summary.mustReport = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var nodeListsAddedHandler = function(nodeLists) {
|
||||||
|
var summary = {
|
||||||
|
what: 'contentScriptSummary',
|
||||||
|
locationURL: window.location.href,
|
||||||
|
scriptSources: {}, // to avoid duplicates
|
||||||
|
pluginSources: {}, // to avoid duplicates
|
||||||
|
mustReport: false
|
||||||
|
};
|
||||||
|
var i = nodeLists.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
nodesAddedHandler(nodeLists[i], summary);
|
||||||
|
}
|
||||||
|
if ( summary.mustReport ) {
|
||||||
|
messaging.tell(summary);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// rhill 2013-11-09: Weird... This code is executed from HTTP Switchboard
|
||||||
|
// context first time extension is launched. Avoid this.
|
||||||
|
// TODO: Investigate if this was a fluke or if it can really happen.
|
||||||
|
// I suspect this could only happen when I was using chrome.tabs.executeScript(),
|
||||||
|
// because now a delarative content script is used, along with "http{s}" URL
|
||||||
|
// pattern matching.
|
||||||
|
|
||||||
|
// console.debug('contentscript-end.js > window.location.href = "%s"', window.location.href);
|
||||||
|
|
||||||
|
if ( /^https?:\/\/./.test(window.location.href) === false ) {
|
||||||
|
console.debug("Huh?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var summary = {
|
||||||
|
what: 'contentScriptSummary',
|
||||||
|
locationURL: window.location.href,
|
||||||
|
scriptSources: {}, // to avoid duplicates
|
||||||
|
pluginSources: {}, // to avoid duplicates
|
||||||
|
mustReport: true
|
||||||
|
};
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/25
|
||||||
|
// &
|
||||||
|
// Looks for inline javascript also in at least one a[href] element.
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/131
|
||||||
|
nodesAddedHandler(document.querySelectorAll('script, a[href^="javascript:"], object, embed'), summary);
|
||||||
|
|
||||||
|
//console.debug('contentscript-end.js > firstObservationHandler(): found %d script tags in "%s"', Object.keys(summary.scriptSources).length, window.location.href);
|
||||||
|
|
||||||
|
messaging.tell(summary);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Observe changes in the DOM
|
||||||
|
|
||||||
|
var mutationObservedHandler = function(mutations) {
|
||||||
|
var i = mutations.length;
|
||||||
|
var nodeLists = [], nodeList;
|
||||||
|
while ( i-- ) {
|
||||||
|
nodeList = mutations[i].addedNodes;
|
||||||
|
if ( nodeList && nodeList.length ) {
|
||||||
|
nodeLists.push(nodeList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( nodeLists.length ) {
|
||||||
|
nodeListsAddedHandler(nodeLists);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This fixes http://acid3.acidtests.org/
|
||||||
|
if ( document.body ) {
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/176
|
||||||
|
var observer = new MutationObserver(mutationObservedHandler);
|
||||||
|
observer.observe(document.body, {
|
||||||
|
attributes: false,
|
||||||
|
childList: true,
|
||||||
|
characterData: false,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
@ -0,0 +1,223 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2014 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint multistr: true */
|
||||||
|
/* global chrome */
|
||||||
|
|
||||||
|
// Injected into content pages
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// OK, I keep changing my mind whether a closure should be used or not. This
|
||||||
|
// will be the rule: if there are any variables directly accessed on a regular
|
||||||
|
// basis, use a closure so that they are cached. Otherwise I don't think the
|
||||||
|
// overhead of a closure is worth it. That's my understanding.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/345
|
||||||
|
|
||||||
|
var messaging = (function(name){
|
||||||
|
var port = null;
|
||||||
|
var requestId = 1;
|
||||||
|
var requestIdToCallbackMap = {};
|
||||||
|
var listenCallback = null;
|
||||||
|
|
||||||
|
var onPortMessage = function(details) {
|
||||||
|
if ( typeof details.id !== 'number' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Announcement?
|
||||||
|
if ( details.id < 0 ) {
|
||||||
|
if ( listenCallback ) {
|
||||||
|
listenCallback(details.msg);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var callback = requestIdToCallbackMap[details.id];
|
||||||
|
if ( !callback ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Must be removed before calling client to be sure to not execute
|
||||||
|
// callback again if the client stops the messaging service.
|
||||||
|
delete requestIdToCallbackMap[details.id];
|
||||||
|
callback(details.msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
var start = function(name) {
|
||||||
|
port = chrome.runtime.connect({ name: name });
|
||||||
|
port.onMessage.addListener(onPortMessage);
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/193
|
||||||
|
port.onDisconnect.addListener(stop);
|
||||||
|
};
|
||||||
|
|
||||||
|
var stop = function() {
|
||||||
|
listenCallback = null;
|
||||||
|
port.disconnect();
|
||||||
|
port = null;
|
||||||
|
flushCallbacks();
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( typeof name === 'string' && name !== '' ) {
|
||||||
|
start(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ask = function(msg, callback) {
|
||||||
|
if ( port === null ) {
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( callback === undefined ) {
|
||||||
|
tell(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var id = requestId++;
|
||||||
|
port.postMessage({ id: id, msg: msg });
|
||||||
|
requestIdToCallbackMap[id] = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
var tell = function(msg) {
|
||||||
|
if ( port !== null ) {
|
||||||
|
port.postMessage({ id: 0, msg: msg });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var listen = function(callback) {
|
||||||
|
listenCallback = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
var flushCallbacks = function() {
|
||||||
|
var callback;
|
||||||
|
for ( var id in requestIdToCallbackMap ) {
|
||||||
|
if ( requestIdToCallbackMap.hasOwnProperty(id) === false ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
callback = requestIdToCallbackMap[id];
|
||||||
|
if ( !callback ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Must be removed before calling client to be sure to not execute
|
||||||
|
// callback again if the client stops the messaging service.
|
||||||
|
delete requestIdToCallbackMap[id];
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
start: start,
|
||||||
|
stop: stop,
|
||||||
|
ask: ask,
|
||||||
|
tell: tell,
|
||||||
|
listen: listen
|
||||||
|
};
|
||||||
|
})('contentscript-start.js');
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// If you play with this code, mind:
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/261
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/252
|
||||||
|
|
||||||
|
var navigatorSpoofer = " \
|
||||||
|
;(function() { \
|
||||||
|
try { \
|
||||||
|
var spoofedUserAgent = {{ua-json}}; \
|
||||||
|
if ( spoofedUserAgent === navigator.userAgent ) { \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
var realNavigator = navigator; \
|
||||||
|
var SpoofedNavigator = function(ua) { \
|
||||||
|
this.navigator = navigator; \
|
||||||
|
}; \
|
||||||
|
var spoofedNavigator = new SpoofedNavigator(spoofedUserAgent); \
|
||||||
|
var makeFunction = function(n, k) { \
|
||||||
|
n[k] = function() { \
|
||||||
|
return this.navigator[k].apply(this.navigator, arguments); }; \
|
||||||
|
}; \
|
||||||
|
for ( var k in realNavigator ) { \
|
||||||
|
if ( typeof realNavigator[k] === 'function' ) { \
|
||||||
|
makeFunction(spoofedNavigator, k); \
|
||||||
|
} else { \
|
||||||
|
spoofedNavigator[k] = realNavigator[k]; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
spoofedNavigator.userAgent = spoofedUserAgent; \
|
||||||
|
var pos = spoofedUserAgent.indexOf('/'); \
|
||||||
|
spoofedNavigator.appName = pos < 0 ? '' : spoofedUserAgent.slice(0, pos); \
|
||||||
|
spoofedNavigator.appVersion = pos < 0 ? spoofedUserAgent : spoofedUserAgent.slice(pos + 1); \
|
||||||
|
navigator = window.navigator = spoofedNavigator; \
|
||||||
|
} catch (e) { \
|
||||||
|
} \
|
||||||
|
})();";
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Because window.userAgent is read-only, we need to create a fake Navigator
|
||||||
|
// object to contain our fake user-agent string.
|
||||||
|
// Because objects created by a content script are local to the content script
|
||||||
|
// and not visible to the web page itself (and vice versa), we need the context
|
||||||
|
// of the web page to create the fake Navigator object directly, and the only
|
||||||
|
// way to do this is to inject appropriate javascript code into the web page.
|
||||||
|
|
||||||
|
var injectNavigatorSpoofer = function(spoofedUserAgent) {
|
||||||
|
if ( typeof spoofedUserAgent !== 'string' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( spoofedUserAgent === navigator.userAgent ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var script = document.createElement('script');
|
||||||
|
script.type = 'text/javascript';
|
||||||
|
script.id = 'umatrix-ua-spoofer';
|
||||||
|
var js = document.createTextNode(navigatorSpoofer.replace('{{ua-json}}', JSON.stringify(spoofedUserAgent)));
|
||||||
|
script.appendChild(js);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var parent = document.head || document.documentElement;
|
||||||
|
parent.appendChild(script);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
messaging.ask({ what: 'getUserAgentReplaceStr' }, injectNavigatorSpoofer);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// The port will never be used again at this point, disconnecting allows
|
||||||
|
// to browser to flush this script from memory.
|
||||||
|
|
||||||
|
messaging.stop();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
@ -0,0 +1,559 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2013 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
// rhill 2013-12-14: the whole cookie management has been rewritten so as
|
||||||
|
// to avoid having to call chrome API whenever a single cookie changes, and
|
||||||
|
// to record cookie for a web page *only* when its value changes.
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/79
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Isolate from global namespace
|
||||||
|
|
||||||
|
// Use cached-context approach rather than object-based approach, as details
|
||||||
|
// of the implementation do not need to be visible
|
||||||
|
|
||||||
|
µMatrix.cookieHunter = (function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var recordPageCookiesQueue = {};
|
||||||
|
var removePageCookiesQueue = {};
|
||||||
|
var removeCookieQueue = {};
|
||||||
|
var cookieDict = {};
|
||||||
|
var cookieEntryJunkyard = [];
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var CookieEntry = function(cookie) {
|
||||||
|
this.set(cookie);
|
||||||
|
};
|
||||||
|
|
||||||
|
CookieEntry.prototype.set = function(cookie) {
|
||||||
|
this.secure = cookie.secure;
|
||||||
|
this.session = cookie.session;
|
||||||
|
this.anySubdomain = cookie.domain.charAt(0) === '.';
|
||||||
|
this.domain = this.anySubdomain ? cookie.domain.slice(1) : cookie.domain;
|
||||||
|
this.path = cookie.path;
|
||||||
|
this.name = cookie.name;
|
||||||
|
this.value = cookie.value;
|
||||||
|
this.tstamp = Date.now();
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Release anything which may consume too much memory
|
||||||
|
|
||||||
|
CookieEntry.prototype.unset = function() {
|
||||||
|
this.domain = '';
|
||||||
|
this.path = '';
|
||||||
|
this.name = '';
|
||||||
|
this.value = '';
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var addCookieToDict = function(cookie) {
|
||||||
|
var cookieKey = cookieKeyFromCookie(cookie);
|
||||||
|
if ( cookieDict.hasOwnProperty(cookieKey) === false ) {
|
||||||
|
var cookieEntry = cookieEntryJunkyard.pop();
|
||||||
|
if ( cookieEntry ) {
|
||||||
|
cookieEntry.set(cookie);
|
||||||
|
} else {
|
||||||
|
cookieEntry = new CookieEntry(cookie);
|
||||||
|
}
|
||||||
|
cookieDict[cookieKey] = cookieEntry;
|
||||||
|
}
|
||||||
|
return cookieDict[cookieKey];
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var addCookiesToDict = function(cookies) {
|
||||||
|
var i = cookies.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
addCookieToDict(cookies[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var removeCookieFromDict = function(cookieKey) {
|
||||||
|
if ( cookieDict.hasOwnProperty(cookieKey) === false ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var cookieEntry = cookieDict[cookieKey];
|
||||||
|
delete cookieDict[cookieKey];
|
||||||
|
if ( cookieEntryJunkyard.length < 25 ) {
|
||||||
|
cookieEntryJunkyard.push(cookieEntry.unset());
|
||||||
|
}
|
||||||
|
// console.log('cookies.js/removeCookieFromDict()> removed cookie key "%s"', cookieKey);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cookieKeyBuilder = [
|
||||||
|
'', // 0 = scheme
|
||||||
|
'://',
|
||||||
|
'', // 2 = domain
|
||||||
|
'', // 3 = path
|
||||||
|
'{',
|
||||||
|
'', // 5 = persistent or session
|
||||||
|
'-cookie:',
|
||||||
|
'', // 7 = name
|
||||||
|
'}'
|
||||||
|
];
|
||||||
|
|
||||||
|
var cookieKeyFromCookie = function(cookie) {
|
||||||
|
var cb = cookieKeyBuilder;
|
||||||
|
cb[0] = cookie.secure ? 'https' : 'http';
|
||||||
|
cb[2] = cookie.domain.charAt(0) === '.' ? cookie.domain.slice(1) : cookie.domain;
|
||||||
|
cb[3] = cookie.path;
|
||||||
|
cb[5] = cookie.session ? 'session' : 'persistent';
|
||||||
|
cb[7] = cookie.name;
|
||||||
|
return cb.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
var cookieKeyFromCookieURL = function(url, type, name) {
|
||||||
|
var µmuri = µMatrix.URI.set(url);
|
||||||
|
var cb = cookieKeyBuilder;
|
||||||
|
cb[0] = µmuri.scheme;
|
||||||
|
cb[2] = µmuri.hostname;
|
||||||
|
cb[3] = µmuri.path;
|
||||||
|
cb[5] = type;
|
||||||
|
cb[7] = name;
|
||||||
|
return cb.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cookieEntryFromCookie = function(cookie) {
|
||||||
|
return cookieDict[cookieKeyFromCookie(cookie)];
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cookieURLFromCookieEntry = function(entry) {
|
||||||
|
if ( !entry ) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return (entry.secure ? 'https://' : 'http://') + entry.domain + entry.path;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cookieMatchDomains = function(cookieKey, domains) {
|
||||||
|
var cookieEntry = cookieDict[cookieKey];
|
||||||
|
if ( !cookieEntry ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( domains.indexOf(' ' + cookieEntry.domain + ' ') < 0 ) {
|
||||||
|
if ( !cookieEntry.anySubdomain ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( domains.indexOf('.' + cookieEntry.domain + ' ') < 0 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Look for cookies to record for a specific web page
|
||||||
|
|
||||||
|
var recordPageCookiesAsync = function(pageStats) {
|
||||||
|
// Store the page stats objects so that it doesn't go away
|
||||||
|
// before we handle the job.
|
||||||
|
// rhill 2013-10-19: pageStats could be nil, for example, this can
|
||||||
|
// happens if a file:// ... makes an xmlHttpRequest
|
||||||
|
if ( !pageStats ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pageURL = µMatrix.pageUrlFromPageStats(pageStats);
|
||||||
|
recordPageCookiesQueue[pageURL] = pageStats;
|
||||||
|
µMatrix.asyncJobs.add(
|
||||||
|
'cookieHunterPageRecord',
|
||||||
|
null,
|
||||||
|
processPageRecordQueue,
|
||||||
|
1000,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var cookieLogEntryBuilder = [
|
||||||
|
'',
|
||||||
|
'{',
|
||||||
|
'',
|
||||||
|
'_cookie:',
|
||||||
|
'',
|
||||||
|
'}'
|
||||||
|
];
|
||||||
|
|
||||||
|
var recordPageCookie = function(pageStats, cookieKey) {
|
||||||
|
var µm = µMatrix;
|
||||||
|
var cookieEntry = cookieDict[cookieKey];
|
||||||
|
var pageURL = pageStats.pageUrl;
|
||||||
|
var block = µm.mustBlock(µm.scopeFromURL(pageURL), cookieEntry.domain, 'cookie');
|
||||||
|
|
||||||
|
cookieLogEntryBuilder[0] = cookieURLFromCookieEntry(cookieEntry);
|
||||||
|
cookieLogEntryBuilder[2] = cookieEntry.session ? 'session' : 'persistent';
|
||||||
|
cookieLogEntryBuilder[4] = encodeURIComponent(cookieEntry.name);
|
||||||
|
|
||||||
|
// rhill 2013-11-20:
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/60
|
||||||
|
// Need to URL-encode cookie name
|
||||||
|
pageStats.recordRequest(
|
||||||
|
'cookie',
|
||||||
|
cookieLogEntryBuilder.join(''),
|
||||||
|
block
|
||||||
|
);
|
||||||
|
|
||||||
|
// rhill 2013-11-21:
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/65
|
||||||
|
// Leave alone cookies from behind-the-scene requests if
|
||||||
|
// behind-the-scene processing is disabled.
|
||||||
|
if ( !block ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( !µm.userSettings.deleteCookies ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeCookieAsync(cookieKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Look for cookies to potentially remove for a specific web page
|
||||||
|
|
||||||
|
var removePageCookiesAsync = function(pageStats) {
|
||||||
|
// Hold onto pageStats objects so that it doesn't go away
|
||||||
|
// before we handle the job.
|
||||||
|
// rhill 2013-10-19: pageStats could be nil, for example, this can
|
||||||
|
// happens if a file:// ... makes an xmlHttpRequest
|
||||||
|
if ( !pageStats ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pageURL = µMatrix.pageUrlFromPageStats(pageStats);
|
||||||
|
removePageCookiesQueue[pageURL] = pageStats;
|
||||||
|
µMatrix.asyncJobs.add(
|
||||||
|
'cookieHunterPageRemove',
|
||||||
|
null,
|
||||||
|
processPageRemoveQueue,
|
||||||
|
15 * 1000,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Candidate for removal
|
||||||
|
|
||||||
|
var removeCookieAsync = function(cookieKey) {
|
||||||
|
// console.log('cookies.js/removeCookieAsync()> cookie key = "%s"', cookieKey);
|
||||||
|
removeCookieQueue[cookieKey] = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var chromeCookieRemove = function(url, name) {
|
||||||
|
var callback = function(details) {
|
||||||
|
if ( !details ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var cookieKey = cookieKeyFromCookieURL(details.url, 'session', details.name);
|
||||||
|
if ( removeCookieFromDict(cookieKey) ) {
|
||||||
|
µMatrix.cookieRemovedCounter += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cookieKey = cookieKeyFromCookieURL(details.url, 'persistent', details.name);
|
||||||
|
if ( removeCookieFromDict(cookieKey) ) {
|
||||||
|
µMatrix.cookieRemovedCounter += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
chrome.cookies.remove({ url: url, name: name }, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var processPageRecordQueue = function() {
|
||||||
|
for ( var pageURL in recordPageCookiesQueue ) {
|
||||||
|
if ( !recordPageCookiesQueue.hasOwnProperty(pageURL) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
findAndRecordPageCookies(recordPageCookiesQueue[pageURL]);
|
||||||
|
delete recordPageCookiesQueue[pageURL];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var processPageRemoveQueue = function() {
|
||||||
|
for ( var pageURL in removePageCookiesQueue ) {
|
||||||
|
if ( !removePageCookiesQueue.hasOwnProperty(pageURL) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
findAndRemovePageCookies(removePageCookiesQueue[pageURL]);
|
||||||
|
delete removePageCookiesQueue[pageURL];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Effectively remove cookies.
|
||||||
|
|
||||||
|
var processRemoveQueue = function() {
|
||||||
|
var userSettings = µMatrix.userSettings;
|
||||||
|
var deleteCookies = userSettings.deleteCookies;
|
||||||
|
|
||||||
|
// Session cookies which timestamp is *after* tstampObsolete will
|
||||||
|
// be left untouched
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/257
|
||||||
|
var tstampObsolete = userSettings.deleteUnusedSessionCookies ?
|
||||||
|
Date.now() - userSettings.deleteUnusedSessionCookiesAfter * 60 * 1000 :
|
||||||
|
0;
|
||||||
|
|
||||||
|
var cookieEntry;
|
||||||
|
for ( var cookieKey in removeCookieQueue ) {
|
||||||
|
if ( removeCookieQueue.hasOwnProperty(cookieKey) === false ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
delete removeCookieQueue[cookieKey];
|
||||||
|
|
||||||
|
cookieEntry = cookieDict[cookieKey];
|
||||||
|
|
||||||
|
// rhill 2014-05-12: Apparently this can happen. I have to
|
||||||
|
// investigate how (A session cookie has same name as a
|
||||||
|
// persistent cookie?)
|
||||||
|
if ( !cookieEntry ) {
|
||||||
|
console.error('HTTP Switchboard> cookies.js/processRemoveQueue(): no cookieEntry for "%s"', cookieKey);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just in case setting was changed after cookie was put in queue.
|
||||||
|
if ( cookieEntry.session === false && deleteCookies === false ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure cookie is not allowed on ALL current web pages: It can
|
||||||
|
// happen that a cookie is blacklisted on one web page while
|
||||||
|
// being whitelisted on another (because of per-page permissions).
|
||||||
|
if ( canRemoveCookie(cookieKey) === false ) {
|
||||||
|
// Exception: session cookie may have to be removed even though
|
||||||
|
// they are seen as being whitelisted.
|
||||||
|
if ( cookieEntry.session === false || cookieEntry.tstamp > tstampObsolete ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = cookieURLFromCookieEntry(cookieEntry);
|
||||||
|
if ( !url ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug('µMatrix> cookies.js/processRemoveQueue(): removing "%s" (age=%s min)', cookieKey, ((Date.now() - cookieEntry.tstamp) / 60000).toFixed(1));
|
||||||
|
chromeCookieRemove(url, cookieEntry.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Once in a while, we go ahead and clean everything that might have been
|
||||||
|
// left behind.
|
||||||
|
|
||||||
|
var processClean = function() {
|
||||||
|
// Remove only some of the cookies which are candidate for removal:
|
||||||
|
// who knows, maybe a user has 1000s of cookies sitting in his
|
||||||
|
// browser...
|
||||||
|
var cookieKeys = Object.keys(cookieDict);
|
||||||
|
if ( cookieKeys.length > 25 ) {
|
||||||
|
cookieKeys = cookieKeys.sort(function(){return Math.random() < 0.5;}).splice(0, 50);
|
||||||
|
}
|
||||||
|
while ( cookieKeys.length ) {
|
||||||
|
removeCookieAsync(cookieKeys.pop());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var findAndRecordPageCookies = function(pageStats) {
|
||||||
|
var domains = ' ' + Object.keys(pageStats.domains).join(' ') + ' ';
|
||||||
|
for ( var cookieKey in cookieDict ) {
|
||||||
|
if ( !cookieDict.hasOwnProperty(cookieKey) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !cookieMatchDomains(cookieKey, domains) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
recordPageCookie(pageStats, cookieKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var findAndRemovePageCookies = function(pageStats) {
|
||||||
|
var domains = ' ' + Object.keys(pageStats.domains).join(' ') + ' ';
|
||||||
|
for ( var cookieKey in cookieDict ) {
|
||||||
|
if ( !cookieDict.hasOwnProperty(cookieKey, domains) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !cookieMatchDomains(cookieKey, domains) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
removeCookieAsync(cookieKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Check all scopes to ensure none of them fulfill the following
|
||||||
|
// conditions:
|
||||||
|
// - The hostname of the target cookie matches the hostname of the scope
|
||||||
|
// - The target cookie is allowed in the scope
|
||||||
|
// Check all pages to ensure none of them fulfill both following
|
||||||
|
// conditions:
|
||||||
|
// - refers to the target cookie
|
||||||
|
// - the target cookie is is allowed
|
||||||
|
// If one of the above set of conditions is fulfilled at least once,
|
||||||
|
// the cookie can NOT be removed.
|
||||||
|
// TODO: cache the joining of hostnames into a single string for search
|
||||||
|
// purpose.
|
||||||
|
|
||||||
|
var canRemoveCookie = function(cookieKey) {
|
||||||
|
var entry = cookieDict[cookieKey];
|
||||||
|
if ( !entry ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var µm = µMatrix;
|
||||||
|
var cookieHostname = entry.domain;
|
||||||
|
var cookieDomain = µm.URI.domainFromHostname(cookieHostname);
|
||||||
|
|
||||||
|
// rhill 2014-01-11: Do not delete cookies which are whitelisted
|
||||||
|
// in at least one scope. Limitation: this can be done only
|
||||||
|
// for cookies which domain matches domain of scope. This is
|
||||||
|
// because a scope with whitelist *|* would cause all cookies to not
|
||||||
|
// be removable.
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/126
|
||||||
|
var srcHostnames = µm.tMatrix.extractAllSourceHostnames();
|
||||||
|
var i = srcHostnames.length;
|
||||||
|
var srcHostname;
|
||||||
|
while ( i-- ) {
|
||||||
|
// Cookie related to scope domain?
|
||||||
|
srcHostname = µm.URI.domainFromHostname(srcHostnames[i]);
|
||||||
|
if ( srcHostname === '' || srcHostname !== cookieDomain ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( µm.mustBlock(srcHostname, cookieHostname, 'cookie') === false ) {
|
||||||
|
// console.log('cookies.js/canRemoveCookie()> can NOT remove "%s" because of scope "%s"', cookieKey, scopeKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach this point, we will check whether the cookie is actually
|
||||||
|
// in use for a currently opened web page. This is necessary to
|
||||||
|
// prevent the deletion of 3rd-party cookies which might be whitelisted
|
||||||
|
// for a currently opened web page.
|
||||||
|
var pageStats = µm.pageStats;
|
||||||
|
for ( var pageURL in pageStats ) {
|
||||||
|
if ( pageStats.hasOwnProperty(pageURL) === false ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( !cookieMatchDomains(cookieKey, ' ' + Object.keys(pageStats[pageURL].domains).join(' ') + ' ') ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( µm.mustAllow(µm.scopeFromURL(pageURL), cookieHostname, 'cookie') ) {
|
||||||
|
// console.log('cookies.js/canRemoveCookie()> can NOT remove "%s" because of scope "%s"', cookieKey, scopeKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('cookies.js/canRemoveCookie()> can remove "%s"', cookieKey);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Listen to any change in cookieland, we will update page stats accordingly.
|
||||||
|
|
||||||
|
var onChromeCookieChanged = function(changeInfo) {
|
||||||
|
if ( changeInfo.removed ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookie = changeInfo.cookie;
|
||||||
|
|
||||||
|
// rhill 2013-12-11: If cookie value didn't change, no need to record.
|
||||||
|
// https://github.com/gorhill/httpswitchboard/issues/79
|
||||||
|
var cookieKey = cookieKeyFromCookie(cookie);
|
||||||
|
var cookieEntry = cookieDict[cookieKey];
|
||||||
|
if ( !cookieEntry ) {
|
||||||
|
cookieEntry = addCookieToDict(cookie);
|
||||||
|
} else {
|
||||||
|
cookieEntry.tstamp = Date.now();
|
||||||
|
if ( cookie.value === cookieEntry.value ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cookieEntry.value = cookie.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through all pages and update if needed, as one cookie can be used
|
||||||
|
// by many web pages, so they need to be recorded for all these pages.
|
||||||
|
var allPageStats = µMatrix.pageStats;
|
||||||
|
var pageStats;
|
||||||
|
for ( var pageURL in allPageStats ) {
|
||||||
|
if ( !allPageStats.hasOwnProperty(pageURL) ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pageStats = allPageStats[pageURL];
|
||||||
|
if ( !cookieMatchDomains(cookieKey, ' ' + Object.keys(pageStats.domains).join(' ') + ' ') ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
recordPageCookie(pageStats, cookieKey);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
chrome.cookies.getAll({}, addCookiesToDict);
|
||||||
|
chrome.cookies.onChanged.addListener(onChromeCookieChanged);
|
||||||
|
|
||||||
|
// µMatrix.asyncJobs.add('cookieHunterRemove', null, processRemoveQueue, 2 * 60 * 1000, true);
|
||||||
|
// µMatrix.asyncJobs.add('cookieHunterClean', null, processClean, 10 * 60 * 1000, true);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Expose only what is necessary
|
||||||
|
|
||||||
|
return {
|
||||||
|
recordPageCookies: recordPageCookiesAsync,
|
||||||
|
removePageCookies: removePageCookiesAsync
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
µMatrix - a Chromium browser extension to black/white list requests.
|
||||||
|
Copyright (C) 2014 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Open links in the proper window
|
||||||
|
$('a').attr('target', '_blank');
|
||||||
|
$('a[href*="dashboard.html"]').attr('target', '_parent');
|
||||||
|
|
||||||
|
$('.whatisthis').on('click', function() {
|
||||||
|
$(this).parent()
|
||||||
|
.find('.whatisthis-expandable')
|
||||||
|
.toggleClass('whatisthis-expanded');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
});
|