From 8b946ee3bf7847ce69dcedec5e50973403a1f2ef Mon Sep 17 00:00:00 2001 From: Elbert Alias Date: Sat, 11 Mar 2017 13:41:43 +1100 Subject: [PATCH] Add WebExtension driver --- bin/wappalyzer-build | 7 + bin/wappalyzer-links | 10 + bin/wappalyzer-validate | 15 +- src/drivers/webextension/.gitignore | 7 + .../webextension/_locales/el/messages.json | 60 ++ .../webextension/_locales/en/messages.json | 60 ++ .../webextension/_locales/es/messages.json | 64 ++ .../webextension/_locales/fr/messages.json | 64 ++ .../webextension/_locales/gr/messages.json | 60 ++ .../webextension/_locales/it/messages.json | 65 ++ .../webextension/_locales/ro/messages.json | 60 ++ .../webextension/_locales/ru/messages.json | 60 ++ src/drivers/webextension/background.html | 15 + src/drivers/webextension/css/options.css | 106 +++ src/drivers/webextension/css/popup.css | 96 ++ src/drivers/webextension/images/github.png | Bin 0 -> 281 bytes src/drivers/webextension/images/icon_128.png | Bin 0 -> 5720 bytes src/drivers/webextension/images/icon_16.png | Bin 0 -> 555 bytes src/drivers/webextension/images/icon_19.png | Bin 0 -> 643 bytes src/drivers/webextension/images/icon_32.png | Bin 0 -> 1046 bytes src/drivers/webextension/images/icon_38.png | Bin 0 -> 1218 bytes .../webextension/images/icons/.gitkeep | 0 .../images/icons/converted/.gitkeep | 0 .../webextension/images/logo-white.svg | 29 + src/drivers/webextension/images/twitter.png | Bin 0 -> 373 bytes .../webextension/js/browser-polyfill.js | 851 ++++++++++++++++++ src/drivers/webextension/js/content.js | 61 ++ src/drivers/webextension/js/defaults.js | 5 + src/drivers/webextension/js/driver.js | 306 +++++++ src/drivers/webextension/js/i18n.js | 11 + src/drivers/webextension/js/inject.js | 14 + src/drivers/webextension/js/options.js | 54 ++ src/drivers/webextension/js/popup.js | 68 ++ src/drivers/webextension/manifest.json | 48 + src/drivers/webextension/options.html | 49 + src/drivers/webextension/popup.html | 21 + 36 files changed, 2257 insertions(+), 9 deletions(-) create mode 100644 src/drivers/webextension/.gitignore create mode 100644 src/drivers/webextension/_locales/el/messages.json create mode 100644 src/drivers/webextension/_locales/en/messages.json create mode 100644 src/drivers/webextension/_locales/es/messages.json create mode 100644 src/drivers/webextension/_locales/fr/messages.json create mode 100644 src/drivers/webextension/_locales/gr/messages.json create mode 100644 src/drivers/webextension/_locales/it/messages.json create mode 100644 src/drivers/webextension/_locales/ro/messages.json create mode 100644 src/drivers/webextension/_locales/ru/messages.json create mode 100644 src/drivers/webextension/background.html create mode 100644 src/drivers/webextension/css/options.css create mode 100644 src/drivers/webextension/css/popup.css create mode 100644 src/drivers/webextension/images/github.png create mode 100644 src/drivers/webextension/images/icon_128.png create mode 100644 src/drivers/webextension/images/icon_16.png create mode 100644 src/drivers/webextension/images/icon_19.png create mode 100644 src/drivers/webextension/images/icon_32.png create mode 100644 src/drivers/webextension/images/icon_38.png create mode 100644 src/drivers/webextension/images/icons/.gitkeep create mode 100644 src/drivers/webextension/images/icons/converted/.gitkeep create mode 100644 src/drivers/webextension/images/logo-white.svg create mode 100644 src/drivers/webextension/images/twitter.png create mode 100644 src/drivers/webextension/js/browser-polyfill.js create mode 100644 src/drivers/webextension/js/content.js create mode 100644 src/drivers/webextension/js/defaults.js create mode 100644 src/drivers/webextension/js/driver.js create mode 100644 src/drivers/webextension/js/i18n.js create mode 100644 src/drivers/webextension/js/inject.js create mode 100644 src/drivers/webextension/js/options.js create mode 100644 src/drivers/webextension/js/popup.js create mode 100644 src/drivers/webextension/manifest.json create mode 100644 src/drivers/webextension/options.html create mode 100644 src/drivers/webextension/popup.html diff --git a/bin/wappalyzer-build b/bin/wappalyzer-build index 3f973b736..6a18d6bb6 100755 --- a/bin/wappalyzer-build +++ b/bin/wappalyzer-build @@ -66,6 +66,13 @@ pushd $WAPPALYZER_ROOT/src/drivers/chrome > /dev/null zip -qr $WAPPALYZER_ROOT/build/wappalyzer_chrome.zip . +# Google Chrome +echo "Building WebExtension driver..." + +pushd $WAPPALYZER_ROOT/src/drivers/webextension > /dev/null + +zip -qr $WAPPALYZER_ROOT/build/wappalyzer_webextension.zip . + popd > /dev/null echo "Done. Builds have been created in $WAPPALYZER_ROOT/build." diff --git a/bin/wappalyzer-links b/bin/wappalyzer-links index 25f589406..dbb52a483 100755 --- a/bin/wappalyzer-links +++ b/bin/wappalyzer-links @@ -46,6 +46,16 @@ if [ "$(compgen -G "$path/icons/converted/*.png" | head -n1)" ]; then ln -f $path/icons/converted/*.png $path/drivers/chrome/images/icons/converted fi +ln -f $path/wappalyzer.js $path/drivers/webextension/js +ln -f $path/apps.json $path/drivers/webextension +ln -f $path/icons/*.png $path/drivers/webextension/images/icons +ln -f $path/icons/*.svg $path/drivers/webextension/images/icons +ln -f $path/utils/*.js $path/drivers/webextension/js + +if [ "$(compgen -G "$path/icons/converted/*.png" | head -n1)" ]; then + ln -f $path/icons/converted/*.png $path/drivers/webextension/images/icons/converted +fi + echo "OK" exit 0 diff --git a/bin/wappalyzer-validate b/bin/wappalyzer-validate index d9d09a617..0106c4d18 100755 --- a/bin/wappalyzer-validate +++ b/bin/wappalyzer-validate @@ -1,23 +1,20 @@ #!/bin/bash -path=$1 +path="$1" -if [ -z $path ] -then - if [ -z $WAPPALYZER_ROOT ] - then +if [ -z "$path" ]; then + if [ -z "$WAPPALYZER_ROOT" ]; then echo "-$(basename $0): No path specified" exit 1 fi - path=$WAPPALYZER_ROOT + path="$WAPPALYZER_ROOT" fi set -eu -if [ ! -d $path/src ] -then +if [ ! -d "$path/src" ]; then echo "-$(basename $0): Incorrect path" exit 1 @@ -27,7 +24,7 @@ path="$path/src" echo "Validating apps.json..." -node $WAPPALYZER_NODE_PATH/node_modules/jsonlint/lib/cli.js --quiet -V $WAPPALYZER_ROOT/schema.json $path/apps.json +node "$WAPPALYZER_NODE_PATH/node_modules/jsonlint/lib/cli.js" --quiet -V "$WAPPALYZER_ROOT/schema.json" "$path/apps.json" echo "Validating regular expressions..." diff --git a/src/drivers/webextension/.gitignore b/src/drivers/webextension/.gitignore new file mode 100644 index 000000000..602df1590 --- /dev/null +++ b/src/drivers/webextension/.gitignore @@ -0,0 +1,7 @@ +apps.json +images/icons/converted/*.png +images/icons/*.png +images/icons/*.svg +js/wappalyzer.js +js/iframe.js +js/network.js diff --git a/src/drivers/webextension/_locales/el/messages.json b/src/drivers/webextension/_locales/el/messages.json new file mode 100644 index 000000000..8b63568f5 --- /dev/null +++ b/src/drivers/webextension/_locales/el/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Κάνε fork το Wappalyzer στο GitHub!" }, + "twitter": { "message": "Ακολούθησε το Wappalyzer στο Twitter" }, + "website": { "message": "Πήγαινε στο wappalyzer.com" }, + "options": { "message": "Ρυθμίσεις Wappalyzer" }, + "optionsSave": { "message": "Ρυθμίσεις αποθήκευσης" }, + "optionsSaved": { "message": "Αποθηκεύτηκε" }, + "optionUpgradeMessage": { "message": "Ενημερώστε με για αναβαθμίσεις" }, + "optionTracking": { "message": "Ανώνυμη αποστολή αναφορών για εντοπισμένες εφαρμογές στο wappalyzer.com για έρευνα" }, + "nothingToDo": { "message": "Καμία ενέργεια." }, + "noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Διαδικτυακό Φόρουμ" }, + "categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" }, + "categoryName4": { "message": "Εργαλείο Τεκμηρίωσης" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework της JavaScript" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Πρόγραμμα αναπαραγωγής Βίντεο" }, + "categoryName15": { "message": "Σύστημα Σχολίων" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Script Γραμματοσειράς" }, + "categoryName18": { "message": "Framework Διαδικτύου" }, + "categoryName19": { "message": "Διάφορα" }, + "categoryName20": { "message": "Επεξεργαστής Κειμένου" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Διακομιστής Διαδικτύου" }, + "categoryName23": { "message": "Εργαλείο Μνήμης Cache" }, + "categoryName24": { "message": "Επεξεργαστής Εμπλουτισμένου Κειμένου" }, + "categoryName25": { "message": "Γραφικά JavaScript" }, + "categoryName26": { "message": "Framework για Κινητά" }, + "categoryName27": { "message": "Γλώσσα Προγραμματισμού" }, + "categoryName28": { "message": "Λειτουργικό Σύστημα" }, + "categoryName29": { "message": "Μηχανή Αναζήτησης" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Αυτοματοποίηση Marketing" }, + "categoryName33": { "message": "Επέκταση Διακομιστή Διαδικτύου" }, + "categoryName34": { "message": "Βάση Δεδομένων" }, + "categoryName35": { "message": "Χάρτης" }, + "categoryName36": { "message": "Δίκτυο Διαφημίσεων" }, + "categoryName37": { "message": "Υπηρεσία Δικτύου" }, + "categoryName38": { "message": "Διακομιστής Πολυμέσων" }, + "categoryName39": { "message": "Διαδικτυακή κάμερα" }, + "categoryName40": { "message": "Εκτυπωτής" }, + "categoryName41": { "message": "Σύστημα Επεξεργασίας Πληρωμών" }, + "categoryName42": { "message": "Σύστημα Διαχείρισης Tags" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Σύστημα Build/CI" }, + "categoryName45": { "message": "Σύστημα SCADA" }, + "categoryName46": { "message": "Απομακρυσμένη Πρόσβαση" }, + "categoryName47": { "message": "Εργαλείο Ανάπτυξης" }, + "categoryName48": { "message": "Δικτυακός Αποθηκευτικός Χώρος" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Συστήματα Διαχειρίσης Εγγράφων" }, + "categoryName51": { "message": "Σύστημα Κατασκευής Σελίδων Υποδοχής" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/en/messages.json b/src/drivers/webextension/_locales/en/messages.json new file mode 100644 index 000000000..ba2c15174 --- /dev/null +++ b/src/drivers/webextension/_locales/en/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Fork Wappalyzer on GitHub!" }, + "twitter": { "message": "Follow Wappalyzer on Twitter" }, + "website": { "message": "Go to wappalyzer.com" }, + "options": { "message": "Options" }, + "optionsSave": { "message": "Save options" }, + "optionsSaved": { "message": "Saved" }, + "optionUpgradeMessage": { "message": "Tell me about upgrades" }, + "optionTracking": { "message": "Anonymously send reports on detected applications to wappalyzer.com for research" }, + "nothingToDo": { "message": "Nothing to do here." }, + "noAppsDetected": { "message": "No applications detected." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Message Board" }, + "categoryName3": { "message": "Database Manager" }, + "categoryName4": { "message": "Documentation Tool" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "JavaScript Framework" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Video Player" }, + "categoryName15": { "message": "Comment System" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Font Script" }, + "categoryName18": { "message": "Web Framework" }, + "categoryName19": { "message": "Miscellaneous" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Web Server" }, + "categoryName23": { "message": "Cache Tool" }, + "categoryName24": { "message": "Rich Text Editor" }, + "categoryName25": { "message": "JavaScript Graphics" }, + "categoryName26": { "message": "Mobile Framework" }, + "categoryName27": { "message": "Programming Language" }, + "categoryName28": { "message": "Operating System" }, + "categoryName29": { "message": "Search Engine" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Marketing Automation" }, + "categoryName33": { "message": "Web Server Extension" }, + "categoryName34": { "message": "Database" }, + "categoryName35": { "message": "Map" }, + "categoryName36": { "message": "Advertising Network" }, + "categoryName37": { "message": "Network Service" }, + "categoryName38": { "message": "Media Server" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Printer" }, + "categoryName41": { "message": "Payment Processor" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Build/CI System" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Remote Access" }, + "categoryName47": { "message": "Development Tool" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Document Management Systems" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/es/messages.json b/src/drivers/webextension/_locales/es/messages.json new file mode 100644 index 000000000..666deb714 --- /dev/null +++ b/src/drivers/webextension/_locales/es/messages.json @@ -0,0 +1,64 @@ +{ + "github": { "message": "¡Forkea Wappalyzer en GitHub!" }, + "twitter": { "message": "Sigue Wappalyzer en Twitter" }, + "website": { "message": "Ir a wappalyzer.com" }, + "options": { "message": "Opciones" }, + "optionsSave": { "message": "Guardar opciones" }, + "optionsSaved": { "message": "Guardado" }, + "optionUpgradeMessage": { "message": "Indicarme actualizaciones" }, + "optionTracking": { "message": "Enviar informes anónimos sobre las aplicaciones detectadas a wappalyzer.com para análisis" }, + "nothingToDo": { "message": "Nada que hacer aquí." }, + "noAppsDetected": { "message": "Aplicaciones no detectadas." }, + "categoryName1": { "message": "Gestor de Contenido" }, + "categoryName2": { "message": "Foro" }, + "categoryName3": { "message": "Gestor de Bases de Datos" }, + "categoryName4": { "message": "Herramienta de Documentación" }, + "categoryName5": { "message": "Widget" }, + "categoryName6": { "message": "Tienda Web" }, + "categoryName7": { "message": "Galería fotográfica" }, + "categoryName8": { "message": "Wiki" }, + "categoryName9": { "message": "Panel de Hosting" }, + "categoryName10": { "message": "Analítica" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Gestor de Incidencias" }, + "categoryName14": { "message": "Reproductor de Vídeo" }, + "categoryName15": { "message": "Sistema de Comentarios" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Tipografía" }, + "categoryName18": { "message": "Framework Web" }, + "categoryName19": { "message": "Miscelánea" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Servidor Web" }, + "categoryName23": { "message": "Herramienta de Cache" }, + "categoryName24": { "message": "Editor de Texto Enriquecido" }, + "categoryName25": { "message": "Gráficos JavaScript" }, + "categoryName26": { "message": "Framework Móvil" }, + "categoryName27": { "message": "Lenguaje de programación" }, + "categoryName28": { "message": "Sistema Operativo" }, + "categoryName29": { "message": "Motor de Búsqueda" }, + "categoryName30": { "message": "Correo Web" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Automatización de Marketing" }, + "categoryName33": { "message": "Extensión de Servidor Web" }, + "categoryName34": { "message": "Base de Datos" }, + "categoryName35": { "message": "Mapa" }, + "categoryName36": { "message": "Red de Publicidad" }, + "categoryName37": { "message": "Network Sevice" }, + "categoryName38": { "message": "Media Server" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Printer" }, + "categoryName41": { "message": "Payment Processor" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Build/CI System" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Remote Access" }, + "categoryName47": { "message": "Development Tool" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Document Management Systems" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/fr/messages.json b/src/drivers/webextension/_locales/fr/messages.json new file mode 100644 index 000000000..743d79210 --- /dev/null +++ b/src/drivers/webextension/_locales/fr/messages.json @@ -0,0 +1,64 @@ +{ + "github": { "message": "Forker Wappalyzer sur GitHub!" }, + "noAppsDetected": { "message": "Pas d'applications détectées." }, + "nothingToDo": { "message": "Rien à faire ici." }, + "optionTracking": { "message": "Envoyer anonymement des rapports sur les applications détectées à wappalyzer.com pour la recherche" }, + "optionUpgradeMessage": { "message": "M'afficher les mises à jour" }, + "options": { "message": "Options" }, + "optionsSave": { "message": "Sauver les options" }, + "optionsSaved": { "message": "Sauvé" }, + "twitter": { "message": "Suivre Wappalyzer sur Twitter" }, + "website": { "message": "Aller à wappalyzer.com" }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Forum" }, + "categoryName3": { "message": "Gestionnaire de base de données" }, + "categoryName4": { "message": "Outil de documentation" }, + "categoryName5": { "message": "Widget" }, + "categoryName6": { "message": "Boutique en ligne" }, + "categoryName7": { "message": "Galerie photo" }, + "categoryName8": { "message": "Wiki" }, + "categoryName9": { "message": "Gestionnaires de serveur" }, + "categoryName10": { "message": "Outil de statistiques" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Outil de suivi de problèmes" }, + "categoryName14": { "message": "Lecteur de vidéos" }, + "categoryName15": { "message": "Système de commentaires" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Script de police" }, + "categoryName18": { "message": "Framework web" }, + "categoryName19": { "message": "Divers" }, + "categoryName20": { "message": "Editeur" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Serveur web" }, + "categoryName23": { "message": "Outil de cache" }, + "categoryName24": { "message": "Editeur WYSIWYG" }, + "categoryName25": { "message": "Graphismes JavaScript" }, + "categoryName26": { "message": "Framework pour mobiles" }, + "categoryName27": { "message": "Language de programmation" }, + "categoryName28": { "message": "Système d'exploitation" }, + "categoryName29": { "message": "Moteur de recherche" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Logiciel de marketing" }, + "categoryName33": { "message": "Extension de serveur web" }, + "categoryName34": { "message": "Base de données" }, + "categoryName35": { "message": "Carte" }, + "categoryName36": { "message": "Réseau publicitaire" }, + "categoryName37": { "message": "Périphérique réseau" }, + "categoryName38": { "message": "Serveur multimédia" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Imprimante" }, + "categoryName41": { "message": "Service de paiement" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Système CI" }, + "categoryName45": { "message": "Système SCADA" }, + "categoryName46": { "message": "Accès à distance" }, + "categoryName47": { "message": "Outil de développement" }, + "categoryName48": { "message": "Stockage réseau" }, + "categoryName49": { "message": "Lecteur RSS" }, + "categoryName50": { "message": "Système de gestion de documents" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Chat en direct" } +} diff --git a/src/drivers/webextension/_locales/gr/messages.json b/src/drivers/webextension/_locales/gr/messages.json new file mode 100644 index 000000000..f512dad8f --- /dev/null +++ b/src/drivers/webextension/_locales/gr/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Κάνε fork το Wappalyzer στο GitHub!" }, + "twitter": { "message": "Ακολούθησε το Wappalyzer στο Twitter" }, + "website": { "message": "Πήγαινε στο wappalyzer.com" }, + "options": { "message": "Ρυθμίσεις" }, + "optionsSave": { "message": "Ρυθμίσεις αποθήκευσης" }, + "optionsSaved": { "message": "Αποθηκεύτηκε" }, + "optionUpgradeMessage": { "message": "Ενημερώστε με για αναβαθμίσεις" }, + "optionTracking": { "message": "Ανώνυμη αποστολή αναφορών για εντοπισμένες εφαρμογές στο wappalyzer.com για έρευνα" }, + "nothingToDo": { "message": "Καμία ενέργεια." }, + "noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Διαδικτυακό Φόρουμ" }, + "categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" }, + "categoryName4": { "message": "Εργαλείο Τεκμηρίωσης" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework της JavaScript" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Πρόγραμμα αναπαραγωγής Βίντεο" }, + "categoryName15": { "message": "Σύστημα Σχολίων" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Script Γραμματοσειράς" }, + "categoryName18": { "message": "Framework Διαδικτύου" }, + "categoryName19": { "message": "Διάφορα" }, + "categoryName20": { "message": "Επεξεργαστής Κειμένου" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Διακομιστής Διαδικτύου" }, + "categoryName23": { "message": "Εργαλείο Μνήμης Cache" }, + "categoryName24": { "message": "Επεξεργαστής Εμπλουτισμένου Κειμένου" }, + "categoryName25": { "message": "Γραφικά JavaScript" }, + "categoryName26": { "message": "Framework για Κινητά" }, + "categoryName27": { "message": "Γλώσσα Προγραμματισμού" }, + "categoryName28": { "message": "Λειτουργικό Σύστημα" }, + "categoryName29": { "message": "Μηχανή Αναζήτησης" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Αυτοματοποίηση Marketing" }, + "categoryName33": { "message": "Επέκταση Διακομιστή Διαδικτύου" }, + "categoryName34": { "message": "Βάση Δεδομένων" }, + "categoryName35": { "message": "Χάρτης" }, + "categoryName36": { "message": "Δίκτυο Διαφημίσεων" }, + "categoryName37": { "message": "Υπηρεσία Δικτύου" }, + "categoryName38": { "message": "Διακομιστής Πολυμέσων" }, + "categoryName39": { "message": "Διαδικτυακή κάμερα" }, + "categoryName40": { "message": "Εκτυπωτής" }, + "categoryName41": { "message": "Σύστημα Επεξεργασίας Πληρωμών" }, + "categoryName42": { "message": "Σύστημα Διαχείρισης Tags" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Σύστημα Build/CI" }, + "categoryName45": { "message": "Σύστημα SCADA" }, + "categoryName46": { "message": "Απομακρυσμένη Πρόσβαση" }, + "categoryName47": { "message": "Εργαλείο Ανάπτυξης" }, + "categoryName48": { "message": "Δικτυακός Αποθηκευτικός Χώρος" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Συστήματα Διαχειρίσης Εγγράφων" }, + "categoryName51": { "message": "Σύστημα Κατασκευής Σελίδων Υποδοχής" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/it/messages.json b/src/drivers/webextension/_locales/it/messages.json new file mode 100644 index 000000000..ede043b3b --- /dev/null +++ b/src/drivers/webextension/_locales/it/messages.json @@ -0,0 +1,65 @@ +{ + "github": { "message": "Fork Wappalyzer su GitHub!" }, + "twitter": { "message": "Follow Wappalyzer su Twitter" }, + "website": { "message": "Vai su wappalyzer.com" }, + "options": { "message": "Opzioni" }, + "optionsSave": { "message": "Salva opzioni" }, + "optionsSaved": { "message": "Salvato" }, + "optionUpgradeMessage": { "message": "Parlami dell'upgrade" }, + "optionTracking": { "message": "Inviare anonimamente un report sulle applicazioni rilevate a wappalyzer.com per l'analisi" }, + "nothingToDo": { "message": "Niente da fare qui." }, + "noAppsDetected": { "message": "Nessuna applicazione rilevata." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Forum" }, + "categoryName3": { "message": "Gestore di Database" }, + "categoryName4": { "message": "Strumento di documentazione" }, + "categoryName5": { "message": "Widget" }, + "categoryName6": { "message": "eCommerce" }, + "categoryName7": { "message": "Galleria fotografica" }, + "categoryName8": { "message": "Wiki" }, + "categoryName9": { "message": "Pannello Hosting" }, + "categoryName10": { "message": "Analytics" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Issue Tracker" }, + "categoryName14": { "message": "Player Video" }, + "categoryName15": { "message": "Sistema di commenti" }, + "categoryName16": { "message": "Captcha" }, + "categoryName17": { "message": "Font Script" }, + "categoryName18": { "message": "Framework Web" }, + "categoryName19": { "message": "Miscellanea" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Web Server" }, + "categoryName23": { "message": "Cache Tool" }, + "categoryName24": { "message": "Editor di Testo Ricco" }, + "categoryName25": { "message": "JavaScript Graphics" }, + "categoryName26": { "message": "Framework Mobile" }, + "categoryName27": { "message": "Linguaggio di programmazione" }, + "categoryName28": { "message": "Sistema Operativo" }, + "categoryName29": { "message": "Motore di Ricerca" }, + "categoryName30": { "message": "Web Mail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Marketing Automation" }, + "categoryName33": { "message": "Estensione Web Server" }, + "categoryName34": { "message": "Database" }, + "categoryName35": { "message": "Mappa" }, + "categoryName36": { "message": "Network Pubblicitario" }, + "categoryName37": { "message": "Network Service" }, + "categoryName38": { "message": "Media Server" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Stampante" }, + "categoryName41": { "message": "Payment Processor" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Sistema Build/CI" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Accesso" }, + "categoryName47": { "message": "Strumenti di Sviluppo" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Lettore di Feed" }, + "categoryName50": { "message": "Sistema di Gestione Documenti" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} + diff --git a/src/drivers/webextension/_locales/ro/messages.json b/src/drivers/webextension/_locales/ro/messages.json new file mode 100644 index 000000000..6d938bc67 --- /dev/null +++ b/src/drivers/webextension/_locales/ro/messages.json @@ -0,0 +1,60 @@ +{ + "github": { "message": "Fork-uiește Wappalyzer pe GitHub!" }, + "twitter": { "message": "Urmărește Wappalyzer pe Twitter" }, + "website": { "message": "Mergi la wappalyzer.com" }, + "options": { "message": "Opțiuni" }, + "optionsSave": { "message": "Salvează opțiuni" }, + "optionsSaved": { "message": "Salvat" }, + "optionUpgradeMessage": { "message": "Anunță-mă dacă sunt actualizări" }, + "optionTracking": { "message": "Trimite rapoarte anonime despre aplicațiile detectate către wappalyzer.com pentru cercetare" }, + "nothingToDo": { "message": "Nimic de făcut pe pagina curentă." }, + "noAppsDetected": { "message": "Nici o aplicație detectată." }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Forum de discuții" }, + "categoryName3": { "message": "Manager baze de date" }, + "categoryName4": { "message": "Unealtă pentru documentare" }, + "categoryName5": { "message": "Widget" }, + "categoryName10": { "message": "Analiză trafic web" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "Framework JavaScript" }, + "categoryName13": { "message": "Tracker probleme" }, + "categoryName14": { "message": "Player Video" }, + "categoryName15": { "message": "Sistem de comentarii" }, + "categoryName16": { "message": "Verificare Captcha" }, + "categoryName17": { "message": "Script pentru fonturi" }, + "categoryName18": { "message": "Framework Web" }, + "categoryName19": { "message": "Divers" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Server Web" }, + "categoryName23": { "message": "Unealtă Cache" }, + "categoryName24": { "message": "Editor Texte Rich" }, + "categoryName25": { "message": "Grafică JavaScript" }, + "categoryName26": { "message": "Framework Mobile" }, + "categoryName27": { "message": "Limbaj de programare" }, + "categoryName28": { "message": "Sistem de operare" }, + "categoryName29": { "message": "Motor de căutare" }, + "categoryName30": { "message": "Poștă electronică" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Automatizare marketing" }, + "categoryName33": { "message": "Extensie server web" }, + "categoryName34": { "message": "Bază de date" }, + "categoryName35": { "message": "Hartă" }, + "categoryName36": { "message": "Rețea de advertising" }, + "categoryName37": { "message": "Serviciu rețea" }, + "categoryName38": { "message": "Server Media" }, + "categoryName39": { "message": "Webcam" }, + "categoryName40": { "message": "Imprimantă" }, + "categoryName41": { "message": "Sistem de plată" }, + "categoryName42": { "message": "Manager cuvinte cheie" }, + "categoryName43": { "message": "Paywall" }, + "categoryName44": { "message": "Build/CI System" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Remote Access" }, + "categoryName47": { "message": "Development Tool" }, + "categoryName48": { "message": "Network Storage" }, + "categoryName49": { "message": "Feed Readers" }, + "categoryName50": { "message": "Document Management Systems" }, + "categoryName51": { "message": "Landing Page Builder" }, + "categoryName52": { "message": "Live Chat" } +} diff --git a/src/drivers/webextension/_locales/ru/messages.json b/src/drivers/webextension/_locales/ru/messages.json new file mode 100644 index 000000000..4cf46ecbc --- /dev/null +++ b/src/drivers/webextension/_locales/ru/messages.json @@ -0,0 +1,60 @@ +{ + "categoryName1" : { "message" : "CMS" }, + "categoryName2" : { "message" : "Форум" }, + "categoryName3" : { "message" : "Менеджер БД" }, + "categoryName4" : { "message" : "Документация" }, + "categoryName5" : { "message" : "Виджет" }, + "categoryName10" : { "message" : "Аналитика" }, + "categoryName11" : { "message" : "Блог" }, + "categoryName12" : { "message" : "JS фреймворк" }, + "categoryName13" : { "message" : "Баг трекер" }, + "categoryName14" : { "message" : "Видео плеер" }, + "categoryName15" : { "message" : "Система комментариев" }, + "categoryName16" : { "message" : "Капча" }, + "categoryName17" : { "message" : "Шрифт" }, + "categoryName18" : { "message" : "Веб фреймворк" }, + "categoryName19" : { "message" : "Прочее" }, + "categoryName20" : { "message" : "HTML редактор" }, + "categoryName21" : { "message" : "LMS" }, + "categoryName22" : { "message" : "Веб сервер" }, + "categoryName23" : { "message" : "Кеширование" }, + "categoryName24" : { "message" : "WYSIWYG редактор" }, + "categoryName25" : { "message" : "JS графика" }, + "categoryName26" : { "message" : "Мобильный фреймворк" }, + "categoryName27" : { "message" : "Язык программирования" }, + "categoryName28" : { "message" : "Операционная система" }, + "categoryName29" : { "message" : "Поисковый движок" }, + "categoryName30" : { "message" : "Веб почта" }, + "categoryName31" : { "message" : "CDN" }, + "categoryName32" : { "message" : "Управление маркетингом" }, + "categoryName33" : { "message" : "Расширение Веб сервера" }, + "categoryName34" : { "message" : "База данных" }, + "categoryName35" : { "message" : "Карта" }, + "categoryName36" : { "message" : "Рекламная сеть" }, + "categoryName37" : { "message" : "Сетевая служба" }, + "categoryName38" : { "message" : "Медиа сервер" }, + "categoryName39" : { "message" : "Вебкамера" }, + "categoryName40" : { "message" : "Принтер" }, + "categoryName41" : { "message" : "Провайдер платежей" }, + "categoryName42" : { "message" : "Менеджер тэгов" }, + "categoryName43" : { "message" : "Paywall" }, + "categoryName44" : { "message" : "Система непрерывной интеграции" }, + "categoryName45" : { "message" : "Система SCADA" }, + "categoryName46" : { "message" : "Удаленное управление" }, + "categoryName47" : { "message" : "Утилита для разработчиков" }, + "categoryName48" : { "message" : "Сетевое хранилище" }, + "categoryName49" : { "message" : "Граббер контента" }, + "categoryName50" : { "message" : "Управление документами" }, + "categoryName51" : { "message": "Генератор лендингов" }, + "categoryName52" : { "message": "Live Chat" }, + "github" : { "message" : "Форкнуть на GitHub!" }, + "noAppsDetected" : { "message" : "Нет данных о сайте" }, + "nothingToDo" : { "message" : "Тут нечего искать" }, + "optionTracking" : { "message" : "Анонимно отправлять статистику распознанных данных на сервер (для улучшения расширения)" }, + "optionUpgradeMessage" : { "message" : "Оповещать меня о новых обновлениях" }, + "options" : { "message" : "Настройки" }, + "optionsSave" : { "message" : "Сохранить" }, + "optionsSaved" : { "message" : "Успешно сохранено!" }, + "twitter" : { "message" : "Следите за новостями в Твиттере" }, + "website" : { "message" : "Перейти на Wappalyzer.com" } +} diff --git a/src/drivers/webextension/background.html b/src/drivers/webextension/background.html new file mode 100644 index 000000000..06e298abf --- /dev/null +++ b/src/drivers/webextension/background.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/drivers/webextension/css/options.css b/src/drivers/webextension/css/options.css new file mode 100644 index 000000000..bd6096f5f --- /dev/null +++ b/src/drivers/webextension/css/options.css @@ -0,0 +1,106 @@ +body { + color: #303942; + cursor: default; + font-family: Helvetica, Arial, sans-serif; + font-size: .8rem; + line-height: 1.4rem; + margin: 0; +} + +p { + margin: 0 0 1rem 0; +} + +h1, h2, h3 { + font-weight: normal; + line-height: 1; +} + +h1 { + border-bottom: 1px solid #dbdbdb; + font-size: 1.5rem; + margin: 0 0 1.5rem 0; + padding: 1rem 0 1.5rem 0; +} + +h2 { + font-size: 1.3em; + margin-bottom: 0.4em; +} + +h3 { + color: black; + font-size: 1.2em; + margin-bottom: 0.5em; +} + +a { + color: rgb(17, 85, 204); + text-decoration: underline; +} + +label { + display: block; +} + +button { + background: #4608ad; + border: none; + border-radius: .2rem; + color: white; + font-size: inherit; + padding: 0 .6rem; + line-height: 1.8rem; +} + +a:active { + color: rgb(5, 37, 119); +} + +.hero { + background: linear-gradient(160deg, #32067c, #150233); + padding: 1.5rem 0 1rem 1.5rem; +} + + .hero img { + height: 3rem; + } + +.container { + margin: 0 auto; + max-width: 800px; +} + +.content { + padding: 1.5rem; +} + +#options-saved { + display: none; + margin-left: .5rem; + -webkit-animation: fadeout 2s; +} + +#about { + border-top: 1px solid #dbdbdb; + margin-top: 2.5rem; + padding: 1.5rem 0 1.5rem 0; +} + + #about img { + margin-right: .2rem; + vertical-align: middle; + } + + #about button { + background: white; + border: 1px solid #dbdbdb; + cursor: pointer; + color: #303942; + margin: 0 1rem .5rem 0; + } + +@-webkit-keyframes fadeout { + from { opacity: 1; } + to { opacity: 0; } +} diff --git a/src/drivers/webextension/css/popup.css b/src/drivers/webextension/css/popup.css new file mode 100644 index 000000000..6c91d5888 --- /dev/null +++ b/src/drivers/webextension/css/popup.css @@ -0,0 +1,96 @@ +body { + background: #fff; + color: #4a4a4a; + font-family: Helvetica, Arial, sans-serif; + font-size: 13px; + line-height: 16px; + margin: 0; + min-width: 200px; + padding: 15px; +} + +a { + color: #4a4a4a; +} + +a:focus { + outline: 0; +} + +img { + display: inline-block; + height: 16px; + margin-right: 8px; + vertical-align: top; + width: 16px; +} + +.detected-app { + padding: 7px 0; +} + +.detected-app:first-child { + padding-top: 0; +} + +.detected-app:last-child { + border: none; + padding-bottom: 0; +} + + .detected-app a { + color: #4608ad; + display: block; + text-decoration: none; + } + + .detected-app a .label .name { + border-bottom: 1px solid transparent; + } + + .detected-app a:hover .label .name { + border-bottom: 1px solid #4608ad; + } + + .detected-app a .category .name { + color: #4a4a4a; + border-bottom: 1px solid transparent; + } + + .detected-app a:hover .category .name { + border-bottom: 1px solid #4a4a4a; + } + +.label { + font-weight: bold; +} + +.category { + display: block; + margin: 5px 0 0 24px; +} + +.empty { + color: #999; + font-style: italic; + text-align: center; +} + +#footer { + border-top: 1px solid #ccc; + margin-top: 17px; + overflow: hidden; + padding-top: 11px; +} + +#footer a { + text-decoration: none; +} + +#footer a:hover { + border-bottom: 1px solid #dbdbdb; +} + +#options { + float: right; +} diff --git a/src/drivers/webextension/images/github.png b/src/drivers/webextension/images/github.png new file mode 100644 index 0000000000000000000000000000000000000000..037d0c33617bc314c9a772e9434801b70f870891 GIT binary patch literal 281 zcmV+!0p|XRP) zLS?J56<&?(J5gDJsE`B3^2!YB0Cq*O`WD}>M>RVn+YF14O*gs}6ZRrF5{x#oYw fMt=FD7vB8~u+DAIWnGkt00000NkvXXu0mjfdTM>M literal 0 HcmV?d00001 diff --git a/src/drivers/webextension/images/icon_128.png b/src/drivers/webextension/images/icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..05ad20d8f1de19fe4fa8a1d34bf0f7858c6e9b34 GIT binary patch literal 5720 zcmZu#bx;)U)2Bl{ItA(e(ycTc-Q6i6B6-Atq!I_xT?YzCw{#pKodVL_kwOITQ{}CR}Gyg4ZJNhhe?9`QkXsG|a z@0}$n&l-FW6;sf&#r*#WU7jNc{H(i7obVK{LdK;jU1^8lrsP?9YOtV zW@pm|WpTnNR&?;Kl5;oC(!;;Z|YA^NVD&9NgI#(^llavZI`FOX* zH!<-WRlOlup0zu zLTblKg*i@m`u|}8(nZa%+wisIuu^{@16%p3DIL@-jZK!X8+hO_dbLy-cPh+9*`y*T z7Bgz;IS;>e3~b9`lO^o;fo0&l7O?Ky-7LLDf24UUj6v8|-41p*+#V4|4_F!^~|NC-Wp~t?}_}4koinD*%;gUkh8n^Ix8jWY#aS$AbgT10nzbRGnA}*ckQEgp3yi) z-hPJ>x!f`EZt;BAfL$|`rN3w`nK*`9zIB}p-^Fw#?aqCm_)v?KA=)GQ#Mmf(I1@(_ zCpCA(iSrZrZPWZzMhIp8b`ky`cp;*_NH#8zW?ht_TG?=IWkqv09?Ij)b&Ws{wMxK2O=g|4_At3l=WXp+hjp8a6ANY^$DMeKx)qNlLH zy}}IX-oq{fW_JFY!4k)AnY)N_@IO3|MZFQ{QEhKk{v+|Q7na5cbaKf?gEr4mavjgh zKH=9UceLw9L=gu+DP8=1&MArYK@HjyU5m8Cwjp-7F?k-CS@25R8N(Z`@FLy+Jln^r z;)ii=LDNU--Cpv1L?guE=Gh;+Ef_y_4-t@GJ$;rxpC0r_`X{7SttB1|^i}+KfwVm9=GE=PLM8L9 ziRUyrjA;MY6%=F%`XL4U8OdmZ!=L%eD9N4ZHZqTsfC)hcb&Hu4^OpLDksdeg+=82PLG zsMc>(QgxwYv?uG62QKf&IzF(9JTMPW&suUMR6(?YcscR-ryjfr(k~R&D7y1wM(91| z=;`i}R4>#81-3sVeY#g*CDaO*$ZDfS54I)FK|Kud#KGl5E5KGA=qDUK_isI{%3m+N zm$B@AoV3#LUA>+m$ZK;V?fNXb*K{$Dw~|EN+{6o#p8uiwli+SE1!J_n^Qk*SaeT?Z zNV&%?CHi&pU_W8X*|_8XRjqh;Kyjb|aU ztbzWoGZ#`ecyJZQg(Eg&+kc$4)S*jM2cGWOSKyF_ehaHmtE8TlfcDe-a-mmW@l9Oh zbc7J!Dt}L;gB<1T?;2CtY(@CH)?}pXNHTqMrEw|acF`J`r{d`CThF78V6NIbkny5W zCSmJlL8%vFyWsc6m3>57d+9T>?rhgPfgW5QAKZ-ve7(^9F)>|(*_j8`jp*rf5aldO zxvBjpq@EQ4JK9~EbWBHI^xLlD?bZ?vHBo=%X(NF-YcbBvuMA281#C> zPkRK=p+&J{7Cfm<4oxoC4?q<#+&taN^X4UnjG?;D@;{s0|2|4}ts0CEvHMGnIUnM{ z0m_Hf!mo2^J{`CIie=>-+X<3 z;S`aY)XjxqOB@c5CZxcO61b#G;;-K_%<_!#k^@5nkNXV+)6G{&z}Cv@#D2eQFV$xdKeok z5K}{J|DA%=49YK~v*nKexp~v$$dR1k%c@TsqrOvQP0nuKOS$*U@>ap2y2b$em6Dg# z(&dEX0MUmgG5+%c$$vdfie3oh<6=vB^8UFB7i19E!fEZ0#ARR(I0OnwxT#X6n(>@(K+uZsCoG+DQs0exfS=i(X` z#hat$-_H$^3sSYlAvj*BZkTr(8YNu2dLv_oaQdE~3zL3o>tIR){>4oKy&^h{4ujrj zQ+F3DIIukO|#a2OsVg;;~Eq?;Jz-DCxvk)IjzaGnW=8 zC25a$L@L(^0X6RA8R+e<;6*bRZc!Ut0(e&(eoeFjsA@Sm315 zejfBb+>#fquCzy9d^Uah20vV#1vX3#9EfdQ%be{tZ*tIg8jq%T{iPF{&IE#STbvZ_ z_kW$M(%KBG$Q@%ItFRgWp>MdIZl1K8HjYeOKDiyyG4$>5gAmRe)Y&?)(ij_(B(##n z4Q-sw^Rk8v)y4IW=#Ua>1mv7U^RY(NCdQE~qYpyc_g^;K(tOf%gYSkM5&y=mvS|+O z4Le>+7Jhu*xA%9JRdNN|9+n=Se{A4iMhS`Q+uEC*;*SPbLf$A2x4w~hQQIV__Q6~# z#4G%p$AZK#(ccJA#s&`f9j}%aX8W)nlCJYu3Ls zTi5D!_Ae%X8K-woCt_Y;WKm?S2$znZR9lg3)S08Ew?Cz3oa~h6;0pO1t9aetf!A@( z!DrD`z}G3Wr=&!Fjl`XeZDpL7#;{7%N!Or~z>PiN42m*KA8yy%3;tGY^8rU}oZGfH zBQjB9?eCG=AQM5?J;ITKZ(%N2W!!z&nye_$=^Vkzh-X`J@7KPuKKaH52FH<#2Qm^; z5!TglohKJ4E*f=3%H<_(^rQ&;KT%pqgwmXFuji^iY);nV*?YS=nq58NX$K#@5rt*7 z`6(|hTp`b@%ueRE@#@Y0YWPg~-PEl?u8tu6l_uMl0;d3=6`B_|V|g4bpm}bSwCf-v zR5dcO=gCsD(yBgT6=la~=%5J>#Ceb3^U1ya;g&rn_7maP$)SzTS=_RkxRS&rY{f=I zm8Zg4)srItw@A@Ch^W&tae$WkK`zs$t`Ltu^RAFsKZbwD9aG`U8c|9>l}LH~J*xuu znLG*i%R0xX=Y;~kn0M$v`I6%*XaaL+zbw)T)_3AJ!b5bPCm21Akzo-q)|$UhL_SSl z>1XHiLLV@mwvwV3n7IBnc9q1@6={n>mgDa?<0ZV@(lkfn;rzE2*odBRi?6~qY(qoa zbo*aHU`uhfpOL|7PxeRo<^3}-;7&pC-HPLIYwPm|CCa6IipBQ;Kw;Y)gL&;n(3*BG zz_`@Ch-bW$dFE>}@0Q4vbyLs{$hG0gF97#z)4MR-&HeeE&&qsHfx|(S-4ijtCA2;_ z!$IZc<*32Eak@1I_~)t0!O8r2S`ewfepoQ^7E)C=*ApQBfn>``kNbl5U2W}4`N%E7 zAqk9pEZt;3hzVWKfmp7mE?tT{{lKgc4*iYT=jL!zaTn&C`FWPIbAW(ru_O%22JrJH ziYzkO_PMt{#D@mEgd4AL7gILGCw?r4Qfu1Mt)7n(eu*WbuISAcd+s3xnCE7&xyN7h7 zP{O)coi(MRs6?)Vxu8;27asCflcJoWH;9;#-`xU{ zIB3)R^TyYU3?w;-UBCAtG&}X z!Op6|j`T9Z!oyOSvk0rE}B~mxXKpp(RQGq(;(W|nU zgq`%?95&S^U|zB*)OS*?vrF~Fz)Ajk%~fIicl?>XZsg?GZgxx5)Pv`gg)7ddU0peY zm+q7qh0NIJJ}5>&AntnD1ULyc$AA+M$>>ay%TKV-Uh4U7uTlkx>%;ah0dNdDdv8kV zS^)7EgPcm@BgRWw?6WOmq?|U0t4-sAzRUd_vrmZlq zD&cuNn^wY^pbup<{BNMxcCUX3G^Bj1R!Zt{J%Q=3@uvAY0)7-z+_A&=3 zWbp>nbn?;7=T!AH)89~m z|Eej#l^W{Cxx0i~BR^jK^8Fky^2C$sxK&P zsR(Gx@r(DBg19)EWth}J1tacg!SDaB7!|)1^|q4HMvrkoXsk)w*bffIG0DbLtH@|G zW=*p@yaIkzES%GIIAK1dD~N57dA@u$|3`pX72f|Ryov*b|ab#AqO7PyXg_6IF|BxJjNgsI^hb>P&wsz zt%#qk5hLlemJwUTB-`g|pWm4V5%p2hagiBGCHi@wCFR+UhdnlN4RPo6&}0%yD4=@v zRg!tQ?%wC2IC;Zs=&efYqizK}d95^R{+Fs>6xj@{1Zm!ZQojn$=H{8*IiE=PE{rq@1@2o{*Ti1Vek$UpODC}#?p z+m&o9-)hM*MmV3Z5|Lx5&+A92ey9@0*M7P7*sl|L6N{yKd4BzYzbx_(uPc<3*{b#W zBopwOF=Prz<10r+{mJ|%`XH+IsU-8_4~IO$ZBVZ#?ZRFSLy8RjY%+FLR!bY#+AJ9( z!w`&EHDSlKe(ky&Z$XVgFEg4Ih19`jV)k`fDQwZQ-LatKUcfNRNzu!)2%jUE#sHs4 zudI#un~vh`O+34WDkb2l`T)KX5w`iJK|7MRR;IT{%iu05)Y@#s)uGM9seV|Z?4mXX z36toCs;dirjB>&VrJczSHX_bqYt~%;-v8%0r}(-jM)ZgT6|@sf?eK<6<6*ANA{Y(B zShnWdSd8j?`Dd)1r#)OW7#WeMS4eY4wN({I zYW7DYk?ow(md9QW@yo~6&(_c!nZi@w(>`~A9@EE#l9dd${aGMSHnk>8AJ##dOarcx1TwsXg0$a-UeY|T-_{Sv#De|?3= zmY+>;<+PK`6Do#ix4Py{bU`UGFqIv+F9Nb}5a{T@(ez!JobP%edF96#Pklc(CUuI5 z`n_^$jskb(VYF9$02lhci8cLJZoWB|AkoCm)&a$VuKx=RYl1Uikan7W4aW#yy1#x{ z$XXvWgeEM6z~yb-H1SyVSKi>D{<0JFuqb@oLGprb|)( z%Xb*GO)p#cjDb#=;~XfffiF1!-#v9&rLbfJg$7z>&GQO?Gd|yd(Nq<+fYtIgU;Yo6 CuPw6x literal 0 HcmV?d00001 diff --git a/src/drivers/webextension/images/icon_16.png b/src/drivers/webextension/images/icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..31e9b7c864d80adbd96c9aa8f817c54baab81c89 GIT binary patch literal 555 zcmV+`0@VG9P)`dfWg20k}y- zK~y-)m6Od&lu;PPf6sky<2y<-ZcIg6sDUJeE2U)%>0?>M5LiY*&?ZP}=f;XzC?dKk zYSXR>(>5WIgtV}Kp^X$|j!_O_R`a3nd!II=`7w^;S)O~&S)6+wVIQ2~nIDNB9acxP zsdBg8t|fk!(n%@uLS(Wd^vnxXP8F#|C(`>acWM5>fOGzfe1jYcZ>$xirH0(qGW<4GT|b+S*9><6|taZQ@9%bAm@J5WZUsg z4FDUOCmk%1b!$5^6#@ZhroS7J6wvzrtAu*ng2D1V_(Vv!-Mpe|ZloEV*&5Ob?!XFU~e6%*Mp#xuk730;hj zRyjP%rDwU;Y@jtTV|?8xt@)WXxL7iUGLUdpj}CKrvS837bVRL4k3hv=`kzAO8vYO`x_@Xw||y-d%XYv002ovPDHLkV1i$u9k~Di literal 0 HcmV?d00001 diff --git a/src/drivers/webextension/images/icon_32.png b/src/drivers/webextension/images/icon_32.png new file mode 100644 index 0000000000000000000000000000000000000000..cc9d30b15574ea8423c0f1a9ba3016e55a7ed1c6 GIT binary patch literal 1046 zcmV+x1nK*UP)>Vlu$Kl)EJ{kF3&u}y0#ZBx^70UHa#y1?Dtd*}EdMcN{}>_YoJ z&YU^lH)qbAIU{6=D)t8b@Wc*LS?SqpYL%gw1_+YbQhb(=PjV3u;W@h{TNtF zZa5~s)ODt64$n4e&7NrO58~ONh5AN9lDsx8QPTtGAI<)LX;N|}lmlKpP zCFnoZ8GCf(5&;#V&u;>IH?WGuML+O(ms|Vow1BC*{Weg@<$({pvefk(r83;HOI5dU zWzcP@66Z=miCbN6%}ZkBVPN<&QYS>@`PGRdwbMyh6>g9EN-MBtak-_>Sf;Lf;*no2 zr~~^ZYn|2Zk^5D(0Z1(dwSm^8>)myp9KfDw&_jnKa5^d2TExu@+^51m5u*~#-=!M3lV!+PUNnM<9K}})jdx+jIz<8Y2S1N zmb10Oh}8*l-+u|J#LO@$5-hQbka-tIWyBxC@%r%bW=<3!tdCe_;h09hk*KxxHB_FP z>tV{50-|{<2}wal)Sjjo^iB{rf2A|sZ-E5TJPqM*P9F zn#4&Rn~~*nqTSN`YKNW2=j@0|*#3ZsX#t8Cs0K}<@`6Lt8yUA`*YV6X0SSBrQk^6D zucFpt{p>uNO;;>YwrxiAbs#wJLLmA9h6ab`$(-8qVjAwS#A*XpWJrF_s6IJJ^H=E% z?M%G=P47^IRUcx>Rlq%IHr|6$F+lTAi}E!q;n{T}ybl!3a<^Y~=x>BC{7n62^*Jr2 zq0|$5H)z?^&>2f*!x@^so3AfB8v&#AjxMLNC1EAPA`Ar7RG+5#cs9QN0YTDlwlpRX QV*mgE07*qoM6N<$f>LAVi2wiq literal 0 HcmV?d00001 diff --git a/src/drivers/webextension/images/icon_38.png b/src/drivers/webextension/images/icon_38.png new file mode 100644 index 0000000000000000000000000000000000000000..95f220ff36a7fd4f3a6ef38fc95b3049a012abd6 GIT binary patch literal 1218 zcmV;z1U>tSP)Mfq}ddwlO!tV!lI%{NJ1Z^WyN+kXU^p|=iS}j_dNZPSkC3%>)nUXpXWU1 zd4K1;&(}G^5XjxU9k?zu;aQMe$99)|++Ea~tW8PQ!0cdcj)U$};m!*~e~QYI1M$L7 zM-41LR9mX(8^G9pQ;O)OaLlZ&p{RGnz!vafdQZrE6ZFw5=_!2cs8#$)yx_#JgXN{| zxkrrb1g831p`9YK;y`R+^)P{{v!)V0=;CpkR5awTr~)7KBVjMhv2x&&1BG zIl{8O%fNEkotmC%y^P9AMn?`CSX|$;@VAR#`DuIa!mv{zhtU+F17vw!tZiYY7TKlm%8_ysHG!*RLV2L#}xD%fHdMw?LG4fbpXQI$+ zio8qG#hldLlPz_2fX*CYs751FQXeb)YQU0IA1i9??HTtF@FL)iND@=CKM)Mw@n0wa zr@!LoJm_Zkto~uF9X^-sOba`bGCYxo;gtIa?OyA2;wA$8^}m^a3Qvc7t=XT6_w_OI7McEunb=Esg zt9t@7kf(@NVOSQWGA0qqQnBO(l(kptB7%}eBru(zq+Mk}^f>AMN{81rnCf?;_9BT$ z`99zge-*>xdgms_+gS-g#Y`7b_ZWQ|_q(LU4wIXTkPig>xa>J|mN@4bBbiuY4Wg~I zxlvl&U+{2!$AyY)6@VLg>Q-B17s%~?SEopjEGLIFD`UX!UQSgBtIOt#uaVlPuu_z;|hKB!G=WJh4-1 zG-?*N@aot7Ck9MRU3}BoN#y+u!iq4{&t=455^-=qYb_1Vk5X~`s_vB$*33f84j_Ao z5`PSpXQI6FTGAKZELKkt(QN_?l7#vZ)#50X-w)~AMK}u4cT8hORksxg0Iik= gA8w-JSh9Bi2Ipg#T)U;h&j0`b07*qoM6N<$f+qh`6#xJL literal 0 HcmV?d00001 diff --git a/src/drivers/webextension/images/icons/.gitkeep b/src/drivers/webextension/images/icons/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/drivers/webextension/images/icons/converted/.gitkeep b/src/drivers/webextension/images/icons/converted/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/src/drivers/webextension/images/logo-white.svg b/src/drivers/webextension/images/logo-white.svg new file mode 100644 index 000000000..bfbecc41b --- /dev/null +++ b/src/drivers/webextension/images/logo-white.svg @@ -0,0 +1,29 @@ + + + + Logo-White + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/drivers/webextension/images/twitter.png b/src/drivers/webextension/images/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..e20284ddf2ce763c5a010720c901f29c7900a2db GIT binary patch literal 373 zcmV-*0gC>KP)s@kGHYL0?0{qm%q#>WXQZbUm9^n<-RAM!}WK( zy^YUyphXfm4FF2=pL_c=D&XknL)bLG|M}M*XCeZHx { + const apiMetadata = { + "alarms": { + "clear": { + "minArgs": 0, + "maxArgs": 1 + }, + "clearAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "bookmarks": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "export": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getChildren": { + "minArgs": 1, + "maxArgs": 1 + }, + "getRecent": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTree": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSubTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "import": { + "minArgs": 0, + "maxArgs": 0 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeTree": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "browserAction": { + "getBadgeBackgroundColor": { + "minArgs": 1, + "maxArgs": 1 + }, + "getBadgeText": { + "minArgs": 1, + "maxArgs": 1 + }, + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "commands": { + "getAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "contextMenus": { + "update": { + "minArgs": 2, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeAll": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "cookies": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAllCookieStores": { + "minArgs": 0, + "maxArgs": 0 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "downloads": { + "download": { + "minArgs": 1, + "maxArgs": 1 + }, + "cancel": { + "minArgs": 1, + "maxArgs": 1 + }, + "erase": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFileIcon": { + "minArgs": 1, + "maxArgs": 2 + }, + "open": { + "minArgs": 1, + "maxArgs": 1 + }, + "pause": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeFile": { + "minArgs": 1, + "maxArgs": 1 + }, + "resume": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "extension": { + "isAllowedFileSchemeAccess": { + "minArgs": 0, + "maxArgs": 0 + }, + "isAllowedIncognitoAccess": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "history": { + "addUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "getVisits": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "deleteRange": { + "minArgs": 1, + "maxArgs": 1 + }, + "deleteUrl": { + "minArgs": 1, + "maxArgs": 1 + }, + "search": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "i18n": { + "detectLanguage": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAcceptLanguages": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "idle": { + "queryState": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "management": { + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getSelf": { + "minArgs": 0, + "maxArgs": 0 + }, + "uninstallSelf": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "notifications": { + "clear": { + "minArgs": 1, + "maxArgs": 1 + }, + "create": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPermissionLevel": { + "minArgs": 0, + "maxArgs": 0 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + }, + "pageAction": { + "getPopup": { + "minArgs": 1, + "maxArgs": 1 + }, + "getTitle": { + "minArgs": 1, + "maxArgs": 1 + }, + "hide": { + "minArgs": 0, + "maxArgs": 0 + }, + "setIcon": { + "minArgs": 1, + "maxArgs": 1 + }, + "show": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "runtime": { + "getBackgroundPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "getBrowserInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "getPlatformInfo": { + "minArgs": 0, + "maxArgs": 0 + }, + "openOptionsPage": { + "minArgs": 0, + "maxArgs": 0 + }, + "requestUpdateCheck": { + "minArgs": 0, + "maxArgs": 0 + }, + "sendMessage": { + "minArgs": 1, + "maxArgs": 3 + }, + "sendNativeMessage": { + "minArgs": 2, + "maxArgs": 2 + }, + "setUninstallURL": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "storage": { + "local": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "managed": { + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + } + }, + "sync": { + "clear": { + "minArgs": 0, + "maxArgs": 0 + }, + "get": { + "minArgs": 0, + "maxArgs": 1 + }, + "getBytesInUse": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "set": { + "minArgs": 1, + "maxArgs": 1 + } + } + }, + "tabs": { + "create": { + "minArgs": 1, + "maxArgs": 1 + }, + "captureVisibleTab": { + "minArgs": 0, + "maxArgs": 2 + }, + "detectLanguage": { + "minArgs": 0, + "maxArgs": 1 + }, + "duplicate": { + "minArgs": 1, + "maxArgs": 1 + }, + "executeScript": { + "minArgs": 1, + "maxArgs": 2 + }, + "get": { + "minArgs": 1, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 0 + }, + "getZoom": { + "minArgs": 0, + "maxArgs": 1 + }, + "getZoomSettings": { + "minArgs": 0, + "maxArgs": 1 + }, + "highlight": { + "minArgs": 1, + "maxArgs": 1 + }, + "insertCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "move": { + "minArgs": 2, + "maxArgs": 2 + }, + "reload": { + "minArgs": 0, + "maxArgs": 2 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "query": { + "minArgs": 1, + "maxArgs": 1 + }, + "removeCSS": { + "minArgs": 1, + "maxArgs": 2 + }, + "sendMessage": { + "minArgs": 2, + "maxArgs": 3 + }, + "setZoom": { + "minArgs": 1, + "maxArgs": 2 + }, + "setZoomSettings": { + "minArgs": 1, + "maxArgs": 2 + }, + "update": { + "minArgs": 1, + "maxArgs": 2 + } + }, + "webNavigation": { + "getAllFrames": { + "minArgs": 1, + "maxArgs": 1 + }, + "getFrame": { + "minArgs": 1, + "maxArgs": 1 + } + }, + "webRequest": { + "handlerBehaviorChanged": { + "minArgs": 0, + "maxArgs": 0 + } + }, + "windows": { + "create": { + "minArgs": 0, + "maxArgs": 1 + }, + "get": { + "minArgs": 1, + "maxArgs": 2 + }, + "getAll": { + "minArgs": 0, + "maxArgs": 1 + }, + "getCurrent": { + "minArgs": 0, + "maxArgs": 1 + }, + "getLastFocused": { + "minArgs": 0, + "maxArgs": 1 + }, + "remove": { + "minArgs": 1, + "maxArgs": 1 + }, + "update": { + "minArgs": 2, + "maxArgs": 2 + } + } + }; + + /** + * A WeakMap subclass which creates and stores a value for any key which does + * not exist when accessed, but behaves exactly as an ordinary WeakMap + * otherwise. + * + * @param {function} createItem + * A function which will be called in order to create the value for any + * key which does not exist, the first time it is accessed. The + * function receives, as its only argument, the key being created. + */ + class DefaultWeakMap extends WeakMap { + constructor(createItem, items = undefined) { + super(items); + this.createItem = createItem; + } + + get(key) { + if (!this.has(key)) { + this.set(key, this.createItem(key)); + } + + return super.get(key); + } + } + + /** + * Returns true if the given object is an object with a `then` method, and can + * therefore be assumed to behave as a Promise. + * + * @param {*} value The value to test. + * @returns {boolean} True if the value is thenable. + */ + const isThenable = value => { + return value && typeof value === "object" && typeof value.then === "function"; + }; + + /** + * Creates and returns a function which, when called, will resolve or reject + * the given promise based on how it is called: + * + * - If, when called, `chrome.runtime.lastError` contains a non-null object, + * the promise is rejected with that value. + * - If the function is called with exactly one argument, the promise is + * resolved to that value. + * - Otherwise, the promise is resolved to an array containing all of the + * function's arguments. + * + * @param {object} promise + * An object containing the resolution and rejection functions of a + * promise. + * @param {function} promise.resolve + * The promise's resolution function. + * @param {function} promise.rejection + * The promise's rejection function. + * + * @returns {function} + * The generated callback function. + */ + const makeCallback = promise => { + return (...callbackArgs) => { + if (chrome.runtime.lastError) { + promise.reject(chrome.runtime.lastError); + } else if (callbackArgs.length === 1) { + promise.resolve(callbackArgs[0]); + } else { + promise.resolve(callbackArgs); + } + }; + }; + + /** + * Creates a wrapper function for a method with the given name and metadata. + * + * @param {string} name + * The name of the method which is being wrapped. + * @param {object} metadata + * Metadata about the method being wrapped. + * @param {integer} metadata.minArgs + * The minimum number of arguments which must be passed to the + * function. If called with fewer than this number of arguments, the + * wrapper will raise an exception. + * @param {integer} metadata.maxArgs + * The maximum number of arguments which may be passed to the + * function. If called with more than this number of arguments, the + * wrapper will raise an exception. + * + * @returns {function(object, ...*)} + * The generated wrapper function. + */ + const wrapAsyncFunction = (name, metadata) => { + const pluralizeArguments = (numArgs) => numArgs == 1 ? "argument" : "arguments"; + + return function asyncFunctionWrapper(target, ...args) { + if (args.length < metadata.minArgs) { + throw new Error(`Expected at least ${metadata.minArgs} ${pluralizeArguments(metadata.minArgs)} for ${name}(), got ${args.length}`); + } + + if (args.length > metadata.maxArgs) { + throw new Error(`Expected at most ${metadata.maxArgs} ${pluralizeArguments(metadata.maxArgs)} for ${name}(), got ${args.length}`); + } + + return new Promise((resolve, reject) => { + target[name](...args, makeCallback({resolve, reject})); + }); + }; + }; + + /** + * Wraps an existing method of the target object, so that calls to it are + * intercepted by the given wrapper function. The wrapper function receives, + * as its first argument, the original `target` object, followed by each of + * the arguments passed to the orginal method. + * + * @param {object} target + * The original target object that the wrapped method belongs to. + * @param {function} method + * The method being wrapped. This is used as the target of the Proxy + * object which is created to wrap the method. + * @param {function} wrapper + * The wrapper function which is called in place of a direct invocation + * of the wrapped method. + * + * @returns {Proxy} + * A Proxy object for the given method, which invokes the given wrapper + * method in its place. + */ + const wrapMethod = (target, method, wrapper) => { + return new Proxy(method, { + apply(targetMethod, thisObj, args) { + return wrapper.call(thisObj, target, ...args); + }, + }); + }; + + let hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty); + + /** + * Wraps an object in a Proxy which intercepts and wraps certain methods + * based on the given `wrappers` and `metadata` objects. + * + * @param {object} target + * The target object to wrap. + * + * @param {object} [wrappers = {}] + * An object tree containing wrapper functions for special cases. Any + * function present in this object tree is called in place of the + * method in the same location in the `target` object tree. These + * wrapper methods are invoked as described in {@see wrapMethod}. + * + * @param {object} [metadata = {}] + * An object tree containing metadata used to automatically generate + * Promise-based wrapper functions for asynchronous. Any function in + * the `target` object tree which has a corresponding metadata object + * in the same location in the `metadata` tree is replaced with an + * automatically-generated wrapper function, as described in + * {@see wrapAsyncFunction} + * + * @returns {Proxy} + */ + const wrapObject = (target, wrappers = {}, metadata = {}) => { + let cache = Object.create(null); + + let handlers = { + has(target, prop) { + return prop in target || prop in cache; + }, + + get(target, prop, receiver) { + if (prop in cache) { + return cache[prop]; + } + + if (!(prop in target)) { + return undefined; + } + + let value = target[prop]; + + if (typeof value === "function") { + // This is a method on the underlying object. Check if we need to do + // any wrapping. + + if (typeof wrappers[prop] === "function") { + // We have a special-case wrapper for this method. + value = wrapMethod(target, target[prop], wrappers[prop]); + } else if (hasOwnProperty(metadata, prop)) { + // This is an async method that we have metadata for. Create a + // Promise wrapper for it. + let wrapper = wrapAsyncFunction(prop, metadata[prop]); + value = wrapMethod(target, target[prop], wrapper); + } else { + // This is a method that we don't know or care about. Return the + // original method, bound to the underlying object. + value = value.bind(target); + } + } else if (typeof value === "object" && value !== null && + (hasOwnProperty(wrappers, prop) || + hasOwnProperty(metadata, prop))) { + // This is an object that we need to do some wrapping for the children + // of. Create a sub-object wrapper for it with the appropriate child + // metadata. + value = wrapObject(value, wrappers[prop], metadata[prop]); + } else { + // We don't need to do any wrapping for this property, + // so just forward all access to the underlying object. + Object.defineProperty(cache, prop, { + configurable: true, + enumerable: true, + get() { + return target[prop]; + }, + set(value) { + target[prop] = value; + }, + }); + + return value; + } + + cache[prop] = value; + return value; + }, + + set(target, prop, value, receiver) { + if (prop in cache) { + cache[prop] = value; + } else { + target[prop] = value; + } + return true; + }, + + defineProperty(target, prop, desc) { + return Reflect.defineProperty(cache, prop, desc); + }, + + deleteProperty(target, prop) { + return Reflect.deleteProperty(cache, prop); + }, + }; + + return new Proxy(target, handlers); + }; + + /** + * Creates a set of wrapper functions for an event object, which handles + * wrapping of listener functions that those messages are passed. + * + * A single wrapper is created for each listener function, and stored in a + * map. Subsequent calls to `addListener`, `hasListener`, or `removeListener` + * retrieve the original wrapper, so that attempts to remove a + * previously-added listener work as expected. + * + * @param {DefaultWeakMap} wrapperMap + * A DefaultWeakMap object which will create the appropriate wrapper + * for a given listener function when one does not exist, and retrieve + * an existing one when it does. + * + * @returns {object} + */ + const wrapEvent = wrapperMap => ({ + addListener(target, listener, ...args) { + target.addListener(wrapperMap.get(listener), ...args); + }, + + hasListener(target, listener) { + return target.hasListener(wrapperMap.get(listener)); + }, + + removeListener(target, listener) { + target.removeListener(wrapperMap.get(listener)); + }, + }); + + const onMessageWrappers = new DefaultWeakMap(listener => { + if (typeof listener !== "function") { + return listener; + } + + /** + * Wraps a message listener function so that it may send responses based on + * its return value, rather than by returning a sentinel value and calling a + * callback. If the listener function returns a Promise, the response is + * sent when the promise either resolves or rejects. + * + * @param {*} message + * The message sent by the other end of the channel. + * @param {object} sender + * Details about the sender of the message. + * @param {function(*)} sendResponse + * A callback which, when called with an arbitrary argument, sends + * that value as a response. + * @returns {boolean} + * True if the wrapped listener returned a Promise, which will later + * yield a response. False otherwise. + */ + return function onMessage(message, sender, sendResponse) { + let result = listener(message, sender); + + if (isThenable(result)) { + result.then(sendResponse, error => { + console.error(error); + sendResponse(error); + }); + + return true; + } else if (result !== undefined) { + sendResponse(result); + } + }; + }); + + const staticWrappers = { + runtime: { + onMessage: wrapEvent(onMessageWrappers), + }, + }; + + return wrapObject(chrome, staticWrappers, apiMetadata); + }; + + this.browser = wrapAPIs(); +} diff --git a/src/drivers/webextension/js/content.js b/src/drivers/webextension/js/content.js new file mode 100644 index 000000000..e394e8eb7 --- /dev/null +++ b/src/drivers/webextension/js/content.js @@ -0,0 +1,61 @@ +(function() { + var c = { + init: function() { + var html = document.documentElement.outerHTML; + + c.log('init'); + + if ( html.length > 50000 ) { + html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length); + } + + browser.runtime.sendMessage({ id: 'analyze', subject: { html: html } }); + + c.getEnvironmentVars(); + }, + + log: function(message) { + browser.runtime.sendMessage({ id: 'log', message: '[ content.js ] ' + message }); + }, + + getEnvironmentVars: function() { + var container, script; + + c.log('getEnvironmentVars'); + + if ( typeof document.documentElement.innerHTML === 'undefined' ) { + return; + } + + try { + container = document.createElement('wappalyzerData'); + + container.setAttribute('id', 'wappalyzerData'); + container.setAttribute('style', 'display: none'); + + script = document.createElement('script'); + + script.setAttribute('id', 'wappalyzerEnvDetection'); + script.setAttribute('src', browser.extension.getURL('js/inject.js')); + + container.addEventListener('wappalyzerEvent', (function(event) { + var environmentVars = event.target.childNodes[0].nodeValue; + + document.documentElement.removeChild(container); + document.documentElement.removeChild(script); + + environmentVars = environmentVars.split(' ').slice(0, 500); + + browser.runtime.sendMessage({ id: 'analyze', subject: { env: environmentVars } }); + }), true); + + document.documentElement.appendChild(container); + document.documentElement.appendChild(script); + } catch(e) { + c.log('Error: ' + e); + } + } + } + + c.init(); +}()); diff --git a/src/drivers/webextension/js/defaults.js b/src/drivers/webextension/js/defaults.js new file mode 100644 index 000000000..3ec2d3e09 --- /dev/null +++ b/src/drivers/webextension/js/defaults.js @@ -0,0 +1,5 @@ +var defaults = { + autoAnalyzeHeaders: 0, + upgradeMessage: 1, + tracking: 1 +}; diff --git a/src/drivers/webextension/js/driver.js b/src/drivers/webextension/js/driver.js new file mode 100644 index 000000000..eeda6b920 --- /dev/null +++ b/src/drivers/webextension/js/driver.js @@ -0,0 +1,306 @@ +/** + * Chrome driver + */ + +(function() { + if ( wappalyzer == null ) { + return; + } + + var w = wappalyzer, + firstRun = false, + upgraded = false, + tab, + tabCache = {}, + headersCache = {}; + + w.driver = { + timeout: 1000, + + /** + * Log messages to console + */ + log: function(args) { + console.log('[wappalyzer ' + args.type + '] ' + args.message); + }, + + /** + * Initialize + */ + init: function() { + w.log('init'); + + // Load apps.json + var xhr = new XMLHttpRequest(); + + xhr.open('GET', 'apps.json', true); + + xhr.overrideMimeType('application/json'); + + xhr.onload = function() { + var json = JSON.parse(xhr.responseText); + + w.categories = json.categories; + w.apps = json.apps; + }; + + xhr.send(null); + + // Version check + try { + var version = browser.app.getDetails().version; + + if ( localStorage['version'] == null ) { + firstRun = true; + + // Set defaults + for ( option in defaults ) { + localStorage[option] = defaults[option]; + } + } else if ( version !== localStorage['version'] && parseInt(localStorage['upgradeMessage'], 10) ) { + upgraded = true; + } + + localStorage['version'] = version; + } catch(e) { } + + browser.runtime.onMessage.addListener(function(message, sender, sendResponse) { + var + hostname, + a = document.createElement('a'); + + if ( typeof message.id != 'undefined' ) { + w.log('message: ' + message.id); + + switch ( message.id ) { + case 'log': + w.log(message.message); + + break; + case 'analyze': + tab = sender.tab; + + a.href = tab.url.replace(/#.*$/, ''); + + hostname = a.hostname; + + if ( headersCache[a.href] !== undefined ) { + message.subject.headers = headersCache[a.href]; + } + + w.analyze(hostname, a.href, message.subject); + + break; + case 'ad_log': + w.adCache.push(message.subject); + + break; + case 'get_apps': + sendResponse({ + tabCache: tabCache[message.tab.id], + apps: w.apps, + categories: w.categories + }); + + break; + } + } + }); + + browser.tabs.query({}).then(function(tabs) { + tabs.forEach(function(tab) { + if ( tab.url.match(/^https?:\/\//) ) { + browser.tabs.executeScript(tab.id, { file: 'js/content.js' }); + } + }) + }); + + browser.tabs.onRemoved.addListener(function(tabId) { + w.log('remove tab'); + + tabCache[tabId] = null; + }); + + // Live intercept headers using webRequest API + browser.webRequest.onCompleted.addListener(function(details) { + var responseHeaders = {}; + + if ( details.responseHeaders ) { + var uri = details.url.replace(/#.*$/, ''); // Remove hash + + details.responseHeaders.forEach(function(header) { + responseHeaders[header.name.toLowerCase()] = header.value || '' + header.binaryValue; + }); + + if ( headersCache.length > 50 ) { + headersCache = {}; + } + + if ( /text\/html/.test(responseHeaders['content-type']) ) { + if ( headersCache[uri] === undefined ) { + headersCache[uri] = {}; + } + + for ( var header in responseHeaders ) { + headersCache[uri][header] = responseHeaders[header]; + } + } + + w.log(JSON.stringify({ uri: uri, headers: responseHeaders })); + } + }, { urls: [ 'http://*/*', 'https://*/*' ], types: [ 'main_frame' ] }, [ 'responseHeaders' ]); + + if ( firstRun ) { + w.driver.goToURL({ url: w.config.websiteURL + 'installed', medium: 'install' }); + + firstRun = false; + } + + if ( upgraded ) { + w.driver.goToURL({ url: w.config.websiteURL + 'upgraded', medium: 'upgrade', background: true }); + + upgraded = false; + } + }, + + goToURL: function(args) { + var url = args.url + ( typeof args.medium === 'undefined' ? '' : '?pk_campaign=chrome&pk_kwd=' + args.medium); + + browser.tabs.create({ url: url, active: args.background === undefined || !args.background }); + }, + + /** + * Display apps + */ + displayApps: function() { + var + url = tab.url.replace(/#.*$/, ''), + count = w.detected[url] ? Object.keys(w.detected[url]).length.toString() : '0'; + + if ( tabCache[tab.id] == null ) { + tabCache[tab.id] = { + count: 0, + appsDetected: [] + }; + } + + tabCache[tab.id].count = count; + tabCache[tab.id].appsDetected = w.detected[url]; + + if ( count > 0 ) { + // Find the main application to display + var i, appName, found = false; + + w.driver.categoryOrder.forEach(function(match) { + for ( appName in w.detected[url] ) { + w.apps[appName].cats.forEach(function(cat) { + var icon = w.apps[appName].icon; + + if ( cat == match && !found ) { + if ( /\.svg$/i.test(icon) ) { + icon = 'converted/' + icon + '.png'; + } + + browser.pageAction.setIcon({ tabId: tab.id, path: 'images/icons/' + icon }); + + found = true; + } + }); + } + }); + + if ( typeof chrome !== 'undefined' ) { + // Browser polyfill doesn't seem to work here + chrome.pageAction.show(tab.id); + } else { + browser.pageAction.show(tab.id); + } + }; + }, + + /** + * Anonymously track detected applications for research purposes + */ + ping: function() { + if ( Object.keys(w.ping.hostnames).length && parseInt(localStorage['tracking'], 10) ) { + w.driver.post('http://ping.wappalyzer.com/v2/', w.ping); + + w.log('w.driver.ping: ' + JSON.stringify(w.ping)); + + w.ping = { hostnames: {} }; + + w.driver.post('https://ad.wappalyzer.com/log/wp/', w.adCache); + + w.adCache = []; + } + }, + + /** + * Make POST request + */ + post: function(url, data) { + var xhr = new XMLHttpRequest(); + + xhr.open('POST', url, true); + + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + + xhr.onreadystatechange = function(e) { + if ( xhr.readyState == 4 ) { + w.log('w.driver.post: status ' + xhr.status + ' (' + url + ')'); + } + }; + + xhr.send('json=' + encodeURIComponent(JSON.stringify(data))); + }, + + categoryOrder: [ // Used to pick the main application + 1, // CMS + 11, // Blog + 6, // Web Shop + 2, // Message Board + 51, // Landing Page Builder + 8, // Wiki + 13, // Issue Tracker + 30, // Web Mail + 18, // Web Framework + 21, // LMS + 7, // Photo Gallery + 38, // Media Server + 3, // Database Manager + 34, // Database + 4, // Documentation Tool + 9, // Hosting Panel + 29, // Search Engine + 12, // JavaScript Framework + 26, // Mobile Framework + 25, // JavaScript Graphics + 22, // Web Server + 27, // Programming Language + 28, // Operating System + 15, // Comment System + 20, // Editor + 41, // Payment Processor + 10, // Analytics + 32, // Marketing Automation + 31, // CDN + 23, // Cache Tool + 17, // Font Script + 24, // Rich Text Editor + 35, // Map + 5, // Widget + 14, // Video Player + 16, // Captcha + 33, // Web Server Extension + 37, // Network Device + 39, // Webcam + 40, // Printer + 36, // Advertising Network + 42, // Tag Managers + 43, // Paywalls + 19 // Miscellaneous + ] + }; + + w.init(); +}()); diff --git a/src/drivers/webextension/js/i18n.js b/src/drivers/webextension/js/i18n.js new file mode 100644 index 000000000..bf9c7e2bc --- /dev/null +++ b/src/drivers/webextension/js/i18n.js @@ -0,0 +1,11 @@ +document.addEventListener('DOMContentLoaded', function() { + var + i, value, + nodes = document.getElementsByTagName('*'); + + for ( i = 0; i < nodes.length; i ++ ) { + if ( attr = nodes[i].dataset.i18n ) { + nodes[i].innerHTML = browser.i18n.getMessage(attr); + } + } +}); diff --git a/src/drivers/webextension/js/inject.js b/src/drivers/webextension/js/inject.js new file mode 100644 index 000000000..fd734182e --- /dev/null +++ b/src/drivers/webextension/js/inject.js @@ -0,0 +1,14 @@ +(function() { + try { + var i, environmentVars, e = document.createEvent('Events'); + + e.initEvent('wappalyzerEvent', true, false); + + for ( i in window ) { + environmentVars += i + ' '; + } + + document.getElementById('wappalyzerData').appendChild(document.createComment(environmentVars)); + document.getElementById('wappalyzerData').dispatchEvent(e); + } catch(e) { } +}()); diff --git a/src/drivers/webextension/js/options.js b/src/drivers/webextension/js/options.js new file mode 100644 index 000000000..71974de97 --- /dev/null +++ b/src/drivers/webextension/js/options.js @@ -0,0 +1,54 @@ +document.addEventListener('DOMContentLoaded', function() { + var d = document; + + var options = { + opts: defaults, + + init: function() { + options.load(); + + d.getElementById('github' ).addEventListener('click', function() { window.open(wappalyzer.config.githubURL); }); + d.getElementById('twitter' ).addEventListener('click', function() { window.open(wappalyzer.config.twitterURL); }); + d.getElementById('wappalyzer').addEventListener('click', function() { window.open(wappalyzer.config.websiteURL + '?pk_campaign=chrome&pk_kwd=options'); }); + + d.getElementById('options-save').addEventListener('click', options.save); + }, + + load: function() { + var option, value; + + for ( option in options.opts ) { + if ( value = localStorage[option] ) { + options.opts[option] = value; + } + } + + if ( parseInt(options.opts.upgradeMessage) ) { + d.getElementById('option-upgrade-message').setAttribute('checked', 'checked'); + } + + if ( parseInt(options.opts.tracking) ) { + d.getElementById('option-tracking').setAttribute('checked', 'checked'); + } + }, + + save: function() { + var option; + + options.opts.upgradeMessage = d.getElementById('option-upgrade-message').checked ? 1 : 0; + options.opts.tracking = d.getElementById('option-tracking' ).checked ? 1 : 0; + + for ( option in options.opts ) { + localStorage[option] = options.opts[option]; + } + + d.getElementById('options-saved').style.display = 'inline'; + + setTimeout(function(){ + d.getElementById('options-saved').style.display = 'none'; + }, 2000); + } + }; + + options.init(); +}); diff --git a/src/drivers/webextension/js/popup.js b/src/drivers/webextension/js/popup.js new file mode 100644 index 000000000..2b0a0e84d --- /dev/null +++ b/src/drivers/webextension/js/popup.js @@ -0,0 +1,68 @@ +document.addEventListener('DOMContentLoaded', function() { + var + slugify, popup, + d = document, + detectedApps = d.getElementById('detected-apps'); + + slugify = function(string) { + return string.toLowerCase().replace(/ /g, '-').replace(/[^\w-]/g, ''); + }; + + popup = { + init: function() { + + d.getElementById('options').addEventListener('click', function() { + window.open(browser.extension.getURL('options.html')); + }); + + browser.tabs.query({ active: true }).then(function(tabs) { + if ( tabs[0].url.match(/https?:\/\//) ) { + detectedApps.innerHTML = '
' + browser.i18n.getMessage('noAppsDetected') + '
'; + } else { + detectedApps.innerHTML = '
' + browser.i18n.getMessage('nothingToDo') + '
'; + } + }); + + popup.displayApps(); + }, + + displayApps: function() { + var appName, confidence, version; + + browser.tabs.query({ active: true }).then(function(tabs) { + browser.runtime.sendMessage({ id: 'get_apps', tab: tabs[0] }, function(response) { + if ( response.tabCache && response.tabCache.count > 0 ) { + detectedApps.innerHTML = ''; + + for ( appName in response.tabCache.appsDetected ) { + confidence = response.tabCache.appsDetected[appName].confidenceTotal; + version = response.tabCache.appsDetected[appName].version; + + html = + ''; + + detectedApps.innerHTML = detectedApps.innerHTML + html; + } + } + }); + }); + } + }; + + popup.init(); +}); diff --git a/src/drivers/webextension/manifest.json b/src/drivers/webextension/manifest.json new file mode 100644 index 000000000..37879632e --- /dev/null +++ b/src/drivers/webextension/manifest.json @@ -0,0 +1,48 @@ +{ "name": "Wappalyzer", + "homepage_url": "https://wappalyzer.com/", + "description": "Identify web technologies", + "version": "3", + "default_locale": "en", + "manifest_version": 2, + "icons": { + "16": "images/icon_16.png", + "32": "images/icon_32.png", + "128": "images/icon_128.png" + }, + "page_action": { + "default_icon": "images/icon_32.png", + "default_title": "Wappalyzer", + "default_popup": "popup.html" + }, + "background": { + "page": "background.html" + }, + "content_scripts": [ { + "matches": [ "http://*/*", "https://*/*" ], + "js": [ + "js/browser-polyfill.js", + "js/content.js" + ], + "run_at": "document_idle" + }, { + "matches": [ "http://*/*", "https://*/*" ], + "js": [ + "js/browser-polyfill.js", + "js/iframe.js" + ], + "run_at": "document_start", + "all_frames": true + } ], + "web_accessible_resources": [ + "js/inject.js" + ], + "options_page": "options.html", + "permissions": [ + "tabs", + "webRequest", + "webNavigation", + "http://*/*", + "https://*/*" + ], + "content_security_policy": "script-src 'self'; object-src 'self'" +} diff --git a/src/drivers/webextension/options.html b/src/drivers/webextension/options.html new file mode 100644 index 000000000..bd7f606d0 --- /dev/null +++ b/src/drivers/webextension/options.html @@ -0,0 +1,49 @@ + + + + + + + Wappalyzer options + + + + + + + + + + + + +
+
+ +
+
+ +
+
+

Options

+ +

+ + +

+ +

+ Saved +

+ +
+

+ +

+
+
+
+ + diff --git a/src/drivers/webextension/popup.html b/src/drivers/webextension/popup.html new file mode 100644 index 000000000..35c1ea3e5 --- /dev/null +++ b/src/drivers/webextension/popup.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + +
+ + + +