├── .eslintrc.json ├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .stylelintrc ├── COPYING ├── README.md ├── crowdin.yml ├── manifest.js ├── package.json ├── privacy-policy.md ├── src ├── _locales │ ├── de │ │ └── messages.json │ ├── en_US │ │ └── messages.json │ ├── fr │ │ └── messages.json │ └── ru │ │ └── messages.json ├── agent-block.json ├── css │ ├── main.css │ └── pm-tab-panel.css ├── img │ ├── logo-16x16.png │ ├── logo-48x48.png │ └── logo.png ├── js │ ├── back.js │ ├── common.js │ ├── data.js │ └── ui │ │ ├── helpers │ │ ├── actionListener.js │ │ ├── settingList.js │ │ └── utils.js │ │ ├── index.js │ │ └── tabs │ │ ├── cookies.js │ │ ├── main.js │ │ └── network.js ├── manifest.json └── popup.html ├── test ├── common.js ├── manifest.js └── puppeteer │ ├── cookie.js │ ├── dataDeletion.js │ ├── main.js │ └── network.js └── webpack.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "es6": true, 5 | "node": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2018 9 | }, 10 | "globals": { 11 | "browser": true, 12 | "it": true, 13 | "describe": true, 14 | "after": true, 15 | "before": true, 16 | "window": true, 17 | "self": true, 18 | "require": true, 19 | "module": true, 20 | "__dirname": true, 21 | "process": true, 22 | "chrome": true, 23 | "getStorage": true, 24 | "setStorage": true, 25 | "startCollectingRequests": true, 26 | "addBlockAgentListener": true, 27 | "browsingData": true, 28 | "deleteCookies": true, 29 | "addRequestListener": true, 30 | "removeRequestListener": true, 31 | "updateRequestObj": true, 32 | "collectedRequests": true, 33 | "stopCollectingRequests": true, 34 | "removeBlockAgentListener": true, 35 | "additionalPermission": true, 36 | "document": true, 37 | "registerActionListener": true, 38 | "createBasicSettingObj": true, 39 | "addSettingItem": true, 40 | "TableList": true, 41 | "alert": true, 42 | "turnSwitchesOff": true, 43 | "result": true, 44 | "Event": true, 45 | "switchEvent": true, 46 | "closeDialog": true, 47 | "checkSettingState": true, 48 | "privacyData": true, 49 | "ext": true 50 | }, 51 | "rules": { 52 | "no-unused-vars": "off", 53 | "no-constant-condition": "off", 54 | "no-trailing-spaces": ["error", { "ignoreComments": true }], 55 | "indent": ["error", 2, { 56 | "FunctionDeclaration": { 57 | "parameters": "first" 58 | }, 59 | "SwitchCase": 1, 60 | "CallExpression": { 61 | "arguments": "first" 62 | }, 63 | "ArrayExpression": "first", 64 | "ObjectExpression": "off" 65 | }], 66 | "space-before-function-paren": ["error", "never"], 67 | "brace-style": ["error", "allman"], 68 | "semi": "error" 69 | } 70 | } -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | - push 5 | 6 | jobs: 7 | run-puppeteer: 8 | name: Run tests 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Setup Node 13 | uses: actions/setup-node@v2 14 | 15 | - name: Prepare dependencies 16 | run: sudo apt-get install xvfb && npm install 17 | 18 | - name: Run tests 19 | run: xvfb-run npm test 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | privacy-manager.zip 2 | .DS_Store 3 | package-lock.json 4 | node_modules 5 | dist 6 | src/css/pm-tab-panel.css 7 | src/src/components.js 8 | -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-recommended", 3 | "rules": { 4 | "no-descending-specificity": null, 5 | "block-closing-brace-newline-after": "always", 6 | "selector-type-no-unknown": [true, { ignoreTypes: ["pm-tab-panel", "pm-tabs", 7 | "pm-tab", "pm-panels", "pm-panel", "pm-toggle", 8 | "pm-dialog", "pm-button"] }] 9 | } 10 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Privacy Manager 2 | 3 | [![Tests](https://github.com/Privacy-Managers/Privacy-Manager/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/Privacy-Managers/Privacy-Manager/actions/workflows/tests.yml) 4 | 5 | ## About 6 | 7 | Privacy Manager is a chrome extension for data transparency and control. 8 | Privacy Manager can help users with: 9 | 10 | - Privacy Management 11 | - Cookie Management 12 | - Browsing data deletion on browser start 13 | - Network monitoring 14 | 15 | Stable version of Privacy Manager can be downloaded from the Chrome Web Store 16 | [here](https://chrome.google.com/webstore/detail/privacy-manager/giccehglhacakcfemddmfhdkahamfcmd). 17 | 18 | ## Installation 19 | 20 | ```bash 21 | npm install 22 | ``` 23 | 24 | ### Setting up development environment 25 | 26 | - Run one of the commands below 27 | ```bash 28 | npm run build:webpack # builds extension in `dist` directory 29 | npm run build:webpack:watch # builds extension and watches for changes 30 | build:webpack:prod # builds production version with minified files 31 | ``` 32 | - Visit `chrome://extensions` in your browser 33 | - Ensure that the **Developer mode** checkbox in the top right-hand corner is 34 | checked 35 | - Click **Load unpacked** button 36 | - Locate and load generated `dist` folder in the repository root directory 37 | 38 | **Note:** When rebuilding the extension changes in background scripts might not 39 | be loaded in the chrome unless actual extension is reloaded in the 40 | `chrome://extensions` page (i.e. By clicking on the reload button). 41 | 42 | ## Testing 43 | 44 | ```bash 45 | npm test # Run all tests 46 | npm run test:puppeteer # Run only puppeteer tests 47 | npm run test:eslint # Run Eslint tests 48 | npm run test:stylelint # Run Style linters 49 | ``` 50 | 51 | ## Publishing 52 | 53 | Command below bundles the extension into `privacy-manager.zip` file: 54 | ```bash 55 | npm run build 56 | ``` 57 | 58 | ## Contribution 59 | 60 | ### Reporting bugs, suggestions and questions 61 | 62 | Use [Github issue tracker](https://github.com/Manvel/Privacy-Manager/issues) for 63 | requesting features, reporting bugs and questions. See [github issues 64 | documentation](https://guides.github.com/features/issues/). 65 | 66 | ### Code contribution 67 | 68 | Code contributions are welcome, you can always consult with me (in issues, or 69 | PRs) when you have a question. If you are developing a new feature, please 70 | consider creating also tests for them when possible. 71 | 72 | ### Translations 73 | 74 | #### Updating existing translation 75 | 76 | Please use [Crowdin project](https://crowdin.com/project/privacy-manager) for 77 | suggesting or improving translation, please note that you might need a crowdin 78 | account for that reason, see [crowdin translation introduction 79 | documentation](https://support.crowdin.com/crowdin-intro/). 80 | 81 | #### Requesting new translation 82 | 83 | In case you can't see the language you want to translate in [Crowdin 84 | project](https://crowdin.com/project/privacy-manager), please use [Github issue 85 | tracker](https://github.com/Manvel/Privacy-Manager/issues) for requesting new 86 | translation language in crowdin. 87 | 88 | ### Thanks to the awesome contributors 89 | 90 | - [@jeroen7s](https://github.com/jeroen7s) 91 | - For [making whitelisting of cookies possible](https://github.com/Manvel/Privacy-Manager/pull/31) 92 | - [@leonid-panich](https://github.com/leonid-panich) 93 | - For helping with Russian Translations 94 | - [@alexdevero](https://github.com/alexdevero) 95 | - For helping identify critical issues - #85, #83 96 | - [Andy K.](https://crowdin.com/profile/AndyKl) 97 | - For helping with German Translations 98 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /src/_locales/en_US/*.json 3 | translation: /src/_locales/%two_letters_code%/%original_file_name% 4 | -------------------------------------------------------------------------------- /manifest.js: -------------------------------------------------------------------------------- 1 | const {version} = require("./package.json"); 2 | 3 | 4 | function transform(content) 5 | { 6 | const manifestJson = JSON.parse(content.toString()); 7 | // Use package version to set manifest one. 8 | manifestJson.version = version; 9 | return Buffer.from(JSON.stringify(manifestJson)); 10 | } 11 | 12 | module.exports = {transform}; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "privacy-manager", 3 | "version": "4.0.0", 4 | "description": "Privacy Manager is a chrome extension for data transparency and control", 5 | "scripts": { 6 | "test": "npm run test:eslint && npm run test:stylelint && npm run test:puppeteer -- --exit", 7 | "test:puppeteer": "npm run build:webpack && mocha test/puppeteer/*.js --timeout 20000", 8 | "test:eslint": "eslint src", 9 | "test:stylelint": "stylelint src/**/*.css", 10 | "build": "npm run build:webpack:prod && cd dist/;zip -r ../privacy-manager.zip .;cd ..", 11 | "import:components": "pm-components --single-bundle --prod --output dist", 12 | "build:webpack": "npm run import:components && webpack --config webpack.config.js", 13 | "build:webpack:watch": "npm run import:components && WATCH=1 webpack --config webpack.config.js", 14 | "build:webpack:prod": "npm run import:components && PROD=1 webpack --config webpack.config.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/Privacy-Managers/Privacy-Manager.git" 19 | }, 20 | "author": "Manvel Saroyan", 21 | "license": "GPL-3.0", 22 | "bugs": { 23 | "url": "https://github.com/Privacy-Managers/Privacy-Manager/issues" 24 | }, 25 | "homepage": "https://chrome.google.com/webstore/detail/privacy-manager/giccehglhacakcfemddmfhdkahamfcmd", 26 | "devDependencies": { 27 | "eslint": "^8.30.0", 28 | "eslint-plugin-import": "^2.26.0", 29 | "eslint-plugin-node": "^11.1.0", 30 | "eslint-plugin-promise": "^6.1.1", 31 | "eslint-plugin-standard": "^4.1.0", 32 | "mocha": "^10.2.0", 33 | "puppeteer": "^18.2.1", 34 | "stylelint": "^14.16.0", 35 | "stylelint-config-recommended": "^9.0.0" 36 | }, 37 | "dependencies": { 38 | "copy-webpack-plugin": "^11.0.0", 39 | "csso": "^5.0.5", 40 | "privacy-manager-components": "^1.0.2", 41 | "webextension-polyfill": "^0.10.0", 42 | "webpack": "^5.75.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /privacy-policy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | ## Privacy Manager extension 4 | 5 | Privacy Manager Extension itself doesn't collect any user data, however your browser might collect some data (for example updates to Privacy Manager for Chrome are handled by the Google Web Store website and are subject to the [Google Privacy Policy](https://policies.google.com/privacy)). 6 | -------------------------------------------------------------------------------- /src/_locales/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "chrome_extension_name": { 3 | "description": "Extension name, used in the Web Store", 4 | "message": "Privacy Manager" 5 | }, 6 | "chrome_extension_description": { 7 | "description": "Extension description, used in the Web Store", 8 | "message": "Datenschutzeinstellungen verwalten, Surf-Daten löschen, Schnellzugriff auf Inkognito-Modus, Cookie-Verwaltung und Netzwerkkontrolle" 9 | }, 10 | "main_tab": { 11 | "description": "Tab label", 12 | "message": "Allgemein" 13 | }, 14 | "cookies_general": { 15 | "description": "General text used in various places", 16 | "message": "Cookies" 17 | }, 18 | "network_tab": { 19 | "description": "Tab label", 20 | "message": "Netzwerk" 21 | }, 22 | "off": { 23 | "description": "Switch's 'off' text", 24 | "message": "Aus" 25 | }, 26 | "on": { 27 | "description": "Switch's 'on' text", 28 | "message": "Ein" 29 | }, 30 | "privacyManagement": { 31 | "description": "Section name", 32 | "message": "Datenschutz-Verwaltung" 33 | }, 34 | "regularSettingChangeIncognito_error": { 35 | "description": "Error thrown in the incognito mode when privacy setting is being modified", 36 | "message": "Reguläre Einstellungen können nicht aus einem inkognitiven Kontext heraus geändert werden. Um die Einstellung zu ändern, verwenden Sie bitte stattdessen das angehängte Profil (das Profil, von dem aus die Erweiterung im inkognitiven Modus aktiviert wurde)." 37 | }, 38 | "thirdPartyCookiesAllowed": { 39 | "description": "Privacy setting label", 40 | "message": "Cookies von Drittanbietern" 41 | }, 42 | "thirdPartyCookiesAllowed_desc": { 43 | "description": "Privacy setting description", 44 | "message": "Wenn deaktiviert, sperrt Chrome die Websites von Drittanbietern für das Anlegen von Cookies. Cookies von Drittanbietern sind Cookies, die mit anderen Domänen als der in der Adressleiste angezeigten gesetzt werden." 45 | }, 46 | "hyperlinkAuditingEnabled": { 47 | "description": "Privacy setting label", 48 | "message": "Prüfung von Hyperlinks" 49 | }, 50 | "hyperlinkAuditingEnabled_desc": { 51 | "description": "Privacy setting description", 52 | "message": "Die Prüfung von Hyperlinks ermöglicht es Ihnen, Ressourcen an einen Link „anzuhängen”, der, wenn er angeklickt wird, eine Quelle anpingt. Dies ermöglicht es, den Klick über die gewünschte Methode zu verfolgen, z.B. das Setzen eines Cookies oder das Senden einer HTTP-Anfrage." 53 | }, 54 | "referrersEnabled": { 55 | "description": "Privacy setting label", 56 | "message": "Referrer senden" 57 | }, 58 | "referrersEnabled_desc": { 59 | "description": "'Referrers' privacy setting description", 60 | "message": "Wenn aktiviert, sendet Chrome Referrer-Kopfzeilen mit Ihren Anfragen, so dass der Browser eine Anfrage an den Server sendet, der die Ziel-Webseite enthält. Die Anforderung enthält das Referer-Feld, das die letzte Seite angibt, auf der der Benutzer war." 61 | }, 62 | "protectedContentEnabled": { 63 | "description": "Privacy setting label", 64 | "message": "Inhalte schützen" 65 | }, 66 | "protectedContentEnabled_desc": { 67 | "description": "'Protect content' privacy setting description (ChromeOS specific)", 68 | "message": "Wenn aktiviert, vergibt Chrome eine eindeutige ID an Plugins, um geschützte Inhalte auszuführen. Dies wird beispielsweise verwendet, um Adobe Flash einen Zugriff auf die Geräte-ID zu ermöglichen, um Musik oder Filme abzuspielen, die durch ein Urheberrecht geschützt sind." 69 | }, 70 | "alternateErrorPagesEnabled": { 71 | "description": "Privacy setting label", 72 | "message": "Alternative Fehlerseiten" 73 | }, 74 | "alternateErrorPagesEnabled_desc": { 75 | "description": "'Alternate error pages' privacy setting description", 76 | "message": "Wenn aktiviert, verwendet Chrome einen Webdienst, der bei der Behebung von Navigationsfehlern hilft. Dieser steuert, ob Google Chrome Vorschläge für die zu erreichende Seite anzeigt, wenn keine Verbindung zu einer Webadresse hergestellt werden kann. Der Nutzer sieht Vorschläge, um zu anderen Teilen der Website zu navigieren oder mit Google nach der Seite zu suchen." 77 | }, 78 | "autofillEnabled": { 79 | "description": "Privacy setting label", 80 | "message": "Formulare automatisch auffüllen" 81 | }, 82 | "autofillEnabled_desc": { 83 | "description": "'Auto fill' privacy setting description", 84 | "message": "Wenn aktiviert, bietet Chrome an, Formulare automatisch auszufüllen. Wenn das automatische Ausfüllen aktiviert ist und Sie ein Formular absenden, sendet Chrome die Datentypen, die Sie tatsächlich zum Ausfüllen des Formulars verwendet haben, um die Vermutungen mit der Zeit zu verbessern. Der von Ihnen tatsächlich in das Formular eingegebene Text wird nicht gesendet. Es ist wichtig, dass Sie das automatische Ausfüllen nur auf Websites verwenden, denen Sie vertrauen, da bestimmte Websites versuchen könnten, Ihre Informationen in ausgeblendeten oder schwer einsehbaren Feldern zu erfassen. Einige Websites verhindern, dass Browser den von Ihnen eingegebenen Text speichern, sodass Google Chrome auf diesen Websites keine Formulare ausfüllen kann." 85 | }, 86 | "hotwordSearchEnabled": { 87 | "description": "Privacy setting label", 88 | "message": "Suchen mit Sprachaufforderung" 89 | }, 90 | "hotwordSearchEnabled_desc": { 91 | "description": "'Hot word search' privacy setting description", 92 | "message": "Wenn aktiviert, aktiviert Chrome „OK, Google”, um eine Suche mit Ihrer Sprachaufforderung zu starten. Der Wert dieser Einstellung ist ein boolescher Wert, der standardmäßig auf „true” festgelegt ist." 93 | }, 94 | "passwordSavingEnabled": { 95 | "description": "Privacy setting label", 96 | "message": "Passwörter speichern" 97 | }, 98 | "passwordSavingEnabled_desc": { 99 | "description": "'Password saving' privacy setting description", 100 | "message": "Ermöglicht, Ihre Web-Passwörter zu speichern" 101 | }, 102 | "safeBrowsingEnabled": { 103 | "description": "Privacy setting label", 104 | "message": "Schutzfunktion „Sicheres Surfen”" 105 | }, 106 | "safeBrowsingEnabled_desc": { 107 | "description": "'Safe browsiong mode' privacy setting description", 108 | "message": "Wenn aktiviert, hilft Chrome, Sie vor Phishing- und Malware-Angriffen zu schützen. Dadurch wird verhindert, dass Sie von Angreifern dazu verleitet werden, persönliche Daten mit ihnen auszutauschen (Phishing) oder schädliche Software auf Ihrem Computer zu installieren (Malware). Bitte beachten Sie, dass Chrome Sie nicht mehr vor Websites schützen kann, die versuchen, Ihre Daten zu stehlen oder schädliche Software zu installieren, wenn Sie diese Funktion deaktivieren. Es ist nicht empfehlenswert, diese Funktion zu deaktivieren." 109 | }, 110 | "safeBrowsingExtendedReportingEnabled": { 111 | "description": "Privacy setting label", 112 | "message": "Safe Browsing-Meldungen" 113 | }, 114 | "safeBrowsingExtendedReportingEnabled_desc": { 115 | "description": "'Safe browsing reporting' privacy setting description", 116 | "message": "Wenn aktiviert, sendet Chrome zusätzliche Informationen an Google, wenn SafeBrowsing eine Seite sperrt, z. B. den Inhalt der gesperrten Seite." 117 | }, 118 | "searchSuggestEnabled": { 119 | "description": "Privacy setting label", 120 | "message": "Suchvorschläge" 121 | }, 122 | "searchSuggestEnabled_desc": { 123 | "description": "'Search suggestions' privacy setting description", 124 | "message": "Wenn aktiviert, sendet Chrome den Text, den Sie in die Omnibox (URL-Leiste) eingeben, an Ihre Standardsuchmaschine, die Vorhersagen zu Websites und Suchvorgängen liefert, die wahrscheinlich eine Vervollständigung dessen darstellen, was Sie bisher getippt haben." 125 | }, 126 | "spellingServiceEnabled": { 127 | "description": "Privacy setting label", 128 | "message": "Rechtschreibprüfung" 129 | }, 130 | "spellingServiceEnabled_desc": { 131 | "description": "'Spelling service' privacy setting description", 132 | "message": "Wenn aktiviert, verwendet Chrome einen Webdienst, der bei der Korrektur von Rechtschreibfehlern hilft. Der Rechtschreibprüfungsdienst von Chrome sendet den von Ihnen in den Browser eingegebenen Text an die Server von Google. Dadurch kann Chrome die gleiche Rechtschreibprüfungstechnologie wie die Google-Suche verwenden." 133 | }, 134 | "translationServiceEnabled": { 135 | "description": "Privacy setting label", 136 | "message": "Übersetzungsdienst" 137 | }, 138 | "translationServiceEnabled_desc": { 139 | "description": "'Translation service' privacy setting description", 140 | "message": "Wenn aktiviert, bietet Chrome die Übersetzung von Seiten an, die nicht in einer von Ihnen lesbaren Sprache vorliegen. Wenn Sie sich für die Übersetzung einer Webseite entscheiden, wird der Text dieser Seite zur Übersetzung an den Übersetzungsdienst von Google gesendet. Ihre Cookies werden nicht zusammen mit dieser Anfrage gesendet, und wenn die Seite, auf der Sie sich befinden, mit SSL verschlüsselt ist, sendet Google Chrome die Übersetzungsanfrage ebenfalls über SSL." 141 | }, 142 | "networkPredictionEnabled": { 143 | "description": "Privacy setting label", 144 | "message": "Netzwerk-Vorabruf" 145 | }, 146 | "networkPredictionEnabled_desc": { 147 | "description": "'Network prediction' privacy setting description", 148 | "message": "Wenn aktiviert, versucht Chrome, Ihr Surfen im Web zu beschleunigen, indem es DNS-Einträge vorauflöst, Sites vorabruft () und TCP- und SSL-Verbindungen zu Servern vorzeitig öffnet." 149 | }, 150 | "webRTCIPHandlingPolicy": { 151 | "description": "Privacy setting label", 152 | "message": "Web RTC IP-Handlungsanweisungen" 153 | }, 154 | "webRTCIPHandlingPolicy_desc": { 155 | "description": "'Web RTC IP handling' privacy setting description", 156 | "message": "Ermöglichen es Benutzern, die Kompromisse zwischen Medienleistung und Datenschutz festzulegen, die sich auf die Art und Weise auswirken, wie der WebRTC-Verkehr weitergeleitet wird und wie viele lokale Adressinformationen offengelegt werden." 157 | }, 158 | "openIncognito": { 159 | "description": "Button text", 160 | "message": "In Privatem Modus öffnen" 161 | }, 162 | "startupClear": { 163 | "description": "Section name", 164 | "message": "Beim Start löschen" 165 | }, 166 | "removeAll": { 167 | "description": "Clear on startup setting label", 168 | "message": "Alles entfernen" 169 | }, 170 | "removeAll_desc": { 171 | "description": "Remove All description in 'Clear on startup' section", 172 | "message": "Löscht alle Surferdaten beim Start des Browsers." 173 | }, 174 | "appcache": { 175 | "description": "Clear on startup setting label", 176 | "message": "App-Cache" 177 | }, 178 | "appcache_desc": { 179 | "description": "App. cache description in 'Clear on startup' section", 180 | "message": "Leert den Zwischenspeicher der Anwendung von Webseiten beim Start des Browsers. Der Anwendungszwischenspeicher-Mechanismus ermöglicht es, webbasierte Anwendungen offline auszuführen. Entwickler können die Schnittstelle für den Anwendungszwischenspeicher (AppCache) verwenden, um Ressourcen anzugeben, die der Browser zwischenspeichern und Offline-Benutzern zur Verfügung stellen soll. Zwischengespeicherte Anwendungen werden geladen und funktionieren auch dann korrekt, wenn Benutzer auf die Aktualisierungsschaltfläche klicken, während sie Offline sind." 181 | }, 182 | "cache": { 183 | "description": "Clear on startup setting label", 184 | "message": "Cache" 185 | }, 186 | "cache_desc": { 187 | "description": "Cache description in 'Clear on startup' section", 188 | "message": "Leert den Zwischenspeicher des Browsers beim Start des Browsers. Browser speichern Elemente von Webseiten, um das Laden der Webseite bei Ihrem nächsten Besuch zu beschleunigen." 189 | }, 190 | "cookies": { 191 | "description": "Clear on startup setting label", 192 | "message": "Cookies" 193 | }, 194 | "cookies_desc": { 195 | "description": "Cookies description in 'Clear on startup' section", 196 | "message": "Löscht Cookies des Browsers beim Start des Browsers. Cookies sind Dateien, die von Websites, die Sie besucht haben, erstellt werden und in denen Informationen zum Surfen gespeichert werden, wie z.B. Ihre Website-Einstellungen oder Profilinformationen. Cookies, die von der in der Adressleiste aufgeführten Website-Domäne gesetzt werden, werden als „Erstanbieter-Cookies” bezeichnet, Cookies, die von anderen Domänenquellen gesetzt werden, werden als „Drittpartei-Cookies” bezeichnet (z.B.: durch auf der Seite eingebettete Anzeigen oder Bilder)." 197 | }, 198 | "downloads": { 199 | "description": "Clear on startup setting label", 200 | "message": "Heruntergeladene Dateien" 201 | }, 202 | "downloads_desc": { 203 | "description": "Downloads description in 'Clear on startup' section", 204 | "message": "Leert die Liste der heruntergeladenen Dateien, die von Google Chrome beim Start des Browsers verwaltet wird. Diese Aktion entfernt KEINE der tatsächliche heruntergeladenen Dateien von Ihrem Computer." 205 | }, 206 | "fileSystems": { 207 | "description": "Clear on startup setting label", 208 | "message": "Dateisysteme" 209 | }, 210 | "fileSystems_desc": { 211 | "description": "File system description in 'Clear on startup' section", 212 | "message": "Leert das Dateisystem des Browsers beim Start des Browsers. Eine Website kann in der Sandbox gespeicherte Abschnitte des lokalen Dateisystems eines Benutzers verwenden. Ein n der Sandbox gespeicherte Abschnitt ist nur ein Unterverzeichnis namens File System in Ihrem Chrome-Profil-Verzeichnis, in dem Daten in Dateien gespeichert werden. Websites können Dateien und Ordner in diesem Sandbox-Unterverzeichnis verwalten (erstellen, löschen, lesen, schreiben)" 213 | }, 214 | "formData": { 215 | "description": "Clear on startup setting label", 216 | "message": "Formulardaten" 217 | }, 218 | "formData_desc": { 219 | "description": "Form data description in 'Clear on startup' section", 220 | "message": "Leert die im Browser gespeicherten Formulardaten beim Start des Browsers. Wenn aktiviert, werden die automatisch ausgefüllten Formulardaten des Chrome-Browsers beim Browser-Start entfernt." 221 | }, 222 | "history": { 223 | "description": "Clear on startup setting label", 224 | "message": "Verlauf" 225 | }, 226 | "history_desc": { 227 | "description": "History description in 'Clear on startup' section", 228 | "message": "Leert den Browserverlauf beim Start des Browsers." 229 | }, 230 | "indexedDB": { 231 | "description": "Clear on startup setting label", 232 | "message": "Indizierte Datenbank" 233 | }, 234 | "indexedDB_desc": { 235 | "description": "Indexed DB description in 'Clear on startup' section", 236 | "message": "Leert die von Webseiten indizierten Datenbank-Daten beim Start des Browsers. IndexedDB ist ein clientseitiger Speicher für signifikante Mengen strukturierter Daten und für die hochperformante Suche auf diesen Daten mit Hilfe von Indizes." 237 | }, 238 | "localStorage": { 239 | "description": "Clear on startup setting label", 240 | "message": "Lokaler Speicher" 241 | }, 242 | "localStorage_desc": { 243 | "description": "Local storage description in 'Clear on startup' section", 244 | "message": "Leert die lokalen Speicherdaten von Websites beim Start des Browsers. Der lokale Speicher ist eine Datenbank, die sich im Browser des Benutzers befindet." 245 | }, 246 | "serverBoundCertificates": { 247 | "description": "Clear on startup setting label", 248 | "message": "Server-gebundene Zertifikate" 249 | }, 250 | "serverBoundCertificates_desc": { 251 | "description": "Server-bound certificates description in 'Clear on startup' section", 252 | "message": "Löscht Server-gebundene Zertifikate beim Browser-Start." 253 | }, 254 | "passwords": { 255 | "description": "Clear on startup setting label", 256 | "message": "Passwörter" 257 | }, 258 | "passwords_desc": { 259 | "description": "Passwords description in 'Clear on startup' section", 260 | "message": "Löscht gespeicherte Passwörter beim Start des Browsers. Google Chrome kann Ihre Benutzernamen und Passwörter für verschiedene Websites speichern. Der Browser kann dann beim nächsten Besuch dieser Websites die Anmeldefelder automatisch für Sie ausfüllen." 261 | }, 262 | "pluginData": { 263 | "description": "Clear on startup setting label", 264 | "message": "Plug-in-Daten" 265 | }, 266 | "pluginData_desc": { 267 | "description": "Plugin data description in 'Clear on startup' section", 268 | "message": "Löscht Plugin-Daten beim Browser-Start. Websites verwenden auch verschiedene Arten der lokalen Speicherung, einschließlich der Adobe Flash Player Local Shared Objects (LSOs), die von einigen Leuten als Flash-Cookies bezeichnet werden. Diese Option löscht also clientseitige Daten, die von Plugins gespeichert werden, die der NPAPI ClearSiteData API gehorchen, wie z.B. Flash Player." 269 | }, 270 | "serviceWorkers": { 271 | "description": "Clear on startup setting label", 272 | "message": "Service Workers" 273 | }, 274 | "serviceWorkers_desc": { 275 | "description": "Service workers description in 'Clear on startup' section", 276 | "message": "Löscht die Daten des „Service Workers” beim Start des Browsers." 277 | }, 278 | "webSQL": { 279 | "description": "Clear on startup setting label", 280 | "message": "WebSQL" 281 | }, 282 | "webSQL_desc": { 283 | "description": "Web SQL description in 'Clear on startup' section", 284 | "message": "Löscht die WebSQL-Daten von Websites beim Start des Browsers. Web SQL-Datenbank ist eine Datenbank, die sich im Browser des Benutzers befindet." 285 | }, 286 | "addCookie": { 287 | "description": "Text in Cookies section", 288 | "message": "Neuer Cookie" 289 | }, 290 | "editCookie": { 291 | "description": "Text in Cookies section", 292 | "message": "Cookie bearbeiten" 293 | }, 294 | "cookieDialog_domain": { 295 | "description": "Text in Cookies edit dialog", 296 | "message": "Domain" 297 | }, 298 | "cookieDialog_name": { 299 | "description": "Text in Cookies edit dialog", 300 | "message": "Name" 301 | }, 302 | "cookieDialog_value": { 303 | "description": "Text in Cookies edit dialog", 304 | "message": "Wert" 305 | }, 306 | "cookieDialog_path": { 307 | "description": "Text in Cookies edit dialog", 308 | "message": "Pfad" 309 | }, 310 | "cookieDialog_expDate": { 311 | "description": "Text in Cookies edit dialog", 312 | "message": "Gültig bis (Datum)" 313 | }, 314 | "cookieDialog_expTime": { 315 | "description": "Text in Cookies edit dialog", 316 | "message": "Gültig bis (Zeit)" 317 | }, 318 | "cookieDialog_hostOnyl": { 319 | "description": "Text in Cookies edit dialog", 320 | "message": "Nur Host" 321 | }, 322 | "cookieDialog_httpOnyl": { 323 | "description": "Text in Cookies edit dialog", 324 | "message": "Nur HTTP" 325 | }, 326 | "cookieDialog_secure": { 327 | "description": "Text in Cookies edit dialog", 328 | "message": "Sicher" 329 | }, 330 | "cookieDialog_session": { 331 | "description": "Text in Cookies edit dialog", 332 | "message": "Sitzung" 333 | }, 334 | "cookieDialog_storeId": { 335 | "description": "Text in Cookies edit dialog", 336 | "message": "Speicher-ID" 337 | }, 338 | "cookieDialog_add": { 339 | "description": "Button text in Cookies edit dialog", 340 | "message": "Hinzufügen" 341 | }, 342 | "cookieDialog_update": { 343 | "description": "Button text in Cookies edit dialog", 344 | "message": "Aktualisieren" 345 | }, 346 | "cookieDialog_delete": { 347 | "description": "Button text in Cookies edit dialog", 348 | "message": "Löschen" 349 | }, 350 | "cookieDialog_cancel": { 351 | "description": "Button text in Cookies 'delete all' dialog", 352 | "message": "Abbrechen" 353 | }, 354 | "cookieDialog_deleteAll_msg": { 355 | "description": "text in Cookies 'delete all' dialog", 356 | "message": "Möchten Sie wirklich alle Cookies löschen?" 357 | }, 358 | "whitelistCookieDomain": { 359 | "description": "text when hovering over Whitelist button for Cookie Domain", 360 | "message": "Diese Schaltfläche fügt alle Cookies einer Domäne zu einer Positivliste hinzu und verhindert, dass diese automatisch gelöscht werden. Dies ist nützlich, wenn Sie bei einigen Websites angemeldet bleiben möchten, aber trotzdem alle anderen Cookies gelöscht werden sollen." 361 | }, 362 | "whitelistSublistCookie": { 363 | "description": "text when hovering over Whitelist button for Sublist Cookies", 364 | "message": "Diese Schaltfläche fügt einen bestimmten Cookie zu einer Positivliste hinzu und verhindert, dass dieses automatisch gelöscht wird. Dies ist nützlich, wenn Sie bei einigen Websites angemeldet bleiben möchten, aber trotzdem alle anderen Cookies gelöscht werden sollen." 365 | }, 366 | "additionalPermissions": { 367 | "description": "Label for requesting additional permission setting", 368 | "message": "Zusätzliche Berechtigungen" 369 | }, 370 | "additionalPermissions_desc": { 371 | "description": "Description for requesting additional permission setting", 372 | "message": "Um Cookies und das Netzwerk modifizieren zu können, müssen Sie dem Host die Berechtigung erteilen." 373 | }, 374 | "additionalPermissions_notification": { 375 | "description": "Notification message for the features that require additional permission", 376 | "message": "Bitte aktivieren Sie Zusätzliche Berechtigungen, um die Funktion nutzen zu können" 377 | }, 378 | "activeTabCookies": { 379 | "description": "Setting label in cookies section", 380 | "message": "Cookies des aktuellen Tabs" 381 | }, 382 | "activeTabCookies_desc": { 383 | "description": "Active tab setting description", 384 | "message": "Wenn aktiviert, werden nur Cookies des aktuell geöffneten Tabs angezeigt." 385 | }, 386 | "blockUserAgent": { 387 | "description": "Label for blocking user agent setting", 388 | "message": "Browserkennung nicht übermitteln" 389 | }, 390 | "blockUserAgent_desc": { 391 | "description": "Descrption for blocking user agent setting", 392 | "message": "Wenn aktivierte, werden die Informationen zum verwendeten Browser nicht mit der Anfrage gesendet, was bedeutet, dass Websites den von Ihnen verwendeten Browser nicht ermitteln können, weshalb Websites in einigen Fällen die Anzeige der Inhalte nicht zulassen, wenn der User-Agent nicht übermittelt wird." 393 | }, 394 | "collectHeaders": { 395 | "description": "Setting label in network section", 396 | "message": "Kopfzeilen sammeln" 397 | }, 398 | "collectHeaders_desc": { 399 | "description": "'Collect headers' setting description", 400 | "message": "Wenn aktiviert, werden Kopfzeilen der besuchten Webseiten gesammelt.\nEs werden jedoch nur die letzten 1.000 Kopfzeilen angezeigt." 401 | }, 402 | "downloadAll": { 403 | "description": "button text in network section", 404 | "message": "Alle herunterladen" 405 | }, 406 | "deleteAll": { 407 | "description": "General text used in various places", 408 | "message": "Alle löschen" 409 | } 410 | } -------------------------------------------------------------------------------- /src/_locales/en_US/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "chrome_extension_name": { 3 | "description": "Extension name, used in the Web Store", 4 | "message": "Privacy Manager" 5 | }, 6 | "chrome_extension_description": { 7 | "description": "Extension description, used in the Web Store", 8 | "message": "Manage privacy settings, browsing data deletion, quickly access page in incognito mode, manage cookies and monitor network traffic" 9 | }, 10 | "main_tab": { 11 | "description": "Tab label", 12 | "message": "Main" 13 | }, 14 | "cookies_general": { 15 | "description": "General text used in various places", 16 | "message": "Cookies" 17 | }, 18 | "network_tab": { 19 | "description": "Tab label", 20 | "message": "Network" 21 | }, 22 | "off": { 23 | "description": "Switch's 'off' text", 24 | "message": "Off" 25 | }, 26 | "on": { 27 | "description": "Switch's 'on' text", 28 | "message": "On" 29 | }, 30 | "privacyManagement": { 31 | "description": "Section name", 32 | "message": "Privacy management" 33 | }, 34 | "regularSettingChangeIncognito_error": { 35 | "description": "Error thrown in the incognito mode when privacy setting is being modified", 36 | "message": "Can't modify regular settings from an incognito context, to change the setting please use attached profile (the one from which the extension was enabled to be available in the incognito mode) instead." 37 | }, 38 | "thirdPartyCookiesAllowed": { 39 | "description": "Privacy setting label", 40 | "message": "3-rd party cookies" 41 | }, 42 | "thirdPartyCookiesAllowed_desc": { 43 | "description": "Privacy setting description", 44 | "message": "If disabled, Chrome blocks third-party sites from setting cookies. Third-party cookies are cookies being set with different domains from the one shown on the address bar." 45 | }, 46 | "hyperlinkAuditingEnabled": { 47 | "description": "Privacy setting label", 48 | "message": "Hyperlink auditing" 49 | }, 50 | "hyperlinkAuditingEnabled_desc": { 51 | "description": "Privacy setting description", 52 | "message": "Hyperlink auditing allows you to 'attach' resources to a link which, when clicked, will ping a source. This enables the click to be tracked via the desired method e.g. dropping a cookie or sending a HTTP request." 53 | }, 54 | "referrersEnabled": { 55 | "description": "Privacy setting label", 56 | "message": "Referrers" 57 | }, 58 | "referrersEnabled_desc": { 59 | "description": "'Referrers' privacy setting description", 60 | "message": "If enabled, Chrome sends referer headers with your requests, so browser sends a request to the server holding the destination webpage. The request includes the referer field, which says the last page the user was on" 61 | }, 62 | "protectedContentEnabled": { 63 | "description": "Privacy setting label", 64 | "message": "Protect content" 65 | }, 66 | "protectedContentEnabled_desc": { 67 | "description": "'Protect content' privacy setting description (ChromeOS specific)", 68 | "message": "If enabled, Chrome provides a unique ID to plugins in order to run protected content. It's used for example to provide Adobe Flash an access to the device ID in order to play music or movies that are protected by a copyright." 69 | }, 70 | "alternateErrorPagesEnabled": { 71 | "description": "Privacy setting label", 72 | "message": "Alternate error pages" 73 | }, 74 | "alternateErrorPagesEnabled_desc": { 75 | "description": "'Alternate error pages' privacy setting description", 76 | "message": "If enabled, Chrome uses a web service to help resolve navigation errors. It Controls whether Google Chrome shows suggestions for the page you were trying to reach when it is unable to connect to a web address. The user sees suggestions to navigate to other parts of the website or to search for the page with Google." 77 | }, 78 | "autofillEnabled": { 79 | "description": "Privacy setting label", 80 | "message": "Auto fill" 81 | }, 82 | "autofillEnabled_desc": { 83 | "description": "'Auto fill' privacy setting description", 84 | "message": "If enabled, Chrome offers to automatically fill in forms. If Autofill is enabled, and you submit a form, Chrome sends the data types you actually used for filling in the form in order to improve its guesses over time. The actual text you typed into the form is not sent. It's important that you use Autofill only on websites you trust, as certain websites might try to capture your information in hidden or hard-to-see fields. Some websites prevent browsers from saving text that you've entered, so Google Chrome won't be able to complete forms on those sites." 85 | }, 86 | "hotwordSearchEnabled": { 87 | "description": "Privacy setting label", 88 | "message": "Hot word search" 89 | }, 90 | "hotwordSearchEnabled_desc": { 91 | "description": "'Hot word search' privacy setting description", 92 | "message": "If enabled, Chrome will enable 'OK, Google' to start a voice search. This preference's value is a boolean, defaulting to true" 93 | }, 94 | "passwordSavingEnabled": { 95 | "description": "Privacy setting label", 96 | "message": "Password saving" 97 | }, 98 | "passwordSavingEnabled_desc": { 99 | "description": "'Password saving' privacy setting description", 100 | "message": "Offer to save your web passwords" 101 | }, 102 | "safeBrowsingEnabled": { 103 | "description": "Privacy setting label", 104 | "message": "Safe browsing mode" 105 | }, 106 | "safeBrowsingEnabled_desc": { 107 | "description": "'Safe browsing mode' privacy setting description", 108 | "message": "If enabled, Chrome help protect you against phishing and malware attacks. This helps prevent evil-doers from tricking you into sharing personal information with them (phishing) or installing malicious software on your computer (malware). Please be aware that Chrome will no longer be able to protect you from websites that try to steal your information or install harmful software if you disable this feature. It's not recomended to turn it off." 109 | }, 110 | "safeBrowsingExtendedReportingEnabled": { 111 | "description": "Privacy setting label", 112 | "message": "Safe browsing reporting" 113 | }, 114 | "safeBrowsingExtendedReportingEnabled_desc": { 115 | "description": "'Safe browsing reporting' privacy setting description", 116 | "message": "If enabled, Chrome will send additional information to Google when SafeBrowsing blocks a page, such as the content of the blocked page." 117 | }, 118 | "searchSuggestEnabled": { 119 | "description": "Privacy setting label", 120 | "message": "Search suggestions" 121 | }, 122 | "searchSuggestEnabled_desc": { 123 | "description": "'Search suggestions' privacy setting description", 124 | "message": "If enabled, Chrome sends the text you type into the Omnibox to your default search engine, which provides predictions of websites and searches that are likely completions of what you've typed so far." 125 | }, 126 | "spellingServiceEnabled": { 127 | "description": "Privacy setting label", 128 | "message": "Spelling service" 129 | }, 130 | "spellingServiceEnabled_desc": { 131 | "description": "'Spelling service' privacy setting description", 132 | "message": "If enabled, Chrome uses a web service to help correct spelling errors. Chrome spell-checking service sends the text you type into the browser to Google's servers. This allows Chrome to use the same spell-checking technology as Google search." 133 | }, 134 | "translationServiceEnabled": { 135 | "description": "Privacy setting label", 136 | "message": "Translation service" 137 | }, 138 | "translationServiceEnabled_desc": { 139 | "description": "'Translation service' privacy setting description", 140 | "message": "If enabled, Chrome offers to translate pages that aren't in a language you read. If you do choose to translate a web page, the text of that page is sent to Google's translation service for translation. Your cookies are not sent along with that request and, if the page you are on is encrypted with SSL, Google Chrome also sends the translation request over SSL." 141 | }, 142 | "networkPredictionEnabled": { 143 | "description": "Privacy setting label", 144 | "message": "Network prediction" 145 | }, 146 | "networkPredictionEnabled_desc": { 147 | "description": "'Network prediction' privacy setting description", 148 | "message": "If enabled, Chrome attempts to speed up your web browsing experience by pre-resolving DNS entries, prerendering sites (), and preemptively opening TCP and SSL connections to servers." 149 | }, 150 | "webRTCIPHandlingPolicy": { 151 | "description": "Privacy setting label", 152 | "message": "Web RTC IP handling" 153 | }, 154 | "webRTCIPHandlingPolicy_desc": { 155 | "description": "'Web RTC IP handling' privacy setting description", 156 | "message": "Allow users to specify the media performance/privacy tradeoffs which impacts how WebRTC traffic will be routed and how much local address information is exposed." 157 | }, 158 | "openIncognito": { 159 | "description": "Button text", 160 | "message": "Open in incognito mode" 161 | }, 162 | "startupClear": { 163 | "description": "Section name", 164 | "message": "Clear on startup" 165 | }, 166 | "removeAll": { 167 | "description": "Clear on startup setting label", 168 | "message": "Remove All" 169 | }, 170 | "removeAll_desc": { 171 | "description": "Remove All description in 'Clear on startup' section", 172 | "message": "Clears all browsing data at browser startup." 173 | }, 174 | "appcache": { 175 | "description": "Clear on startup setting label", 176 | "message": "App. cache" 177 | }, 178 | "appcache_desc": { 179 | "description": "App. cache description in 'Clear on startup' section", 180 | "message": "Clear websites Application cache at browser startup. Application caching mechanism lets web-based applications run offline. Developers can use the Application Cache (AppCache) interface to specify resources that the browser should cache and make available to offline users. Applications that are cached load and work correctly even if users click the refresh button when they are offline." 181 | }, 182 | "cache": { 183 | "description": "Clear on startup setting label", 184 | "message": "Cache" 185 | }, 186 | "cache_desc": { 187 | "description": "Cache description in 'Clear on startup' section", 188 | "message": "Clear browser cache at browser startup. Browsers store elements of webpages to speed up the loading of the webpage during your next visit." 189 | }, 190 | "cookies": { 191 | "description": "Clear on startup setting label", 192 | "message": "Cookies" 193 | }, 194 | "cookies_desc": { 195 | "description": "Cookies description in 'Clear on startup' section", 196 | "message": "Clear browser cookies at browser startup. Cookies are files created by websites you've visited that store browsing information, such as your site preferences or profile information. Cookies that are set by the site domain listed in the address bar are called 'First-party cookies', cookies that are set by other domain sources are called 'Third-party cookies' (e.g.: by ads or images embedded on the page)." 197 | }, 198 | "downloads": { 199 | "description": "Clear on startup setting label", 200 | "message": "Downloads" 201 | }, 202 | "downloads_desc": { 203 | "description": "Downloads description in 'Clear on startup' section", 204 | "message": "Clear browser download list maintained by Google Chrome at browser startup. This action doesn't remove the actual files from your computer." 205 | }, 206 | "fileSystems": { 207 | "description": "Clear on startup setting label", 208 | "message": "File systems" 209 | }, 210 | "fileSystems_desc": { 211 | "description": "File system description in 'Clear on startup' section", 212 | "message": "Clear browser file system at browser startup. A website may use sandboxed sections of a user's local filesystem. A sanboxed section is just a subdirectory named File System in your Chrome profile directory, where data is stored in files. Websites can manage (create, delete, read, write) files and folders in this sandboxed subdirectory." 213 | }, 214 | "formData": { 215 | "description": "Clear on startup setting label", 216 | "message": "Form data" 217 | }, 218 | "formData_desc": { 219 | "description": "Form data description in 'Clear on startup' section", 220 | "message": "Clear browser stored data at browser startup. If enabled, Chrome browser Auto fill data will be removed on browser startup." 221 | }, 222 | "history": { 223 | "description": "Clear on startup setting label", 224 | "message": "History" 225 | }, 226 | "history_desc": { 227 | "description": "History description in 'Clear on startup' section", 228 | "message": "Clears browser history at browser startup." 229 | }, 230 | "indexedDB": { 231 | "description": "Clear on startup setting label", 232 | "message": "Indexed DB" 233 | }, 234 | "indexedDB_desc": { 235 | "description": "Indexed DB description in 'Clear on startup' section", 236 | "message": "Clear websites Indexed DB data at browser startup. IndexedDB is an client-side storage of significant amounts of structured data and for high performance searches on this data using indexes." 237 | }, 238 | "localStorage": { 239 | "description": "Clear on startup setting label", 240 | "message": "Local storage" 241 | }, 242 | "localStorage_desc": { 243 | "description": "Local storage description in 'Clear on startup' section", 244 | "message": "Clear websites local storage data at browser startup. Local storage is a database that resides in the users browser" 245 | }, 246 | "serverBoundCertificates": { 247 | "description": "Clear on startup setting label", 248 | "message": "Server-bound certificates" 249 | }, 250 | "serverBoundCertificates_desc": { 251 | "description": "Server-bound certificates description in 'Clear on startup' section", 252 | "message": "Clear Server-bound certificates at browser startup." 253 | }, 254 | "passwords": { 255 | "description": "Clear on startup setting label", 256 | "message": "Passwords" 257 | }, 258 | "passwords_desc": { 259 | "description": "Passwords description in 'Clear on startup' section", 260 | "message": "Clear stored passwords at browser startup. Google Chrome can save your usernames and passwords for different websites. The browser can then automatically complete the sign-in fields for you when you next visit these websites." 261 | }, 262 | "pluginData": { 263 | "description": "Clear on startup setting label", 264 | "message": "Plugin data" 265 | }, 266 | "pluginData_desc": { 267 | "description": "Plugin data description in 'Clear on startup' section", 268 | "message": "Clear plugins data at browser startup. Websites also use different types of local storage as well, including Adobe Flash Player Local Shared Objects (LSOs), referred to by some people as Flash cookies, so this option will delete client-side data stored by plug-ins that obey the NPAPI ClearSiteData API, such as Flash Player." 269 | }, 270 | "serviceWorkers": { 271 | "description": "Clear on startup setting label", 272 | "message": "Service Workers" 273 | }, 274 | "serviceWorkers_desc": { 275 | "description": "Service workers description in 'Clear on startup' section", 276 | "message": "Clear Service Workers data at browser startup." 277 | }, 278 | "webSQL": { 279 | "description": "Clear on startup setting label", 280 | "message": "Web SQL" 281 | }, 282 | "webSQL_desc": { 283 | "description": "Web SQL description in 'Clear on startup' section", 284 | "message": "Clear websites WebSQL data at browser startup. Web SQL Database is a database that resides in the users browser." 285 | }, 286 | "addCookie": { 287 | "description": "Text in Cookies section", 288 | "message": "Add cookie" 289 | }, 290 | "editCookie": { 291 | "description": "Text in Cookies section", 292 | "message": "Edit cookie" 293 | }, 294 | "cookieDialog_domain": { 295 | "description": "Text in Cookies edit dialog", 296 | "message": "Domain" 297 | }, 298 | "cookieDialog_name": { 299 | "description": "Text in Cookies edit dialog", 300 | "message": "Name" 301 | }, 302 | "cookieDialog_value": { 303 | "description": "Text in Cookies edit dialog", 304 | "message": "Value" 305 | }, 306 | "cookieDialog_path": { 307 | "description": "Text in Cookies edit dialog", 308 | "message": "Path" 309 | }, 310 | "cookieDialog_expDate": { 311 | "description": "Text in Cookies edit dialog", 312 | "message": "Exp. date" 313 | }, 314 | "cookieDialog_expTime": { 315 | "description": "Text in Cookies edit dialog", 316 | "message": "Exp. time" 317 | }, 318 | "cookieDialog_hostOnyl": { 319 | "description": "Text in Cookies edit dialog", 320 | "message": "Host only" 321 | }, 322 | "cookieDialog_httpOnyl": { 323 | "description": "Text in Cookies edit dialog", 324 | "message": "HTTP only" 325 | }, 326 | "cookieDialog_secure": { 327 | "description": "Text in Cookies edit dialog", 328 | "message": "Secure" 329 | }, 330 | "cookieDialog_session": { 331 | "description": "Text in Cookies edit dialog", 332 | "message": "Session" 333 | }, 334 | "cookieDialog_storeId": { 335 | "description": "Text in Cookies edit dialog", 336 | "message": "Store ID" 337 | }, 338 | "cookieDialog_add": { 339 | "description": "Button text in Cookies edit dialog", 340 | "message": "Add" 341 | }, 342 | "cookieDialog_update": { 343 | "description": "Button text in Cookies edit dialog", 344 | "message": "Update" 345 | }, 346 | "cookieDialog_delete": { 347 | "description": "Button text in Cookies edit dialog", 348 | "message": "Delete" 349 | }, 350 | "cookieDialog_cancel": { 351 | "description": "Button text in Cookies 'delete all' dialog", 352 | "message": "Cancel" 353 | }, 354 | "cookieDialog_deleteAll_msg": { 355 | "description": "text in Cookies 'delete all' dialog", 356 | "message": "You are about to delete all cookies, are you sure ?" 357 | }, 358 | "whitelistCookieDomain": { 359 | "description": "text when hovering over Whitelist button for Cookie Domain", 360 | "message": "This button adds all cookies from a domain to a Whitelist, preventing them from being automatically deleted, usefull if you want to stay logged in to some websites but still want all other cookies to be deleted" 361 | }, 362 | "whitelistSublistCookie": { 363 | "description": "text when hovering over Whitelist button for Sublist Cookies", 364 | "message": "This button adds a specific cookie to a Whitelist, preventing them from being automatically deleted, usefull if you want to stay logged in to some websites but still want all other cookies to be deleted" 365 | }, 366 | "additionalPermissions": { 367 | "description": "Label for requesting additional permission setting", 368 | "message": "Additional permissions" 369 | }, 370 | "additionalPermissions_desc": { 371 | "description": "Description for requesting additional permission setting", 372 | "message": "You need to allow host permission to be able to manipulate cookies and network." 373 | }, 374 | "additionalPermissions_notification": { 375 | "description": "Notification message for the features that require additional permission", 376 | "message": "Please enable Additional permissions to use the feature" 377 | }, 378 | "activeTabCookies": { 379 | "description": "Setting label in cookies section", 380 | "message": "Active tab cookies" 381 | }, 382 | "activeTabCookies_desc": { 383 | "description": "Active tab setting description", 384 | "message": "If enabled the cookies tab will always be filtered with the cookie data of active tab page domain." 385 | }, 386 | "blockUserAgent": { 387 | "description": "Label for blocking user agent setting", 388 | "message": "Block user agent" 389 | }, 390 | "blockUserAgent_desc": { 391 | "description": "Descrption for blocking user agent setting", 392 | "message": "If enabled user agent data will not be sent with request, that mean websites couldn't determine the browser you are using, that why in some cases websites will not allow you to view the content when user agent is blocked." 393 | }, 394 | "collectHeaders": { 395 | "description": "Setting label in network section", 396 | "message": "Collect headers" 397 | }, 398 | "collectHeaders_desc": { 399 | "description": "'Collect headers' setting description", 400 | "message": "If enabled the headers will start to be collected, only last 1000 headers would be shown." 401 | }, 402 | "downloadAll": { 403 | "description": "button text in network section", 404 | "message": "Download all" 405 | }, 406 | "deleteAll": { 407 | "description": "General text used in various places", 408 | "message": "Delete all" 409 | } 410 | } 411 | -------------------------------------------------------------------------------- /src/_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "chrome_extension_name": { 3 | "description": "Extension name, used in the Web Store", 4 | "message": "Privacy manager" 5 | }, 6 | "chrome_extension_description": { 7 | "description": "Extension description, used in the Web Store", 8 | "message": "Gestion des paramètres de confidentialité. Suppression des données de navigation. Accès rapide au mode vie privée. Gestion des cookies et surveillance du trafic réseau" 9 | }, 10 | "main_tab": { 11 | "description": "Tab label", 12 | "message": "Général" 13 | }, 14 | "cookies_general": { 15 | "description": "General text used in various places", 16 | "message": "Cookies" 17 | }, 18 | "network_tab": { 19 | "description": "Tab label", 20 | "message": "Réseau" 21 | }, 22 | "off": { 23 | "description": "Switch's 'off' text", 24 | "message": "Off" 25 | }, 26 | "on": { 27 | "description": "Switch's 'on' text", 28 | "message": "On" 29 | }, 30 | "privacyManagement": { 31 | "description": "Section name", 32 | "message": "Gestion de la vie privée" 33 | }, 34 | "regularSettingChangeIncognito_error": { 35 | "description": "Error thrown in the incognito mode when privacy setting is being modified", 36 | "message": "Les paramètres courants ne peuvent être modifiés en mode \"vie privée\" que lorsque vous cochez la case \"Autoriser en mode de navigation privée\" dans votre gestionnaire d'extensions." 37 | }, 38 | "thirdPartyCookiesAllowed": { 39 | "description": "Privacy setting label", 40 | "message": "Cookies tiers" 41 | }, 42 | "thirdPartyCookiesAllowed_desc": { 43 | "description": "Privacy setting description", 44 | "message": "Si désactivé, Chrome bloque les cookies tiers provenant d'autres sites. Les cookies tiers sont des cookies déposés par des domaines différents de celui présenté dans la barre d’adresse." 45 | }, 46 | "hyperlinkAuditingEnabled": { 47 | "description": "Privacy setting label", 48 | "message": "Audit de lien hypertexte" 49 | }, 50 | "hyperlinkAuditingEnabled_desc": { 51 | "description": "Privacy setting description", 52 | "message": "Audit de lien hypertexte permet \"d'attacher\" des ressources à un lien qui, lorsque vous cliquez dessus, interroge une source. Cela enclenche des méthodes de suivis, par exemple déposer un cookie ou envoyer une requête HTTP." 53 | }, 54 | "referrersEnabled": { 55 | "description": "Privacy setting label", 56 | "message": "Référents" 57 | }, 58 | "referrersEnabled_desc": { 59 | "description": "'Referrers' privacy setting description", 60 | "message": "Si activé, Chrome envoie des en-têtes de site référant avec vos demandes, alors le navigateur envoie une requête vers le serveur détenant la page Web de destination. La demande comprend le champ référant, qui indique la page sur laquelle vous étiez." 61 | }, 62 | "protectedContentEnabled": { 63 | "description": "Privacy setting label", 64 | "message": "Contenu Protégé" 65 | }, 66 | "protectedContentEnabled_desc": { 67 | "description": "'Protect content' privacy setting description (ChromeOS specific)", 68 | "message": "Si activé, Chrome fournit un identifiant unique aux plugins afin d’exécuter du contenu protégé. C'est utilisé par exemple pour fournir à Adobe Flash un accès à l’identifiant de périphérique afin de jouer de la musique ou des films qui sont protégés par un copyright." 69 | }, 70 | "alternateErrorPagesEnabled": { 71 | "description": "Privacy setting label", 72 | "message": "Page d’erreur alternative" 73 | }, 74 | "alternateErrorPagesEnabled_desc": { 75 | "description": "'Alternate error pages' privacy setting description", 76 | "message": "Si activé, Chrome utilise un service web pour aider à résoudre les erreurs de navigation. Il contrôle si Google Chrome affiche des suggestions pour la page que vous tentiez d’atteindre lorsqu’il est impossible d'atteindre l'adresse web. L’utilisateur voit des suggestions pour naviguer vers d’autres parties du site ou pour rechercher la page avec Google." 77 | }, 78 | "autofillEnabled": { 79 | "description": "Privacy setting label", 80 | "message": "Auto fill" 81 | }, 82 | "autofillEnabled_desc": { 83 | "description": "'Auto fill' privacy setting description", 84 | "message": "If enabled, Chrome offers to automatically fill in forms. If Autofill is enabled, and you submit a form, Chrome sends the data types you actually used for filling in the form in order to improve its guesses over time. The actual text you typed into the form is not sent. It's important that you use Autofill only on websites you trust, as certain websites might try to capture your information in hidden or hard-to-see fields. Some websites prevent browsers from saving text that you've entered, so Google Chrome won't be able to complete forms on those sites." 85 | }, 86 | "hotwordSearchEnabled": { 87 | "description": "Privacy setting label", 88 | "message": "Hot word search" 89 | }, 90 | "hotwordSearchEnabled_desc": { 91 | "description": "'Hot word search' privacy setting description", 92 | "message": "If enabled, Chrome will enable 'OK, Google' to start a voice search. This preference's value is a boolean, defaulting to true" 93 | }, 94 | "passwordSavingEnabled": { 95 | "description": "Privacy setting label", 96 | "message": "Password saving" 97 | }, 98 | "passwordSavingEnabled_desc": { 99 | "description": "'Password saving' privacy setting description", 100 | "message": "Offer to save your web passwords" 101 | }, 102 | "safeBrowsingEnabled": { 103 | "description": "Privacy setting label", 104 | "message": "Safe browsiong mode" 105 | }, 106 | "safeBrowsingEnabled_desc": { 107 | "description": "'Safe browsiong mode' privacy setting description", 108 | "message": "If enabled, Chrome help protect you against phishing and malware attacks. This helps prevent evil-doers from tricking you into sharing personal information with them (phishing) or installing malicious software on your computer (malware). Please be aware that Chrome will no longer be able to protect you from websites that try to steal your information or install harmful software if you disable this feature. It's not recomended to turn it off." 109 | }, 110 | "safeBrowsingExtendedReportingEnabled": { 111 | "description": "Privacy setting label", 112 | "message": "Safe browsing reporting" 113 | }, 114 | "safeBrowsingExtendedReportingEnabled_desc": { 115 | "description": "'Safe browsing reporting' privacy setting description", 116 | "message": "If enabled, Chrome will send additional information to Google when SafeBrowsing blocks a page, such as the content of the blocked page." 117 | }, 118 | "searchSuggestEnabled": { 119 | "description": "Privacy setting label", 120 | "message": "Search suggestions" 121 | }, 122 | "searchSuggestEnabled_desc": { 123 | "description": "'Search suggestions' privacy setting description", 124 | "message": "If enabled, Chrome sends the text you type into the Omnibox to your default search engine, which provides predictions of websites and searches that are likely completions of what you've typed so far." 125 | }, 126 | "spellingServiceEnabled": { 127 | "description": "Privacy setting label", 128 | "message": "Spelling service" 129 | }, 130 | "spellingServiceEnabled_desc": { 131 | "description": "'Spelling service' privacy setting description", 132 | "message": "If enabled, Chrome uses a web service to help correct spelling errors. Chrome spell-checking service sends the text you type into the browser to Google's servers. This allows Chrome to use the same spell-checking technology as Google search." 133 | }, 134 | "translationServiceEnabled": { 135 | "description": "Privacy setting label", 136 | "message": "Translation service" 137 | }, 138 | "translationServiceEnabled_desc": { 139 | "description": "'Translation service' privacy setting description", 140 | "message": "If enabled, Chrome offers to translate pages that aren't in a language you read. If you do choose to translate a web page, the text of that page is sent to Google's translation service for translation. Your cookies are not sent along with that request and, if the page you are on is encrypted with SSL, Google Chrome also sends the translation request over SSL." 141 | }, 142 | "networkPredictionEnabled": { 143 | "description": "Privacy setting label", 144 | "message": "Network prediction" 145 | }, 146 | "networkPredictionEnabled_desc": { 147 | "description": "'Network prediction' privacy setting description", 148 | "message": "If enabled, Chrome attempts to speed up your web browsing experience by pre-resolving DNS entries, prerendering sites (), and preemptively opening TCP and SSL connections to servers." 149 | }, 150 | "webRTCIPHandlingPolicy": { 151 | "description": "Privacy setting label", 152 | "message": "Web RTC IP handling" 153 | }, 154 | "webRTCIPHandlingPolicy_desc": { 155 | "description": "'Web RTC IP handling' privacy setting description", 156 | "message": "Allow users to specify the media performance/privacy tradeoffs which impacts how WebRTC traffic will be routed and how much local address information is exposed." 157 | }, 158 | "openIncognito": { 159 | "description": "Button text", 160 | "message": "Open in incognito mode" 161 | }, 162 | "startupClear": { 163 | "description": "Section name", 164 | "message": "Clear on startup" 165 | }, 166 | "removeAll": { 167 | "description": "Clear on startup setting label", 168 | "message": "Remove All" 169 | }, 170 | "removeAll_desc": { 171 | "description": "Remove All description in 'Clear on startup' section", 172 | "message": "Clears all browsing data at browser startup." 173 | }, 174 | "appcache": { 175 | "description": "Clear on startup setting label", 176 | "message": "App. cache" 177 | }, 178 | "appcache_desc": { 179 | "description": "App. cache description in 'Clear on startup' section", 180 | "message": "Clear websites Application cache at browser startup. Application caching mechanism lets web-based applications run offline. Developers can use the Application Cache (AppCache) interface to specify resources that the browser should cache and make available to offline users. Applications that are cached load and work correctly even if users click the refresh button when they are offline." 181 | }, 182 | "cache": { 183 | "description": "Clear on startup setting label", 184 | "message": "Cache" 185 | }, 186 | "cache_desc": { 187 | "description": "Cache description in 'Clear on startup' section", 188 | "message": "Clear browser cache at browser startup. Browsers store elements of webpages to speed up the loading of the webpage during your next visit." 189 | }, 190 | "cookies": { 191 | "description": "Clear on startup setting label", 192 | "message": "Cookies" 193 | }, 194 | "cookies_desc": { 195 | "description": "Cookies description in 'Clear on startup' section", 196 | "message": "Clear browser cookies at browser startup. Cookies are files created by websites you've visited that store browsing information, such as your site preferences or profile information. Cookies that are set by the site domain listed in the address bar are called 'First-party cookies', cookies that are set by other domain sources are called 'Third-party cookies' (e.g.: by ads or images embedded on the page)." 197 | }, 198 | "downloads": { 199 | "description": "Clear on startup setting label", 200 | "message": "Downloads" 201 | }, 202 | "downloads_desc": { 203 | "description": "Downloads description in 'Clear on startup' section", 204 | "message": "Clear browser download list maintained by Google Chrome at browser startup. This action doesn't remove the actual files from your computer." 205 | }, 206 | "fileSystems": { 207 | "description": "Clear on startup setting label", 208 | "message": "File systems" 209 | }, 210 | "fileSystems_desc": { 211 | "description": "File system description in 'Clear on startup' section", 212 | "message": "Clear browser file system at browser startup. A website may use sandboxed sections of a user's local filesystem. A sanboxed section is just a subdirectory named File System in your Chrome profile directory, where data is stored in files. Websites can manage (create, delete, read, write) files and folders in this sandboxed subdirectory." 213 | }, 214 | "formData": { 215 | "description": "Clear on startup setting label", 216 | "message": "Form data" 217 | }, 218 | "formData_desc": { 219 | "description": "Form data description in 'Clear on startup' section", 220 | "message": "Clear browser stored data at browser startup. If enabled, Chrome browser Auto fill data will be removed on browser startup." 221 | }, 222 | "history": { 223 | "description": "Clear on startup setting label", 224 | "message": "History" 225 | }, 226 | "history_desc": { 227 | "description": "History description in 'Clear on startup' section", 228 | "message": "Clears browser history at browser startup." 229 | }, 230 | "indexedDB": { 231 | "description": "Clear on startup setting label", 232 | "message": "Indexed DB" 233 | }, 234 | "indexedDB_desc": { 235 | "description": "Indexed DB description in 'Clear on startup' section", 236 | "message": "Clear websites Indexed DB data at browser startup. IndexedDB is an client-side storage of significant amounts of structured data and for high performance searches on this data using indexes." 237 | }, 238 | "localStorage": { 239 | "description": "Clear on startup setting label", 240 | "message": "Local storage" 241 | }, 242 | "localStorage_desc": { 243 | "description": "Local storage description in 'Clear on startup' section", 244 | "message": "Clear websites local storage data at browser startup. Local storage is a database that resides in the users browser" 245 | }, 246 | "serverBoundCertificates": { 247 | "description": "Clear on startup setting label", 248 | "message": "Server-bound certificates" 249 | }, 250 | "serverBoundCertificates_desc": { 251 | "description": "Server-bound certificates description in 'Clear on startup' section", 252 | "message": "Clear Server-bound certificates at browser startup." 253 | }, 254 | "passwords": { 255 | "description": "Clear on startup setting label", 256 | "message": "Passwords" 257 | }, 258 | "passwords_desc": { 259 | "description": "Passwords description in 'Clear on startup' section", 260 | "message": "Clear stored passwords at browser startup. Google Chrome can save your usernames and passwords for different websites. The browser can then automatically complete the sign-in fields for you when you next visit these websites." 261 | }, 262 | "pluginData": { 263 | "description": "Clear on startup setting label", 264 | "message": "Plugin data" 265 | }, 266 | "pluginData_desc": { 267 | "description": "Plugin data description in 'Clear on startup' section", 268 | "message": "Clear plugins data at browser startup. Websites also use different types of local storage as well, including Adobe Flash Player Local Shared Objects (LSOs), referred to by some people as Flash cookies, so this option will delete client-side data stored by plug-ins that obey the NPAPI ClearSiteData API, such as Flash Player.”" 269 | }, 270 | "serviceWorkers": { 271 | "description": "Clear on startup setting label", 272 | "message": "Service Workers" 273 | }, 274 | "serviceWorkers_desc": { 275 | "description": "Service workers description in 'Clear on startup' section", 276 | "message": "Clear Service Workers data at browser startup." 277 | }, 278 | "webSQL": { 279 | "description": "Clear on startup setting label", 280 | "message": "Web SQL" 281 | }, 282 | "webSQL_desc": { 283 | "description": "Web SQL description in 'Clear on startup' section", 284 | "message": "Clear websites WebSQL data at browser startup. Web SQL Database is a database that resides in the users browser." 285 | }, 286 | "addCookie": { 287 | "description": "Text in Cookies section", 288 | "message": "Add cookie" 289 | }, 290 | "editCookie": { 291 | "description": "Text in Cookies section", 292 | "message": "Edit cookie" 293 | }, 294 | "cookieDialog_domain": { 295 | "description": "Text in Cookies edit dialog", 296 | "message": "Domain" 297 | }, 298 | "cookieDialog_name": { 299 | "description": "Text in Cookies edit dialog", 300 | "message": "Name" 301 | }, 302 | "cookieDialog_value": { 303 | "description": "Text in Cookies edit dialog", 304 | "message": "Value" 305 | }, 306 | "cookieDialog_path": { 307 | "description": "Text in Cookies edit dialog", 308 | "message": "Path" 309 | }, 310 | "cookieDialog_expDate": { 311 | "description": "Text in Cookies edit dialog", 312 | "message": "Exp. date" 313 | }, 314 | "cookieDialog_expTime": { 315 | "description": "Text in Cookies edit dialog", 316 | "message": "Exp. time" 317 | }, 318 | "cookieDialog_hostOnyl": { 319 | "description": "Text in Cookies edit dialog", 320 | "message": "Host only" 321 | }, 322 | "cookieDialog_httpOnyl": { 323 | "description": "Text in Cookies edit dialog", 324 | "message": "HTTP only" 325 | }, 326 | "cookieDialog_secure": { 327 | "description": "Text in Cookies edit dialog", 328 | "message": "Secure" 329 | }, 330 | "cookieDialog_session": { 331 | "description": "Text in Cookies edit dialog", 332 | "message": "Session" 333 | }, 334 | "cookieDialog_storeId": { 335 | "description": "Text in Cookies edit dialog", 336 | "message": "Store ID" 337 | }, 338 | "cookieDialog_add": { 339 | "description": "Button text in Cookies edit dialog", 340 | "message": "Add" 341 | }, 342 | "cookieDialog_update": { 343 | "description": "Button text in Cookies edit dialog", 344 | "message": "Update" 345 | }, 346 | "cookieDialog_delete": { 347 | "description": "Button text in Cookies edit dialog", 348 | "message": "Delete" 349 | }, 350 | "cookieDialog_cancel": { 351 | "description": "Button text in Cookies 'delete all' dialog", 352 | "message": "Cancel" 353 | }, 354 | "cookieDialog_deleteAll_msg": { 355 | "description": "text in Cookies 'delete all' dialog", 356 | "message": "You are about to delete all cookies, are you sure ?" 357 | }, 358 | "whitelistCookieDomain": { 359 | "description": "text when hovering over Whitelist button for Cookie Domain", 360 | "message": "This button adds all cookies from a domain to a Whitelist, preventing them from being automatically deleted, usefull if you want to stay logged in to some websites but still want all other cookies to be deleted" 361 | }, 362 | "whitelistSublistCookie": { 363 | "description": "text when hovering over Whitelist button for Sublist Cookies", 364 | "message": "This button adds a specific cookie to a Whitelist, preventing them from being automatically deleted, usefull if you want to stay logged in to some websites but still want all other cookies to be deleted" 365 | }, 366 | "additionalPermissions": { 367 | "description": "Label for requesting additional permission setting", 368 | "message": "Additional permissions" 369 | }, 370 | "additionalPermissions_desc": { 371 | "description": "Description for requesting additional permission setting", 372 | "message": "You need to allow host permission to be able to manipulate cookies and network." 373 | }, 374 | "additionalPermissions_notification": { 375 | "description": "Notification message for the features that require additional permission", 376 | "message": "Please enable Additional permissions to use the feature" 377 | }, 378 | "activeTabCookies": { 379 | "description": "Setting label in cookies section", 380 | "message": "Active tab cookies" 381 | }, 382 | "activeTabCookies_desc": { 383 | "description": "Active tab setting description", 384 | "message": "If enabled the cookies tab will always be filtered with the cookie data of active tab page domain." 385 | }, 386 | "blockUserAgent": { 387 | "description": "Label for blocking user agent setting", 388 | "message": "Block user agent" 389 | }, 390 | "blockUserAgent_desc": { 391 | "description": "Descrption for blocking user agent setting", 392 | "message": "If enabled user agent data will not be sent with request, that mean websites couldn't determine the browser you are using, that why in some cases websites will not allow you to view the content when user agent is blocked." 393 | }, 394 | "collectHeaders": { 395 | "description": "Setting label in network section", 396 | "message": "Collect headers" 397 | }, 398 | "collectHeaders_desc": { 399 | "description": "'Collect headers' setting description", 400 | "message": "If enabled the headers will start to be collected, only last 1000 headers would be shown." 401 | }, 402 | "downloadAll": { 403 | "description": "button text in network section", 404 | "message": "Download all" 405 | }, 406 | "deleteAll": { 407 | "description": "General text used in various places", 408 | "message": "Delete all" 409 | } 410 | } -------------------------------------------------------------------------------- /src/_locales/ru/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "chrome_extension_name": { 3 | "description": "Extension name, used in the Web Store", 4 | "message": "Privacy manager" 5 | }, 6 | "chrome_extension_description": { 7 | "description": "Extension description, used in the Web Store", 8 | "message": "Управление личными настройками, данными браузера, быстрый доступ к активной странице в режиме Incognito, работа с куки и мониторинг сетевого трафика" 9 | }, 10 | "main_tab": { 11 | "description": "Tab label", 12 | "message": "Главное" 13 | }, 14 | "cookies_general": { 15 | "description": "General text used in various places", 16 | "message": "Куки" 17 | }, 18 | "network_tab": { 19 | "description": "Tab label", 20 | "message": "Сеть" 21 | }, 22 | "off": { 23 | "description": "Switch's 'off' text", 24 | "message": "Выкл" 25 | }, 26 | "on": { 27 | "description": "Switch's 'on' text", 28 | "message": "Вкл" 29 | }, 30 | "privacyManagement": { 31 | "description": "Section name", 32 | "message": "Личные настройки" 33 | }, 34 | "regularSettingChangeIncognito_error": { 35 | "description": "Error thrown in the incognito mode when privacy setting is being modified", 36 | "message": "Не возможно изменить регулярные параметры из окна инкогнито, чтобы изменить параметр воспользуйтесь пожалуйста прикрепленным профилем (профиль из которого был разрещен доступ расширения в режиме инкогнито)." 37 | }, 38 | "thirdPartyCookiesAllowed": { 39 | "description": "Privacy setting label", 40 | "message": "Сторонние куки:" 41 | }, 42 | "thirdPartyCookiesAllowed_desc": { 43 | "description": "Privacy setting description", 44 | "message": "Если выключено, Хром блокирует сторонние сайты от установки данных и файлов в куки (cookie) браузера." 45 | }, 46 | "hyperlinkAuditingEnabled": { 47 | "description": "Privacy setting label", 48 | "message": "Аудит ссылок" 49 | }, 50 | "hyperlinkAuditingEnabled_desc": { 51 | "description": "Privacy setting description", 52 | "message": "Если включено, Хром посылает проверочный запрос для проверки гиперссылок." 53 | }, 54 | "referrersEnabled": { 55 | "description": "Privacy setting label", 56 | "message": "Реферер" 57 | }, 58 | "referrersEnabled_desc": { 59 | "description": "'Referrers' privacy setting description", 60 | "message": "Если включено, Хром посылает Реферер (referer) headers вместе с запросами." 61 | }, 62 | "protectedContentEnabled": { 63 | "description": "Privacy setting label", 64 | "message": "Защищенный контент" 65 | }, 66 | "protectedContentEnabled_desc": { 67 | "description": "'Protect content' privacy setting description (ChromeOS specific)", 68 | "message": "Если эта опция включена, Chrome предоставляет уникальный идентификатор плагинам, чтобы запустить защищенное содержимое. К примеру, это может быть использовано для предоставления Adobe Flash доступа к идентификатору устройства, чтобы воспроизводить музыку или фильмы, которые защищены авторским правом." 69 | }, 70 | "alternateErrorPagesEnabled": { 71 | "description": "Privacy setting label", 72 | "message": "Разрешения проблем:" 73 | }, 74 | "alternateErrorPagesEnabled_desc": { 75 | "description": "'Alternate error pages' privacy setting description", 76 | "message": "Если включено, Хром использует веб-службу для разрешения проблем, связанных с навигацией." 77 | }, 78 | "autofillEnabled": { 79 | "description": "Privacy setting label", 80 | "message": "Автозаполнение" 81 | }, 82 | "autofillEnabled_desc": { 83 | "description": "'Auto fill' privacy setting description", 84 | "message": "Если включено, Хром автоматически заполняет веб-форму." 85 | }, 86 | "hotwordSearchEnabled": { 87 | "description": "Privacy setting label", 88 | "message": "Поиск 'ок гугл'" 89 | }, 90 | "hotwordSearchEnabled_desc": { 91 | "description": "'Hot word search' privacy setting description", 92 | "message": "Если этот параметр включен, Chrome позволит «OK, Google» начать голосовой поиск." 93 | }, 94 | "passwordSavingEnabled": { 95 | "description": "Privacy setting label", 96 | "message": "Сохранение пароля:" 97 | }, 98 | "passwordSavingEnabled_desc": { 99 | "description": "'Password saving' privacy setting description", 100 | "message": "Предложение о сохраниние пароля" 101 | }, 102 | "safeBrowsingEnabled": { 103 | "description": "Privacy setting label", 104 | "message": "Безопасный режим" 105 | }, 106 | "safeBrowsingEnabled_desc": { 107 | "description": "'Safe browsiong mode' privacy setting description", 108 | "message": "Включить/выключить защиту от фишинга и вредоносного ПО." 109 | }, 110 | "safeBrowsingExtendedReportingEnabled": { 111 | "description": "Privacy setting label", 112 | "message": "Отчет опасных сайтов" 113 | }, 114 | "safeBrowsingExtendedReportingEnabled_desc": { 115 | "description": "'Safe browsing reporting' privacy setting description", 116 | "message": "Если этот параметр включен, Chrome отправит дополнительную информацию в Google, когда SafeBrowsing блокирует страницы." 117 | }, 118 | "searchSuggestEnabled": { 119 | "description": "Privacy setting label", 120 | "message": "Подсказки поиска" 121 | }, 122 | "searchSuggestEnabled_desc": { 123 | "description": "'Search suggestions' privacy setting description", 124 | "message": "Если включено, Хром Использует подсказки для завершения поисковых запросов и URL, вводимых в адресную строку." 125 | }, 126 | "spellingServiceEnabled": { 127 | "description": "Privacy setting label", 128 | "message": "Пров. правописания" 129 | }, 130 | "spellingServiceEnabled_desc": { 131 | "description": "'Spelling service' privacy setting description", 132 | "message": "Если включено, Хром использует веб-службу для проверки правописания." 133 | }, 134 | "translationServiceEnabled": { 135 | "description": "Privacy setting label", 136 | "message": "Перевод страниц" 137 | }, 138 | "translationServiceEnabled_desc": { 139 | "description": "'Translation service' privacy setting description", 140 | "message": "Если включено, Хром предлагает перевести страницу с иностранного языка." 141 | }, 142 | "networkPredictionEnabled": { 143 | "description": "Privacy setting label", 144 | "message": "Сетевые подсказки" 145 | }, 146 | "networkPredictionEnabled_desc": { 147 | "description": "'Network prediction' privacy setting description", 148 | "message": "Если включено, Хром предсказывает сетевые действия для ускорения загрузки страниц." 149 | }, 150 | "webRTCIPHandlingPolicy": { 151 | "description": "Privacy setting label", 152 | "message": "Настройка WebRTC IP" 153 | }, 154 | "webRTCIPHandlingPolicy_desc": { 155 | "description": "'Web RTC IP handling' privacy setting description", 156 | "message": "Позволяют пользователям указывать компромиссы производительность/конфиденциальности средства массовой информации, которые влияет как WebRTC трафик будет маршрутизироваться и сколько локальной информаций посылается." 157 | }, 158 | "openIncognito": { 159 | "description": "Button text", 160 | "message": "Открыть в режиме инкогнито" 161 | }, 162 | "startupClear": { 163 | "description": "Section name", 164 | "message": "Стереть при запуске" 165 | }, 166 | "removeAll": { 167 | "description": "Clear on startup setting label", 168 | "message": "Удалить все" 169 | }, 170 | "removeAll_desc": { 171 | "description": "Remove All description in 'Clear on startup' section", 172 | "message": "Очистить все данные навигации во время старта браузера." 173 | }, 174 | "appcache": { 175 | "description": "Clear on startup setting label", 176 | "message": "Кэш приложений" 177 | }, 178 | "appcache_desc": { 179 | "description": "App. cache description in 'Clear on startup' section", 180 | "message": "Очистить кэш приложений (Application cache) во время загрузки Браузера." 181 | }, 182 | "cache": { 183 | "description": "Clear on startup setting label", 184 | "message": "Кэш" 185 | }, 186 | "cache_desc": { 187 | "description": "Cache description in 'Clear on startup' section", 188 | "message": "Очистить кэш во время загрузки Браузера." 189 | }, 190 | "cookies": { 191 | "description": "Clear on startup setting label", 192 | "message": "Куки" 193 | }, 194 | "cookies_desc": { 195 | "description": "Cookies description in 'Clear on startup' section", 196 | "message": "Очистить Куки (cookies) во время загрузки Браузера." 197 | }, 198 | "downloads": { 199 | "description": "Clear on startup setting label", 200 | "message": "Загрузки" 201 | }, 202 | "downloads_desc": { 203 | "description": "Downloads description in 'Clear on startup' section", 204 | "message": "Очистить историю загрузок во время загрузки Браузера." 205 | }, 206 | "fileSystems": { 207 | "description": "Clear on startup setting label", 208 | "message": "Файл. Система" 209 | }, 210 | "fileSystems_desc": { 211 | "description": "File system description in 'Clear on startup' section", 212 | "message": "Очистить Файловую систему (File system) во время загрузки Браузера." 213 | }, 214 | "formData": { 215 | "description": "Clear on startup setting label", 216 | "message": "Данные форм" 217 | }, 218 | "formData_desc": { 219 | "description": "Form data description in 'Clear on startup' section", 220 | "message": "Очистить сохраненные данные авто-заполнения форм во время загрузки Браузера." 221 | }, 222 | "history": { 223 | "description": "Clear on startup setting label", 224 | "message": "История" 225 | }, 226 | "history_desc": { 227 | "description": "History description in 'Clear on startup' section", 228 | "message": "Очистить историю просмотров во время загрузки Браузера." 229 | }, 230 | "indexedDB": { 231 | "description": "Clear on startup setting label", 232 | "message": "Индексированная БД" 233 | }, 234 | "indexedDB_desc": { 235 | "description": "Indexed DB description in 'Clear on startup' section", 236 | "message": "Очистить индексируемые БД (indexedDB) во время загрузки Браузера." 237 | }, 238 | "localStorage": { 239 | "description": "Clear on startup setting label", 240 | "message": "Локальное хранилище" 241 | }, 242 | "localStorage_desc": { 243 | "description": "Local storage description in 'Clear on startup' section", 244 | "message": "Очистить Интернет-хранилище (Local Storage) во время загрузки Браузера." 245 | }, 246 | "serverBoundCertificates": { 247 | "description": "Clear on startup setting label", 248 | "message": "Сертификаты сервера" 249 | }, 250 | "serverBoundCertificates_desc": { 251 | "description": "Server-bound certificates description in 'Clear on startup' section", 252 | "message": "Удалить Server-bound certificates при запуске браузера." 253 | }, 254 | "passwords": { 255 | "description": "Clear on startup setting label", 256 | "message": "Пароли" 257 | }, 258 | "passwords_desc": { 259 | "description": "Passwords description in 'Clear on startup' section", 260 | "message": "Очистить сохраненные пароли во время загрузки Браузера." 261 | }, 262 | "pluginData": { 263 | "description": "Clear on startup setting label", 264 | "message": "Модули" 265 | }, 266 | "pluginData_desc": { 267 | "description": "Plugin data description in 'Clear on startup' section", 268 | "message": "Очистить данные подключаемых модулей во время загрузки Браузера." 269 | }, 270 | "serviceWorkers": { 271 | "description": "Clear on startup setting label", 272 | "message": "Service Workers" 273 | }, 274 | "serviceWorkers_desc": { 275 | "description": "Service workers description in 'Clear on startup' section", 276 | "message": "Удалить данные Service Workers при запуске браузера." 277 | }, 278 | "webSQL": { 279 | "description": "Clear on startup setting label", 280 | "message": "Web SQL" 281 | }, 282 | "webSQL_desc": { 283 | "description": "Web SQL description in 'Clear on startup' section", 284 | "message": "Очистить WebSQL-данные сайтов во время загрузки Браузера." 285 | }, 286 | "addCookie": { 287 | "description": "Text in Cookies section", 288 | "message": "Добавить куки" 289 | }, 290 | "editCookie": { 291 | "description": "Text in Cookies section", 292 | "message": "Редактировать куки" 293 | }, 294 | "cookieDialog_domain": { 295 | "description": "Text in Cookies edit dialog", 296 | "message": "Домен" 297 | }, 298 | "cookieDialog_name": { 299 | "description": "Text in Cookies edit dialog", 300 | "message": "Имя" 301 | }, 302 | "cookieDialog_value": { 303 | "description": "Text in Cookies edit dialog", 304 | "message": "Значение" 305 | }, 306 | "cookieDialog_path": { 307 | "description": "Text in Cookies edit dialog", 308 | "message": "Путь" 309 | }, 310 | "cookieDialog_expDate": { 311 | "description": "Text in Cookies edit dialog", 312 | "message": "До даты" 313 | }, 314 | "cookieDialog_expTime": { 315 | "description": "Text in Cookies edit dialog", 316 | "message": "До времени" 317 | }, 318 | "cookieDialog_hostOnyl": { 319 | "description": "Text in Cookies edit dialog", 320 | "message": "Только Host" 321 | }, 322 | "cookieDialog_httpOnyl": { 323 | "description": "Text in Cookies edit dialog", 324 | "message": "Только HTTP" 325 | }, 326 | "cookieDialog_secure": { 327 | "description": "Text in Cookies edit dialog", 328 | "message": "Зашищенный" 329 | }, 330 | "cookieDialog_session": { 331 | "description": "Text in Cookies edit dialog", 332 | "message": "Сессия" 333 | }, 334 | "cookieDialog_storeId": { 335 | "description": "Text in Cookies edit dialog", 336 | "message": "ID базы" 337 | }, 338 | "cookieDialog_add": { 339 | "description": "Button text in Cookies edit dialog", 340 | "message": "Добавить" 341 | }, 342 | "cookieDialog_update": { 343 | "description": "Button text in Cookies edit dialog", 344 | "message": "Обновить" 345 | }, 346 | "cookieDialog_delete": { 347 | "description": "Button text in Cookies edit dialog", 348 | "message": "Удалить" 349 | }, 350 | "cookieDialog_cancel": { 351 | "description": "Button text in Cookies 'delete all' dialog", 352 | "message": "Отмена" 353 | }, 354 | "cookieDialog_deleteAll_msg": { 355 | "description": "text in Cookies 'delete all' dialog", 356 | "message": "Вы собираетесь удалить все cookies, вы уверены?" 357 | }, 358 | "whitelistCookieDomain": { 359 | "description": "text when hovering over Whitelist button for Cookie Domain", 360 | "message": "Эта кнопка добавляет все файлы cookie домена в белый список, предотвращая их автоматическое удаление. Это полезно, если вы хотите сохранить авторизацию на некоторых сайтах, но при этом хотите, чтобы все остальные файлы cookie были удалены." 361 | }, 362 | "whitelistSublistCookie": { 363 | "description": "text when hovering over Whitelist button for Sublist Cookies", 364 | "message": "Эта кнопка добавляет конкретный файл cookie в белый список, предотвращая его автоматическое удаление. Это полезно, если вы хотите сохранить авторизацию на некоторых сайтах, но при этом хотите, чтобы все остальные файлы cookie были удалены." 365 | }, 366 | "additionalPermissions": { 367 | "description": "Label for requesting additional permission setting", 368 | "message": "Доп. разрешения" 369 | }, 370 | "additionalPermissions_desc": { 371 | "description": "Description for requesting additional permission setting", 372 | "message": "Вам нужно дополнительное разрешение чтобы манипулировать куки и сетевой трафик." 373 | }, 374 | "additionalPermissions_notification": { 375 | "description": "Notification message for the features that require additional permission", 376 | "message": "Пожалуйста включите 'Доп. разрешения' чтобы воспользоваться функцией" 377 | }, 378 | "activeTabCookies": { 379 | "description": "Setting label in cookies section", 380 | "message": "Куки текущей вкладки" 381 | }, 382 | "activeTabCookies_desc": { 383 | "description": "Active tab setting description", 384 | "message": "Если включено, Куки будут всегда отфильтрованным по домену текущей активной вкладки." 385 | }, 386 | "blockUserAgent": { 387 | "description": "Label for blocking user agent setting", 388 | "message": "Блокировать User Agent" 389 | }, 390 | "blockUserAgent_desc": { 391 | "description": "Descrption for blocking user agent setting", 392 | "message": "Если включено, user-agent не будет посылаться вместе с запросом. Это значит, что сайты не смогут получать данные о вашем браузере, поэтому в некоторых случаях сайты могут не показать вам свой контент." 393 | }, 394 | "collectHeaders": { 395 | "description": "Setting label in network section", 396 | "message": "Собирать заголовки" 397 | }, 398 | "collectHeaders_desc": { 399 | "description": "'Collect headers' setting description", 400 | "message": "Если включено, заголовки (HTTP Headers) будут собираться с каждым запросом, показываться будут только последние 1000 заголовков." 401 | }, 402 | "downloadAll": { 403 | "description": "button text in network section", 404 | "message": "Скачать все" 405 | }, 406 | "deleteAll": { 407 | "description": "General text used in various places", 408 | "message": "Удалить все" 409 | } 410 | } -------------------------------------------------------------------------------- /src/agent-block.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id" : 1, 4 | "priority": 1, 5 | "action" : { 6 | "type" : "modifyHeaders", 7 | "requestHeaders": [ 8 | { 9 | "header": "user-agent", 10 | "operation": "remove" 11 | } 12 | ] 13 | }, 14 | "condition" : { 15 | "urlFilter" : "*", 16 | "resourceTypes": [ 17 | "main_frame", 18 | "sub_frame" 19 | ] 20 | } 21 | } 22 | ] -------------------------------------------------------------------------------- /src/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | :root 20 | { 21 | --color-primary: #679c16; 22 | --color-secondary: #989898; 23 | } 24 | 25 | body 26 | { 27 | margin: 0px; 28 | position: relative; 29 | max-width: 700px; 30 | background-color: #F9F9F9; 31 | font-family: Arial, Helvetica, sans-serif; 32 | } 33 | 34 | #main 35 | { 36 | min-width: 520px; 37 | } 38 | 39 | [lang="ru"] 40 | { 41 | min-width: 550px; 42 | } 43 | 44 | pm-toggle 45 | { 46 | width: 100%; 47 | } 48 | 49 | .controls 50 | { 51 | height: 30px; 52 | display: flex; 53 | justify-content: flex-end; 54 | padding: 0px; 55 | align-items: center; 56 | } 57 | 58 | /******************************************************************************* 59 | * Buttons 60 | ******************************************************************************/ 61 | pm-button 62 | { 63 | border-radius: 3px; 64 | } 65 | pm-button[theme="secondary"] 66 | { 67 | width: 100%; 68 | border-radius: 5px; 69 | } 70 | 71 | /******************************************************************************* 72 | * Tabs 73 | ******************************************************************************/ 74 | 75 | pm-panel:not([hidden]) 76 | { 77 | padding: 10px; 78 | display: block; 79 | } 80 | 81 | /******************************************************************************* 82 | * Settings list 83 | ******************************************************************************/ 84 | .settings-list 85 | { 86 | list-style: none; 87 | padding: 0px; 88 | margin: 0px; 89 | } 90 | 91 | .settings-list li 92 | { 93 | margin-top: 3px; 94 | display: flex; 95 | align-items: center; 96 | } 97 | 98 | /******************************************************************************* 99 | * DIALOG 100 | ******************************************************************************/ 101 | pm-dialog .fieldset-container 102 | { 103 | display: flex; 104 | justify-content: space-between; 105 | } 106 | pm-dialog form label 107 | { 108 | display: flex; 109 | justify-content: flex-end; 110 | align-items: center; 111 | flex-wrap: wrap; 112 | padding: 0px 20px; 113 | } 114 | pm-dialog form label span 115 | { 116 | padding: 0px 10px; 117 | } 118 | pm-dialog form input[type="text"], 119 | pm-dialog form input[type="date"], 120 | pm-dialog form input[type="time"] 121 | { 122 | width: 150px; 123 | } 124 | pm-dialog form input[type="number"] 125 | { 126 | width: 30px; 127 | } 128 | pm-dialog .controls 129 | { 130 | padding: 0px 10px; 131 | height: 30px; 132 | display: flex; 133 | justify-content: flex-end; 134 | align-items: center; 135 | } 136 | 137 | pm-dialog.cookies.add #delete-cookie 138 | { 139 | display: none; 140 | } 141 | 142 | /******************************************************************************* 143 | * Main 144 | ******************************************************************************/ 145 | 146 | html[lang="de"] #panel-main 147 | { 148 | min-width: 585px; 149 | } 150 | 151 | #panel-main .settings-list-container 152 | { 153 | justify-content: center; 154 | } 155 | 156 | #panel-main section 157 | { 158 | display: flex; 159 | flex-direction: column; 160 | vertical-align: top; 161 | padding-right: 28px; 162 | } 163 | 164 | #panel-main section:last-child 165 | { 166 | padding-right: 0px; 167 | } 168 | 169 | #panel-main section h2 170 | { 171 | font-size: 21px; 172 | font-weight: bold; 173 | padding-bottom: 8px; 174 | color: var(--color-primary); 175 | margin: 0px; 176 | } 177 | 178 | #incognito 179 | { 180 | margin-top: auto; 181 | width: 100%; 182 | display: inline-block; 183 | border-radius: 5px; 184 | border: 1px solid var(--color-secondary); 185 | color: var(--color-primary); 186 | font-size: 13px; 187 | font-weight: bold; 188 | padding: 8px 0px; 189 | text-decoration: none; 190 | background-color: transparent; 191 | } 192 | 193 | #incognito:hover 194 | { 195 | cursor: pointer; 196 | box-shadow: 0px 0px 1px #000; 197 | } 198 | 199 | /******************************************************************************* 200 | * Cookies tab 201 | ******************************************************************************/ 202 | .settings-list-container 203 | { 204 | display: flex; 205 | } 206 | 207 | .settings-list-container .separator 208 | { 209 | flex-grow: 1; 210 | } 211 | 212 | section h4 213 | { 214 | font-weight: bold; 215 | color:var(--color-primary); 216 | } 217 | 218 | #cookiesContainer 219 | { 220 | position: relative; 221 | } 222 | 223 | #search-domain 224 | { 225 | -webkit-appearance: textfield; 226 | flex-grow: 1; 227 | padding: 5px; 228 | height: 30px; 229 | margin: 5px; 230 | } 231 | -------------------------------------------------------------------------------- /src/css/pm-tab-panel.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | pm-tab-panel 20 | { 21 | --color-primary: #679c16; 22 | --color-secondary: #989898; 23 | display: block; 24 | } 25 | 26 | pm-tabs 27 | { 28 | display: inline-block; 29 | width: 100%; 30 | margin: 0px; 31 | padding: 0px; 32 | border-bottom: 1px solid var(--color-secondary); 33 | } 34 | 35 | pm-tab 36 | { 37 | list-style: none; 38 | float: left; 39 | position: relative; 40 | top: 2px; 41 | margin: 0 .2em 1px 0; 42 | border-bottom: 0 !important; 43 | white-space: nowrap; 44 | color: var(--color-secondary); 45 | padding: 10px 16px; 46 | cursor: pointer; 47 | text-decoration: none; 48 | font-weight: bold; 49 | } 50 | 51 | pm-tab[aria-selected] 52 | { 53 | color: #459e00; 54 | background: transparent url(../../img/pm-tab-panel/tabs-arrow.svg) no-repeat bottom center; 55 | } 56 | 57 | pm-panels 58 | { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /src/img/logo-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Privacy-Managers/Privacy-Manager/ff03ac42d94e6b8281be7e20cd7de77db215e7f1/src/img/logo-16x16.png -------------------------------------------------------------------------------- /src/img/logo-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Privacy-Managers/Privacy-Manager/ff03ac42d94e6b8281be7e20cd7de77db215e7f1/src/img/logo-48x48.png -------------------------------------------------------------------------------- /src/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Privacy-Managers/Privacy-Manager/ff03ac42d94e6b8281be7e20cd7de77db215e7f1/src/img/logo.png -------------------------------------------------------------------------------- /src/js/back.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | "use strict"; 20 | 21 | const browser = require("webextension-polyfill"); 22 | self.browser = browser; 23 | const {additionalPermission, addRequestListener, removeRequestListener, 24 | updateRequestObj, addBlockAgentListener, removeBlockAgentListener, 25 | deleteCookies} = require("./common"); 26 | const {browsingData} = require("./data"); 27 | 28 | let collectedRequests = []; 29 | let collectedRequestsReady = false; 30 | let savingProgress = false; // Network requests saving indication. 31 | getCollectedNetworkRequests().then((requests) => 32 | { 33 | collectedRequests = requests; 34 | collectedRequestsReady = true; 35 | }); 36 | 37 | const requestCollectionLength = 500; 38 | 39 | async function profileStart() 40 | { 41 | const data = await browser.storage.local.get("cookieWhitelist"); 42 | if (!data || !data.cookieWhitelist) 43 | browser.storage.local.set({"cookieWhitelist": {} }); 44 | const {settingList} = await browser.storage.local.get("settingList"); 45 | deleteBrowsingData(settingList); 46 | deleteCollectedNetworkRequests(); 47 | } 48 | 49 | async function getCollectedNetworkRequests() 50 | { 51 | try 52 | { 53 | const {networkRequests} = await browser.storage.local.get("networkRequests"); 54 | return networkRequests ? JSON.parse(networkRequests) : []; 55 | } 56 | catch(e) 57 | { 58 | console.error("Couldn't fetch networkRequests from storage"); 59 | return []; 60 | } 61 | } 62 | 63 | function deleteCollectedNetworkRequests() 64 | { 65 | collectedRequests = []; 66 | try 67 | { 68 | return browser.storage.local.remove("networkRequests"); 69 | } 70 | catch(e) 71 | { 72 | console.error("Couldn't delete networkRequests from the storage"); 73 | } 74 | } 75 | 76 | function saveRequestsToStorage() 77 | { 78 | browser.storage.local.set({"networkRequests": JSON.stringify(collectedRequests)}); 79 | } 80 | 81 | browser.storage.local.get("settingList").then((data) => 82 | { 83 | if (data.settingList && data.settingList.collectHeaders) 84 | startCollectingRequests(); 85 | 86 | if (data.settingList && data.settingList.blockUserAgent) 87 | addBlockAgentListener(); 88 | }); 89 | 90 | function deleteBrowsingData(data) 91 | { 92 | if (!data) 93 | return; 94 | 95 | // Filter "data" object to only match properties from "browsingData". 96 | let browsingDataObj = Object.keys(data).filter((key) => 97 | { 98 | return browsingData.includes(key); 99 | }).reduce((accumulator, dataType) => 100 | { 101 | accumulator[dataType] = data[dataType]; 102 | return accumulator; 103 | }, {}); 104 | 105 | if (browsingDataObj.removeAll == true) 106 | { 107 | browsingDataObj = browsingData.reduce((accumulator, dataType) => 108 | { 109 | if (dataType != "removeAll") 110 | accumulator[dataType] = true; 111 | 112 | return accumulator; 113 | }, {}); 114 | 115 | if (browsingDataObj.cookies) 116 | { 117 | deleteCookies(); 118 | } 119 | browsingDataObj.cookies = false; 120 | browser.browsingData.remove({}, browsingDataObj); 121 | } 122 | else 123 | { 124 | delete browsingDataObj.removeAll; 125 | if (browsingDataObj.cookies) 126 | { 127 | deleteCookies(); 128 | } 129 | browsingDataObj.cookies = false; 130 | browser.browsingData.remove({}, browsingDataObj); 131 | } 132 | } 133 | 134 | function startCollectingRequests() 135 | { 136 | addRequestListener(onSendHeaders, onHeadersReceived); 137 | } 138 | 139 | function stopCollectingRequests() 140 | { 141 | removeRequestListener(onSendHeaders, onHeadersReceived); 142 | } 143 | 144 | function onSendHeaders(details) 145 | { 146 | const itemObj = updateRequestObj(details, "send"); 147 | addToRequestArray(itemObj); 148 | } 149 | 150 | function onHeadersReceived(details) 151 | { 152 | const itemObj = updateRequestObj(details, "receive"); 153 | addToRequestArray(itemObj); 154 | } 155 | 156 | function addToRequestArray(details) 157 | { 158 | if (collectedRequests.length > requestCollectionLength) 159 | collectedRequests.shift(); 160 | 161 | collectedRequests.push(details); 162 | // Optimization for saving requests in storage. 163 | if (!savingProgress) 164 | { 165 | savingProgress = true; 166 | setTimeout(() => 167 | { 168 | savingProgress = false; 169 | saveRequestsToStorage(); 170 | }, 5000); 171 | } 172 | } 173 | 174 | async function handleMessage(request) 175 | { 176 | /* eslint-disable */ 177 | switch (request.message) 178 | { 179 | case "deleteBrowsingData": 180 | const {settingList} = await browser.storage.local.get("settingList"); 181 | deleteBrowsingData(settingList); 182 | break; 183 | case "getCollectedRequests": 184 | if (collectedRequestsReady) { 185 | return collectedRequests 186 | } else { 187 | return getCollectedNetworkRequests(); 188 | } 189 | case "deleteCollectedNetworkRequests": 190 | return deleteCollectedNetworkRequests(); 191 | default: 192 | break; 193 | } 194 | } 195 | 196 | browser.storage.onChanged.addListener(async(change) => 197 | { 198 | if (change.settingList) 199 | { 200 | const result = await browser.permissions.contains(additionalPermission); 201 | let newValue = change.settingList.newValue.collectHeaders; 202 | let oldValue = change.settingList.oldValue; 203 | if (!oldValue || newValue != oldValue.collectHeaders) 204 | { 205 | if (result && newValue) 206 | startCollectingRequests(); 207 | else 208 | stopCollectingRequests(); 209 | } 210 | 211 | newValue = change.settingList.newValue.blockUserAgent; 212 | oldValue = change.settingList.oldValue; 213 | if (oldValue && newValue != oldValue.blockUserAgent) 214 | { 215 | if (result && newValue) 216 | addBlockAgentListener(); 217 | else 218 | removeBlockAgentListener(); 219 | } 220 | } 221 | }); 222 | 223 | browser.permissions.onRemoved.addListener(() => 224 | { 225 | removeBlockAgentListener(); 226 | removeRequestListener(onSendHeaders, onHeadersReceived); 227 | }); 228 | 229 | browser.runtime.onMessage.addListener(handleMessage); 230 | 231 | // Fired on a profile start up 232 | browser.runtime.onInstalled.addListener(profileStart); 233 | browser.runtime.onStartup.addListener(profileStart); 234 | -------------------------------------------------------------------------------- /src/js/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | "use strict"; 20 | 21 | const browser = require("webextension-polyfill"); 22 | const additionalPermission = {"origins": [""]}; 23 | const getAllCookies = browser.cookies.getAll; 24 | const removeCookie = browser.cookies.remove; 25 | const onBeforeSendHeaders = browser.webRequest.onBeforeSendHeaders; 26 | const onSendHeaders = browser.webRequest.onSendHeaders; 27 | const onHeadersReceived = browser.webRequest.onHeadersReceived; 28 | 29 | function getUrl(domain, path, isSecure) 30 | { 31 | return "http" + (isSecure ? "s" : "") + "://" + domain + path; 32 | } 33 | function addRequestListener(sendHeaders, headersReceived) 34 | { 35 | const urls = {urls: [""]}; 36 | onSendHeaders.addListener(sendHeaders, urls, ["requestHeaders"]); 37 | onHeadersReceived.addListener(headersReceived, urls, ["responseHeaders"]); 38 | } 39 | 40 | function removeRequestListener(sendHeaders, headersReceived) 41 | { 42 | onSendHeaders.removeListener(sendHeaders); 43 | onHeadersReceived.removeListener(headersReceived); 44 | } 45 | 46 | function updateRequestObj(details, actionType) 47 | { 48 | const {url, type} = details; 49 | return {texts: {url, type}, data: {type: actionType}, request: details}; 50 | } 51 | 52 | /******************************************************************************* 53 | * Block user agent 54 | ******************************************************************************/ 55 | function addBlockAgentListener() 56 | { 57 | browser.declarativeNetRequest.updateEnabledRulesets({ 58 | enableRulesetIds: ["block-agent"] 59 | }); 60 | } 61 | 62 | function removeBlockAgentListener() 63 | { 64 | browser.declarativeNetRequest.updateEnabledRulesets({ 65 | disableRulesetIds: ["block-agent"] 66 | }); 67 | } 68 | 69 | function removeStartDot(string) 70 | { 71 | return string.replace(/^\./, ""); 72 | } 73 | 74 | async function deleteCookies() 75 | { 76 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 77 | const cookies = await browser.cookies.getAll({}); 78 | for (const cookie of cookies) 79 | { 80 | const url = getUrl(cookie.domain, cookie.path, cookie.secure); 81 | const domainWhitelist = cookieWhitelist[removeStartDot(cookie.domain)]; 82 | if (!domainWhitelist || (!domainWhitelist.cookies.includes(cookie.name) && 83 | !domainWhitelist.domainWhitelist)) 84 | { 85 | removeCookie({ "url": url, "name": cookie.name }); 86 | } 87 | } 88 | } 89 | 90 | module.exports = {additionalPermission, getAllCookies, 91 | removeCookie, getUrl, addRequestListener, 92 | removeRequestListener, updateRequestObj, 93 | addBlockAgentListener, removeBlockAgentListener, 94 | deleteCookies}; 95 | -------------------------------------------------------------------------------- /src/js/data.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const privacyData = { 20 | websites: 21 | ["thirdPartyCookiesAllowed", "hyperlinkAuditingEnabled", 22 | "referrersEnabled", "protectedContentEnabled"], 23 | services: 24 | ["alternateErrorPagesEnabled", "autofillEnabled", 25 | "hotwordSearchEnabled", "passwordSavingEnabled", 26 | "safeBrowsingEnabled", 27 | "safeBrowsingExtendedReportingEnabled", 28 | "searchSuggestEnabled", "spellingServiceEnabled", 29 | "translationServiceEnabled"], 30 | network: 31 | ["networkPredictionEnabled"] 32 | }; 33 | 34 | // https://developer.chrome.com/extensions/browsingData#type-DataTypeSet 35 | const browsingData = ["removeAll", "appcache", "cache", "cookies", "downloads", 36 | "fileSystems", "formData", "history", "indexedDB", 37 | "localStorage", "serverBoundCertificates", "passwords", 38 | "pluginData", "serviceWorkers", "webSQL"]; 39 | 40 | module.exports = {privacyData, browsingData}; 41 | -------------------------------------------------------------------------------- /src/js/ui/helpers/actionListener.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | function registerActionListener(target, callback) 20 | { 21 | target.addEventListener("keyup", (ev) => 22 | { 23 | onKeyUp(ev, callback); 24 | }, false); 25 | 26 | target.addEventListener("click", (ev) => 27 | { 28 | onClick(ev, callback); 29 | }, false); 30 | } 31 | 32 | function onKeyUp(ev, callback) 33 | { 34 | const {key} = ev; 35 | const {activeElem} = document; 36 | let actions = null; 37 | 38 | switch (key) 39 | { 40 | case " ": 41 | case "Enter": 42 | actions = activeElem.dataset.keyAction; 43 | break; 44 | case "Delete": 45 | case "Backspace": 46 | actions = activeElem.dataset.keyDelete; 47 | break; 48 | case "ArrowUp": 49 | actions = activeElem.dataset.keyUp; 50 | break; 51 | case "ArrowDown": 52 | actions = activeElem.dataset.keyDown; 53 | break; 54 | case "ArrowRight": 55 | actions = activeElem.dataset.keyRight; 56 | break; 57 | case "ArrowLeft": 58 | actions = activeElem.dataset.keyLeft; 59 | break; 60 | case "Escape": 61 | actions = activeElem.dataset.keyQuite; 62 | break; 63 | } 64 | 65 | if (!actions) 66 | return; 67 | 68 | // TODO: Fix duplication 69 | ev.preventDefault; 70 | actions.split(",").forEach(function(action) 71 | { 72 | callback(action, activeElem); 73 | }); 74 | } 75 | 76 | function onClick(ev, callback) 77 | { 78 | let element = ev.target; 79 | let actions = null; 80 | 81 | while (true) 82 | { 83 | if (!element) 84 | break; 85 | 86 | if (element.hasAttribute("data-action")) 87 | { 88 | actions = element.getAttribute("data-action"); 89 | break; 90 | } 91 | 92 | element = element.parentElement; 93 | } 94 | 95 | if (!actions) 96 | return; 97 | 98 | ev.preventDefault; 99 | actions.split(",").forEach(function(action) 100 | { 101 | callback(action, element); 102 | }); 103 | } 104 | 105 | module.exports = {registerActionListener}; 106 | -------------------------------------------------------------------------------- /src/js/ui/helpers/settingList.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const {$, getMessage, $$} = require("./utils"); 20 | const {additionalPermission} = require("../../common"); 21 | const browser = require("webextension-polyfill"); 22 | 23 | async function _createPmToggle(accessor) 24 | { 25 | const content = $("#settings-list").content; 26 | const listElem = document.importNode(content, true); 27 | const dialog = document.querySelector("pm-dialog.info"); 28 | const pmToggle = listElem.querySelector("pm-toggle"); 29 | pmToggle.setAttribute("text", await getMessage(accessor)); 30 | pmToggle.setAttribute("description", await getMessage(accessor + "_desc")); 31 | pmToggle.addEventListener("info", (e) => 32 | { 33 | const {text, description} = e.target; 34 | dialog.showDialog(text, {description}); 35 | }); 36 | 37 | pmToggle.dataset.access = accessor; 38 | return [pmToggle, listElem]; 39 | } 40 | 41 | async function addPrivacyToggle(accessor, privacyObject, parentItem) 42 | { 43 | const [pmToggle, listElem] = await _createPmToggle(accessor); 44 | parentItem.appendChild(listElem); 45 | 46 | _updateSettingButton(pmToggle, (await privacyObject.get({})).value); 47 | pmToggle.addEventListener("change", async() => 48 | { 49 | const details = await privacyObject.get({}); 50 | if (details.levelOfControl == "controllable_by_this_extension" || 51 | details.levelOfControl == "controlled_by_this_extension") 52 | { 53 | try 54 | { 55 | await privacyObject.set({ value: !details.value }); 56 | } 57 | catch ({message}) 58 | { 59 | if (message == 60 | "Can't modify regular settings from an incognito context.") 61 | { 62 | alert(await getMessage("regularSettingChangeIncognito_error")); 63 | } 64 | else 65 | { 66 | alert(message); 67 | } 68 | } 69 | } 70 | else 71 | { 72 | //TODO: Inform user if control level is not controlable by 73 | //extension details.levelOfControl 74 | } 75 | }, false); 76 | 77 | privacyObject.onChange.addListener((detail) => 78 | { 79 | _updateSettingButton(accessor, detail.value); 80 | }); 81 | 82 | return listElem; 83 | } 84 | 85 | async function addStorageToggle(accessor, parentItem, callback) 86 | { 87 | const [pmToggle, listElem] = await _createPmToggle(accessor); 88 | parentItem.appendChild(listElem); 89 | 90 | const state = await getSettingListData(accessor); 91 | _updateSettingButton(pmToggle, state === true); 92 | pmToggle.addEventListener("change", async() => 93 | { 94 | const currentState = await getSettingListData(accessor); 95 | await setSettingListData(accessor, !currentState); 96 | }, false); 97 | 98 | if (callback) 99 | { 100 | new Listener().on(accessor, (active)=> 101 | { 102 | callback(active); 103 | }); 104 | } 105 | } 106 | 107 | async function addPermissionToggle(accessor, parentItem) 108 | { 109 | const [pmToggle, listElem] = await _createPmToggle(accessor); 110 | parentItem.appendChild(listElem); 111 | 112 | const isGranted = await browser.permissions.contains(additionalPermission); 113 | _updateSettingButton(accessor, isGranted); 114 | 115 | pmToggle.addEventListener("click", async() => 116 | { 117 | if (await browser.permissions.contains(additionalPermission)) 118 | browser.permissions.remove(additionalPermission); 119 | else 120 | browser.permissions.request(additionalPermission); 121 | }, false); 122 | 123 | browser.permissions.onAdded.addListener(() => 124 | { 125 | _updateSettingButton(accessor, true); // Currently called multiple times 126 | }); 127 | browser.permissions.onRemoved.addListener(() => 128 | { 129 | _updateSettingButton(accessor, false); 130 | }); 131 | } 132 | 133 | async function setSettingListData(name, value) 134 | { 135 | const data = await browser.storage.local.get("settingList"); 136 | if (!data.settingList) 137 | data.settingList = {}; 138 | data.settingList[name] = value; 139 | await browser.storage.local.set(data); 140 | } 141 | 142 | async function getSettingListData(name) 143 | { 144 | const data = await browser.storage.local.get("settingList"); 145 | const settingList = data["settingList"]; 146 | if (!settingList) 147 | return; 148 | return settingList[name]; 149 | } 150 | 151 | async function resetSettingListData(settingNames) 152 | { 153 | if (!Array.isArray(settingNames)) 154 | { 155 | await setSettingListData(settingNames, false); 156 | return; 157 | } 158 | settingNames.forEach(async(settingName) => 159 | { 160 | await setSettingListData(settingName, false); 161 | }); 162 | } 163 | 164 | function _updateSettingButton(setting, state) 165 | { 166 | if (typeof setting == "string") 167 | { 168 | $$("[data-access='" + setting + "']").forEach((settingItem) => 169 | { 170 | _updateSettingButton(settingItem, state); 171 | }); 172 | } 173 | else 174 | { 175 | setting.setEnabled(state); 176 | } 177 | } 178 | 179 | browser.storage.onChanged.addListener((data) => 180 | { 181 | if ("settingList" in data) 182 | { 183 | const {newValue} = data.settingList; 184 | for (const accessor in newValue) 185 | _updateSettingButton(accessor, newValue[accessor]); 186 | } 187 | }); 188 | 189 | /** 190 | * Change listener for settingList in the local storage 191 | */ 192 | class Listener 193 | { 194 | constructor() 195 | { 196 | this.settingList = {}; 197 | browser.storage.onChanged.addListener(this._onChage.bind(this)); 198 | } 199 | 200 | /** 201 | * Set a listener 202 | * @param {String} settingName setting name (ex.: cookies) 203 | * @param {Function} callback function to call on a setting value change 204 | */ 205 | async on(settingName, callback) 206 | { 207 | if (!this.settingList[settingName]) 208 | this.settingList[settingName] = {}; 209 | this.settingList[settingName].value = await getSettingListData(settingName); 210 | this.settingList[settingName].callback = callback; 211 | } 212 | 213 | _onChage({settingList}) 214 | { 215 | if (!settingList) 216 | return; 217 | 218 | const {newValue} = settingList; 219 | for (const settingName in this.settingList) 220 | { 221 | if (newValue[settingName] !== this.settingList[settingName].value) 222 | { 223 | this.settingList[settingName].value = newValue[settingName]; 224 | this.settingList[settingName].callback(newValue[settingName]); 225 | } 226 | } 227 | } 228 | } 229 | 230 | module.exports = {addPrivacyToggle, addStorageToggle, 231 | addPermissionToggle, getSettingListData, resetSettingListData, 232 | Listener}; 233 | -------------------------------------------------------------------------------- /src/js/ui/helpers/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | "use strict"; 20 | const browser = require("webextension-polyfill"); 21 | 22 | function $(selector, parent) 23 | { 24 | return (parent || document).querySelector(selector); 25 | } 26 | 27 | function $$(selector, parent) 28 | { 29 | return (parent || document).querySelectorAll(selector); 30 | } 31 | 32 | async function getMessage(text) 33 | { 34 | return (await browser.i18n.getMessage(text)) || text; 35 | } 36 | 37 | module.exports = {$, $$, getMessage}; 38 | -------------------------------------------------------------------------------- /src/js/ui/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | "use strict"; 20 | 21 | require("./tabs/cookies"); 22 | require("./tabs/main"); 23 | require("./tabs/network"); 24 | 25 | const browser = require("webextension-polyfill"); 26 | window.browser = browser; 27 | 28 | const {getMessage} = require("./helpers/utils"); 29 | 30 | 31 | /******************************************************************************* 32 | * i18n 33 | ******************************************************************************/ 34 | document.addEventListener("DOMContentLoaded", async function() 35 | { 36 | document.querySelectorAll("[data-i18n]").forEach(async function(node) 37 | { 38 | node.textContent = await getMessage(node.dataset.i18n); 39 | }); 40 | 41 | document.documentElement.lang = await getMessage("@@ui_locale"); 42 | }, false); 43 | 44 | /******************************************************************************* 45 | * Init Tabs 46 | ******************************************************************************/ 47 | document.addEventListener("DOMContentLoaded", async() => 48 | { 49 | const pmTabPanel = document.querySelector("pm-tab-panel"); 50 | const {lastSelectedTab} = await browser.storage.local.get("lastSelectedTab"); 51 | if (!lastSelectedTab) 52 | pmTabPanel.select("tab-main"); 53 | else 54 | pmTabPanel.select(lastSelectedTab); 55 | pmTabPanel.addEventListener("tabChange", ({detail}) => 56 | { 57 | browser.storage.local.set({"lastSelectedTab": detail}); 58 | }); 59 | }, false); 60 | -------------------------------------------------------------------------------- /src/js/ui/tabs/cookies.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | "use strict"; 20 | 21 | const {$, getMessage} = require("../helpers/utils"); 22 | const {registerActionListener} = require("../helpers/actionListener"); 23 | const {deleteCookies, additionalPermission} = require("../../common"); 24 | const permittedUrls = additionalPermission.origins[0]; 25 | const {addStorageToggle, addPermissionToggle, 26 | getSettingListData} = require("../helpers/settingList"); 27 | const browser = require("webextension-polyfill"); 28 | 29 | let cookieWhitelistTitle = ""; 30 | let domainWhitelistTitle = ""; 31 | getMessage("whitelistSublistCookie").then(msg => cookieWhitelistTitle = msg); 32 | getMessage("whitelistCookieDomain").then(msg => domainWhitelistTitle = msg); 33 | 34 | const activeTabCookieId = "activeTabCookies"; 35 | 36 | let pmTable = null; 37 | let cookieDialog = null; 38 | let removeCookieDialog = null; 39 | document.addEventListener("DOMContentLoaded" , async() => 40 | { 41 | $("#search-domain").addEventListener("search", populateDomainList, false); 42 | $("#search-domain").addEventListener("keyup", function(ev) 43 | { 44 | if (ev.key != "Enter" && ev.key != "Escape") 45 | populateDomainList(); 46 | }, false); 47 | pmTable = document.querySelector("pm-table"); 48 | pmTable.setListener(onCookiesAction); 49 | 50 | const cookiesTab = $("#panel-cookies"); 51 | const leftSettingList = $("ul.settings-list:nth-of-type(1)", cookiesTab); 52 | const rightSettingList = $("ul.settings-list:nth-of-type(2)", cookiesTab); 53 | 54 | addPermissionToggle("additionalPermissions", leftSettingList); 55 | permissionChange(await browser.permissions.contains(additionalPermission)); 56 | 57 | browser.permissions.onAdded.addListener(({origins}) => 58 | { 59 | if (origins.includes(permittedUrls)) 60 | permissionChange(true); 61 | }); 62 | 63 | browser.permissions.onRemoved.addListener(({origins}) => 64 | { 65 | if (origins.includes(permittedUrls)) 66 | permissionChange(false); 67 | }); 68 | 69 | addStorageToggle(activeTabCookieId, rightSettingList, (active) => 70 | { 71 | if (active) 72 | updateFilterToActiveDomain(); 73 | }); 74 | 75 | cookieDialog = document.querySelector("pm-dialog.cookies"); 76 | removeCookieDialog = document.querySelector("pm-dialog.delete-cookies"); 77 | 78 | registerActionListener($("#cookiesContainer"), onCookiesAction); 79 | registerActionListener(cookieDialog, onCookiesAction); 80 | registerActionListener(removeCookieDialog, onCookiesAction); 81 | }, false); 82 | 83 | async function permissionChange(granted) 84 | { 85 | disableControls(!granted); 86 | if (granted) 87 | { 88 | const state = await getSettingListData(activeTabCookieId); 89 | if (state) 90 | updateFilterToActiveDomain(); 91 | else 92 | populateDomainList(); 93 | } 94 | } 95 | 96 | async function getCookiesCountForDomain() 97 | { 98 | const cookies = await browser.cookies.getAll({}); 99 | return cookies.reduce((acc, {domain}) => 100 | { 101 | const genericDomain = removeStartDot(domain); 102 | if (acc[genericDomain]) 103 | acc[genericDomain] = ++acc[genericDomain]; 104 | else 105 | acc[genericDomain] = 1; 106 | return acc; 107 | }, {}); 108 | } 109 | 110 | async function populateDomainList() 111 | { 112 | pmTable.empty(); 113 | const searchExpression = new RegExp($("#search-domain").value); 114 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 115 | const isWhitelisted = (domain) => 116 | { 117 | return cookieWhitelist[domain] && cookieWhitelist[domain].domainWhitelist; 118 | }; 119 | const domainObjs = []; 120 | const domainCounts = await getCookiesCountForDomain(); 121 | for (const domain in domainCounts) 122 | { 123 | if (!searchExpression.test(domain)) 124 | continue; 125 | 126 | const count = domainCounts[domain]; 127 | domainObjs.push(createDomainObj(domain, count, isWhitelisted(domain))); 128 | } 129 | document.querySelector("pm-table").addItems(domainObjs); 130 | } 131 | 132 | function setCookiesNum(cookienum) 133 | { 134 | return cookienum + " Cookies"; 135 | } 136 | 137 | /** 138 | * Create a Table List Item Structure Object 139 | * @param {String} domain Domain name 140 | * @param {Number} cookienum Number of domain cookies 141 | * @param {Boolean} whitelist specifies whether domain is whitelisted 142 | */ 143 | function createDomainObj(domain, cookienum, whitelist) 144 | { 145 | return { 146 | id: domain, 147 | texts: { 148 | domain: domain, 149 | cookienum: setCookiesNum(cookienum) 150 | }, 151 | titles: { 152 | whitelist: domainWhitelistTitle 153 | }, 154 | dataset: {whitelist} 155 | }; 156 | } 157 | 158 | async function isCookieWhitelisted(domain, cookie) 159 | { 160 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 161 | return cookieWhitelist[domain] && cookieWhitelist[domain].cookies && 162 | cookieWhitelist[domain].cookies.includes(cookie); 163 | } 164 | 165 | async function isDomainWhitelisted(domain) 166 | { 167 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 168 | return cookieWhitelist && cookieWhitelist[domain] && cookieWhitelist[domain].domainWhitelist; 169 | } 170 | 171 | async function setWhitelistDomain(domain, value) 172 | { 173 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 174 | if (!(domain in cookieWhitelist)) 175 | cookieWhitelist[domain] = {domainWhitelist: value, cookies: []}; 176 | else 177 | cookieWhitelist[domain].domainWhitelist = value; 178 | await browser.storage.local.set({cookieWhitelist}); 179 | } 180 | 181 | async function toggleWhitelistCookie(domain, cookie) 182 | { 183 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 184 | if (!(domain in cookieWhitelist)) 185 | cookieWhitelist[domain] = {domainWhitelist: false, cookies: [cookie]}; 186 | else if (cookieWhitelist[domain].cookies.includes(cookie)) 187 | cookieWhitelist[domain].cookies = cookieWhitelist[domain].cookies.filter(el => el !== cookie); 188 | else 189 | cookieWhitelist[domain].cookies.push(cookie); 190 | await browser.storage.local.set({cookieWhitelist}); 191 | } 192 | 193 | async function removeWhitelistCookie(domain, cookie) 194 | { 195 | if (!await isCookieWhitelisted(domain, cookie)) 196 | return; 197 | 198 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 199 | cookieWhitelist[domain].cookies = cookieWhitelist[domain].cookies.filter(el => el !== cookie); 200 | await browser.storage.local.set({cookieWhitelist}); 201 | } 202 | 203 | async function onCookiesAction(action, item, parentItem) 204 | { 205 | pmTable = document.querySelector("pm-table"); 206 | switch (action) 207 | { 208 | case "get-cookies": 209 | { 210 | const subitems = item.subItems; 211 | if (subitems) 212 | { 213 | onCookiesAction("close-expanded-domain", null, item); 214 | return; 215 | } 216 | const domain = item.id; 217 | const cookies = await browser.cookies.getAll({domain}); 218 | 219 | for (const cookie of cookies) 220 | { 221 | // Filter subdomains matched cookies 222 | if (cookie.domain.indexOf(domain) > 1) 223 | continue; 224 | 225 | const isWhitelisted = await isCookieWhitelisted(domain, cookie.name); 226 | pmTable.addItems([createCookieSubitemObj(cookie, isWhitelisted)], domain); 227 | } 228 | const subitemId = pmTable.getItem(domain).subItems[0].id; 229 | pmTable.selectItem(subitemId, domain); 230 | break; 231 | } 232 | case "close-expanded-domain": 233 | { 234 | pmTable.empty(parentItem.id); 235 | break; 236 | } 237 | case "whitelist-domain": 238 | { 239 | const domain = item.texts.domain; 240 | const currentValue = await isDomainWhitelisted(domain); 241 | await setWhitelistDomain(domain, !currentValue); 242 | break; 243 | } 244 | case "whitelist-cookie": 245 | { 246 | const cookie = item.texts.name; 247 | const domain = parentItem.texts.domain; 248 | await toggleWhitelistCookie(domain, cookie); 249 | break; 250 | } 251 | case "delete-domain": 252 | { 253 | const domain = item.texts.domain; 254 | const cookies = await browser.cookies.getAll({domain}); 255 | for (const cookie of cookies) 256 | { 257 | const url = getUrl(cookie.domain, cookie.path, cookie.secure); 258 | browser.cookies.remove({"url": url, "name": cookie.name}); 259 | } 260 | break; 261 | } 262 | case "delete-cookie": 263 | { 264 | const domain = parentItem.texts.domain; 265 | const cookies = await browser.cookies.getAll({domain}); 266 | for (const cookie of cookies) 267 | { 268 | // Filter subdomains matched cookies 269 | if (cookie.domain.indexOf(domain) > 1) 270 | continue; 271 | 272 | if (item.texts.name === cookie.name) 273 | { 274 | const url = getUrl(domain, cookie.path, cookie.secure); 275 | browser.cookies.remove({url, "name": cookie.name}); 276 | } 277 | } 278 | break; 279 | } 280 | case "edit-cookie": { 281 | const cookieName = item.texts.name; 282 | const url = getUrl(parentItem.texts.domain, 283 | item.dataset.path, 284 | item.dataset.secure); 285 | const {name, value, domain, path, hostOnly, httpOnly, secure, 286 | session, storeId, 287 | expirationDate} = await browser.cookies.get({url, name: cookieName}); 288 | let date, time = ""; 289 | if (expirationDate) 290 | { 291 | const dateTime = new Date(expirationDate * 1000); 292 | // supports -> yyyy-mm-dd 293 | const convertMonth = (month) => month < 9 ? `0${month + 1}` : month + 1; 294 | const convertDate = (date) => date < 10 ? `0${date}` : date; 295 | const year = dateTime.getFullYear(); 296 | const month = convertMonth(dateTime.getMonth()); 297 | const day = convertDate(dateTime.getDate()); 298 | date = `${year}-${month}-${day}`; 299 | // supports -> hh:mm:ss 300 | const twoDigits = (value) => value < 10 ? `0${value}` : value; 301 | const hour = twoDigits(dateTime.getHours()); 302 | const minute = twoDigits(dateTime.getMinutes()); 303 | const second = twoDigits(dateTime.getSeconds()); 304 | time = `${hour}:${minute}:${second}`; 305 | } 306 | const title = await getMessage("editCookie"); 307 | const actionBtn = await getMessage("cookieDialog_update"); 308 | const data = { 309 | name, value, path, hostOnly, httpOnly, session, storeId, secure, 310 | actionBtn, 311 | domain: removeStartDot(domain), 312 | expirationDate: date, 313 | expirationTime: time 314 | }; 315 | resetDialog(); 316 | getDialogField("name").setAttribute("disabled", "disabled"); 317 | getDialogField("domain").setAttribute("disabled", "disabled"); 318 | cookieDialog.classList.remove("add"); 319 | cookieDialog.showDialog(title, data); 320 | break; 321 | } 322 | case "add-cookie": { 323 | resetDialog(); 324 | const title = await getMessage("addCookie"); 325 | getDialogField("name").removeAttribute("disabled"); 326 | getDialogField("domain").removeAttribute("disabled"); 327 | const actionBtn = await getMessage("cookieDialog_add"); 328 | cookieDialog.classList.add("add"); 329 | cookieDialog.showDialog(title, {actionBtn}); 330 | break; 331 | } 332 | case "update-cookie": { 333 | if (!cookieDialog.querySelector("form").reportValidity()) 334 | return; 335 | 336 | const time = getDialogField("expirationTime").value; 337 | const date = getDialogField("expirationDate").value; 338 | const dateTime = time ? `${date}T${time}` : date; 339 | 340 | const cookie = { 341 | "url": getUrl(getDialogField("domain").value, 342 | getDialogField("path").value, 343 | getDialogField("secure").checked), 344 | "path": getDialogField("path").value, 345 | "domain": getDialogField("domain").value, 346 | "name": getDialogField("name").value, 347 | "value": getDialogField("value").value, 348 | "secure": getDialogField("secure").checked, 349 | "httpOnly": getDialogField("httpOnly").checked, 350 | "storeId": getDialogField("storeId").value, 351 | "expirationDate": new Date(dateTime).getTime() / 1000 352 | }; 353 | 354 | if (getDialogField("hostOnly").checked) 355 | { 356 | // Omitted domain makes host-only cookie 357 | delete cookie.domain; 358 | } 359 | if (getDialogField("session").checked) 360 | { 361 | // Omitted expirationDate makes session cookie 362 | delete cookie.expirationDate; 363 | } 364 | if (await browser.cookies.set(cookie)) 365 | { 366 | cookieDialog.closeDialog(); 367 | } 368 | break; 369 | } 370 | case "open-cookie-removal-dialog": { 371 | const title = await getMessage("deleteAll"); 372 | removeCookieDialog.showDialog(title); 373 | break; 374 | } 375 | case "delete-all-cookies": { 376 | deleteCookies(); 377 | removeCookieDialog.closeDialog(); 378 | break; 379 | } 380 | } 381 | } 382 | 383 | function getDialogField(id) 384 | { 385 | return cookieDialog.querySelector(`[data-id='${id}']`); 386 | } 387 | 388 | function resetDialog() 389 | { 390 | cookieDialog.querySelector("form").reset(); 391 | } 392 | 393 | /** 394 | * Filter cookies list according to the active tab URL 395 | */ 396 | async function updateFilterToActiveDomain() 397 | { 398 | const tab = await browser.tabs.query({active: true}); 399 | const url = tab[0].url; 400 | if (url.indexOf("://") > -1) 401 | { 402 | const domain = url.split('/')[2].split(':')[0].replace("www.", ""); 403 | $("#search-domain").value = domain; 404 | populateDomainList(); 405 | } 406 | } 407 | 408 | /** 409 | * Enable/disable control elements 410 | * @param {Boolean} disabled 411 | */ 412 | function disableControls(disabled) 413 | { 414 | $("#cookie-controls").childNodes.forEach(function(Node) 415 | { 416 | if (Node.nodeType == 1) 417 | if (disabled) 418 | Node.setAttribute("disabled", disabled); 419 | else 420 | Node.removeAttribute("disabled"); 421 | }); 422 | } 423 | 424 | function removeStartDot(string) 425 | { 426 | return string.replace(/^\./, ""); 427 | } 428 | 429 | function getUrl(domain, path, isSecure) 430 | { 431 | return "http" + (isSecure ? "s" : "") + "://" + domain + path; 432 | } 433 | 434 | function createCookieSubitemObj(cookie, whitelist) 435 | { 436 | return { 437 | id: cookie.name, 438 | texts: { 439 | name: cookie.name, 440 | value: cookie.value 441 | }, 442 | titles: { 443 | whitelist: cookieWhitelistTitle 444 | }, 445 | dataset: {whitelist, path: cookie.path, secure: cookie.secure} 446 | }; 447 | } 448 | 449 | browser.storage.onChanged.addListener(async({cookieWhitelist}) => 450 | { 451 | if (cookieWhitelist) 452 | { 453 | const {newValue, oldValue} = cookieWhitelist; 454 | 455 | for (const domain in newValue) 456 | { 457 | if (!oldValue[domain]) 458 | { 459 | if (newValue[domain].domainWhitelist) 460 | { 461 | const item = pmTable.getItem(domain); 462 | if (item) 463 | { 464 | item.dataset.whitelist = true; 465 | pmTable.updateItem(item, domain); 466 | } 467 | } 468 | for (const cookie of newValue[domain].cookies) 469 | { 470 | const item = pmTable.getItem(cookie, domain); 471 | if (item) 472 | { 473 | item.dataset.whitelist = true; 474 | pmTable.updateItem(item, cookie, domain); 475 | } 476 | } 477 | } 478 | else 479 | { 480 | const oldDomainObj = oldValue[domain]; 481 | const newDomainObj = newValue[domain]; 482 | if (newDomainObj.domainWhitelist !== oldDomainObj.domainWhitelist) 483 | { 484 | const item = pmTable.getItem(domain); 485 | if (item) 486 | { 487 | item.dataset.whitelist = newDomainObj.domainWhitelist; 488 | pmTable.updateItem(item, domain); 489 | } 490 | } 491 | const oldCookies = oldDomainObj.cookies; 492 | const newCookies = newDomainObj.cookies; 493 | for (const cookie of newCookies) 494 | { 495 | if (!oldCookies.includes(cookie)) 496 | { 497 | const item = pmTable.getItem(cookie, domain); 498 | if (item) 499 | { 500 | item.dataset.whitelist = true; 501 | pmTable.updateItem(item, cookie, domain); 502 | } 503 | } 504 | oldCookies.splice(oldCookies.indexOf(cookie), 1); 505 | } 506 | for (const cookie of oldCookies) 507 | { 508 | const item = pmTable.getItem(cookie, domain); 509 | if (item) 510 | { 511 | item.dataset.whitelist = false; 512 | pmTable.updateItem(item, cookie, domain); 513 | } 514 | } 515 | } 516 | } 517 | } 518 | }); 519 | 520 | browser.cookies.onChanged.addListener(async({cookie, removed}) => 521 | { 522 | const domain = removeStartDot(cookie.domain); 523 | const domainCounts = await getCookiesCountForDomain(); 524 | 525 | if (removed) 526 | { 527 | if (!pmTable.getItem(domain)) 528 | return; 529 | 530 | if (!domainCounts[domain]) 531 | { 532 | pmTable.removeItem(domain); 533 | const {cookieWhitelist} = await browser.storage.local.get("cookieWhitelist"); 534 | delete cookieWhitelist[domain]; 535 | await browser.storage.local.set({cookieWhitelist}); 536 | } 537 | else 538 | { 539 | pmTable.removeItem(cookie.name, domain); 540 | removeWhitelistCookie(domain, cookie.name); 541 | const domainItem = pmTable.getItem(domain); 542 | domainItem.texts.cookienum = setCookiesNum(domainCounts[domain]); 543 | pmTable.updateItem(domainItem, domain); 544 | } 545 | } 546 | else 547 | { 548 | const isWhitelisted = await isCookieWhitelisted(domain, cookie.name); 549 | const newItem = createCookieSubitemObj(cookie, isWhitelisted); 550 | const hasDomainItem = pmTable.getItem(domain); 551 | const isDomainExpanded = hasDomainItem && hasDomainItem.subItems; 552 | const hasCookieItem = pmTable.getItem(cookie.name, domain); 553 | 554 | if (hasCookieItem && hasDomainItem) 555 | { 556 | pmTable.updateItem(newItem, cookie.name, domain); 557 | } 558 | else if (!hasDomainItem) 559 | { 560 | const whitelisted = await isDomainWhitelisted(domain); 561 | const domainItem = createDomainObj(domain, domainCounts[domain], 562 | whitelisted); 563 | pmTable.addItems([domainItem]); 564 | } 565 | else if (isDomainExpanded) 566 | { 567 | const domainItem = pmTable.getItem(domain); 568 | domainItem.texts.cookienum = setCookiesNum(domainCounts[domain]); 569 | pmTable.updateItem(domainItem, domain); 570 | pmTable.addItems([newItem], domain); 571 | } 572 | else 573 | { 574 | const domainItem = pmTable.getItem(domain); 575 | domainItem.texts.cookienum = setCookiesNum(domainCounts[domain]); 576 | pmTable.updateItem(domainItem, domain); 577 | } 578 | } 579 | }); 580 | -------------------------------------------------------------------------------- /src/js/ui/tabs/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | "use strict"; 20 | 21 | const {$} = require("../helpers/utils"); 22 | const {addStorageToggle, addPrivacyToggle} = require("../helpers/settingList"); 23 | const {privacyData, browsingData} = require("../../data"); 24 | const {registerActionListener} = require("../helpers/actionListener"); 25 | const browser = require("webextension-polyfill"); 26 | 27 | async function generateMainContent() 28 | { 29 | for (const category in privacyData) 30 | { 31 | privacyData[category].forEach(async function(settingName) 32 | { 33 | if (!browser.privacy[category][settingName]) 34 | return; 35 | 36 | addPrivacyToggle(settingName, browser.privacy[category][settingName], 37 | $("#privacyManagement ul")); 38 | }); 39 | } 40 | 41 | for (const dataName of browsingData) 42 | addStorageToggle(dataName, $("#startupClear ul")); 43 | } 44 | 45 | async function onAction(action) 46 | { 47 | switch (action) 48 | { 49 | case "open-in-incognito": { 50 | const tab = await browser.tabs.query({active:true}); 51 | if (tab[0].url.toString().indexOf("chrome://") == -1) 52 | browser.windows.create({url: tab[0].url, incognito: true}); 53 | else 54 | alert("Sorry you can't run current active page in incognito mode."); 55 | break; 56 | } 57 | } 58 | } 59 | 60 | document.addEventListener("DOMContentLoaded" , function() 61 | { 62 | registerActionListener($("#panel-main"), onAction); 63 | generateMainContent(); 64 | }, false); 65 | -------------------------------------------------------------------------------- /src/js/ui/tabs/network.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | "use strict"; 20 | 21 | const {$, getMessage} = require("../helpers/utils"); 22 | const {registerActionListener} = require("../helpers/actionListener"); 23 | const {additionalPermission, addRequestListener, removeRequestListener, 24 | updateRequestObj, addBlockAgentListener, removeBlockAgentListener} = require("../../common"); 25 | const {addStorageToggle, addPermissionToggle, getSettingListData, resetSettingListData} = require("../helpers/settingList"); 26 | const browser = require("webextension-polyfill"); 27 | 28 | const blockUserAgentId = "blockUserAgent"; 29 | const collectHeadersId = "collectHeaders"; 30 | const permissionNotificationMsgId = "additionalPermissions_notification"; 31 | const filterParams = ["statusLine", "statusCode", "type", "url", "method"]; 32 | 33 | let tableList = null; 34 | 35 | document.addEventListener("DOMContentLoaded" , async function() 36 | { 37 | const networkTab = $("#panel-network"); 38 | const leftSettingList = $("ul.settings-list:nth-of-type(1)", networkTab); 39 | const rightSettingList = $("ul.settings-list:nth-of-type(2)", networkTab); 40 | 41 | addPermissionToggle("additionalPermissions", leftSettingList); 42 | 43 | addStorageToggle(blockUserAgentId, leftSettingList, (enabled) => 44 | { 45 | onNetworkSettingChange(blockUserAgentId, enabled, true); 46 | }); 47 | onNetworkSettingChange(blockUserAgentId, 48 | await getSettingListData(blockUserAgentId)); 49 | 50 | addStorageToggle(collectHeadersId,rightSettingList, (enabled) => 51 | { 52 | onNetworkSettingChange(collectHeadersId, enabled); 53 | }); 54 | onNetworkSettingChange(collectHeadersId, 55 | await getSettingListData(collectHeadersId), true); 56 | 57 | tableList = document.querySelector("#panel-network pm-table"); 58 | 59 | registerActionListener($("#requestsWidget"), onRequestsWidgetAction); 60 | tableList.setListener(onRequestsWidgetActionComp); 61 | browser.runtime.sendMessage({message: "getCollectedRequests"}).then((requests) => 62 | { 63 | tableList.addItems(requests); 64 | }); 65 | },false); 66 | 67 | async function onNetworkSettingChange(settingName, isActive, permissionChange) 68 | { 69 | switch(settingName) 70 | { 71 | case "blockUserAgent": 72 | if (isActive) 73 | { 74 | const result = await browser.permissions.contains(additionalPermission); 75 | if (result) 76 | { 77 | addBlockAgentListener(); 78 | } 79 | else 80 | { 81 | if (!permissionChange) 82 | alert(await getMessage(permissionNotificationMsgId)); 83 | await resetSettingListData(settingName); 84 | } 85 | } 86 | else 87 | { 88 | removeBlockAgentListener(); 89 | } 90 | break; 91 | case "collectHeaders": 92 | if (isActive) 93 | { 94 | const result = await browser.permissions.contains(additionalPermission); 95 | if (result) 96 | { 97 | addRequestListener(onSendHeaders, onHeadersReceived); 98 | } 99 | else 100 | { 101 | if (!permissionChange) 102 | alert(await getMessage(permissionNotificationMsgId)); 103 | await resetSettingListData(settingName); 104 | } 105 | } 106 | else 107 | { 108 | removeRequestListener(onSendHeaders, onHeadersReceived); 109 | } 110 | break; 111 | } 112 | } 113 | 114 | browser.permissions.onRemoved.addListener(async(result) => 115 | { 116 | await resetSettingListData([blockUserAgentId, collectHeadersId]); 117 | removeBlockAgentListener(); 118 | removeRequestListener(onSendHeaders, onHeadersReceived); 119 | }); 120 | 121 | 122 | function onSendHeaders(details) 123 | { 124 | const itemObj = updateRequestObj(details, "send"); 125 | tableList.addItems([itemObj]); 126 | } 127 | 128 | 129 | function onHeadersReceived(details) 130 | { 131 | const itemObj = updateRequestObj(details, "receive"); 132 | tableList.addItems([itemObj]); 133 | } 134 | 135 | function onRequestsWidgetActionComp(action, item, parentItem) 136 | { 137 | switch (action) 138 | { 139 | case "get-request": 140 | { 141 | if (item.subItems) 142 | { 143 | onRequestsWidgetActionComp("close-expanded-request", null, item); 144 | return; 145 | } 146 | 147 | const {request} = item; 148 | for (const param in request) 149 | { 150 | const headers = request[param]; 151 | if (param == "requestHeaders" || param == "responseHeaders") 152 | { 153 | for (const {name, value} of headers) 154 | { 155 | const id = name; 156 | tableList.addItems([{id, texts: {name, value}}], item.id); 157 | } 158 | } 159 | else if (filterParams.indexOf(param) >= 0) 160 | { 161 | const name = param; 162 | const value = headers; 163 | tableList.addItems([{id: name, texts: {name, value}}], item.id); 164 | } 165 | } 166 | break; 167 | } 168 | case "close-expanded-request": 169 | tableList.empty(parentItem.id); 170 | break; 171 | } 172 | } 173 | 174 | async function onRequestsWidgetAction(action, element) 175 | { 176 | switch (action) 177 | { 178 | case "delete-all": { 179 | await browser.runtime.sendMessage({message: "deleteCollectedNetworkRequests"}); 180 | tableList.empty(); 181 | break; 182 | } 183 | case "download-all": { 184 | const downloadJson = []; 185 | for (const {request, data} of tableList.items) 186 | { 187 | const requestObj = {}; 188 | requestObj["action"] = data.type; 189 | for (const param in request) 190 | { 191 | if (param == "requestHeaders" || param == "responseHeaders") 192 | { 193 | requestObj["headers"] = {}; 194 | for (const {name, value} of request[param]) 195 | { 196 | requestObj["headers"][name] = value; 197 | } 198 | } 199 | else if (filterParams.indexOf(param) >= 0) 200 | { 201 | requestObj[param] = request[param]; 202 | } 203 | } 204 | downloadJson.push(requestObj); 205 | } 206 | 207 | //Download requests 208 | const anchorElem = document.createElement("a"); 209 | anchorElem.setAttribute("href", "data:text/plain;charset=utf-8," + 210 | encodeURIComponent(JSON.stringify(downloadJson, null, 2))); 211 | 212 | anchorElem.setAttribute("download", "requests.json"); 213 | anchorElem.style.display = 'none'; 214 | document.body.appendChild(anchorElem); 215 | anchorElem.click(); 216 | document.body.removeChild(anchorElem); 217 | break; 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "default_locale": "en_US", 4 | "name": "__MSG_chrome_extension_name__", 5 | "description": "__MSG_chrome_extension_description__", 6 | "background": { 7 | "service_worker": "js/back.js" 8 | }, 9 | "action": { 10 | "default_icon": "img/logo.png", 11 | "default_popup": "popup.html" 12 | }, 13 | "icons": { 14 | "128": "img/logo.png", 15 | "16": "img/logo-16x16.png", 16 | "48": "img/logo-48x48.png" 17 | }, 18 | "declarative_net_request" : { 19 | "rule_resources": [{ 20 | "id" : "block-agent", 21 | "enabled": false, 22 | "path": "agent-block.json" 23 | }] 24 | }, 25 | "optional_host_permissions": [""], 26 | "permissions": ["privacy", "cookies", "browsingData", "tabs", "webRequest", "declarativeNetRequestWithHostAccess", "storage"], 27 | "incognito": "split", 28 | "update_url": "https://clients2.google.com/service/update2/crx" 29 | } 30 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | Privacy Manager 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 37 |
38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |
50 |
51 |

52 |
    53 | 54 |
    55 |
    56 |

    57 |
      58 |
      59 |
      60 | 61 |
      62 | 63 | 64 |
      65 |
        66 |
        67 |
          68 |
          69 | 70 |
          71 |

          72 | 73 | 74 | 93 | 116 | 117 | 118 |
          119 | 124 |
          125 |
          126 | 127 |
          128 | 129 | 130 |
          131 |
            132 |
            133 |
              134 |
              135 |
              136 | 137 | 150 | 161 | 162 |
              163 | 164 | 165 |
              166 |
              167 | 168 |
              169 |
              170 | 171 |
              172 | 173 | 174 | 175 |
              176 |
              177 |
              178 |
              179 |

              180 | 185 |

              186 |

              187 | 191 |

              192 |

              193 | 197 |

              198 |

              199 | 203 |

              204 |

              205 | 209 |

              210 |

              211 | 215 |

              216 |
              217 |
              218 |

              219 | 223 |

              224 |

              225 | 229 |

              230 |

              231 | 235 |

              236 |

              237 | 241 |

              242 |

              243 | 247 |

              248 |
              249 |
              250 | 254 |
              255 |
              256 |
              257 | 258 | 259 |
              260 |
              261 |
              262 | 263 | 264 |
              265 |
              266 |
              267 | 268 | 269 |
              270 |
              271 |
              272 |
              273 | 274 |
              275 | 276 | 277 | -------------------------------------------------------------------------------- /test/common.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const puppeteer = require("puppeteer"); 20 | const extensionPath = "dist"; 21 | let browser; 22 | 23 | async function openPopupPage() 24 | { 25 | // https://gokatz.me/blog/automate-chrome-extension-testing/ 26 | browser = await puppeteer.launch({ 27 | headless: false, // extension are allowed only in the head-full mode 28 | args: [ 29 | `--disable-extensions-except=${extensionPath}`, 30 | `--load-extension=${extensionPath}` 31 | ] 32 | }); 33 | // The extension target is not ready at this point, so we wait a bit. 34 | await sleep(1000); 35 | 36 | const targets = await browser.targets(); 37 | const extensionTarget = targets.find((target) => 38 | target.url().startsWith("chrome-extension://") && target.type() === "service_worker" 39 | ); 40 | const extensionUrl = extensionTarget.url() || ''; 41 | const [,, extensionID] = extensionUrl.split('/'); 42 | const extensionPopupHtml = "popup.html"; 43 | 44 | const page = await browser.newPage(); 45 | await page.goto(`chrome-extension://${extensionID}/${extensionPopupHtml}`, { 46 | waitUntil: "networkidle0" 47 | }); 48 | return page; 49 | } 50 | 51 | function sleep(ms) 52 | { 53 | return new Promise(resolve => setTimeout(resolve, ms)); 54 | } 55 | 56 | async function closeBrowser() 57 | { 58 | await browser.close(); 59 | } 60 | 61 | function getBrowser() 62 | { 63 | return browser; 64 | } 65 | 66 | module.exports = {openPopupPage, closeBrowser, getBrowser}; -------------------------------------------------------------------------------- /test/manifest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | /** 20 | * Currently there is no way to request and remove "" optional 21 | * permissions when running tests in puppeteer, so in order to test the 22 | * functionality that rely on the permission, the manifest file needs to be 23 | * modified before running the puppeteer test. 24 | * https://github.com/GoogleChrome/puppeteer/issues/5054 25 | */ 26 | 27 | const {readFile, writeFile} = require("fs").promises; 28 | const manifestFile = "dist/manifest.json"; 29 | const allOrigins = ""; 30 | 31 | /** 32 | * Move "" from optional_host_permissions to host_permissions 33 | */ 34 | async function allUrlsToPermissions() 35 | { 36 | const manifest = await getManifestFile(); 37 | manifest.host_permissions = manifest.optional_host_permissions; 38 | delete manifest.optional_host_permissions; 39 | await writeFile(manifestFile, JSON.stringify(manifest, null, 2), "utf8"); 40 | } 41 | 42 | /** 43 | * Move "" back from permissions to optional_permissions. 44 | */ 45 | async function restorePermissions() 46 | { 47 | const manifest = await getManifestFile(); 48 | manifest.optional_host_permissions = [allOrigins]; 49 | delete manifest.host_permissions; 50 | await writeFile(manifestFile, JSON.stringify(manifest, null, 2), "utf8"); 51 | } 52 | 53 | async function getManifestFile() 54 | { 55 | return JSON.parse(await readFile(manifestFile)); 56 | } 57 | 58 | module.exports = {allUrlsToPermissions, restorePermissions}; 59 | -------------------------------------------------------------------------------- /test/puppeteer/cookie.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const {allUrlsToPermissions, restorePermissions} = require("../manifest"); 20 | const {openPopupPage, closeBrowser} = require("../common"); 21 | const {equal} = require("assert"); 22 | 23 | let page; 24 | let tableListHandle; 25 | 26 | const tableList = {}; 27 | const methods = ["getItemIndex", "addItems", "getItem", "removeItem", 28 | "selectItem", "removeItem", "empty", "updateItem"]; 29 | 30 | methods.forEach((methodName) => 31 | { 32 | tableList[methodName] = (...args) => runComponentMethod(methodName, ...args); 33 | }); 34 | 35 | async function getHandle(access) 36 | { 37 | return await page.$(`[data-access='${access}']`); 38 | } 39 | 40 | function clickToggle(pmToggleHandle) 41 | { 42 | return page.evaluate((pmToggleHandle) => 43 | { 44 | pmToggleHandle.shadowRoot.querySelector("#toggle").click(); 45 | }, pmToggleHandle); 46 | } 47 | 48 | function runComponentMethod() 49 | { 50 | const functionName = arguments[0]; 51 | const args = Array.prototype.slice.call(arguments, 1); 52 | return page.evaluate((tableListHandle, functionName, args) => 53 | { 54 | return tableListHandle[functionName](...args); 55 | }, tableListHandle, functionName, args); 56 | } 57 | 58 | function getItemElemDatasetId(id, parentId) 59 | { 60 | return page.evaluate((tableListHandle, id, parentId) => 61 | { 62 | if (tableListHandle.getItemElem(id, parentId)) 63 | return tableListHandle.getItemElem(id, parentId).dataset.id; 64 | }, tableListHandle, id, parentId); 65 | } 66 | 67 | function closeEditCookieDialog() 68 | { 69 | return page.evaluate(async() => 70 | { 71 | const dialog = document.querySelector("pm-dialog.cookies"); 72 | dialog.closeDialog(); 73 | return dialog; 74 | }); 75 | } 76 | 77 | async function ensureItem(id, parentId) 78 | { 79 | return !!(await getItemElemDatasetId(id, parentId) || 80 | await tableList.getItem(id, parentId)); 81 | } 82 | 83 | async function getItemElemHandle(id, parentId) 84 | { 85 | return await page.evaluateHandle((handle, id, parentId) => handle.getItemElem(id, parentId), tableListHandle, id, parentId); 86 | } 87 | 88 | async function whitelistButtonHandle(id, parentId) 89 | { 90 | return await page.evaluateHandle((handle, id, parentId) => handle.getItemElem(id, parentId).querySelector(".whitelist"), tableListHandle, id, parentId); 91 | } 92 | 93 | async function isWhitelisted(id, parentId) 94 | { 95 | return page.evaluate(async(tableListHandle, id, parentId) => 96 | { 97 | return tableListHandle.getItemElem(id, parentId).dataset.whitelist; 98 | }, tableListHandle, id, parentId); 99 | } 100 | 101 | async function deleteButtonHandle(id, parentId) 102 | { 103 | return await page.evaluateHandle((handle, id, parentId) => handle.getItemElem(id, parentId).querySelector(".delete"), tableListHandle, id, parentId); 104 | } 105 | 106 | async function editButtonHandle(id, parentId) 107 | { 108 | return await page.evaluateHandle((handle, id, parentId) => handle.getItemElem(id, parentId).querySelector(".edit"), tableListHandle, id, parentId); 109 | } 110 | 111 | async function addCookie(url, name, value) 112 | { 113 | return page.evaluate(async(url, name, value) => 114 | { 115 | await browser.cookies.set({url, name, value}); 116 | }, url, name, value); 117 | } 118 | 119 | async function setCookieDialog(fieldId, value) 120 | { 121 | return page.evaluate(async(fieldId, value) => 122 | { 123 | const field = document.querySelector(`pm-dialog.cookies [data-id="${fieldId}"]`); 124 | if (field.type && field.type === "checkbox") 125 | field.checked = value; 126 | else 127 | field.value = value; 128 | return value; 129 | }, fieldId, value); 130 | } 131 | 132 | async function getCookieDialogField(fieldId) 133 | { 134 | return page.evaluate(async(fieldId) => 135 | { 136 | const field = document.querySelector(`pm-dialog.cookies [data-id="${fieldId}"]`); 137 | if (field.type && field.type === "checkbox") 138 | { 139 | return field.checked; 140 | } 141 | else 142 | { 143 | return field.value; 144 | } 145 | }, fieldId); 146 | } 147 | 148 | describe("Testing Cookies tab", () => 149 | { 150 | before(async() => 151 | { 152 | await allUrlsToPermissions(); 153 | page = await openPopupPage(); 154 | await page.click("#tab-cookies"); 155 | tableListHandle = await page.$('pm-table'); 156 | }); 157 | 158 | it("Adding cookies should populate the list of domains, but not individual cookies", async() => 159 | { 160 | const getCookieNum = async(id) => 161 | { 162 | return (await tableList.getItem(id)).texts.cookienum; 163 | }; 164 | for (let i = 1; i < 5; i++) 165 | { 166 | for (let j = 1; j <= i; j++) 167 | { 168 | await addCookie(`https://domain${i}.com`, `name${j}`, `value${j}`); 169 | } 170 | } 171 | 172 | equal(await getCookieNum("domain1.com"), "1 Cookies"); 173 | equal(await getCookieNum("domain2.com"), "2 Cookies"); 174 | equal(await getCookieNum("domain3.com"), "3 Cookies"); 175 | equal(await ensureItem("domain1.com"), true); 176 | equal(await ensureItem("domain2.com"), true); 177 | equal(await ensureItem("domain3.com"), true); 178 | }); 179 | 180 | it("Clicking on domain row should populate individual cookies", async() => 181 | { 182 | equal(await ensureItem("name1", "domain1.com"), false); 183 | await (await getItemElemHandle("domain1.com")).click(); 184 | await page.waitForTimeout(30); 185 | equal(await ensureItem("name1", "domain1.com"), true); 186 | 187 | equal(await ensureItem("name1", "domain2.com"), false); 188 | equal(await ensureItem("name2", "domain2.com"), false); 189 | await (await getItemElemHandle("domain2.com")).click(); 190 | await page.waitForTimeout(30); 191 | equal(await ensureItem("name1", "domain2.com"), true); 192 | equal(await ensureItem("name2", "domain2.com"), true); 193 | 194 | equal(await ensureItem("name3", "domain3.com"), false); 195 | await (await getItemElemHandle("domain3.com")).click(); 196 | await page.waitForTimeout(30); 197 | equal(await ensureItem("name3", "domain3.com"), true); 198 | }); 199 | 200 | it("Clicking whitelist button should whitelist domain and/or cookie accordingly", async() => 201 | { 202 | equal(await isWhitelisted("domain3.com"), "false"); 203 | await (await whitelistButtonHandle("domain3.com")).click(); 204 | await page.waitForTimeout(30); 205 | equal(await isWhitelisted("domain3.com"), "true"); 206 | 207 | equal(await isWhitelisted("name2", "domain3.com"), "false"); 208 | await (await whitelistButtonHandle("name2", "domain3.com")).click(); 209 | await page.waitForTimeout(30); 210 | equal(await isWhitelisted("name2", "domain3.com"), "true"); 211 | }); 212 | 213 | it("Clicking delete button should delete domain and/or cookie accordingly", async() => 214 | { 215 | await (await getItemElemHandle("domain4.com")).click(); 216 | await page.waitForTimeout(30); 217 | equal(await ensureItem("domain4.com"), true); 218 | equal(await ensureItem("name2", "domain4.com"), true); 219 | await (await deleteButtonHandle("name2", "domain4.com")).click(); 220 | await page.waitForTimeout(30); 221 | equal(await ensureItem("name2", "domain4.com"), false); 222 | equal(await ensureItem("name3", "domain4.com"), true); 223 | await (await deleteButtonHandle("domain4.com")).click(); 224 | await page.waitForTimeout(30); 225 | equal(await ensureItem("name3", "domain4.com"), false); 226 | equal(await ensureItem("domain4.com"), false); 227 | 228 | // Ensure that non expanded domain item is removed on delete 229 | // https://github.com/Privacy-Managers/Privacy-Manager/issues/83 230 | await addCookie("https://domain4.com", "name1", "value1"); 231 | await page.waitForTimeout(30); 232 | await (await deleteButtonHandle("domain4.com")).click(); 233 | await page.waitForTimeout(30); 234 | equal(await ensureItem("domain4.com"), false); 235 | }); 236 | 237 | it("Deleting cookies should also unset whitelisting", async() => 238 | { 239 | await addCookie("https://domain5.com", "name1", "value1"); 240 | await addCookie("https://domain5.com", "name2", "value2"); 241 | await page.waitForTimeout(30); 242 | 243 | await (await getItemElemHandle("domain5.com")).click(); 244 | equal(await ensureItem("name1", "domain5.com"), true); 245 | equal(await isWhitelisted("name1", "domain5.com"), "false"); 246 | 247 | await (await whitelistButtonHandle("domain5.com")).click(); 248 | await page.waitForTimeout(30); 249 | await (await whitelistButtonHandle("name2", "domain5.com")).click(); 250 | await page.waitForTimeout(30); 251 | equal(await isWhitelisted("name2", "domain5.com"), "true"); 252 | equal(await isWhitelisted("domain5.com"), "true"); 253 | 254 | await (await deleteButtonHandle("domain5.com")).click(); 255 | await page.waitForTimeout(30); 256 | await addCookie("https://domain5.com", "name1", "value1"); 257 | await addCookie("https://domain5.com", "name2", "value2"); 258 | await page.waitForTimeout(30); 259 | 260 | await (await getItemElemHandle("domain5.com")).click(); 261 | await page.waitForTimeout(30); 262 | equal(await isWhitelisted("name2", "domain5.com"), "false"); 263 | equal(await isWhitelisted("domain5.com"), "false"); 264 | 265 | // see https://github.com/Privacy-Managers/Privacy-Manager/issues/85 266 | equal(await isWhitelisted("name2", "domain5.com"), "false"); 267 | await (await deleteButtonHandle("name1", "domain5.com")).click(); 268 | await addCookie("https://domain5.com", "name1", "value1"); 269 | await page.waitForTimeout(30); 270 | equal(await isWhitelisted("name1", "domain5.com"), "false"); 271 | }); 272 | it("Cookie is added using 'add cookies' dialog", async() => 273 | { 274 | await (await page.evaluateHandle(() => document.querySelector("#panel-cookies [data-action='add-cookie']"))).click(); 275 | await setCookieDialog("domain", "domain6.com"); 276 | await setCookieDialog("name", "name1"); 277 | await setCookieDialog("value", "value1"); 278 | await setCookieDialog("path", "/about1"); 279 | await setCookieDialog("expirationDate", `${new Date().getFullYear() + 1}-01-01`); 280 | await setCookieDialog("expirationTime", "01:01:01"); 281 | await setCookieDialog("hostOnly", true); 282 | await setCookieDialog("httpOnly", true); 283 | await setCookieDialog("secure", true); 284 | await setCookieDialog("storeId", "0"); 285 | await page.waitForTimeout(100); 286 | const handle = await page.waitForSelector("pm-dialog.cookies pm-button[data-action='update-cookie']"); 287 | await handle.click(); 288 | await page.waitForTimeout(150); 289 | 290 | equal(await ensureItem("domain6.com"), true); 291 | await page.waitForTimeout(100); 292 | await (await getItemElemHandle("domain6.com")).click(); 293 | await page.waitForTimeout(30); 294 | equal(await ensureItem("name1", "domain6.com"), true); 295 | }); 296 | 297 | it("Cookie is updated using 'edit cookies' dialog", async() => 298 | { 299 | await (await editButtonHandle("name1", "domain6.com")).click(); 300 | await page.waitForTimeout(100); 301 | equal(await getCookieDialogField("domain"), "domain6.com"); 302 | equal(await getCookieDialogField("name"), "name1"); 303 | equal(await getCookieDialogField("value"), "value1"); 304 | equal(await getCookieDialogField("path"), "/about1"); 305 | equal(await getCookieDialogField("expirationDate"), `${new Date().getFullYear() + 1}-01-01`); 306 | equal(await getCookieDialogField("expirationTime"), "01:01:01"); 307 | equal(await getCookieDialogField("hostOnly"), true); 308 | equal(await getCookieDialogField("httpOnly"), true); 309 | equal(await getCookieDialogField("secure"), true); 310 | equal(await getCookieDialogField("session"), false); 311 | equal(await getCookieDialogField("storeId"), "0"); 312 | 313 | await setCookieDialog("domain", "domain6.com"); 314 | await setCookieDialog("value", "value2"); 315 | await setCookieDialog("path", "/about1"); 316 | await setCookieDialog("expirationDate", `${new Date().getFullYear() + 1}-02-02`); 317 | await setCookieDialog("expirationTime", "02:02:02"); 318 | 319 | const updateButtonHandle = await page.evaluateHandle(() => document.querySelector("pm-dialog.cookies pm-button[data-action='update-cookie']")); 320 | await page.waitForTimeout(100); 321 | await updateButtonHandle.click(); 322 | await page.waitForTimeout(200); 323 | await (await getItemElemHandle("domain6.com")).click(); 324 | await page.waitForTimeout(100); 325 | await (await editButtonHandle("name1", "domain6.com")).click(); 326 | await page.waitForTimeout(30); 327 | 328 | equal(await getCookieDialogField("domain"), "domain6.com"); 329 | equal(await getCookieDialogField("name"), "name1"); 330 | equal(await getCookieDialogField("value"), "value2"); 331 | equal(await getCookieDialogField("path"), "/about1"); 332 | equal(await getCookieDialogField("expirationDate"), `${new Date().getFullYear() + 1}-02-02`); 333 | equal(await getCookieDialogField("expirationTime"), "02:02:02"); 334 | await setCookieDialog("session", true); 335 | await updateButtonHandle.click(); 336 | 337 | await page.waitForTimeout(100); 338 | await (await getItemElemHandle("domain6.com")).click(); 339 | await page.waitForTimeout(30); 340 | await (await editButtonHandle("name1", "domain6.com")).click(); 341 | await page.waitForTimeout(30); 342 | equal(await getCookieDialogField("session"), true); 343 | equal(await getCookieDialogField("expirationDate"), ""); 344 | equal(await getCookieDialogField("expirationTime"), ""); 345 | await closeEditCookieDialog(); 346 | }); 347 | 348 | it("'Delete all' button in the delete all cookies dialog should remove all cookies instead of those which were whitelisted", async() => 349 | { 350 | await (await whitelistButtonHandle("name1", "domain5.com")).click(); 351 | await page.waitForTimeout(30); 352 | equal(await ensureItem("domain1.com"), true); 353 | equal(await ensureItem("domain2.com"), true); 354 | 355 | const dialogOpener = await page.evaluateHandle(() => document.querySelector("pm-button[data-action='open-cookie-removal-dialog']")); 356 | await dialogOpener.click(); 357 | await page.waitForTimeout(30); 358 | const deleteAllCookieButton = await page.evaluateHandle(() => document.querySelector("pm-button[data-action='delete-all-cookies']")); 359 | await deleteAllCookieButton.click(); 360 | 361 | equal(await ensureItem("domain3.com"), true); 362 | equal(await isWhitelisted("domain3.com"), "true"); 363 | 364 | equal(await isWhitelisted("name1", "domain3.com"), "false"); 365 | equal(await isWhitelisted("name2", "domain3.com"), "true"); 366 | 367 | equal(await isWhitelisted("domain5.com"), "false"); 368 | equal(await isWhitelisted("name1", "domain5.com"), "true"); 369 | 370 | equal(await ensureItem("domain1.com"), false); 371 | equal(await ensureItem("domain2.com"), false); 372 | }); 373 | 374 | it("Updating Search domain field should filter domains", async() => 375 | { 376 | equal(await ensureItem("domain3.com"), true); 377 | equal(await ensureItem("domain5.com"), true); 378 | await page.focus("#search-domain"); 379 | await page.keyboard.type("3"); 380 | await page.waitForTimeout(100); 381 | equal(await ensureItem("domain5.com"), false); 382 | equal(await ensureItem("domain3.com"), true); 383 | }); 384 | 385 | it("Switching 'Active tab cookies' on should set Search field to the current page domain", async() => 386 | { 387 | equal(await ensureItem("domain3.com"), true); 388 | await clickToggle(await getHandle("activeTabCookies")); 389 | await page.waitForTimeout(30); 390 | const searchDomainValue = await page.evaluate(element => element.value, await page.$("#search-domain")); 391 | equal(searchDomainValue, page.url().split('/')[2].split(':')[0]); 392 | equal(await ensureItem("domain3.com"), false); 393 | }); 394 | 395 | after(async() => 396 | { 397 | await restorePermissions(); 398 | if (!process.env.KEEP_BROWSER) 399 | { 400 | await closeBrowser(); 401 | } 402 | }); 403 | }); 404 | -------------------------------------------------------------------------------- /test/puppeteer/dataDeletion.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const {allUrlsToPermissions, restorePermissions} = require("../manifest"); 20 | const {openPopupPage, closeBrowser, getBrowser} = require("../common"); 21 | const {equal, ok} = require("assert"); 22 | 23 | let page; 24 | let page2; 25 | let tableListHandle; 26 | let server = null; 27 | 28 | const tableList = {}; 29 | const methods = ["getItemIndex", "addItems", "getItem", "removeItem", 30 | "selectItem", "removeItem", "empty", "updateItem"]; 31 | 32 | function startHttpServer() 33 | { 34 | const http = require('http'); 35 | 36 | server = http.createServer((req, res) => 37 | { 38 | res.writeHead(200, {'Content-Type': 'text/html'}); 39 | res.end("Hello"); 40 | }).listen(4000); 41 | } 42 | 43 | methods.forEach((methodName) => 44 | { 45 | tableList[methodName] = (...args) => runComponentMethod(methodName, ...args); 46 | }); 47 | 48 | function runComponentMethod() 49 | { 50 | const functionName = arguments[0]; 51 | const args = Array.prototype.slice.call(arguments, 1); 52 | return page.evaluate((tableListHandle, functionName, args) => 53 | { 54 | return tableListHandle[functionName](...args); 55 | }, tableListHandle, functionName, args); 56 | } 57 | 58 | function isEnabled(pmToggleHandle) 59 | { 60 | return page.evaluate((pmToggleHandle) => 61 | { 62 | return pmToggleHandle.isEnabled(); 63 | }, pmToggleHandle); 64 | } 65 | 66 | function isLocalStorageSet() 67 | { 68 | return page2.evaluate(() => 69 | { 70 | return window.localStorage.getItem("test-key"); 71 | }); 72 | } 73 | 74 | function isCookieSet() 75 | { 76 | return page2.evaluate(() => 77 | { 78 | return document.cookie.includes("test-name"); 79 | }); 80 | } 81 | 82 | function triggerDataDeletion(pmToggleHandle) 83 | { 84 | return page.evaluate(async() => 85 | { 86 | await browser.runtime.sendMessage({message: "deleteBrowsingData"}); 87 | }, pmToggleHandle); 88 | } 89 | 90 | function clickToggle(pmToggleHandle) 91 | { 92 | return page.evaluate((pmToggleHandle) => 93 | { 94 | pmToggleHandle.shadowRoot.querySelector("#toggle").click(); 95 | }, pmToggleHandle); 96 | } 97 | 98 | async function getHandle(access) 99 | { 100 | return await page.$(`[data-access='${access}']`); 101 | } 102 | 103 | function setCookie() 104 | { 105 | return page2.evaluate(() => 106 | { 107 | const setCookie = (name, value, days = 7, path = '/') => 108 | { 109 | const expires = new Date(Date.now() + days * 864e5).toUTCString(); 110 | document.cookie = name + '=' + encodeURIComponent(value) + '; expires=' + 111 | expires + '; path=' + path; 112 | }; 113 | setCookie("test-name", "test-value"); 114 | }); 115 | } 116 | 117 | function setStorage() 118 | { 119 | return page2.evaluate(() => 120 | { 121 | window.localStorage.setItem("test-key", "test-value"); 122 | }); 123 | } 124 | 125 | describe("Testing Data deletion", () => 126 | { 127 | before(async() => 128 | { 129 | await allUrlsToPermissions(); 130 | page = await openPopupPage(); 131 | startHttpServer(); 132 | page2 = await getBrowser().newPage(); 133 | page.bringToFront(); 134 | tableListHandle = await page.$("#panel-network pm-table"); 135 | }); 136 | 137 | it("Data deletion should clear data for the toggles that are enabled in the 'Clear on startup' section", async() => 138 | { 139 | await page2.goto("http://127.0.0.1:4000/"); 140 | await page2.waitForTimeout(50); 141 | ok(!await isLocalStorageSet()); 142 | ok(!await isCookieSet()); 143 | await setCookie(); 144 | await setStorage(); 145 | await page2.waitForTimeout(50); 146 | ok(await isLocalStorageSet()); 147 | ok(await isCookieSet()); 148 | 149 | const cookieHandle = await getHandle("cookies"); 150 | const localStorage = await getHandle("localStorage"); 151 | await clickToggle(await getHandle("cookies")); 152 | await clickToggle(await getHandle("localStorage")); 153 | equal(await isEnabled(cookieHandle), true); 154 | equal(await isEnabled(localStorage), true); 155 | await triggerDataDeletion(); 156 | await page2.waitForTimeout(50); 157 | ok(!await isLocalStorageSet()); 158 | ok(!await isCookieSet()); 159 | await clickToggle(await getHandle("cookies")); 160 | await clickToggle(await getHandle("localStorage")); 161 | await page2.waitForTimeout(200); 162 | equal(await isEnabled(cookieHandle), false); 163 | equal(await isEnabled(localStorage), false); 164 | }); 165 | 166 | it("Data deletion should clear all browsing datas when 'Remove All' is enabled in the 'Clear on startup' section", async() => 167 | { 168 | await setCookie(); 169 | await setStorage(); 170 | await page2.waitForTimeout(50); 171 | ok(await isLocalStorageSet()); 172 | ok(await isCookieSet()); 173 | 174 | await clickToggle(await getHandle("removeAll")); 175 | await page2.waitForTimeout(50); 176 | await triggerDataDeletion(); 177 | await page2.waitForTimeout(50); 178 | ok(!await isLocalStorageSet()); 179 | ok(!await isCookieSet()); 180 | }); 181 | 182 | after(async() => 183 | { 184 | await restorePermissions(); 185 | if (!process.env.KEEP_BROWSER) 186 | { 187 | await closeBrowser(); 188 | } 189 | server.close(); 190 | }); 191 | }); 192 | -------------------------------------------------------------------------------- /test/puppeteer/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const assert = require("assert"); 20 | const {allUrlsToPermissions, restorePermissions} = require("../manifest"); 21 | const {openPopupPage, closeBrowser} = require("../common"); 22 | 23 | let browser; 24 | let page; 25 | 26 | async function getHandle(access) 27 | { 28 | return await page.$(`[data-access='${access}']`); 29 | } 30 | 31 | function getLabel(pmToggleHandle) 32 | { 33 | return page.evaluate((pmToggleHandle) => 34 | { 35 | return pmToggleHandle.shadowRoot.querySelector("#label").textContent; 36 | }, pmToggleHandle); 37 | } 38 | 39 | function isEnabled(pmToggleHandle) 40 | { 41 | return page.evaluate((pmToggleHandle) => 42 | { 43 | return pmToggleHandle.isEnabled(); 44 | }, pmToggleHandle); 45 | } 46 | 47 | function clickToggle(pmToggleHandle) 48 | { 49 | return page.evaluate((pmToggleHandle) => 50 | { 51 | pmToggleHandle.shadowRoot.querySelector("#toggle").click(); 52 | }, pmToggleHandle); 53 | } 54 | 55 | function clickToggleLabel(pmToggleHandle) 56 | { 57 | return page.evaluate((pmToggleHandle) => 58 | { 59 | pmToggleHandle.shadowRoot.querySelector("#label").click(); 60 | }, pmToggleHandle); 61 | } 62 | 63 | function closeDialog(selector) 64 | { 65 | return page.evaluate((selector) => 66 | { 67 | return document.querySelector(selector).closeDialog(); 68 | }, selector); 69 | } 70 | 71 | function isDialogHidden(selector) 72 | { 73 | return page.evaluate((selector) => 74 | { 75 | return document.querySelector(selector).shadowRoot.querySelector("#dialog").getAttribute("aria-hidden") == "true"; 76 | }, selector); 77 | } 78 | 79 | function setWebsitePrivacy(settingName, value) 80 | { 81 | return page.evaluate((settingName, value) => 82 | { 83 | browser.privacy.websites[settingName].set({ value }); 84 | }, settingName, value); 85 | } 86 | 87 | function getWebsitePrivacy(settingName) 88 | { 89 | return page.evaluate(async(settingName) => 90 | { 91 | return (await browser.privacy.websites[settingName].get({})).value; 92 | }, settingName); 93 | } 94 | 95 | function getSettingListData(name) 96 | { 97 | return page.evaluate(async(name) => 98 | { 99 | return (await browser.storage.local.get("settingList")).settingList[name]; 100 | }, name); 101 | } 102 | 103 | function setSettingListData(name, value) 104 | { 105 | return page.evaluate(async(name, value) => 106 | { 107 | const data = await browser.storage.local.get("settingList"); 108 | if (!data.settingList) 109 | data.settingList = {}; 110 | data.settingList[name] = value; 111 | await browser.storage.local.set(data); 112 | }, name, value); 113 | } 114 | 115 | function getLastSelectedTab() 116 | { 117 | return page.evaluate(async() => 118 | { 119 | return (await browser.storage.local.get("lastSelectedTab")).lastSelectedTab; 120 | }); 121 | } 122 | 123 | function isPanelHidden(tabId) 124 | { 125 | return page.evaluate(async(tabId) => 126 | { 127 | return document.querySelector(`[aria-labelledby="${tabId}"]`).getAttribute("hidden") == "true"; 128 | }, tabId); 129 | } 130 | 131 | function getSearchDomainValue() 132 | { 133 | return page.evaluate(() => 134 | { 135 | return document.querySelector("#search-domain").value; 136 | }); 137 | } 138 | 139 | describe("Testing main tab and tabs component", () => 140 | { 141 | before(async() => 142 | { 143 | await allUrlsToPermissions(); 144 | page = await openPopupPage(); 145 | }); 146 | 147 | it("The first PM item is '3-rd party cookies' and is enabled", async() => 148 | { 149 | const handle = await getHandle("thirdPartyCookiesAllowed"); 150 | assert.equal(await getLabel(handle), "3-rd party cookies"); 151 | assert.equal(await isEnabled(handle), true); 152 | }); 153 | it("Setting chrome.privacy.websites.thirdPartyCookiesAllowed should switch the toggle", async() => 154 | { 155 | const handle = await getHandle("thirdPartyCookiesAllowed"); 156 | await setWebsitePrivacy("thirdPartyCookiesAllowed", false); 157 | assert.equal(await isEnabled(handle), false); 158 | await setWebsitePrivacy("thirdPartyCookiesAllowed", true); 159 | assert.equal(await isEnabled(handle), true); 160 | }); 161 | it("Clicking the '3-rd party cookies' toggle should change the chrome.privacy.websites.thirdPartyCookiesAllowed", async() => 162 | { 163 | const handle = await getHandle("thirdPartyCookiesAllowed"); 164 | await clickToggle(handle); 165 | assert.equal(await getWebsitePrivacy("thirdPartyCookiesAllowed"), false); 166 | await clickToggle(handle); 167 | assert.equal(await getWebsitePrivacy("thirdPartyCookiesAllowed"), true); 168 | }); 169 | it("Setting settingList.cookies in local storage should switch 'Cookies' toggle", async() => 170 | { 171 | const handle = await getHandle("cookies"); 172 | await setSettingListData("cookies", true); 173 | assert.equal(await isEnabled(handle), true); 174 | await setSettingListData("cookies", false); 175 | assert.equal(await isEnabled(handle), false); 176 | }); 177 | it("Clicking the 'Cookies' toggle should set settingList.cookies in local storage", async() => 178 | { 179 | const handle = await getHandle("cookies"); 180 | await clickToggle(handle); 181 | await page.waitForTimeout(10); 182 | assert.equal(await getSettingListData("cookies"), true); 183 | await clickToggle(handle); 184 | await page.waitForTimeout(10); 185 | assert.equal(await getSettingListData("cookies"), false); 186 | }); 187 | it("Setting settingList.cookies in local storage should switch 'Cookies' toggle", async() => 188 | { 189 | const handle = await getHandle("cookies"); 190 | await setSettingListData("cookies", true); 191 | assert.equal(await isEnabled(handle), true); 192 | await setSettingListData("cookies", false); 193 | assert.equal(await isEnabled(handle), false); 194 | }); 195 | it("Clicking activeTabCookies should set activeTabCookies settingList and set current URL as search domain", async() => 196 | { 197 | const handle = await getHandle("activeTabCookies"); 198 | assert.equal(await getSearchDomainValue(), ""); 199 | await clickToggle(handle); 200 | await page.waitForTimeout(10); 201 | assert.equal(await getSettingListData("activeTabCookies"), true); 202 | const url = await page.url(); 203 | const domain = url.split('/')[2].split(':')[0].replace("www.", ""); 204 | assert.equal(await getSearchDomainValue(), domain); 205 | }); 206 | 207 | it("Clicking on pm-toggle name opens pm-dialog", async() => 208 | { 209 | assert.equal(await isDialogHidden("pm-dialog.info"), true); 210 | await clickToggleLabel(await getHandle("thirdPartyCookiesAllowed")); 211 | await page.waitForTimeout(10); 212 | assert.equal(await isDialogHidden("pm-dialog.info"), false); 213 | await closeDialog("pm-dialog.info"); 214 | }); 215 | 216 | it("Clicking tabs should set lastSelectedTab", async() => 217 | { 218 | assert.equal(await getLastSelectedTab(), undefined); 219 | assert.equal(await isPanelHidden("tab-main"), false); 220 | assert.equal(await isPanelHidden("tab-cookies"), true); 221 | await page.click("#tab-cookies"); 222 | assert.equal(await isPanelHidden("tab-cookies"), false); 223 | assert.equal(await getLastSelectedTab(), "tab-cookies"); 224 | }); 225 | it("Reloading the page should set switch to lastSelectedTab", async() => 226 | { 227 | await page.reload({waitUntil: "domcontentloaded"}); 228 | assert.equal(await isPanelHidden("tab-main"), true); 229 | assert.equal(await isPanelHidden("tab-cookies"), false); 230 | }); 231 | it("When additional permissions are changed, 'Additional Permissions' toggle is updated accordingly"); 232 | 233 | after(async() => 234 | { 235 | await restorePermissions(); 236 | if (!process.env.KEEP_BROWSER) 237 | { 238 | await closeBrowser(); 239 | } 240 | }); 241 | }); 242 | -------------------------------------------------------------------------------- /test/puppeteer/network.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const {allUrlsToPermissions, restorePermissions} = require("../manifest"); 20 | const {openPopupPage, closeBrowser, getBrowser} = require("../common"); 21 | const {equal, ok} = require("assert"); 22 | const path = require("path"); 23 | const {readFileSync, unlinkSync} = require("fs"); 24 | 25 | let page; 26 | let page2; 27 | let tableListHandle; 28 | let server = null; 29 | 30 | const tableList = {}; 31 | const methods = ["getItemIndex", "addItems", "getItem", "removeItem", 32 | "selectItem", "removeItem", "empty", "updateItem"]; 33 | 34 | function startHttpServer() 35 | { 36 | const http = require('http'); 37 | 38 | server = http.createServer((req, res) => 39 | { 40 | res.writeHead(200, {'Content-Type': 'text/plain', 'test-header': 'test-value'}); 41 | res.end("Hello"); 42 | }).listen(4000); 43 | } 44 | 45 | function getLoadedAmount(id) 46 | { 47 | return page.evaluate((tableListHandle, id) => 48 | { 49 | let elements = tableListHandle.shadowRoot.querySelector("ul").children; 50 | if (id) 51 | { 52 | const index = tableListHandle.getItemIndex(id); 53 | elements = elements[index].querySelector("ul").children; 54 | } 55 | return elements.length; 56 | }, tableListHandle, id); 57 | } 58 | 59 | function getItemElemId(num) 60 | { 61 | return page.evaluate((tableListHandle, num) => 62 | { 63 | let elements = tableListHandle.shadowRoot.querySelector("ul").children; 64 | return elements[num].dataset.id; 65 | }, tableListHandle, num); 66 | } 67 | 68 | function clickToggle(pmToggleHandle) 69 | { 70 | return page.evaluate((pmToggleHandle) => 71 | { 72 | pmToggleHandle.shadowRoot.querySelector("#toggle").click(); 73 | }, pmToggleHandle); 74 | } 75 | 76 | async function getHandle(access) 77 | { 78 | return await page.$(`[data-access='${access}']`); 79 | } 80 | 81 | async function getItemText(id, parentId, text) 82 | { 83 | if (!(await tableList.getItem(id, parentId))) 84 | return null; 85 | return (await tableList.getItem(id, parentId)).texts[text]; 86 | } 87 | 88 | async function getItemData(id, parentId, name) 89 | { 90 | return (await tableList.getItem(id, parentId)).data[name]; 91 | } 92 | 93 | async function getItemElemHandle(id, parentId) 94 | { 95 | return await page.evaluateHandle((handle, id, parentId) => handle.getItemElem(id, parentId), tableListHandle, id, parentId); 96 | } 97 | 98 | function getItemElemDatasetId(id, parentId) 99 | { 100 | return page.evaluate((tableListHandle, id, parentId) => 101 | { 102 | if (tableListHandle.getItemElem(id, parentId)) 103 | return tableListHandle.getItemElem(id, parentId).dataset.id; 104 | }, tableListHandle, id, parentId); 105 | } 106 | 107 | async function ensureItem(id, parentId) 108 | { 109 | return !!(await getItemElemDatasetId(id, parentId) || 110 | await tableList.getItem(id, parentId)); 111 | } 112 | 113 | methods.forEach((methodName) => 114 | { 115 | tableList[methodName] = (...args) => runComponentMethod(methodName, ...args); 116 | }); 117 | 118 | function runComponentMethod() 119 | { 120 | const functionName = arguments[0]; 121 | const args = Array.prototype.slice.call(arguments, 1); 122 | return page.evaluate((tableListHandle, functionName, args) => 123 | { 124 | return tableListHandle[functionName](...args); 125 | }, tableListHandle, functionName, args); 126 | } 127 | 128 | describe("Testing Network tab", () => 129 | { 130 | before(async() => 131 | { 132 | await allUrlsToPermissions(); 133 | page = await openPopupPage(); 134 | await page.click("#tab-network"); 135 | startHttpServer(); 136 | page2 = await getBrowser().newPage(); 137 | page.bringToFront(); 138 | tableListHandle = await page.$("#panel-network pm-table"); 139 | }); 140 | 141 | it("When collectHeaders is set requests should be added into network tab", async() => 142 | { 143 | const handle = await getHandle("collectHeaders"); 144 | await clickToggle(handle); 145 | await page.waitForTimeout(10); 146 | await page2.goto("http://127.0.0.1:4000/"); 147 | await page.waitForTimeout(100); 148 | equal((await getItemText("pm-table-item1", null, "type")), "main_frame"); 149 | equal((await getItemText("pm-table-item1", null, "url")), "http://127.0.0.1:4000/"); 150 | equal((await getItemData("pm-table-item1", null, "type")), "send"); 151 | equal((await getItemText("pm-table-item2", null, "type")), "main_frame"); 152 | equal((await getItemText("pm-table-item2", null, "url")), "http://127.0.0.1:4000/"); 153 | equal((await getItemData("pm-table-item2", null, "type")), "receive"); 154 | equal((await getItemText("pm-table-item3", null, "type")), "image"); 155 | equal((await getItemText("pm-table-item3", null, "url")), "http://127.0.0.1:4000/favicon.ico"); 156 | equal((await getItemData("pm-table-item3", null, "type")), "send"); 157 | (await getItemElemHandle("pm-table-item1")).click(); 158 | await page.waitForTimeout(50); 159 | equal((await getItemText("method", "pm-table-item1", "name")), "method"); 160 | equal((await getItemText("method", "pm-table-item1", "value")), "GET"); 161 | equal((await getItemText("type", "pm-table-item1", "name")), "type"); 162 | equal((await getItemText("type", "pm-table-item1", "value")), "main_frame"); 163 | equal((await getItemText("url", "pm-table-item1", "name")), "url"); 164 | equal((await getItemText("url", "pm-table-item1", "value")), "http://127.0.0.1:4000/"); 165 | (await getItemElemHandle("pm-table-item2")).click(); 166 | await page.waitForTimeout(50); 167 | equal((await getItemText("statusCode", "pm-table-item2", "name")), "statusCode"); 168 | equal((await getItemText("statusCode", "pm-table-item2", "value")), "200"); 169 | equal((await getItemText("statusCode", "pm-table-item2", "name")), "statusCode"); 170 | equal((await getItemText("statusCode", "pm-table-item2", "value")), "200"); 171 | equal((await getItemText("test-header", "pm-table-item2", "name")), "test-header"); 172 | equal((await getItemText("test-header", "pm-table-item2", "value")), "test-value"); 173 | }); 174 | 175 | it("Reloading the page should keep the replace already recorded", async() => 176 | { 177 | await page.reload(); 178 | await page.waitForTimeout(100); 179 | tableListHandle = await page.$("#panel-network pm-table"); 180 | equal(await ensureItem("pm-table-item1"), true); 181 | equal(await ensureItem("pm-table-item2"), true); 182 | equal(await ensureItem("pm-table-item3"), true); 183 | equal(await ensureItem("pm-table-item4"), true); 184 | }); 185 | 186 | it("Hitting 'Delete All' button should empty network table", async() => 187 | { 188 | await page.click("pm-button[data-action='delete-all']"); 189 | await page.waitForTimeout(30); 190 | equal(await ensureItem("pm-table-item1"), false); 191 | equal(await ensureItem("pm-table-item2"), false); 192 | }); 193 | 194 | it("Switching blockUserAgent on should block User agent from the request", async() => 195 | { 196 | await page2.reload(); 197 | await page.waitForTimeout(100); 198 | (await getItemElemHandle(await getItemElemId(0))).click(); 199 | await page.waitForTimeout(50); 200 | equal(await getItemText("User-Agent", await getItemElemId(0), "name"), "User-Agent"); 201 | await clickToggle(await getHandle("blockUserAgent")); 202 | await page.waitForTimeout(30); 203 | await page.click("pm-button[data-action='delete-all']"); 204 | await page.reload(); 205 | await page2.reload(); 206 | await page.waitForTimeout(100); 207 | tableListHandle = await page.$("#panel-network pm-table"); 208 | (await getItemElemHandle(await getItemElemId(0))).click(); 209 | await page.waitForTimeout(50); 210 | equal(await getItemText("User-Agent", await getItemElemId(0), "name"), null); 211 | }); 212 | 213 | it("Clicking on 'Download All' button should download all collected requests", async() => 214 | { 215 | const pageCDP = await (await page.target().createCDPSession()); 216 | await pageCDP.send('Page.setDownloadBehavior', { 217 | behavior: "allow", 218 | downloadPath: __dirname 219 | }); 220 | await page.click("pm-button[data-action='download-all']"); 221 | await page.waitForTimeout(300); 222 | const file = readFileSync(path.join(__dirname, "requests.json")); 223 | const requests = JSON.parse(file); 224 | 225 | equal(requests[0].action, "send"); 226 | equal(requests[0].method, "GET"); 227 | ok(requests[0].headers); 228 | equal(requests[0].type, "main_frame"); 229 | equal(requests[0].url, "http://127.0.0.1:4000/"); 230 | 231 | equal(requests[1].action, "receive"); 232 | equal(requests[1].headers["Content-Type"], "text/plain"); 233 | equal(requests[1].headers["test-header"], "test-value"); 234 | equal(requests[1].statusCode, 200); 235 | 236 | equal(requests[2].url, "http://127.0.0.1:4000/favicon.ico"); 237 | 238 | equal(requests[3].url, "http://127.0.0.1:4000/favicon.ico"); 239 | equal(requests[3].type, "image"); 240 | }); 241 | 242 | it("Switching collectHeaders off should stop adding into network tab", async() => 243 | { 244 | await page.click("pm-button[data-action='delete-all']"); 245 | await page.waitForTimeout(30); 246 | await page2.goto("http://127.0.0.1:4000/"); 247 | await page.waitForTimeout(30); 248 | equal(await getLoadedAmount(), 4); 249 | await clickToggle(await getHandle("collectHeaders")); 250 | await page.click("pm-button[data-action='delete-all']"); 251 | await page.waitForTimeout(30); 252 | await page2.goto("http://127.0.0.1:4000/"); 253 | await page.waitForTimeout(30); 254 | equal(await getLoadedAmount(), 0); 255 | }); 256 | 257 | after(async() => 258 | { 259 | unlinkSync(path.join(__dirname, "requests.json")); 260 | await restorePermissions(); 261 | if (!process.env.KEEP_BROWSER) 262 | { 263 | await closeBrowser(); 264 | } 265 | server.close(); 266 | }); 267 | }); 268 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Privacy Manager. 3 | * Copyright (C) 2017-present Manvel Saroyan 4 | * 5 | * Privacy Manager is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * Privacy Manager is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Privacy Manager. If not, see . 17 | */ 18 | 19 | const path = require("path"); 20 | const argv = require("minimist")(process.argv.slice(2)); 21 | const CopyPlugin = require('copy-webpack-plugin'); 22 | const csso = require("csso"); 23 | const manifest = require("./manifest"); 24 | 25 | module.exports = 26 | { 27 | context: path.resolve(__dirname), 28 | entry: { 29 | "ui": "./src/js/ui/index.js", 30 | "back": "./src/js/back.js", 31 | "common": "./src/js/common.js" 32 | }, 33 | output: { 34 | path: path.resolve('dist'), 35 | filename: "js/[name].js" 36 | }, 37 | optimization: { 38 | minimize: false 39 | }, 40 | plugins: [ 41 | new CopyPlugin({patterns : [ 42 | { from: './src/_locales', to: "_locales" }, 43 | { from: './src/css', to: "css" , 44 | transform: (content) => argv.prod ? csso.minify(content).css : content}, 45 | { from: './src/img', to: "img" }, 46 | { from: "./src/popup.html", to: "popup.html" }, 47 | { from: "./src/agent-block.json", to: "agent-block.json" }, 48 | { from: "./src/manifest.json", to: "manifest.json", 49 | transform: manifest.transform } 50 | ]}) 51 | ] 52 | }; 53 | 54 | if (process.env.WATCH) 55 | { 56 | module.exports.watch = true; 57 | } 58 | 59 | if (argv.PROD) 60 | { 61 | module.exports.mode = "production"; 62 | module.exports.optimization.minimize = true; 63 | } 64 | --------------------------------------------------------------------------------