Injection de scripts JavaScript et de styles CSS suivant l'URL
Ce projet est open source et accessible depuis GitHub GL-Browser.
Cet article est le 3ème d'une série d'articles concernant la création d'un navigateur internet incluant par défaut des fonctionnalités que l'on retrouve habituellement sous forme d'extensions dans Chrome ou Firefox.
Exemples de fonctionnalités :
Nous souhaitons :
Nous utilisons Electron qui mixe le navigateur open source Chromium avec la richesse du framework Node.js.
Dans ce 3ème article, nous réaliserons l'injection d'un style CSS et d'un code JavaScript correspondant à l'URL visitée, afin de supprimer par exemple, le voile Pinterest, les annonces/recommandations de Twitter, ...
Supprimer le voile Pinterest | Supprimer les annonces/recommandations de Twitter |
---|---|
Article 1 : initialisation du projet avec Electron.
Article 2 : injection du CSS et du JavaScript dans la page de Google.
Pour récupérer directement les sources du 2nd article :
git clone https://github.com/emmanuelroecker/GL-Browser
git checkout article2
La table d'association est représentée sous la forme d'un fichier contenant une liste d'identifiants.
Une liste de motifs/masques d'URL est associée à chaque identifiant.
Chaque identifiant est également un nom de fichier contenant le code js ou le css (suivant son extension) à injecter dans l'URL compatible avec l'un des masques associés.
Généralement, un motif (un ensemble de chaînes de caractères possibles) est représenté à l'aide d'une expression régulière.
Les chaînes de caractères étant limitées à des URL, les motifs peuvent être simplifiés en employant les match patterns.
Les match patterns utilisent uniquement le caractère spécial *
pour indiquer qu'à cet emplacement n'importe quelle chaîne de caractères peut être présente.
Exemple :
https://*.example.com/foo*bar
correspond à toutes les URL composées :
https://www.example.com/foo/baz/bar
et https://docs.example.com/foobar
sont compatibles avec https://*.example.com/foo*bar
YAML est un standard de sérialisation de données facile à lire, à comprendre et à éditer.
Créer le fichier config.yml au format YAML qui contiendra la liste des identifiants et les match patterns :
- name : pinterest
patterns:
- https://*.pinterest.com/*
- http://*.pinterest.com/*
- name: twitter
patterns:
- https://*.twitter.com/*
- name: google
patterns:
- https://www.google.com/*
- https://www.google.fr/*
L'arborescence du projet est désormais :
│ .gitignore
│ config.yml
│ index.html
│ LICENSE
│ main.js
│ package.json
│ README.md
│
├───cfg
│ google.css
│ google.js
│ pinterest.css
│ pinterest.js
│ twitter.css
│ twitter.js
│
└───node_modules
Utiliser la librairie js-yaml pour lire un fichier YAML.
Utiliser la librairie match-pattern pour convertir un match patterns en expression régulière.
Ajouter au fichier package.json les dépendances :
{
"devDependencies": {
"electron-prebuilt": "^0.36.0",
"js-yaml": "^3.5.0",
"match-pattern": "^0.0.1"
}
}
Pour les installer :
npm install
Regrouper ensemble les opérations pouvant être effectuées une seule fois au lancement du navigateur :
Ajouter dans le fichier index.html :
let fs = require('fs');
let yaml = require('js-yaml');
let matchPattern = require('match-pattern');
let directoryCfg = 'cfg'; //répertoire des css et js
let fileCfg = 'config.yml';
let cfg;
try {
cfg = yaml.safeLoad(fs.readFileSync(fileCfg, 'utf8')); //lecture du fichier yaml
cfg = cfg.map(function(elem) { //parcourt l'ensemble des identifiants
let name = elem['name'];
elem['css'] = fs.readFileSync(directoryCfg + "/" + name + ".css", 'utf-8'); //lit le contenu du fichier css associé à l'identifiant
elem['js'] = fs.readFileSync(directoryCfg + "/" + name + ".js", 'utf-8'); //lit le contenu du fichier js associé à l'identifiant
let patterns = elem['patterns'];
elem['patterns'] = patterns.map(function(pattern) { //parcourt l'ensemble des match patterns associés à un identifiant
pattern = matchPattern.parse(pattern); //convertit le match pattern en regexp
if (pattern === null) {
console.log("Bad pattern : " + pattern + " in " + name);
}
return pattern;
});
return elem;
});
} catch (e) {
console.log(e);
}
À la validation de l'URL saisie par l'utilisateur, parcourir tous les motifs jusqu'au premier compatible et injecter les CSS et JavaScript correspondants.
Ajouter au fichier index.html :
function getToInject(url) {
for (let elem of cfg) { //parcourt l'ensemble des identifiants
let patterns = elem['patterns']
for (let pattern of patterns) { //parcourt l'ensemble des motifs d'un identifiant
if (pattern.test(url)) { //test si l'url est compatible avec le motif
injectCSS = elem['css'];
injectJS = elem['js'];
return; //arrête la recherche au premier trouvé
}
};
};
}
let url = document.getElementById("urltext").value;
getToInject(url);
webview.src = url;
Créer le fichier pinterest.js à injecter au chargement de la page de Pinterest et indiquant l'élément HTML à supprimer :
let signeup = document.getElementsByClassName('InlineSignup')
if (signeup && signeup[0]) {
signeup[0].remove();
}
Le rendu :
Avant | Après |
---|---|
Créer le fichier twitter.js à injecter au chargement de la page de Twitter et indiquant les éléments HTML à supprimer :
let recommendations = document.getElementById("empty-timeline-recommendations");
if (recommendations) {
recommendations.remove();
}
let trends = document.getElementsByClassName('Trends');
if (trends && trends[0]) {
trends[0].remove();
}
let whoToFollow = document.getElementsByClassName('WhoToFollow');
if (whoToFollow && whoToFollow[0]) {
whoToFollow[0].remove();
}
let relatedUsers = document.getElementsByClassName('RelatedUsers');
if (relatedUsers && relatedUsers[0]) {
relatedUsers[0].remove();
}
Le rendu :
Avant | Après |
---|---|
git add -A
git commit -m "article 3"
git push
Le prochain article traitera du blocage des requêtes de suivi/tracking des utilisateurs à l'instar de Adblock ou uBlock.