├── .eslintignore
├── .eslintrc.json
├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .gitlab-ci.yml
├── .gitlab
├── CODEOWNERS
└── issue_templates
│ ├── bug.md
│ └── change.md
├── .gitmodules
├── .npmrc
├── COPYING
├── README.md
├── build
├── .eslintrc.json
├── config
│ ├── base.mjs
│ ├── chrome.mjs
│ ├── devenv.mjs
│ ├── firefox.mjs
│ ├── index.mjs
│ └── webpack.config.mjs
├── configParser.mjs
├── manifest.json
├── tasks
│ ├── dependencies.mjs
│ ├── devenv.mjs
│ ├── index.mjs
│ ├── manifest.mjs
│ ├── mapping.mjs
│ ├── sourceDistribution.mjs
│ ├── translations.mjs
│ └── webpack.mjs
├── templates
│ ├── info.chrome.js.tmpl
│ ├── info.gecko.js.tmpl
│ └── testIndex.html.tmpl
└── utils
│ ├── git.mjs
│ ├── gulp-change-path.mjs
│ ├── gulp-merge-translations.mjs
│ └── wp-template-loader.js
├── composer.postload.js
├── devtools.html
├── devtools.js
├── ext
├── background.js
├── common.js
├── content.js
└── devtools.js
├── gulpfile.mjs
├── icons
└── detailed
│ ├── abp-48.png
│ └── abp-64.png
├── include.preload.js
├── inject.preload.js
├── jsdoc.conf
├── lib
├── allowlisting.js
├── browserAction.js
├── contentFiltering.js
├── csp.js
├── debug.js
├── devenvPoller.js
├── devtools.js
├── filterComposer.js
├── filterConfiguration.js
├── hitLogger.js
├── icon.js
├── io.js
├── messageResponder.js
├── messaging.js
├── ml.js
├── notificationHelper.js
├── options.js
├── popupBlocker.js
├── prefs.js
├── requestBlocker.js
├── stats.js
├── subscriptionInit.js
├── uninstall.js
└── url.js
├── managed-storage-schema.json
├── options.html
├── options.js
├── package-lock.json
├── package.json
├── polyfill.js
├── qunit
├── .eslintrc.json
├── qunit.css
├── qunit.js
├── subscriptions.json
└── tests
│ ├── cssEscaping.js
│ ├── prefs.js
│ ├── subscriptionInit.js
│ ├── uninstall.js
│ └── url.js
├── subscriptionLink.postload.js
└── test
├── .eslintrc.json
├── bin
└── downloadBrowsers.mjs
├── browsers
├── chromium.mjs
├── edge.mjs
└── firefox.mjs
├── entrypoint.mjs
├── helper-extension
├── background.js
└── manifest.json
├── misc
└── utils.mjs
└── suites
├── pages
├── index.mjs
├── specialized.mjs
├── subscribe.mjs
├── uninstall.mjs
└── utils.mjs
└── qunit.mjs
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib/publicSuffixList.js
2 | qunit/qunit.js
3 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-eyeo",
3 | "root": true,
4 | "env": {
5 | "browser": true,
6 | "webextensions": true
7 | },
8 | "globals": {
9 | "exports": true,
10 | "ext": true,
11 | "module": true,
12 | "require": true
13 | },
14 | "parserOptions": {
15 | "ecmaVersion": 11
16 | },
17 | "rules": {
18 | "curly": ["error", "multi-or-nest", "consistent"]
19 | },
20 | "overrides": [
21 | {
22 | "files": ["*.mjs"],
23 | "parserOptions": {
24 | "sourceType": "module"
25 | }
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Hi,
2 |
3 | You tried to submit a pull request for adblockpluschrome.
4 |
5 | While we love GitHub, the project is hosted on GitLab, therefore we do
6 | not monitor pull request submitted on GitHub.
7 |
8 | Please visit: https://gitlab.com/eyeo/adblockplus/adblockpluschrome
9 |
10 | Thank you.
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /devenv.*/
2 | /node_modules/
3 | /docs/
4 | /test/screenshots/
5 | /.last_ui_build
6 | /adblockpluschrome-*.zip
7 | /adblockplusfirefox-*.xpi
8 | /adblockplus-*.tar.gz
9 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | # This file is part of Adblock Plus ,
2 | # Copyright (C) 2006-present eyeo GmbH
3 | #
4 | # Adblock Plus is free software: you can redistribute it and/or modify
5 | # it under the terms of the GNU General Public License version 3 as
6 | # published by the Free Software Foundation.
7 | #
8 | # Adblock Plus is distributed in the hope that it will be useful,
9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | # GNU General Public License for more details.
12 | #
13 | # You should have received a copy of the GNU General Public License
14 | # along with Adblock Plus. If not, see .
15 |
16 | ---
17 |
18 | default:
19 | image: "registry.gitlab.com/eyeo/docker/adblockplus-ci:node12"
20 |
21 | stages:
22 | - "prepare"
23 | - "build"
24 | - "publish"
25 | - "test"
26 |
27 | variables:
28 | GIT_SUBMODULE_STRATEGY: "recursive"
29 | npm_config_audit: "false"
30 | npm_config_prefer_offline: "true"
31 | npm_config_unsafe_perm: "true"
32 |
33 | .cache:
34 | cache: &cache
35 | key: "global-cache"
36 | untracked: true
37 | paths:
38 | - "adblockpluscore/*-snapshots/download-cache/"
39 | - "adblockpluscore/chromium-snapshots/chromedriver/"
40 | - "adblockplusui/node_modules/"
41 |
42 | .default_template:
43 | needs:
44 | - job: "prepare-dependencies"
45 | before_script:
46 | - "npm install"
47 | retry:
48 | max: 2
49 | when: "stuck_or_timeout_failure"
50 | except:
51 | - "schedules"
52 | cache:
53 | <<: *cache
54 | policy: "pull"
55 | interruptible: true
56 |
57 | prepare-dependencies:
58 | stage: "prepare"
59 | script:
60 | - "git clean -x -d -ff"
61 | - "git -C adblockpluscore clean -e '/*-snapshots' -x -d -ff"
62 | - "git -C adblockplusui clean -x -d -ff"
63 | - "npm install"
64 | - "npm run download-test-browsers"
65 | except:
66 | - "schedules"
67 | cache: *cache
68 | interruptible: true
69 |
70 | ###############################################################################
71 | # Build & document #
72 | ###############################################################################
73 |
74 | .build:
75 | extends: ".default_template"
76 | stage: "build"
77 | script:
78 | - "npx gulp build -t $TARGET -c development"
79 | - "npx gulp devenv -t $TARGET"
80 |
81 | build:chrome:
82 | extends: ".build"
83 | variables:
84 | TARGET: "chrome"
85 | artifacts:
86 | paths:
87 | - "adblockpluschrome-*.zip"
88 | - "devenv.chrome/qunit/"
89 |
90 | build:firefox:
91 | extends: ".build"
92 | variables:
93 | TARGET: "firefox"
94 | artifacts:
95 | paths:
96 | - "adblockplusfirefox-*.xpi"
97 | - "devenv.firefox/qunit/"
98 |
99 | build:source:
100 | extends: ".build"
101 | script:
102 | - "npx gulp source -c development"
103 | artifacts:
104 | paths:
105 | - "adblockplus-*.tar.gz"
106 |
107 | docs:
108 | extends: ".default_template"
109 | stage: "build"
110 | script:
111 | - "npm run docs"
112 | artifacts:
113 | paths:
114 | - "docs/"
115 | expire_in: "1 week"
116 |
117 | ###############################################################################
118 | # Tests & checks #
119 | ###############################################################################
120 |
121 | .test_template:
122 | extends: ".default_template"
123 | stage: "test"
124 | before_script:
125 | - "npm install"
126 | - "unzip -q $EXTENSION_FILE -d devenv.*"
127 | variables:
128 | SKIP_BUILD: "true"
129 | artifacts:
130 | paths:
131 | - "test/screenshots/"
132 | when: "on_failure"
133 | expire_in: "1 mo"
134 |
135 | .test_template_chromium:
136 | extends: ".test_template"
137 | needs:
138 | - job: "build:chrome"
139 | artifacts: true
140 | variables:
141 | EXTENSION_FILE: "adblockpluschrome-*.zip"
142 |
143 | .test_template_firefox:
144 | extends: ".test_template"
145 | needs:
146 | - job: "build:firefox"
147 | artifacts: true
148 | variables:
149 | EXTENSION_FILE: "adblockplusfirefox-*.xpi"
150 |
151 | lint:yaml:
152 | image: "registry.gitlab.com/eyeo/docker/yamllint:1.14.0-configured"
153 | stage: "test"
154 | needs: []
155 | script:
156 | - "yamllint .gitlab-ci.yml"
157 | except:
158 | - "schedules"
159 | interruptible: true
160 |
161 | lint:js:
162 | extends: ".default_template"
163 | stage: "test"
164 | script:
165 | - "npm run lint"
166 |
167 | audit:
168 | extends: ".default_template"
169 | stage: "test"
170 | script:
171 | - "npm audit"
172 | allow_failure: true
173 |
174 | test:firefox:oldest:
175 | extends: ".test_template_firefox"
176 | script:
177 | - "npm run test-only -- -g 'Firefox \\(oldest\\)'"
178 |
179 | test:firefox:latest:
180 | extends: ".test_template_firefox"
181 | script:
182 | - "npm run test-only -- -g 'Firefox \\(latest\\)'"
183 |
184 | test:chromium:oldest:
185 | extends: ".test_template_chromium"
186 | script:
187 | - "xvfb-run -a npm run test-only -- -g 'Chromium \\(oldest\\)'"
188 |
189 | test:chromium:latest:
190 | extends: ".test_template_chromium"
191 | script:
192 | - "xvfb-run -a npm run test-only -- -g 'Chromium \\(latest\\)'"
193 |
194 | test:edge:
195 | extends: ".test_template_chromium"
196 | before_script:
197 | - "Expand-Archive -Path $EXTENSION_FILE -DestinationPath devenv.chrome"
198 | - "choco upgrade -y nodejs --version=12.17.0"
199 | - "choco install -y microsoft-edge --version=79.0.309.71"
200 | - "npm install"
201 | script:
202 | - "npm run test-only -- -g 'Edge'"
203 | tags:
204 | - shared-windows
205 | - windows
206 | - windows-1809
207 | # https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25980
208 | cache: null
209 |
210 | ###############################################################################
211 | # Public pages #
212 | ###############################################################################
213 |
214 | .pages:
215 | stage: "publish"
216 | needs:
217 | - job: "docs"
218 | artifacts: true
219 | except:
220 | - "schedules"
221 |
222 | include:
223 | project: "eyeo/adblockplus/adblockpluscore"
224 | file: ".gitlab-pages.yml"
225 |
--------------------------------------------------------------------------------
/.gitlab/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * sebastian@adblockplus.org geo@adblockplus.org t.feliu@eyeo.com
2 |
3 | # Build and CI
4 | /build/ sebastian@adblockplus.org tristan@adblockplus.org t.feliu@eyeo.com
5 | /.gitlab-ci.yml sebastian@adblockplus.org tristan@adblockplus.org t.feliu@eyeo.com
6 | /gulpfile.mjs sebastian@adblockplus.org tristan@adblockplus.org t.feliu@eyeo.com
7 | /package.json sebastian@adblockplus.org tristan@adblockplus.org t.feliu@eyeo.com
8 | /package-lock.json sebastian@adblockplus.org tristan@adblockplus.org t.feliu@eyeo.com
9 |
10 | # Test automation
11 | /test/ sebastian@adblockplus.org t.feliu@eyeo.com
12 |
--------------------------------------------------------------------------------
/.gitlab/issue_templates/bug.md:
--------------------------------------------------------------------------------
1 | ## Environment
2 |
3 | Replace this text with the Version of your operating system, your exact browser version, the used Adblock Plus version and the enabled Filter lists.
4 |
5 | ## How to reproduce
6 |
7 | 1. Step one of your reproduction process (e.g: Go to http://example.com/
8 | 2. Step two of your reproduction process (e.g: Click the big red button labelled "click me" at the left)
9 | 3. Step three of your reproduction process
10 | 4. Step four of your reproduction process
11 | ...
12 |
13 | ## Observed behavior
14 |
15 | Replace this text with your detailed observations.
16 |
17 | ## Expected behavior
18 |
19 | Replace this text with your detailed expectations.
20 |
21 | /label ~bug
22 |
--------------------------------------------------------------------------------
/.gitlab/issue_templates/change.md:
--------------------------------------------------------------------------------
1 | ## Background
2 |
3 | Replace this text with your reasoning that lead to wanting the change.
4 |
5 | ## What to change
6 |
7 | Replace this text with the detailed description of what exactly shall be changed/added and where.
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "adblockpluscore"]
2 | path = adblockpluscore
3 | url = https://gitlab.com/eyeo/adblockplus/adblockpluscore.git
4 | [submodule "adblockplusui"]
5 | path = adblockplusui
6 | url = https://gitlab.com/eyeo/adblockplus/abpui/adblockplusui.git
7 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | edgechromiumdriver_skip_download=true
2 | chromedriver_skip_download=true
3 | engine-strict=true
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Adblock Plus for Chrome, Opera, Microsoft Edge and Firefox (DEPRECATED!)
2 | ========================================================================
3 |
4 | IMPORTANT: Deprecation notice
5 | -----------------------------
6 |
7 | This codebase is deprecated. As of Adblock Plus 3.11, Adblock Plus for Chrome,
8 | Firefox, Microsoft Edge and Opera is based on the
9 | [adblockplusui repository](https://gitlab.com/eyeo/adblockplus/abpui/adblockplusui/).
10 |
11 | Development of the core ad blocking integration for web extensions has moved to
12 | the [webext-sdk repository](https://gitlab.com/eyeo/adblockplus/webext-sdk).
13 |
14 | ---
15 |
16 | This repository contains the platform-specific Adblock Plus source code for
17 | Chrome, Opera, Microsoft Edge and Firefox. It can be used to build
18 | Adblock Plus for these platforms.
19 |
20 | Building
21 | ---------
22 |
23 | ### Requirements
24 |
25 | - [Node.js](https://nodejs.org/) (>= 12.17.0)
26 |
27 | ### Building on Windows
28 |
29 | On Windows, you need a [Linux environment running on WSL](https://docs.microsoft.com/windows/wsl/install-win10).
30 | Then install the above requirements and run the commands below from within Bash.
31 |
32 | ### Updating the dependencies
33 |
34 | Clone the external repositories:
35 |
36 | git submodule update --init --recursive
37 |
38 | _Note: when building from a source archive, this step must be skipped._
39 |
40 | Install the required npm packages:
41 |
42 | npm install
43 |
44 | Rerun the above commands when the dependencies might have changed,
45 | e.g. after checking out a new revison.
46 |
47 | ### Building the extension
48 |
49 | Run the following command in the project directory:
50 |
51 | npx gulp build -t {chrome|firefox} [-c development]
52 |
53 | This will create a build with a name in the form
54 | _adblockpluschrome-n.n.n.zip_ or _adblockplusfirefox-n.n.n.xpi_. These builds
55 | are unsigned. They can be submitted as-is to the extension stores, or if
56 | unpacked loaded in development mode for testing (same as devenv builds below).
57 |
58 | ### Development environment
59 |
60 | To simplify the process of testing your changes you can create an unpacked
61 | development environment. For that run one of the following command:
62 |
63 | npx gulp devenv -t {chrome|firefox}
64 |
65 | This will create a _devenv.*_ directory in the project directory. You can load
66 | the directory as an unpacked extension under _chrome://extensions_ in
67 | Chromium-based browsers, and under _about:debugging_ in Firefox. After making
68 | changes to the source code re-run the command to update the development
69 | environment, and the extension should reload automatically after a few seconds.
70 |
71 | ### Customization
72 |
73 | If you wish to create an extension based on our code and use the same
74 | build tools, we offer some customization options.
75 |
76 | This can be done by:
77 |
78 | - Specifying a path to a new configuration file relative to `gulpfile.mjs`
79 | (it should match the structure found in `build/config/`).
80 |
81 | npx gulp {build|devenv} -t {chrome|firefox} --config config.mjs
82 |
83 | - Specifying a path to a new `manifest.json` file relative to `gulpfile.mjs`.
84 | You should check `build/manifest.json` and `build/tasks/manifest.mjs` to see
85 | how we modify it.
86 |
87 | npx gulp {build|devenv} -t {chrome|firefox} -m manifest.json
88 |
89 | Running tests
90 | -------------
91 |
92 | ### Unit tests
93 |
94 | To verify your changes you can use the unit test suite located in the _qunit_
95 | directory of the repository. In order to run the unit tests go to the
96 | extension's Options page, open the JavaScript Console and type in:
97 |
98 | location.href = "qunit/index.html";
99 |
100 | The unit tests will run automatically once the page loads.
101 |
102 | ### External test runner
103 |
104 | There is also an external test runner that can be invoked from the
105 | command line in order to run the unit tests along some integration
106 | tests on different browsers, and automatically run the linter as well.
107 |
108 | On Windows, in order to use the test runner, in addition to setting up a Linux
109 | environment as outlined above, you need to have Node.js installed in your native
110 | Windows environment. Then run the commands below from within PowerShell or
111 | cmd.exe (unlike when building the extension which needs to be done from Bash).
112 |
113 | On Linux, newer versions of Chromium require `libgbm`.
114 |
115 | Make sure the required packages are installed and up-to-date:
116 |
117 | npm install
118 |
119 | Start the testing process for all browsers:
120 |
121 | npm test
122 |
123 | Start the testing process in one browser only:
124 |
125 | npm test -- -g
126 |
127 | In order to run other test subsets, please check `-g` option on
128 | [Mocha's documentation](https://mochajs.org/#-grep-regexp-g-regexp).
129 |
130 | By default it downloads (and caches) and runs the tests against the
131 | oldest compatible version and the latest release version of each browser.
132 | In order to run the tests against a different version set the `CHROMIUM_BINARY`,
133 | `FIREFOX_BINARY` or `EDGE_BINARY` environment variables. Following values are
134 | accepted:
135 |
136 | * `installed`
137 | * Uses the version installed on the system.
138 | * `path:`
139 | * Uses the binary located at the given path.
140 | * `download:`
141 | * Downloads the given version (for Firefox the version must be in the
142 | form `.`, for Chromium this must be the revision number).
143 | This option is not available for Edge.
144 |
145 | Filter tests subset uses [ABP Test pages](https://testpages.adblockplus.org/).
146 | In order to run those tests on a different version of the test pages, set
147 | the _TEST_PAGES_URL_ environment variable. Additionally, in order to accept
148 | insecure `https` certificates set the _TEST_PAGES_INSECURE_ environment variable
149 | to `"true"`.
150 |
151 | [Edge Chromium](https://www.microsoft.com/en-us/edge/business/download) needs to
152 | be installed before running the Edge tests.
153 |
154 | Linting
155 | -------
156 |
157 | You can lint the code using [ESLint](http://eslint.org).
158 |
159 | You will need to setup first. This will install our configuration
160 | [eslint-config-eyeo](https://gitlab.com/eyeo/auxiliary/eyeo-coding-style/-/tree/master/eslint-config-eyeo)
161 | and everything needed after you run:
162 |
163 | npm install
164 |
165 | Then you can run to lint the code:
166 |
167 | npm run lint
168 |
--------------------------------------------------------------------------------
/build/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true
4 | },
5 | "rules": {
6 | "no-console": "off"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/build/config/base.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | export default {
19 | basename: "adblockplus",
20 | version: "3.10.2",
21 | webpack: {
22 | bundles: [
23 | {
24 | dest: "background.js",
25 | src: [
26 | "lib/devtools.js",
27 | "lib/debug.js",
28 | "lib/requestBlocker.js",
29 | "lib/popupBlocker.js",
30 | "lib/subscriptionInit.js",
31 | "adblockplusui/lib/init.js",
32 | "lib/filterComposer.js",
33 | "lib/stats.js",
34 | "lib/uninstall.js",
35 | "lib/csp.js",
36 | "lib/contentFiltering.js",
37 | "lib/messageResponder.js",
38 | "lib/filterConfiguration.js",
39 | "lib/ml.js"
40 | ]
41 | },
42 | {
43 | dest: "include.preload.js",
44 | src: [
45 | "include.preload.js",
46 | "inject.preload.js"
47 | ]
48 | },
49 | {
50 | dest: "composer.postload.js",
51 | src: [
52 | "composer.postload.js"
53 | ]
54 | },
55 | {
56 | dest: "subscriptionLink.postload.js",
57 | src: [
58 | "subscriptionLink.postload.js"
59 | ]
60 | }
61 | ]
62 | },
63 | mapping: {
64 | copy: [
65 | {
66 | dest: "skin",
67 | src: [
68 | "adblockplusui/skin/**",
69 | "!adblockplusui/skin/fonts/*00/**",
70 | "!adblockplusui/skin/icons/toolbar/**",
71 | "!adblockplusui/skin/icons/abp-128.png",
72 | "!adblockplusui/skin/icons/arrow.svg",
73 | "!adblockplusui/skin/icons/iconClose.svg",
74 | "!adblockplusui/skin/icons/iconCritical.svg",
75 | "!adblockplusui/skin/icons/mobile/**",
76 | "!adblockplusui/skin/mobile-options.css"
77 | ]
78 | },
79 | {
80 | dest: "icons/detailed",
81 | src: [
82 | "icons/detailed/*.png",
83 | "adblockplusui/skin/icons/abp-128.png"
84 | ]
85 | },
86 | {
87 | dest: "data",
88 | src: "adblockplusui/data/*.json"
89 | },
90 | {
91 | dest: "data/mlHideIfGraphMatches",
92 | src: [
93 | "adblockpluscore/data/mlHideIfGraphMatches/model.json",
94 | "adblockpluscore/data/mlHideIfGraphMatches/group1-shard1of1.dat"
95 | ]
96 | },
97 | {
98 | dest: "ext",
99 | src: [
100 | "ext/**"
101 | ]
102 | },
103 | {
104 | dest: "",
105 | src: [
106 | "adblockplusui/*.js",
107 | "adblockplusui/*.html",
108 | "adblockpluscore/lib/content/snippets.js",
109 | "options.*",
110 | "devtools.*",
111 | "managed-storage-schema.json",
112 | "polyfill.js",
113 | "!adblockplusui/polyfill.js",
114 | "!adblockplusui/mobile-options.*"
115 | ]
116 | }
117 | ],
118 | rename: [
119 | {
120 | dest: "icons/abp-16-notification.png",
121 | src: "adblockplusui/skin/icons/toolbar/notification-16.png"
122 | },
123 | {
124 | dest: "icons/abp-16-allowlisted.png",
125 | src: "adblockplusui/skin/icons/toolbar/disabled-16.png"
126 | },
127 | {
128 | dest: "icons/abp-16.png",
129 | src: "adblockplusui/skin/icons/toolbar/default-16.png"
130 | },
131 | {
132 | dest: "icons/abp-20-notification.png",
133 | src: "adblockplusui/skin/icons/toolbar/notification-20.png"
134 | },
135 | {
136 | dest: "icons/abp-20-allowlisted.png",
137 | src: "adblockplusui/skin/icons/toolbar/disabled-20.png"
138 | },
139 | {
140 | dest: "icons/abp-20.png",
141 | src: "adblockplusui/skin/icons/toolbar/default-20.png"
142 | },
143 | {
144 | dest: "icons/abp-32-notification.png",
145 | src: "adblockplusui/skin/icons/toolbar/notification-32.png"
146 | },
147 | {
148 | dest: "icons/abp-32-allowlisted.png",
149 | src: "adblockplusui/skin/icons/toolbar/disabled-32.png"
150 | },
151 | {
152 | dest: "icons/abp-32.png",
153 | src: "adblockplusui/skin/icons/toolbar/default-32.png"
154 | },
155 | {
156 | dest: "icons/abp-40-notification.png",
157 | src: "adblockplusui/skin/icons/toolbar/notification-40.png"
158 | },
159 | {
160 | dest: "icons/abp-40-allowlisted.png",
161 | src: "adblockplusui/skin/icons/toolbar/disabled-40.png"
162 | },
163 | {
164 | dest: "icons/abp-40.png",
165 | src: "adblockplusui/skin/icons/toolbar/default-40.png"
166 | }
167 | ]
168 | },
169 | translations: {
170 | dest: "_locales",
171 | src: [
172 | "adblockplusui/locale/**/*.json",
173 | "!adblockplusui/locale/*/mobile-options.json"
174 | ]
175 | }
176 | };
177 |
--------------------------------------------------------------------------------
/build/config/chrome.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | export default {
19 | extends: "base",
20 | webpack: {
21 | alias: {
22 | info$: "info.chrome.js.tmpl"
23 | }
24 | },
25 | translations: {
26 | dest: "_locales",
27 | src: [
28 | "!adblockplusui/locale/es_AR/*.json",
29 | "!adblockplusui/locale/es_CL/*.json"
30 | ]
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/build/config/devenv.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | let common = {
19 | webpack: {
20 | bundles: [
21 | {
22 | dest: "qunit/tests.js",
23 | src: ["qunit/tests/*"]
24 | },
25 | {
26 | dest: "background.js",
27 | src: ["lib/devenvPoller.js"]
28 | }
29 | ]
30 | },
31 | mapping: {
32 | copy: [
33 | {
34 | dest: "qunit",
35 | src: ["qunit/qunit.*"]
36 | }
37 | ]
38 | },
39 | tests: {
40 | scripts: [
41 | "qunit.js",
42 | "../polyfill.js",
43 | " ../ext/common.js",
44 | "../ext/background.js",
45 | "tests.js"
46 | ]
47 | }
48 | };
49 |
50 | export let chromeDev = {...common, extends: "chrome"};
51 | export let firefoxDev = {...common, extends: "firefox"};
52 |
--------------------------------------------------------------------------------
/build/config/firefox.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | export default {
19 | extends: "base",
20 | webpack: {
21 | alias: {
22 | info$: "info.gecko.js.tmpl"
23 | }
24 | },
25 | mapping: {
26 | copy: [
27 | {
28 | dest: "skin",
29 | src: [
30 | "adblockplusui/skin/icons/mobile/**",
31 | "adblockplusui/skin/mobile-options.css"
32 | ]
33 | },
34 | {
35 | dest: "",
36 | src: ["adblockplusui/mobile-options.*"]
37 | }
38 | ]
39 | },
40 | translations: {
41 | src: ["adblockplusui/locale/*/mobile-options.json"]
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/build/config/index.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | export {default as base} from "./base.mjs";
19 | export {default as chrome} from "./chrome.mjs";
20 | export {default as firefox} from "./firefox.mjs";
21 | export {default as webpack} from "./webpack.config.mjs";
22 | export {chromeDev, firefoxDev} from "./devenv.mjs";
23 |
--------------------------------------------------------------------------------
/build/config/webpack.config.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import path from "path";
19 |
20 | let tmplLoaderPath = path.resolve("build", "utils", "wp-template-loader.js");
21 |
22 | export default {
23 | optimization: {
24 | minimize: false
25 | },
26 | output: {
27 | path: path.resolve("")
28 | },
29 | node: {
30 | global: false
31 | },
32 | resolve: {
33 | modules: [
34 | path.resolve("lib"),
35 | path.resolve("adblockpluscore/lib"),
36 | path.resolve("adblockplusui/lib"),
37 | path.resolve("build/templates"),
38 | "node_modules"
39 | ]
40 | },
41 | resolveLoader: {
42 | alias: {
43 | "wp-template-loader": tmplLoaderPath
44 | }
45 | }
46 | };
47 |
--------------------------------------------------------------------------------
/build/configParser.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | let configs = {};
19 | let parsedConfigs = {};
20 |
21 | function mergeObjectArray(base, override = [])
22 | {
23 | let result = base.slice(0);
24 |
25 | override.forEach(elem =>
26 | {
27 | let commonDest = base.findIndex(baseElem => baseElem.dest == elem.dest);
28 |
29 | if (commonDest != -1)
30 | {
31 | result[commonDest] = {
32 | ...result[commonDest],
33 | src: removeDuplicates(result[commonDest].src, elem.src)
34 | };
35 | }
36 | else
37 | {
38 | result.push(elem);
39 | }
40 | });
41 |
42 | return result;
43 | }
44 |
45 | function removeDuplicates(base, override = [])
46 | {
47 | let unique = base.filter(elem =>
48 | {
49 | let duplicate = override
50 | .find(value => value.replace(/!/, "") == elem.replace(/!/, ""));
51 | return !duplicate;
52 | });
53 |
54 | return unique.concat(override);
55 | }
56 |
57 | function mergeWebpack(base, override)
58 | {
59 | return {
60 | alias: {...base.alias, ...override.alias},
61 | bundles: mergeObjectArray(base.bundles, override.bundles)
62 | };
63 | }
64 |
65 | function mergeTranslations(base, override = {})
66 | {
67 | return {
68 | dest: override.dest || base.dest,
69 | src: removeDuplicates(base.src, override.src)
70 | };
71 | }
72 |
73 | function mergeTests(base = {}, override = {})
74 | {
75 | let result = {};
76 | let baseScripts = base.scripts || [];
77 | let overrideScripts = override.scripts || [];
78 |
79 | result.scripts = [...new Set([...baseScripts, ...overrideScripts])];
80 |
81 | return result;
82 | }
83 |
84 | function mergeMapping(base, override = {})
85 | {
86 | let result = {};
87 |
88 | result.copy = mergeObjectArray(base.copy, override.copy);
89 | result.rename = mergeObjectArray(base.rename, override.rename);
90 |
91 | return result;
92 | }
93 |
94 | function mergeConfig(target)
95 | {
96 | if (parsedConfigs[target])
97 | return parsedConfigs[target];
98 |
99 | let config = configs[target];
100 |
101 | if (!config.extends)
102 | {
103 | parsedConfigs[target] = {...config};
104 | parsedConfigs[target].webpack.baseConfig = configs.webpack;
105 |
106 | return parsedConfigs[target];
107 | }
108 |
109 | let baseConfig = mergeConfig(config.extends);
110 |
111 | let version = config.version || baseConfig.version;
112 | let webpack = mergeWebpack(baseConfig.webpack, config.webpack);
113 | let translations = mergeTranslations(
114 | baseConfig.translations,
115 | config.translations);
116 |
117 | webpack.baseConfig = configs.webpack;
118 |
119 | parsedConfigs[target] = {
120 | basename: baseConfig.basename,
121 | version,
122 | webpack,
123 | mapping: mergeMapping(baseConfig.mapping, config.mapping),
124 | translations,
125 | tests: mergeTests(baseConfig.tests, config.tests)
126 | };
127 |
128 | return parsedConfigs[target];
129 | }
130 |
131 | export function getSection(target, section)
132 | {
133 | return mergeConfig(target)[section];
134 | }
135 |
136 | export function setConfig(config)
137 | {
138 | configs = config;
139 | }
140 |
141 | export function hasTarget(name)
142 | {
143 | return !!configs[name];
144 | }
145 |
--------------------------------------------------------------------------------
/build/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "author": "eyeo GmbH",
3 | "applications": {
4 | "gecko": {
5 | "strict_min_version": "52.0",
6 | "app_id_release": "{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}",
7 | "app_id_devbuild": "devbuild@adblockplus.org",
8 | "devbuildUpdateURL": "https://downloads.adblockplus.org/devbuilds/adblockplusfirefox/updates.json"
9 | }
10 | },
11 | "background": {
12 | "persistent": true,
13 | "scripts": [
14 | "polyfill.js",
15 | "ext/common.js",
16 | "ext/background.js",
17 | "background.js"
18 | ]
19 | },
20 | "content_scripts": [
21 | {
22 | "all_frames": true,
23 | "js": [
24 | "polyfill.js",
25 | "ext/common.js",
26 | "ext/content.js",
27 | "include.preload.js"
28 | ],
29 | "match_about_blank": true,
30 | "matches": [
31 | "http://*/*",
32 | "https://*/*"
33 | ],
34 | "run_at": "document_start"
35 | },
36 | {
37 | "all_frames": true,
38 | "js": [
39 | "composer.postload.js"
40 | ],
41 | "match_about_blank": true,
42 | "matches": [
43 | "http://*/*",
44 | "https://*/*"
45 | ],
46 | "run_at": "document_end"
47 | },
48 | {
49 | "all_frames": true,
50 | "js": [
51 | "subscriptionLink.postload.js"
52 | ],
53 | "matches": [
54 | "https://*.abpchina.org/*",
55 | "https://*.abpindo.blogspot.com/*",
56 | "https://*.abpvn.com/*",
57 | "https://*.adblock.ee/*",
58 | "https://*.adblock.gardar.net/*",
59 | "https://*.adblockplus.me/*",
60 | "https://*.adblockplus.org/*",
61 | "https://*.commentcamarche.net/*",
62 | "https://*.droit-finances.commentcamarche.com/*",
63 | "https://*.easylist.to/*",
64 | "https://*.eyeo.com/*",
65 | "https://*.fanboy.co.nz/*",
66 | "https://*.filterlists.com/*",
67 | "https://*.forums.lanik.us/*",
68 | "https://*.gitee.com/*",
69 | "https://*.gitee.io/*",
70 | "https://*.github.com/*",
71 | "https://*.github.io/*",
72 | "https://*.gitlab.com/*",
73 | "https://*.gitlab.io/*",
74 | "https://*.gurud.ee/*",
75 | "https://*.hugolescargot.com/*",
76 | "https://*.i-dont-care-about-cookies.eu/*",
77 | "https://*.journaldesfemmes.fr/*",
78 | "https://*.journaldunet.com/*",
79 | "https://*.linternaute.com/*",
80 | "https://*.spam404.com/*",
81 | "https://*.stanev.org/*",
82 | "https://*.void.gr/*",
83 | "https://*.xfiles.noads.it/*",
84 | "https://*.zoso.ro/*"
85 | ],
86 | "run_at": "document_end"
87 | }
88 | ],
89 | "manifest_version": 2,
90 | "minimum_chrome_version": "60.0",
91 | "minimum_opera_version": "47.0",
92 | "name": "__MSG_name_releasebuild__",
93 | "short_name": "__MSG_name__",
94 | "description": "__MSG_description__",
95 | "browser_action": {
96 | "default_icon": {
97 | "16": "icons/abp-16.png",
98 | "20": "icons/abp-20.png",
99 | "32": "icons/abp-32.png",
100 | "40": "icons/abp-40.png"
101 | },
102 | "default_popup": "popup.html",
103 | "default_title": "__MSG_name__"
104 | },
105 | "default_locale": "en_US",
106 | "devtools_page": "devtools.html",
107 | "icons": {
108 | "128": "icons/detailed/abp-128.png",
109 | "16": "icons/abp-16.png",
110 | "32": "icons/abp-32.png",
111 | "48": "icons/detailed/abp-48.png",
112 | "64": "icons/detailed/abp-64.png"
113 | },
114 | "options_ui": {
115 | "open_in_tab": true,
116 | "page": "options.html"
117 | },
118 | "permissions": [
119 | "tabs",
120 | "",
121 | "contextMenus",
122 | "webRequest",
123 | "webRequestBlocking",
124 | "webNavigation",
125 | "storage",
126 | "unlimitedStorage",
127 | "notifications"
128 | ],
129 | "optional_permissions": [
130 | "contentSettings",
131 | "management"
132 | ],
133 | "storage": {
134 | "managed_schema": "managed-storage-schema.json"
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/build/tasks/dependencies.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 |
19 | import gulp from "gulp";
20 | import fs from "fs";
21 | import {promisify} from "util";
22 | import glob from "glob";
23 | import {exec} from "child_process";
24 | import {Readable} from "stream";
25 | import Vinyl from "vinyl";
26 |
27 | async function getMTime(file)
28 | {
29 | return (await fs.promises.stat(file)).mtimeMs;
30 | }
31 |
32 | function createBuild()
33 | {
34 | return (promisify(exec))("bash -c \"npm run --prefix adblockplusui/ dist\"");
35 | }
36 |
37 | async function mustBuildUI(lastUIBuildTime)
38 | {
39 | let matches = await (promisify(glob))(
40 | "adblockplusui/**",
41 | {
42 | ignore: ["**/node_modules/**"]
43 | }
44 | );
45 |
46 | return await new Promise((resolve, reject) =>
47 | {
48 | Promise.all(matches.map(filename =>
49 | getMTime(filename).then(mtime =>
50 | {
51 | if (mtime > lastUIBuildTime)
52 | resolve(true);
53 | })
54 | )).then(() => { resolve(false); }, reject);
55 | });
56 | }
57 |
58 | function updateLastUIBuildTime()
59 | {
60 | return fs.promises.utimes(".last_ui_build", new Date(), new Date());
61 | }
62 |
63 | function createLastUIBuildTime()
64 | {
65 | return new Readable.from([
66 | new Vinyl({
67 | path: ".last_ui_build",
68 | contents: Buffer.from("")
69 | })
70 | ]).pipe(gulp.dest("."));
71 | }
72 |
73 | export async function buildUI(cb)
74 | {
75 | let lastUIBuildTime;
76 |
77 | try
78 | {
79 | lastUIBuildTime = await getMTime(".last_ui_build");
80 | }
81 | catch (e)
82 | {
83 | await createBuild();
84 | return createLastUIBuildTime();
85 | }
86 |
87 | if (await mustBuildUI(lastUIBuildTime))
88 | {
89 | await createBuild();
90 | return updateLastUIBuildTime();
91 | }
92 |
93 | return cb();
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/build/tasks/devenv.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import handlebars from "handlebars";
19 | import fs from "fs";
20 | import {Readable} from "stream";
21 | import Vinyl from "vinyl";
22 |
23 | export function addDevEnvVersion()
24 | {
25 | let randNumber = Number(new Date()).toString();
26 |
27 | return new Readable.from([
28 | new Vinyl({
29 | contents: Buffer.from(randNumber),
30 | path: "devenvVersion__"
31 | })
32 | ]);
33 | }
34 |
35 | export async function addTestsPage(templateData)
36 | {
37 | let file = await fs.promises.readFile("build/templates/testIndex.html.tmpl");
38 | let template = handlebars.compile(file.toString());
39 | let data = template(templateData);
40 |
41 | return new Readable.from([
42 | new Vinyl({
43 | contents: Buffer.from(data),
44 | path: "qunit/index.html"
45 | })
46 | ]);
47 | }
48 |
--------------------------------------------------------------------------------
/build/tasks/index.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | export {createManifest, getManifestContent} from "./manifest.mjs";
19 | export {default as webpack} from "./webpack.mjs";
20 | export {default as mapping} from "./mapping.mjs";
21 | export {translations, chromeTranslations} from "./translations.mjs";
22 | export {addDevEnvVersion, addTestsPage} from "./devenv.mjs";
23 | export {buildUI} from "./dependencies.mjs";
24 | export {default as sourceDistribution} from "./sourceDistribution.mjs";
25 |
--------------------------------------------------------------------------------
/build/tasks/manifest.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import {resolve} from "path";
19 | import fs from "fs";
20 | import {Readable} from "stream";
21 | import Vinyl from "vinyl";
22 |
23 | let manifest;
24 |
25 | function editManifest(data, version, channel, target)
26 | {
27 | data.version = version;
28 | data.name = `__MSG_name_${channel == "development" ? "dev" : channel}build__`;
29 |
30 | if (target == "chrome")
31 | delete data.applications;
32 |
33 | if (target == "firefox")
34 | {
35 | let gecko = {
36 | strict_min_version: data.applications.gecko.strict_min_version
37 | };
38 |
39 | if (channel == "development")
40 | {
41 | gecko.id = data.applications.gecko.app_id_devbuild;
42 | gecko.update_url = data.applications.gecko.devbuildUpdateURL;
43 | }
44 | else
45 | {
46 | gecko.id = data.applications.gecko.app_id_release;
47 | }
48 |
49 | let composerScriptIndex = data.content_scripts.findIndex(
50 | script => script.js.includes("composer.postload.js")
51 | );
52 | let preloadScript = data.content_scripts.find(
53 | script => script.run_at == "document_start"
54 | );
55 |
56 | preloadScript.js.push(...data.content_scripts[composerScriptIndex].js);
57 | data.content_scripts.splice(composerScriptIndex, 1);
58 |
59 | delete data.minimum_chrome_version;
60 | delete data.minimum_opera_version;
61 | delete data.browser_action.default_popup;
62 | delete data.optional_permissions;
63 |
64 | data.applications.gecko = gecko;
65 | }
66 |
67 | return data;
68 | }
69 |
70 | export function createManifest(contents)
71 | {
72 | return new Readable.from([
73 | new Vinyl({
74 | contents: Buffer.from(JSON.stringify(contents, null, 2)),
75 | path: "manifest.json"
76 | })
77 | ]);
78 | }
79 |
80 | export async function getManifestContent({target, version, channel, path})
81 | {
82 | if (manifest)
83 | return manifest;
84 |
85 | let raw = JSON.parse(
86 | await fs.promises.readFile(resolve(path || "build/manifest.json"))
87 | );
88 |
89 | manifest = editManifest(raw, version, channel, target);
90 |
91 | return manifest;
92 | }
93 |
--------------------------------------------------------------------------------
/build/tasks/mapping.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import gulp from "gulp";
19 | import merge from "merge-stream";
20 | import changePath from "../utils/gulp-change-path.mjs";
21 |
22 | export default function mapping(bundles)
23 | {
24 | return merge(
25 | bundles.copy.map(bundle =>
26 | gulp.src(bundle.src)
27 | .pipe(changePath(bundle.dest))
28 | ),
29 | bundles.rename.map(bundle =>
30 | gulp.src(bundle.src)
31 | .pipe(changePath(bundle.dest, {rename: true}))
32 | )
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/build/tasks/sourceDistribution.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import gulp from "gulp";
19 | import tar from "gulp-tar";
20 | import gzip from "gulp-gzip";
21 | import {lsFiles} from "../utils/git.mjs";
22 |
23 | export default async function sourceDistribution(filename)
24 | {
25 | let sourceFiles = await lsFiles();
26 | return gulp.src(sourceFiles, {base: process.cwd()})
27 | .pipe(tar(`${filename}.tar`))
28 | .pipe(gzip())
29 | .pipe(gulp.dest(process.cwd()));
30 | }
31 |
--------------------------------------------------------------------------------
/build/tasks/translations.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import gulp from "gulp";
19 | import mergeTranslations from "../utils/gulp-merge-translations.mjs";
20 | import changePath from "../utils/gulp-change-path.mjs";
21 |
22 | export function translations(locales)
23 | {
24 | return gulp.src(locales.src)
25 | .pipe(mergeTranslations(
26 | {
27 | fileName: "messages.json"
28 | }))
29 | .pipe(changePath(locales.dest));
30 | }
31 |
32 | function getRequiredInfo(manifest)
33 | {
34 | let result = {};
35 | let limits = {
36 | name: 12,
37 | name_releasebuild: 45,
38 | name_devbuild: 45,
39 | description: 132
40 | };
41 |
42 | result.fields = Object.values(manifest)
43 | .filter(value => typeof value == "string" && value.match("__MSG"))
44 | .map(name =>
45 | {
46 | let parsed = name.replace(/(__MSG_)|(__)/g, "");
47 | return {
48 | name: parsed,
49 | limit: limits[parsed]
50 | };
51 | });
52 |
53 | result.locale = manifest["default_locale"];
54 |
55 | return result;
56 | }
57 |
58 | export function chromeTranslations(locales, manifest)
59 | {
60 | return gulp.src(locales.src)
61 | .pipe(mergeTranslations(
62 | {
63 | fileName: "messages.json",
64 | defaults: getRequiredInfo(manifest)
65 | }))
66 | .pipe(changePath(
67 | locales.dest,
68 | {
69 | match: /es_MX/g,
70 | replace: "es_419"
71 | }
72 | ));
73 | }
74 |
--------------------------------------------------------------------------------
/build/tasks/webpack.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import gulp from "gulp";
19 | import merge from "merge-stream";
20 | import webpackStream from "webpack-stream";
21 | import webpackMerge from "webpack-merge";
22 |
23 | export default function webpack({webpackInfo, addonName, addonVersion,
24 | sourceMapType})
25 | {
26 | return merge(webpackInfo.bundles.map(bundle =>
27 | gulp.src(bundle.src)
28 | .pipe(webpackStream(
29 | {
30 | quiet: true,
31 | config: webpackMerge.merge(
32 | webpackInfo.baseConfig,
33 | {
34 | devtool: sourceMapType,
35 | output: {
36 | filename: bundle.dest
37 | },
38 | resolve: {
39 | alias: webpackInfo.alias,
40 | symlinks: false
41 | },
42 | module: {
43 | rules: [
44 | {
45 | test: /info.?/,
46 | loader: "wp-template-loader",
47 | options: {
48 | data: {addonName, addonVersion}
49 | }
50 | }
51 | ]
52 | }
53 | })
54 | }))
55 | ));
56 | }
57 |
--------------------------------------------------------------------------------
/build/templates/info.chrome.js.tmpl:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | let platformVersion = null;
21 | let application = null;
22 | let applicationVersion;
23 |
24 | let regexp = /(\S+)\/(\S+)(?:\s*\(.*?\))?/g;
25 | let match;
26 |
27 | while (match = regexp.exec(navigator.userAgent))
28 | {
29 | let app = match[1];
30 | let ver = match[2];
31 |
32 | if (app == "Chrome")
33 | {
34 | platformVersion = ver;
35 | }
36 | else if (app != "Mozilla" && app != "AppleWebKit" && app != "Safari")
37 | {
38 | // For compatibility with legacy websites, Chrome's UA
39 | // also includes a Mozilla, AppleWebKit and Safari token.
40 | // Any further name/version pair indicates a fork.
41 | application = app == "OPR" ? "opera" : app.toLowerCase();
42 | applicationVersion = ver;
43 | }
44 | }
45 |
46 | // not a Chromium-based UA, probably modifed by the user
47 | if (!platformVersion)
48 | {
49 | application = "unknown";
50 | applicationVersion = platformVersion = "0";
51 | }
52 |
53 | // no additional name/version, so this is upstream Chrome
54 | if (!application)
55 | {
56 | application = "chrome";
57 | applicationVersion = platformVersion;
58 | }
59 |
60 |
61 | exports.addonName = "{{addonName}}";
62 | exports.addonVersion = "{{addonVersion}}";
63 |
64 | exports.application = application;
65 | exports.applicationVersion = applicationVersion;
66 |
67 | exports.platform = "chromium";
68 | exports.platformVersion = platformVersion;
69 |
--------------------------------------------------------------------------------
/build/templates/info.gecko.js.tmpl:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | exports.addonName = "{{addonName}}";
21 | exports.addonVersion = "{{addonVersion}}";
22 |
23 | exports.application = "unknown";
24 | exports.applicationVersion = "0";
25 |
26 | exports.platform = "gecko";
27 | exports.platformVersion = "0";
28 |
29 | let match = /\brv:(\d+(?:\.\d+)?)\b/.exec(navigator.userAgent);
30 | if (match)
31 | exports.platformVersion = match[1];
32 |
33 | browser.runtime.getBrowserInfo().then(browserInfo =>
34 | {
35 | exports.application = browserInfo.name.toLowerCase();
36 | exports.applicationVersion = browserInfo.version;
37 | });
38 |
--------------------------------------------------------------------------------
/build/templates/testIndex.html.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
21 |
22 |
23 |
24 | {{#each scripts }}
25 |
26 | {{/each}}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/build/utils/git.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import argparse from "argparse";
19 | import {pathToFileURL} from "url";
20 | import {execFile} from "child_process";
21 | import {promisify} from "util";
22 | import {EOL} from "os";
23 |
24 | const BUILDNUM_OFFSET = 10000;
25 |
26 | export async function getBuildnum(revision = "HEAD")
27 | {
28 | let until = (await promisify(execFile)("git", ["log", "--pretty=%ct", "-n1",
29 | revision])).stdout.trim();
30 |
31 | return BUILDNUM_OFFSET +
32 | parseInt((await promisify(execFile)("git", ["rev-list", "--count",
33 | "--until", until,
34 | "origin/next",
35 | "origin/master",
36 | revision])).stdout, 10);
37 | }
38 |
39 | export async function lsFiles()
40 | {
41 | let {stdout} = await promisify(execFile)(
42 | "git", ["ls-files", "--recurse-submodules"]
43 | );
44 | return stdout.trim().split(EOL);
45 | }
46 |
47 | if (import.meta.url == pathToFileURL(process.argv[1]))
48 | {
49 | let parser = argparse.ArgumentParser();
50 | parser.addArgument(["-r", "--revision"],
51 | {required: false, defaultValue: "HEAD"});
52 | let args = parser.parseArgs();
53 |
54 | (async() =>
55 | {
56 | try
57 | {
58 | console.log(await getBuildnum(args.revision));
59 | }
60 | catch (err)
61 | {
62 | console.error(err);
63 | process.exit(1);
64 | }
65 | })();
66 | }
67 |
--------------------------------------------------------------------------------
/build/utils/gulp-change-path.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import {Transform} from "stream";
19 | import path from "path";
20 |
21 | function changePath(destination, custom = {})
22 | {
23 | let transform = new Transform({objectMode: true});
24 |
25 | transform._transform = (file, encoding, cb) =>
26 | {
27 | if (custom.match && file.path.match(custom.match))
28 | {
29 | file.path = path.join(destination,
30 | file.relative.replace(custom.match, custom.replace)
31 | );
32 | }
33 | else if (custom.rename)
34 | {
35 | file.path = destination;
36 | }
37 | else
38 | {
39 | file.path = path.join(destination, file.relative);
40 | }
41 |
42 | file.base = null;
43 | cb(null, file);
44 | };
45 |
46 | return transform;
47 | }
48 |
49 | export default changePath;
50 |
--------------------------------------------------------------------------------
/build/utils/gulp-merge-translations.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import through from "through";
19 | import Vinyl from "vinyl";
20 | import path from "path";
21 |
22 | const PLUGIN_NAME = "gulp-merge-translations";
23 |
24 | function mergeTranslations(options = {})
25 | {
26 | let merged = {};
27 | let info;
28 | let mandatoryInfo = {};
29 | let fields = options.defaults ? options.defaults.fields : [];
30 |
31 | function getLocaleName(fullPath)
32 | {
33 | let parts = fullPath.split(path.sep);
34 |
35 | return parts[parts.length - 2];
36 | }
37 |
38 | function truncate(text, limit)
39 | {
40 | if (text.length <= limit)
41 | return text;
42 | return text.slice(0, limit - 1).concat("\u2026");
43 | }
44 |
45 | function groupByLocale(file)
46 | {
47 | if (file.isBuffer())
48 | {
49 | try
50 | {
51 | let locale = getLocaleName(file.path);
52 | let content = JSON.parse(file.contents.toString());
53 |
54 | info = info || {
55 | cwd: file.cwd,
56 | base: file.base
57 | };
58 |
59 | if (options.defaults)
60 | {
61 | fields.forEach(field =>
62 | {
63 | if (content[field.name])
64 | {
65 | content[field.name] = {
66 | message: field.limit ?
67 | truncate(content[field.name].message, field.limit) :
68 | content[field.name].message,
69 | description: content[field.name].description
70 | };
71 |
72 | if (locale == options.defaults.locale)
73 | mandatoryInfo[field.name] = content[field.name];
74 | }
75 | });
76 | }
77 |
78 | merged[locale] = merged[locale] || {};
79 | merged[locale] = {...merged[locale], ...content};
80 | }
81 | catch (error)
82 | {
83 | let msg = `${PLUGIN_NAME} parsing: ${file.path} : ${error.message}`;
84 | this.emit("error", msg);
85 | }
86 | }
87 | }
88 |
89 | function emitByLocale()
90 | {
91 | Object.keys(merged).forEach(localeName =>
92 | {
93 | let mergedFile = merged[localeName];
94 |
95 | if (options.defaults)
96 | mergedFile = {...mandatoryInfo, ...mergedFile};
97 |
98 | this.emit("data", new Vinyl({
99 | contents: Buffer.from(JSON.stringify(mergedFile, null, 2)),
100 | cwd: info.cwd,
101 | base: info.base,
102 | path: path.join(info.base, localeName, options.fileName)
103 | }));
104 | });
105 |
106 | this.emit("end");
107 | }
108 |
109 | return through(groupByLocale, emitByLocale);
110 | }
111 |
112 | export default mergeTranslations;
113 |
--------------------------------------------------------------------------------
/build/utils/wp-template-loader.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | const handlebars = require("handlebars");
21 |
22 | module.exports = function wpTemplateLoader(source)
23 | {
24 | let template = handlebars.compile(source);
25 |
26 | return template(this.query.data);
27 | };
28 |
--------------------------------------------------------------------------------
/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/devtools.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | let panelWindow = null;
21 |
22 | // Versions of Firefox before 54 do not support the devtools.panels API; on
23 | // these platforms, even when the option is enabled, we cannot show the
24 | // devtools panel.
25 | if ("panels" in browser.devtools)
26 | {
27 | browser.runtime.sendMessage({type: "prefs.get",
28 | key: "show_devtools_panel"}).then(enabled =>
29 | {
30 | if (enabled)
31 | {
32 | browser.devtools.panels.create("Adblock Plus",
33 | "icons/abp-32.png",
34 | "devtools-panel.html").then(panel =>
35 | {
36 | panel.onShown.addListener(window =>
37 | {
38 | panelWindow = window;
39 | });
40 |
41 | panel.onHidden.addListener(window =>
42 | {
43 | panelWindow = null;
44 | });
45 |
46 | if (panel.onSearch)
47 | {
48 | panel.onSearch.addListener((eventName, queryString) =>
49 | {
50 | if (panelWindow)
51 | panelWindow.postMessage({type: eventName, queryString}, "*");
52 | });
53 | }
54 | });
55 | }
56 | });
57 | }
58 |
--------------------------------------------------------------------------------
/ext/common.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | {
21 | self.ext = {};
22 |
23 | let EventTarget = ext._EventTarget = function()
24 | {
25 | this._listeners = new Set();
26 | };
27 | EventTarget.prototype = {
28 | addListener(listener)
29 | {
30 | this._listeners.add(listener);
31 | },
32 | removeListener(listener)
33 | {
34 | this._listeners.delete(listener);
35 | },
36 | _dispatch(...args)
37 | {
38 | let results = [];
39 |
40 | for (let listener of this._listeners)
41 | results.push(listener(...args));
42 |
43 | return results;
44 | }
45 | };
46 |
47 |
48 | /* Message passing */
49 |
50 | ext.onMessage = new ext._EventTarget();
51 | }
52 |
--------------------------------------------------------------------------------
/ext/content.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1408996
4 | let ext = window.ext; // eslint-disable-line no-redeclare
5 |
6 | // Firefox 55 erroneously sends messages from the content script to the
7 | // devtools panel:
8 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1383310
9 | // As a workaround, listen for messages only if this isn't the devtools panel.
10 | // Note that Firefox processes API access lazily, so browser.devtools will
11 | // always exist but will have undefined as its value on other pages.
12 | if (!browser.devtools)
13 | {
14 | // Listen for messages from the background page.
15 | browser.runtime.onMessage.addListener((message, sender, sendResponse) =>
16 | {
17 | return ext.onMessage._dispatch(message, {}, sendResponse).includes(true);
18 | });
19 | }
20 |
21 | {
22 | let port = null;
23 | let registeredListeners = null;
24 |
25 | ext.onExtensionUnloaded = {
26 | addListener(listener)
27 | {
28 | if (!port)
29 | {
30 | port = browser.runtime.connect();
31 | registeredListeners = 0;
32 | }
33 |
34 | if (!port.onDisconnect.hasListener(listener))
35 | {
36 | // When the extension is reloaded, disabled or uninstalled the
37 | // background page dies and automatically disconnects all ports
38 | port.onDisconnect.addListener(listener);
39 | registeredListeners++;
40 | }
41 | },
42 | removeListener(listener)
43 | {
44 | if (port)
45 | {
46 | if (port.onDisconnect.hasListener(listener))
47 | {
48 | port.onDisconnect.removeListener(listener);
49 | registeredListeners--;
50 | }
51 |
52 | if (registeredListeners == 0)
53 | {
54 | port.disconnect();
55 | port = null;
56 | registeredListeners = null;
57 | }
58 | }
59 | }
60 | };
61 | }
62 |
--------------------------------------------------------------------------------
/ext/devtools.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | {
21 | let inspectedTabId = browser.devtools.inspectedWindow.tabId;
22 | let port = browser.runtime.connect({name: "devtools-" + inspectedTabId});
23 |
24 | ext.onMessage = port.onMessage;
25 | ext.devtools = browser.devtools;
26 | }
27 |
--------------------------------------------------------------------------------
/gulpfile.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import gulp from "gulp";
19 | import argparse from "argparse";
20 | import merge from "merge-stream";
21 | import zip from "gulp-vinyl-zip";
22 | import del from "del";
23 | import * as tasks from "./build/tasks/index.mjs";
24 | import * as config from "./build/config/index.mjs";
25 | import * as configParser from "./build/configParser.mjs";
26 | import * as gitUtils from "./build/utils/git.mjs";
27 | import url from "url";
28 |
29 | let argumentParser = new argparse.ArgumentParser({
30 | description: "Build the extension"
31 | });
32 |
33 | argumentParser.addArgument(
34 | ["-t", "--target"],
35 | {choices: ["chrome", "firefox"]}
36 | );
37 | argumentParser.addArgument(
38 | ["-c", "--channel"],
39 | {
40 | choices: ["development", "release"],
41 | defaultValue: "release"
42 | }
43 | );
44 | argumentParser.addArgument(["-b", "--build-num"]);
45 | argumentParser.addArgument("--config");
46 | argumentParser.addArgument(["-m", "--manifest"]);
47 |
48 | let args = argumentParser.parseKnownArgs()[0];
49 |
50 | let targetDir = `devenv.${args.target}`;
51 |
52 | async function getBuildSteps(options)
53 | {
54 | let translations = options.target == "chrome" ?
55 | tasks.chromeTranslations :
56 | tasks.translations;
57 | let buildSteps = [];
58 | let addonName = `${options.basename}${options.target}`;
59 |
60 | if (options.isDevenv)
61 | {
62 | buildSteps.push(
63 | tasks.addDevEnvVersion(),
64 | await tasks.addTestsPage({scripts: options.tests.scripts, addonName})
65 | );
66 | }
67 |
68 | buildSteps.push(
69 | tasks.mapping(options.mapping),
70 | tasks.webpack({
71 | webpackInfo: options.webpackInfo,
72 | addonName,
73 | addonVersion: options.version,
74 | sourceMapType: options.sourceMapType
75 | }),
76 | tasks.createManifest(options.manifest),
77 | translations(options.translations, options.manifest)
78 | );
79 |
80 | return buildSteps;
81 | }
82 |
83 | async function getBuildOptions(isDevenv, isSource)
84 | {
85 | if (!isSource && !args.target)
86 | argumentParser.error("Argument \"-t/--target\" is required");
87 |
88 | let opts = {
89 | isDevenv,
90 | target: args.target,
91 | channel: args.channel,
92 | archiveType: args.target == "chrome" ? ".zip" : ".xpi"
93 | };
94 |
95 | opts.sourceMapType = opts.target == "chrome" ?
96 | isDevenv == true ? "inline-cheap-source-maps" : "none" :
97 | "source-maps";
98 |
99 | if (args.config)
100 | configParser.setConfig(await import(url.pathToFileURL(args.config)));
101 | else
102 | configParser.setConfig(config);
103 |
104 | let configName;
105 | if (isSource)
106 | configName = "base";
107 | else if (isDevenv && configParser.hasTarget(`${opts.target}Dev`))
108 | configName = `${opts.target}Dev`;
109 | else
110 | configName = opts.target;
111 |
112 | opts.webpackInfo = configParser.getSection(configName, "webpack");
113 | opts.mapping = configParser.getSection(configName, "mapping");
114 | opts.tests = configParser.getSection(configName, "tests");
115 | opts.basename = configParser.getSection(configName, "basename");
116 | opts.version = configParser.getSection(configName, "version");
117 | opts.translations = configParser.getSection(configName, "translations");
118 |
119 | if (isDevenv)
120 | {
121 | opts.output = gulp.dest(targetDir);
122 | }
123 | else
124 | {
125 | if (opts.channel == "development")
126 | {
127 | opts.version = args["build_num"] ?
128 | opts.version.concat(".", args["build_num"]) :
129 | opts.version.concat(".", await gitUtils.getBuildnum());
130 | }
131 |
132 | opts.output = zip.dest(
133 | `${opts.basename}${opts.target}-${opts.version}${opts.archiveType}`
134 | );
135 | }
136 |
137 | opts.manifest = await tasks.getManifestContent({
138 | target: opts.target,
139 | version: opts.version,
140 | channel: opts.channel,
141 | path: args.manifest
142 | });
143 |
144 | return opts;
145 | }
146 |
147 | async function buildDevenv()
148 | {
149 | let options = await getBuildOptions(true);
150 |
151 | return merge(await getBuildSteps(options))
152 | .pipe(options.output);
153 | }
154 |
155 | async function buildPacked()
156 | {
157 | let options = await getBuildOptions(false);
158 |
159 | return merge(await getBuildSteps(options))
160 | .pipe(options.output);
161 | }
162 |
163 | function cleanDir()
164 | {
165 | return del(targetDir);
166 | }
167 |
168 | export let devenv = gulp.series(
169 | cleanDir,
170 | tasks.buildUI,
171 | buildDevenv
172 | );
173 |
174 | export let build = gulp.series(
175 | tasks.buildUI,
176 | buildPacked
177 | );
178 |
179 | export async function source()
180 | {
181 | let options = await getBuildOptions(false, true);
182 | return tasks.sourceDistribution(`${options.basename}-${options.version}`);
183 | }
184 |
185 | function startWatch()
186 | {
187 | gulp.watch(
188 | [
189 | "*.js",
190 | "*.html",
191 | "qunit/**",
192 | "lib/*",
193 | "ext/*",
194 | "adblockpluscore/lib/*",
195 | "adblockplusui/*.js",
196 | "!gulpfile.js"
197 | ],
198 | {
199 | ignoreInitial: false
200 | },
201 | gulp.series(
202 | cleanDir,
203 | buildDevenv
204 | )
205 | );
206 | }
207 |
208 | export let watch = gulp.series(
209 | tasks.buildUI,
210 | startWatch
211 | );
212 |
--------------------------------------------------------------------------------
/icons/detailed/abp-48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adblockplus/adblockpluschrome/1affa87724a7334e589c9a7bb197da8d5e5bf878/icons/detailed/abp-48.png
--------------------------------------------------------------------------------
/icons/detailed/abp-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/adblockplus/adblockpluschrome/1affa87724a7334e589c9a7bb197da8d5e5bf878/icons/detailed/abp-64.png
--------------------------------------------------------------------------------
/jsdoc.conf:
--------------------------------------------------------------------------------
1 | {
2 | "opts": {
3 | "access": "all"
4 | },
5 | "templates": {
6 | "default": {
7 | "useLongnameInNav": true
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/lib/browserAction.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module browserAction */
19 |
20 | "use strict";
21 |
22 | let changesByTabId = new Map();
23 | let badgeStateByPage = new ext.PageMap();
24 |
25 | function setBadgeState(tabId, key, value)
26 | {
27 | let page = new ext.Page(tabId);
28 | let badgeState = badgeStateByPage.get(page);
29 |
30 | if (!badgeState)
31 | {
32 | badgeState = {
33 | hiddenState: "visible",
34 | text: ""
35 | };
36 | badgeStateByPage.set(page, badgeState);
37 | }
38 |
39 | // We need to ignore any text changes while we're hiding the badge
40 | if (!(badgeState.hiddenState == "hiding" && key == "text"))
41 | badgeState[key] = value;
42 |
43 | return badgeState;
44 | }
45 |
46 | function applyChanges(tabId, changes)
47 | {
48 | return Promise.all(Object.keys(changes).map(change =>
49 | {
50 | // Firefox for Android displays the browser action not as an icon but
51 | // as a menu item. There is no icon, but such an option may be added
52 | // in the future.
53 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1331746
54 | if (change == "iconPath" && "setIcon" in browser.browserAction)
55 | {
56 | return browser.browserAction.setIcon({
57 | tabId,
58 | path: {
59 | 16: changes.iconPath.replace("$size", "16"),
60 | 20: changes.iconPath.replace("$size", "20"),
61 | 32: changes.iconPath.replace("$size", "32"),
62 | 40: changes.iconPath.replace("$size", "40")
63 | }
64 | });
65 | }
66 |
67 | if (change == "iconImageData" && "setIcon" in browser.browserAction)
68 | {
69 | return browser.browserAction.setIcon({
70 | tabId,
71 | imageData: changes.iconImageData
72 | });
73 | }
74 |
75 | // There is no badge on Firefox for Android; the browser action is
76 | // simply a menu item.
77 | if (change == "badgeText" && "setBadgeText" in browser.browserAction)
78 | {
79 | // Remember changes to the badge text but don't apply them yet
80 | // as long as the badge is hidden.
81 | let badgeState = setBadgeState(tabId, "text", changes.badgeText);
82 | if (badgeState.hiddenState == "hidden")
83 | return;
84 |
85 | return browser.browserAction.setBadgeText({
86 | tabId,
87 | text: changes.badgeText
88 | });
89 | }
90 |
91 | // There is no badge on Firefox for Android; the browser action is
92 | // simply a menu item.
93 | if (change == "badgeColor" &&
94 | "setBadgeBackgroundColor" in browser.browserAction)
95 | {
96 | return browser.browserAction.setBadgeBackgroundColor({
97 | tabId,
98 | color: changes.badgeColor
99 | });
100 | }
101 | }));
102 | }
103 |
104 | function addChange(tabId, name, value)
105 | {
106 | let changes = changesByTabId.get(tabId);
107 | if (!changes)
108 | {
109 | changes = {};
110 | changesByTabId.set(tabId, changes);
111 | }
112 | changes[name] = value;
113 |
114 | function cleanup()
115 | {
116 | changesByTabId.delete(tabId);
117 | }
118 |
119 | function onReplaced(addedTabId, removedTabId)
120 | {
121 | if (addedTabId == tabId)
122 | {
123 | browser.tabs.onReplaced.removeListener(onReplaced);
124 | applyChanges(tabId, changes)
125 | .then(cleanup)
126 | .catch(cleanup);
127 | }
128 | }
129 |
130 | if (!browser.tabs.onReplaced.hasListener(onReplaced))
131 | {
132 | applyChanges(tabId, changes)
133 | .then(cleanup)
134 | .catch(() =>
135 | {
136 | // If the tab is prerendered, browser.browserAction.set* fails
137 | // and we have to delay our changes until the currently visible tab
138 | // is replaced with the prerendered tab.
139 | browser.tabs.onReplaced.addListener(onReplaced);
140 | });
141 | }
142 | }
143 |
144 | /**
145 | * Sets icon badge for given tab.
146 | *
147 | * @param {number} tabId
148 | * @param {object} badge
149 | * @param {string} badge.color
150 | * @param {string} badge.number
151 | */
152 | exports.setBadge = (tabId, badge) =>
153 | {
154 | if (!badge)
155 | {
156 | addChange(tabId, "badgeText", "");
157 | }
158 | else
159 | {
160 | if ("number" in badge)
161 | addChange(tabId, "badgeText", badge.number.toString());
162 |
163 | if ("color" in badge)
164 | addChange(tabId, "badgeColor", badge.color);
165 | }
166 | };
167 |
168 | /**
169 | * Sets icon image for given tab using image data.
170 | *
171 | * @param {number} tabId
172 | * @param {object} imageData
173 | */
174 | exports.setIconImageData = (tabId, imageData) =>
175 | {
176 | addChange(tabId, "iconImageData", imageData);
177 | };
178 |
179 | /**
180 | * Sets icon image for given tab using file path.
181 | *
182 | * @param {number} tabId
183 | * @param {string} path - expected to include "$size" placeholder
184 | */
185 | exports.setIconPath = (tabId, path) =>
186 | {
187 | addChange(tabId, "iconPath", path);
188 | };
189 |
190 | /**
191 | * Toggles icon badge for given tab.
192 | *
193 | * @param {number} tabId
194 | * @param {boolean} shouldHide
195 | */
196 | exports.toggleBadge = (tabId, shouldHide) =>
197 | {
198 | if (shouldHide)
199 | {
200 | setBadgeState(tabId, "hiddenState", "hiding");
201 | addChange(tabId, "badgeText", "");
202 | setBadgeState(tabId, "hiddenState", "hidden");
203 | }
204 | else
205 | {
206 | let badgeState = setBadgeState(tabId, "hiddenState", "visible");
207 | addChange(tabId, "badgeText", badgeState.text);
208 | }
209 | };
210 |
--------------------------------------------------------------------------------
/lib/csp.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | const {defaultMatcher} = require("../adblockpluscore/lib/matcher");
21 | const {AllowingFilter} = require("../adblockpluscore/lib/filterClasses");
22 | const {contentTypes} = require("../adblockpluscore/lib/contentTypes");
23 | const {parseURL} = require("../adblockpluscore/lib/url");
24 | const {extractHostFromFrame} = require("./url");
25 | const {checkAllowlisted} = require("./allowlisting");
26 | const {filterNotifier} = require("filterNotifier");
27 | const {logRequest} = require("./hitLogger");
28 | const {recordBlockedRequest} = require("./stats");
29 |
30 | browser.webRequest.onHeadersReceived.addListener(details =>
31 | {
32 | let url = parseURL(details.url);
33 | let parentFrame = ext.getFrame(details.tabId, details.parentFrameId);
34 | let hostname = extractHostFromFrame(parentFrame) || url.hostname;
35 |
36 | let cspMatch = defaultMatcher.match(url, contentTypes.CSP,
37 | hostname, null, false);
38 | if (cspMatch)
39 | {
40 | let page = new ext.Page({id: details.tabId, url: details.url});
41 | let frame = ext.getFrame(details.tabId, details.frameId);
42 |
43 | if (checkAllowlisted(page, frame))
44 | return;
45 |
46 | // To avoid an extra match for the common case we assumed no
47 | // $genericblock filters applied when searching for a matching $csp filter.
48 | // We must now pay the price by first checking for a $genericblock filter
49 | // and if necessary that our $csp filter is specific.
50 | let specificOnly = !!checkAllowlisted(page, frame, null,
51 | contentTypes.GENERICBLOCK);
52 | if (specificOnly && !(cspMatch instanceof AllowingFilter))
53 | {
54 | cspMatch = defaultMatcher.match(url, contentTypes.CSP,
55 | hostname, null, specificOnly);
56 | if (!cspMatch)
57 | return;
58 | }
59 |
60 | if (cspMatch instanceof AllowingFilter)
61 | {
62 | logRequest([details.tabId], {
63 | url: details.url, type: "CSP", docDomain: hostname,
64 | specificOnly
65 | }, cspMatch);
66 | recordBlockedRequest(cspMatch, [details.tabId]);
67 | return;
68 | }
69 |
70 | let {blocking} = defaultMatcher.search(url, contentTypes.CSP, hostname,
71 | null, specificOnly, "blocking");
72 | for (cspMatch of blocking)
73 | {
74 | logRequest([details.tabId], {
75 | url: details.url, type: "CSP", docDomain: hostname,
76 | specificOnly
77 | }, cspMatch);
78 | recordBlockedRequest(cspMatch, [details.tabId]);
79 |
80 | details.responseHeaders.push({
81 | name: "Content-Security-Policy",
82 | value: cspMatch.csp
83 | });
84 | }
85 |
86 | return {responseHeaders: details.responseHeaders};
87 | }
88 | }, {
89 | urls: ["http://*/*", "https://*/*"],
90 | types: ["main_frame", "sub_frame"]
91 | }, ["blocking", "responseHeaders"]);
92 |
--------------------------------------------------------------------------------
/lib/debug.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | const {port} = require("messaging");
21 |
22 | let lastError = null;
23 |
24 | function safeToString(value)
25 | {
26 | try
27 | {
28 | return String(value);
29 | }
30 | catch (e)
31 | {
32 | return "";
33 | }
34 | }
35 |
36 | self.addEventListener("error", event =>
37 | {
38 | lastError = safeToString(event.error);
39 | });
40 |
41 | self.addEventListener("unhandledrejection", event =>
42 | {
43 | lastError = safeToString(event.reason);
44 | });
45 |
46 | let consoleError = console.error;
47 | console.error = function error(...args)
48 | {
49 | lastError = args.map(safeToString).join(" ");
50 | consoleError.apply(this, args);
51 | };
52 |
53 | port.on("debug.getLastError", () =>
54 | {
55 | let error = lastError;
56 | lastError = null;
57 | return error;
58 | });
59 |
--------------------------------------------------------------------------------
/lib/devenvPoller.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | let version = null;
21 |
22 | function doPoll()
23 | {
24 | fetch(browser.extension.getURL("devenvVersion__"))
25 | .then(response => response.text())
26 | .then(text =>
27 | {
28 | if (version == null)
29 | version = text;
30 |
31 | if (text != version)
32 | browser.runtime.reload();
33 | else
34 | self.setTimeout(doPoll, 5000);
35 | });
36 | }
37 |
38 | doPoll();
39 |
--------------------------------------------------------------------------------
/lib/hitLogger.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module hitLogger */
19 |
20 | "use strict";
21 |
22 | const {extractHostFromFrame} = require("./url");
23 | const {EventEmitter} = require("../adblockpluscore/lib/events");
24 | const {filterStorage} = require("../adblockpluscore/lib/filterStorage");
25 | const {port} = require("./messaging");
26 | const {Filter,
27 | ElemHideFilter} = require("../adblockpluscore/lib/filterClasses");
28 | const {contentTypes} = require("../adblockpluscore/lib/contentTypes");
29 |
30 | const nonRequestTypes = exports.nonRequestTypes = [
31 | "DOCUMENT", "ELEMHIDE", "SNIPPET", "GENERICBLOCK", "GENERICHIDE", "CSP"
32 | ];
33 |
34 | let eventEmitter = new EventEmitter();
35 |
36 | /**
37 | * @namespace
38 | * @static
39 | */
40 | let HitLogger = exports.HitLogger = {
41 | /**
42 | * Adds a listener for requests, filter hits etc related to the tab.
43 | *
44 | * Note: Calling code is responsible for removing the listener again,
45 | * it will not be automatically removed when the tab is closed.
46 | *
47 | * @param {number} tabId
48 | * @param {function} listener
49 | */
50 | addListener: eventEmitter.on.bind(eventEmitter),
51 |
52 | /**
53 | * Removes a listener for the tab.
54 | *
55 | * @param {number} tabId
56 | * @param {function} listener
57 | */
58 | removeListener: eventEmitter.off.bind(eventEmitter),
59 |
60 | /**
61 | * Checks whether a tab is being inspected by anything.
62 | *
63 | * @param {number} tabId
64 | * @return {boolean}
65 | */
66 | hasListener: eventEmitter.hasListeners.bind(eventEmitter)
67 | };
68 |
69 | /**
70 | * Logs a request associated with a tab or multiple tabs.
71 | *
72 | * @param {number[]} tabIds
73 | * The tabIds associated with the request
74 | * @param {Object} request
75 | * The request to log
76 | * @param {string} request.url
77 | * The URL of the request
78 | * @param {string} request.type
79 | * The request type
80 | * @param {string} request.docDomain
81 | * The hostname of the document
82 | * @param {boolean} request.thirdParty
83 | * Whether the origin of the request and document differs
84 | * @param {?string} request.sitekey
85 | * The active sitekey if there is any
86 | * @param {?boolean} request.specificOnly
87 | * Whether generic filters should be ignored
88 | * @param {?BlockingFilter} filter
89 | * The matched filter or null if there is no match
90 | */
91 | exports.logRequest = (tabIds, request, filter) =>
92 | {
93 | for (let tabId of tabIds)
94 | eventEmitter.emit(tabId, request, filter);
95 | };
96 |
97 | function logHiddenElements(tabId, selectors, filters, docDomain)
98 | {
99 | if (HitLogger.hasListener(tabId))
100 | {
101 | for (let subscription of filterStorage.subscriptions())
102 | {
103 | if (subscription.disabled)
104 | continue;
105 |
106 | for (let text of subscription.filterText())
107 | {
108 | let filter = Filter.fromText(text);
109 |
110 | // We only know the exact filter in case of element hiding emulation.
111 | // For regular element hiding filters, the content script only knows
112 | // the selector, so we have to find a filter that has an identical
113 | // selector and is active on the domain the match was reported from.
114 | let isActiveElemHideFilter = filter instanceof ElemHideFilter &&
115 | selectors.includes(filter.selector) &&
116 | filter.isActiveOnDomain(docDomain);
117 |
118 | if (isActiveElemHideFilter || filters.includes(text))
119 | eventEmitter.emit(tabId, {type: "ELEMHIDE", docDomain}, filter);
120 | }
121 | }
122 | }
123 | }
124 |
125 | /**
126 | * Logs an allowing filter that disables (some kind of)
127 | * blocking for a particular document.
128 | *
129 | * @param {number} tabId The tabId the allowlisting is active for
130 | * @param {string} url The url of the allowlisted document
131 | * @param {number} typeMask The bit mask of allowing types checked
132 | * for
133 | * @param {string} docDomain The hostname of the parent document
134 | * @param {AllowingFilter} filter The matched allowing filter
135 | */
136 | exports.logAllowlistedDocument = (tabId, url, typeMask, docDomain, filter) =>
137 | {
138 | if (HitLogger.hasListener(tabId))
139 | {
140 | for (let type of nonRequestTypes)
141 | {
142 | if (typeMask & filter.contentType & contentTypes[type])
143 | eventEmitter.emit(tabId, {url, type, docDomain}, filter);
144 | }
145 | }
146 | };
147 |
148 | /**
149 | * Logs active element hiding filters for a tab.
150 | *
151 | * @event "hitLogger.traceElemHide"
152 | * @property {string[]} selectors The selectors of applied ElemHideFilters
153 | * @property {string[]} filters The text of applied ElemHideEmulationFilters
154 | */
155 | port.on("hitLogger.traceElemHide", (message, sender) =>
156 | {
157 | logHiddenElements(
158 | sender.page.id, message.selectors, message.filters,
159 | extractHostFromFrame(sender.frame)
160 | );
161 | });
162 |
--------------------------------------------------------------------------------
/lib/io.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | const keyPrefix = "file:";
21 |
22 | function fileToKey(fileName)
23 | {
24 | return keyPrefix + fileName;
25 | }
26 |
27 | function loadFile(fileName)
28 | {
29 | let key = fileToKey(fileName);
30 | return browser.storage.local.get(key).then(items =>
31 | {
32 | let entry = items[key];
33 | if (entry)
34 | return entry;
35 | throw {type: "NoSuchFile"};
36 | });
37 | }
38 |
39 | function saveFile(fileName, data)
40 | {
41 | return browser.storage.local.set({
42 | [fileToKey(fileName)]: {
43 | content: Array.from(data),
44 | lastModified: Date.now()
45 | }
46 | });
47 | }
48 |
49 | exports.IO =
50 | {
51 | /**
52 | * Reads text lines from a file.
53 | * @param {string} fileName
54 | * Name of the file to be read
55 | * @param {TextSink} listener
56 | * Function that will be called for each line in the file
57 | * @return {Promise}
58 | * Promise to be resolved or rejected once the operation is completed
59 | */
60 | readFromFile(fileName, listener)
61 | {
62 | return loadFile(fileName).then(entry =>
63 | {
64 | for (let line of entry.content)
65 | listener(line);
66 | });
67 | },
68 |
69 | /**
70 | * Writes text lines to a file.
71 | * @param {string} fileName
72 | * Name of the file to be written
73 | * @param {Iterable.} data
74 | * An array-like or iterable object containing the lines (without line
75 | * endings)
76 | * @return {Promise}
77 | * Promise to be resolved or rejected once the operation is completed
78 | */
79 | writeToFile(fileName, data)
80 | {
81 | return saveFile(fileName, data);
82 | },
83 |
84 | /**
85 | * Renames a file.
86 | * @param {string} fromFile
87 | * Name of the file to be renamed
88 | * @param {string} newName
89 | * New file name, will be overwritten if exists
90 | * @return {Promise}
91 | * Promise to be resolved or rejected once the operation is completed
92 | */
93 | renameFile(fromFile, newName)
94 | {
95 | return loadFile(fromFile)
96 | .then(entry => browser.storage.local.set({[fileToKey(newName)]: entry}))
97 | .then(() => browser.storage.local.remove(fileToKey(fromFile)));
98 | },
99 |
100 | /**
101 | * Retrieves file metadata.
102 | * @param {string} fileName
103 | * Name of the file to be looked up
104 | * @return {Promise.}
105 | * Promise to be resolved with file metadata once the operation is
106 | * completed
107 | */
108 | statFile(fileName)
109 | {
110 | return loadFile(fileName).then(entry =>
111 | {
112 | return {
113 | exists: true,
114 | lastModified: entry.lastModified
115 | };
116 | }).catch(error =>
117 | {
118 | if (error.type == "NoSuchFile")
119 | return {exists: false};
120 | throw error;
121 | });
122 | }
123 | };
124 |
--------------------------------------------------------------------------------
/lib/messageResponder.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module messageResponder */
19 |
20 | "use strict";
21 |
22 | const {port} = require("messaging");
23 | const info = require("info");
24 |
25 | function forward(type, message, sender)
26 | {
27 | return new Promise(resolve =>
28 | {
29 | port._onMessage(Object.assign({}, message, {type}), sender, resolve);
30 | });
31 | }
32 |
33 | /**
34 | * @deprecated Please send the "filters.getTypes" message instead.
35 | *
36 | * @event "types.get"
37 | */
38 | port.on("types.get",
39 | (message, sender) => forward("filters.getTypes", message, sender));
40 |
41 | /**
42 | * @deprecated Please send the "options.open" message instead.
43 | *
44 | * @event "app.open"
45 | */
46 | port.on("app.open", (message, sender) =>
47 | {
48 | if (message.what == "options")
49 | return forward("options.open", message, sender);
50 | });
51 |
52 | /**
53 | * @deprecated Please send the "subscriptions.getInitIssues",
54 | * "prefs.getDocLink", "subscriptions.getRecommendations",
55 | * "devtools.supported" or "info.get" messages, or call the
56 | * browser.tabs.getCurrent(), browser.i18n.getUILanguage(),
57 | * browser.i18n.getMessage("@@bidi_dir") APIs instead.
58 | *
59 | * @event "app.get"
60 | */
61 | port.on("app.get", (message, sender) =>
62 | {
63 | if (message.what == "localeInfo")
64 | {
65 | return {
66 | locale: browser.i18n.getUILanguage(),
67 | bidiDir: browser.i18n.getMessage("@@bidi_dir")
68 | };
69 | }
70 |
71 | if (message.what == "senderId")
72 | return sender.page.id;
73 |
74 | if (message.what == "doclink")
75 | return forward("prefs.getDocLink", message, sender);
76 |
77 | if (message.what == "recommendations")
78 | return forward("subscriptions.getRecommendations", message, sender);
79 |
80 | if (message.what == "features")
81 | {
82 | return forward("devtools.supported", message, sender)
83 | .then(devToolsPanel => ({devToolsPanel}));
84 | }
85 |
86 | return info[message.what];
87 | });
88 |
89 | /**
90 | * @typedef {object} infoGetResult
91 | * @property {string} addonName
92 | * The extension's name, e.g. "adblockpluschrome".
93 | * @property {string} addonVersion
94 | * The extension's version, e.g. "3.6.3".
95 | * @property {string} application
96 | * The browser's name, e.g. "chrome".
97 | * @property {string} applicationVersion
98 | * The browser's version, e.g. "77.0.3865.90".
99 | * @property {string} platform
100 | * The browser platform, e.g. "chromium".
101 | * @property {string} platformVersion
102 | * The browser platform's version, e.g. "77.0.3865.90".
103 | */
104 |
105 | /**
106 | * Returns the browser platform information.
107 | *
108 | * @event "info.get"
109 | * @returns {infoGetResult}
110 | */
111 | port.on("info.get", (message, sender) => info);
112 |
--------------------------------------------------------------------------------
/lib/messaging.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module messaging */
19 |
20 | "use strict";
21 |
22 | const {EventEmitter} = require("../adblockpluscore/lib/events");
23 |
24 | /**
25 | * Communication port wrapping ext.onMessage to receive messages.
26 | *
27 | * @constructor
28 | */
29 | function Port()
30 | {
31 | this._eventEmitter = new EventEmitter();
32 | this._onMessage = this._onMessage.bind(this);
33 | ext.onMessage.addListener(this._onMessage);
34 | }
35 |
36 | Port.prototype = {
37 | _onMessage(message, sender, sendResponse)
38 | {
39 | let async = false;
40 | let callbacks = this._eventEmitter.listeners(message.type);
41 |
42 | for (let callback of callbacks)
43 | {
44 | let response = callback(message, sender);
45 |
46 | if (response && typeof response.then == "function")
47 | {
48 | response.then(
49 | sendResponse,
50 | reason =>
51 | {
52 | console.error(reason);
53 | sendResponse();
54 | }
55 | );
56 | async = true;
57 | }
58 | else
59 | {
60 | sendResponse(response);
61 | }
62 | }
63 |
64 | return async;
65 | },
66 |
67 | /**
68 | * Function to be called when a particular message is received.
69 | *
70 | * @callback Port~messageCallback
71 | * @param {object} message
72 | * @param {object} sender
73 | * @return The callback can return undefined (no response),
74 | * a value (response to be sent to sender immediately)
75 | * or a promise (asynchronous response).
76 | */
77 |
78 | /**
79 | * Adds a callback for the specified message.
80 | *
81 | * The return value of the callback (if not undefined) is sent as response.
82 | * @param {string} name
83 | * @param {Port~messageCallback} callback
84 | */
85 | on(name, callback)
86 | {
87 | this._eventEmitter.on(name, callback);
88 | },
89 |
90 | /**
91 | * Removes a callback for the specified message.
92 | *
93 | * @param {string} name
94 | * @param {Port~messageCallback} callback
95 | */
96 | off(name, callback)
97 | {
98 | this._eventEmitter.off(name, callback);
99 | },
100 |
101 | /**
102 | * Disables the port and makes it stop listening to incoming messages.
103 | */
104 | disconnect()
105 | {
106 | ext.onMessage.removeListener(this._onMessage);
107 | }
108 | };
109 |
110 | /**
111 | * The default port to receive messages.
112 | *
113 | * @type {Port}
114 | */
115 | exports.port = new Port();
116 |
--------------------------------------------------------------------------------
/lib/ml.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module */
19 |
20 | "use strict";
21 |
22 | const {port} = require("./messaging");
23 | const {ML} = require("../adblockpluscore/lib/ml");
24 |
25 | const tfCore = require("@tensorflow/tfjs-core");
26 | const tfConverter = require("@tensorflow/tfjs-converter");
27 |
28 | let tf = {};
29 |
30 | for (let object of [tfCore, tfConverter])
31 | {
32 | for (let property in object)
33 | {
34 | if (!Object.prototype.hasOwnProperty.call(tf, property))
35 | tf[property] = object[property];
36 | }
37 | }
38 |
39 | let mlByModel = new Map([
40 | ["mlHideIfGraphMatches", new ML(tf)]
41 | ]);
42 |
43 | for (let [key, value] of mlByModel)
44 | value.modelURL = browser.runtime.getURL(`data/${key}/model.json`);
45 |
46 | /**
47 | * Returns the inference on a ML model.
48 | *
49 | * @event "ml.inference"
50 | * @property {string} model Name of the model to use
51 | * @returns {Array.}
52 | */
53 | port.on("ml.inference", (message, sender) =>
54 | {
55 | let ml = mlByModel.get(message.model);
56 | if (!ml)
57 | return Promise.reject(new Error("Model not found."));
58 |
59 | return ml.predict(message.inputs);
60 | });
61 |
--------------------------------------------------------------------------------
/lib/options.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module options */
19 |
20 | "use strict";
21 |
22 | const {checkAllowlisted} = require("./allowlisting");
23 | const info = require("info");
24 | const {port} = require("./messaging");
25 |
26 | const optionsUrl = browser.runtime.getManifest().options_ui.page;
27 |
28 | const openOptionsPageAPISupported = (
29 | // Some versions of Firefox for Android before version 57 do have a
30 | // runtime.openOptionsPage but it doesn't do anything.
31 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1364945
32 | (info.application != "fennec" ||
33 | parseInt(info.applicationVersion, 10) >= 57)
34 | );
35 |
36 | function findOptionsPage()
37 | {
38 | return browser.tabs.query({}).then(tabs =>
39 | {
40 | return new Promise((resolve, reject) =>
41 | {
42 | // Firefox won't let us query for moz-extension:// pages, though
43 | // starting with Firefox 56 an extension can query for its own URLs:
44 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1271354
45 | let fullOptionsUrl = browser.extension.getURL(optionsUrl);
46 | let optionsTab = tabs.find(tab => tab.url == fullOptionsUrl);
47 | if (optionsTab)
48 | {
49 | resolve(optionsTab);
50 | return;
51 | }
52 |
53 | // Newly created tabs might have about:blank as their URL in Firefox or
54 | // an empty string on Chrome (80) rather than the final options page URL,
55 | // we need to wait for those to finish loading.
56 | let potentialOptionTabIds = new Set(
57 | tabs.filter(tab => (tab.url == "about:blank" || !tab.url) &&
58 | tab.status == "loading").map(tab => tab.id)
59 | );
60 | if (potentialOptionTabIds.size == 0)
61 | {
62 | resolve();
63 | return;
64 | }
65 | let removeListener;
66 | let updateListener = (tabId, changeInfo, tab) =>
67 | {
68 | if (potentialOptionTabIds.has(tabId) &&
69 | changeInfo.status == "complete")
70 | {
71 | potentialOptionTabIds.delete(tabId);
72 | let urlMatch = tab.url == fullOptionsUrl;
73 | if (urlMatch || potentialOptionTabIds.size == 0)
74 | {
75 | browser.tabs.onUpdated.removeListener(updateListener);
76 | browser.tabs.onRemoved.removeListener(removeListener);
77 | resolve(urlMatch ? tab : null);
78 | }
79 | }
80 | };
81 | browser.tabs.onUpdated.addListener(updateListener);
82 | removeListener = removedTabId =>
83 | {
84 | potentialOptionTabIds.delete(removedTabId);
85 | if (potentialOptionTabIds.size == 0)
86 | {
87 | browser.tabs.onUpdated.removeListener(updateListener);
88 | browser.tabs.onRemoved.removeListener(removeListener);
89 | }
90 | };
91 | browser.tabs.onRemoved.addListener(removeListener);
92 | });
93 | });
94 | }
95 |
96 | function openOptionsPage()
97 | {
98 | if (openOptionsPageAPISupported)
99 | return browser.runtime.openOptionsPage();
100 | return browser.tabs.create({url: optionsUrl});
101 | }
102 |
103 | function waitForOptionsPage(tab)
104 | {
105 | return new Promise(resolve =>
106 | {
107 | function onMessage(message, optionsPort)
108 | {
109 | if (message.type != "app.listen")
110 | return;
111 |
112 | optionsPort.onMessage.removeListener(onMessage);
113 | resolve([tab, optionsPort]);
114 | }
115 |
116 | function onConnect(optionsPort)
117 | {
118 | if (optionsPort.name != "ui" || optionsPort.sender.tab.id != tab.id)
119 | return;
120 |
121 | browser.runtime.onConnect.removeListener(onConnect);
122 | optionsPort.onMessage.addListener(onMessage);
123 | }
124 |
125 | browser.runtime.onConnect.addListener(onConnect);
126 | });
127 | }
128 |
129 | function focusOptionsPage(tab)
130 | {
131 | if (openOptionsPageAPISupported)
132 | return browser.runtime.openOptionsPage();
133 |
134 | let focusTab = () => browser.tabs.update(tab.id, {active: true});
135 |
136 | if ("windows" in browser)
137 | return browser.windows.update(tab.windowId, {focused: true}).then(focusTab);
138 |
139 | // Firefox for Android before version 57 does not support
140 | // runtime.openOptionsPage, nor does it support the windows API.
141 | // Since there is effectively only one window on the mobile browser,
142 | // we can just bring the tab to focus instead.
143 | return focusTab();
144 | }
145 |
146 | let showOptions =
147 | /**
148 | * Opens the options page, or switches to its existing tab.
149 | * @returns {Promise.}
150 | * Promise resolving to an Array containg the tab Object of the options page
151 | * and sometimes (when the page was just opened) a messaging port.
152 | */
153 | exports.showOptions = () =>
154 | {
155 | return findOptionsPage().then(existingTab =>
156 | {
157 | if (existingTab)
158 | return focusOptionsPage(existingTab).then(() => existingTab);
159 |
160 | return openOptionsPage().then(findOptionsPage).then(waitForOptionsPage);
161 | });
162 | };
163 |
164 | // We need to clear the popup URL on Firefox for Android in order for the
165 | // options page to open instead of the bubble. Unfortunately there's a bug[1]
166 | // which prevents us from doing that, so we must avoid setting the URL on
167 | // Firefox from the manifest at all, instead setting it here only for
168 | // non-mobile.
169 | // [1] - https://bugzilla.mozilla.org/show_bug.cgi?id=1414613
170 | if ("getBrowserInfo" in browser.runtime)
171 | {
172 | Promise.all([browser.browserAction.getPopup({}),
173 | browser.runtime.getBrowserInfo()]).then(
174 | ([popup, browserInfo]) =>
175 | {
176 | if (!popup && browserInfo.name != "Fennec")
177 | browser.browserAction.setPopup({popup: "popup.html"});
178 | }
179 | );
180 | }
181 |
182 | // On Firefox for Android, open the options page directly when the browser
183 | // action is clicked.
184 | browser.browserAction.onClicked.addListener(() =>
185 | {
186 | browser.tabs.query({active: true, lastFocusedWindow: true}).then(
187 | ([tab]) =>
188 | {
189 | let currentPage = new ext.Page(tab);
190 |
191 | showOptions().then(([optionsTab, optionsPort]) =>
192 | {
193 | if (!/^https?:$/.test(currentPage.url.protocol))
194 | return;
195 |
196 | optionsPort.postMessage({
197 | type: "app.respond",
198 | action: "showPageOptions",
199 | args: [
200 | {
201 | host: currentPage.url.hostname.replace(/^www\./, ""),
202 | whitelisted: !!checkAllowlisted(currentPage)
203 | }
204 | ]
205 | });
206 | });
207 | }
208 | );
209 | });
210 |
211 | /**
212 | * Opens the options page in a new tab and waits for it to load, or switches to
213 | * the existing tab if the options page is already open.
214 | *
215 | * @event "options.open"
216 | * @returns {object} optionsTab
217 | */
218 | port.on("options.open", (message, sender) =>
219 | showOptions().then(([optionsTab, optionsPort]) => optionsTab)
220 | );
221 |
--------------------------------------------------------------------------------
/lib/popupBlocker.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module popupBlocker */
19 |
20 | "use strict";
21 |
22 | const {defaultMatcher} = require("../adblockpluscore/lib/matcher");
23 | const {BlockingFilter} = require("../adblockpluscore/lib/filterClasses");
24 | const {contentTypes} = require("../adblockpluscore/lib/contentTypes");
25 | const {parseURL} = require("../adblockpluscore/lib/url");
26 | const {extractHostFromFrame} = require("./url");
27 | const {checkAllowlisted} = require("./allowlisting");
28 | const {logRequest} = require("./hitLogger");
29 | const info = require("info");
30 |
31 | let loadingPopups = new Map();
32 |
33 | function forgetPopup(tabId)
34 | {
35 | loadingPopups.delete(tabId);
36 |
37 | if (loadingPopups.size == 0)
38 | {
39 | browser.webRequest.onBeforeRequest.removeListener(onPopupURLChanged);
40 | browser.webNavigation.onCommitted.removeListener(onPopupURLChanged);
41 | browser.webNavigation.onCompleted.removeListener(onCompleted);
42 | browser.tabs.onRemoved.removeListener(forgetPopup);
43 | }
44 | }
45 |
46 | function checkPotentialPopup(tabId, popup)
47 | {
48 | let url = popup.url || "about:blank";
49 | let documentHost = extractHostFromFrame(popup.sourceFrame);
50 |
51 | let specificOnly = !!checkAllowlisted(
52 | popup.sourcePage, popup.sourceFrame, null,
53 | contentTypes.GENERICBLOCK
54 | );
55 |
56 | let filter = defaultMatcher.match(
57 | parseURL(url), contentTypes.POPUP,
58 | documentHost, null, specificOnly
59 | );
60 |
61 | if (filter instanceof BlockingFilter)
62 | browser.tabs.remove(tabId).catch(() => {});
63 |
64 | logRequest(
65 | [popup.sourcePage.id],
66 | {url, type: "POPUP", docDomain: documentHost, specificOnly},
67 | filter
68 | );
69 | }
70 |
71 | function onPopupURLChanged(details)
72 | {
73 | // Ignore frames inside the popup window.
74 | if (details.frameId != 0)
75 | return;
76 |
77 | let popup = loadingPopups.get(details.tabId);
78 | if (popup)
79 | {
80 | popup.url = details.url;
81 | if (popup.sourceFrame)
82 | checkPotentialPopup(details.tabId, popup);
83 | }
84 | }
85 |
86 | function onCompleted(details)
87 | {
88 | if (details.frameId == 0 && details.url != "about:blank")
89 | forgetPopup(details.tabId);
90 | }
91 |
92 | function onPopupCreated(tabId, url, sourceTabId, sourceFrameId)
93 | {
94 | if (loadingPopups.size == 0)
95 | {
96 | browser.webRequest.onBeforeRequest.addListener(
97 | onPopupURLChanged,
98 | {
99 | urls: ["http://*/*", "https://*/*"],
100 | types: ["main_frame"]
101 | }
102 | );
103 | browser.webNavigation.onCommitted.addListener(onPopupURLChanged);
104 | browser.webNavigation.onCompleted.addListener(onCompleted);
105 | browser.tabs.onRemoved.addListener(forgetPopup);
106 | }
107 |
108 | let popup = {
109 | url,
110 | sourcePage: new ext.Page({id: sourceTabId}),
111 | sourceFrame: null
112 | };
113 |
114 | loadingPopups.set(tabId, popup);
115 |
116 | let frame = ext.getFrame(sourceTabId, sourceFrameId);
117 |
118 | if (checkAllowlisted(popup.sourcePage, frame))
119 | {
120 | forgetPopup(tabId);
121 | }
122 | else
123 | {
124 | popup.sourceFrame = frame;
125 | checkPotentialPopup(tabId, popup);
126 | }
127 | }
128 |
129 | // Versions of Firefox before 54 do not support
130 | // webNavigation.onCreatedNavigationTarget
131 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1190687
132 | if ("onCreatedNavigationTarget" in browser.webNavigation)
133 | {
134 | browser.webNavigation.onCreatedNavigationTarget.addListener(details =>
135 | {
136 | onPopupCreated(details.tabId, details.url, details.sourceTabId,
137 | details.sourceFrameId);
138 | });
139 | }
140 |
141 | // On Firefox, clicking on a link doesn't
142 | // emit the webNavigation.onCreatedNavigationTarget event (and since Firefox 79,
143 | // "noopener" is implied by default). But on Chrome, opening a new empty tab
144 | // emits the tabs.onCreated event with openerTabId set. So the code below would
145 | // cause new tabs created by the user to be considered popups too, on Chrome.
146 | if (info.platform == "gecko")
147 | {
148 | browser.tabs.onCreated.addListener(details =>
149 | {
150 | // We only care about tabs created by another tab.
151 | // e.g. clicking on a link with target=_blank.
152 | if (typeof details.openerTabId == "undefined")
153 | return;
154 |
155 | // onCreated doesn't provide the frameId of the opener.
156 | onPopupCreated(details.id, details.url, details.openerTabId, 0);
157 | });
158 | }
159 |
--------------------------------------------------------------------------------
/lib/stats.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module stats */
19 |
20 | "use strict";
21 |
22 | const {BlockingFilter} = require("../adblockpluscore/lib/filterClasses");
23 | const {filterNotifier} = require("../adblockpluscore/lib/filterNotifier");
24 | const browserAction = require("./browserAction");
25 | const {port} = require("./messaging");
26 | const {Prefs} = require("./prefs");
27 |
28 | const badgeColor = "#646464";
29 | const badgeRefreshRate = 4;
30 |
31 | let blockedPerPage = new ext.PageMap();
32 |
33 | let getBlockedPerPage =
34 | /**
35 | * Gets the number of requests blocked on the given page.
36 | *
37 | * @param {Page} page
38 | * @return {Number}
39 | */
40 | exports.getBlockedPerPage = page => blockedPerPage.get(page) || 0;
41 |
42 | let activeTabIds = new Set();
43 | let activeTabIdByWindowId = new Map();
44 |
45 | let badgeUpdateScheduled = false;
46 |
47 | function updateBadge(tabId)
48 | {
49 | if (!Prefs.show_statsinicon)
50 | return;
51 |
52 | for (let id of (typeof tabId == "undefined" ? activeTabIds : [tabId]))
53 | {
54 | let page = new ext.Page({id});
55 | let blockedCount = blockedPerPage.get(page);
56 |
57 | browserAction.setBadge(page.id, blockedCount && {
58 | color: badgeColor,
59 | number: blockedCount
60 | });
61 | }
62 | }
63 |
64 | function scheduleBadgeUpdate(tabId)
65 | {
66 | if (!badgeUpdateScheduled && Prefs.show_statsinicon &&
67 | (typeof tabId == "undefined" || activeTabIds.has(tabId)))
68 | {
69 | setTimeout(() => { badgeUpdateScheduled = false; updateBadge(); },
70 | 1000 / badgeRefreshRate);
71 | badgeUpdateScheduled = true;
72 | }
73 | }
74 |
75 | // Once nagivation for the tab has been committed to (e.g. it's no longer
76 | // being prerendered) we clear its badge, or if some requests were already
77 | // blocked beforehand we display those on the badge now.
78 | browser.webNavigation.onCommitted.addListener(details =>
79 | {
80 | if (details.frameId == 0)
81 | updateBadge(details.tabId);
82 | });
83 |
84 | /**
85 | * Records a blocked request.
86 | *
87 | * @param {Filter} filter
88 | * @param {Array.} tabIds
89 | */
90 | exports.recordBlockedRequest = (filter, tabIds) =>
91 | {
92 | if (!(filter instanceof BlockingFilter))
93 | return;
94 |
95 | for (let tabId of tabIds)
96 | {
97 | let page = new ext.Page({id: tabId});
98 | let blocked = blockedPerPage.get(page) || 0;
99 |
100 | blockedPerPage.set(page, ++blocked);
101 | scheduleBadgeUpdate(tabId);
102 | }
103 |
104 | // Make sure blocked_total is only read after the storage was loaded.
105 | Prefs.untilLoaded.then(() => { Prefs.blocked_total++; });
106 | };
107 |
108 | Prefs.on("show_statsinicon", () =>
109 | {
110 | browser.tabs.query({}).then(tabs =>
111 | {
112 | for (let tab of tabs)
113 | {
114 | if (Prefs.show_statsinicon)
115 | updateBadge(tab.id);
116 | else
117 | browserAction.setBadge(tab.id, null);
118 | }
119 | });
120 | });
121 |
122 | /**
123 | * Returns the number of blocked requests for the sender's page.
124 | *
125 | * @event "stats.getBlockedPerPage"
126 | * @returns {number}
127 | */
128 | port.on("stats.getBlockedPerPage",
129 | message => getBlockedPerPage(new ext.Page(message.tab)));
130 |
131 | browser.tabs.query({active: true}).then(tabs =>
132 | {
133 | for (let tab of tabs)
134 | {
135 | activeTabIds.add(tab.id);
136 | activeTabIdByWindowId.set(tab.windowId, tab.id);
137 | }
138 |
139 | scheduleBadgeUpdate();
140 | });
141 |
142 | browser.tabs.onActivated.addListener(tab =>
143 | {
144 | let lastActiveTabId = activeTabIdByWindowId.get(tab.windowId);
145 | if (typeof lastActiveTabId != "undefined")
146 | activeTabIds.delete(lastActiveTabId);
147 |
148 | activeTabIds.add(tab.tabId);
149 | activeTabIdByWindowId.set(tab.windowId, tab.tabId);
150 |
151 | scheduleBadgeUpdate();
152 | });
153 |
154 | if ("windows" in browser)
155 | {
156 | browser.windows.onRemoved.addListener(windowId =>
157 | {
158 | activeTabIds.delete(activeTabIdByWindowId.get(windowId));
159 | activeTabIdByWindowId.delete(windowId);
160 | });
161 | }
162 |
--------------------------------------------------------------------------------
/lib/uninstall.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module uninstall */
19 |
20 | "use strict";
21 |
22 | const info = require("info");
23 | const {analytics} = require("../adblockpluscore/lib/analytics");
24 | const {filterNotifier} = require("../adblockpluscore/lib/filterNotifier");
25 | const {filterStorage} = require("../adblockpluscore/lib/filterStorage");
26 | const {recommendations} = require("../adblockpluscore/lib/recommendations");
27 | const {isDataCorrupted} = require("./subscriptionInit");
28 | const {Prefs} = require("./prefs");
29 |
30 | const abbreviations = [
31 | ["an", "addonName"], ["av", "addonVersion"],
32 | ["ap", "application"], ["apv", "applicationVersion"],
33 | ["p", "platform"], ["fv", "firstVersion"], ["pv", "platformVersion"],
34 | ["ndc", "notificationDownloadCount"], ["c", "corrupted"],
35 | ["s", "subscriptions"]
36 | ];
37 |
38 | /**
39 | * Retrieves set of URLs of recommended ad blocking filter lists
40 | *
41 | * @return {Set}
42 | */
43 | function getAdsSubscriptions()
44 | {
45 | let subscriptions = new Set();
46 | for (let subscription of recommendations())
47 | {
48 | if (subscription.type == "ads")
49 | subscriptions.add(subscription.url);
50 | }
51 | return subscriptions;
52 | }
53 |
54 | /**
55 | * Determines whether any of the given subscriptions are installed and enabled
56 | *
57 | * @param {Set} urls
58 | *
59 | * @return {boolean}
60 | */
61 | function isAnySubscriptionActive(urls)
62 | {
63 | for (let subscription of filterStorage.subscriptions())
64 | {
65 | if (!subscription.disabled && urls.has(subscription.url))
66 | return true;
67 | }
68 |
69 | return false;
70 | }
71 |
72 | let setUninstallURL =
73 | /**
74 | * Sets (or updates) the URL that is openend when the extension is uninstalled.
75 | *
76 | * Must be called after prefs got initialized and a data corruption
77 | * if any was detected, as well when notification data change.
78 | */
79 | exports.setUninstallURL = () =>
80 | {
81 | let search = [];
82 | let params = Object.create(info);
83 |
84 | params.corrupted = isDataCorrupted() ? "1" : "0";
85 | params.firstVersion = analytics.getFirstVersion();
86 |
87 | let notificationDownloadCount = Prefs.notificationdata.downloadCount || 0;
88 | if (notificationDownloadCount < 5)
89 | params.notificationDownloadCount = notificationDownloadCount;
90 | else if (notificationDownloadCount < 8)
91 | params.notificationDownloadCount = "5-7";
92 | else if (notificationDownloadCount < 30)
93 | params.notificationDownloadCount = "8-29";
94 | else if (notificationDownloadCount < 90)
95 | params.notificationDownloadCount = "30-89";
96 | else if (notificationDownloadCount < 180)
97 | params.notificationDownloadCount = "90-179";
98 | else
99 | params.notificationDownloadCount = "180+";
100 |
101 | let aaSubscriptions = new Set([Prefs.subscriptions_exceptionsurl]);
102 | let adsSubscriptions = getAdsSubscriptions();
103 | let isAcceptableAdsActive = isAnySubscriptionActive(aaSubscriptions);
104 | let isAdBlockingActive = isAnySubscriptionActive(adsSubscriptions);
105 | params.subscriptions = (isAcceptableAdsActive << 1) | isAdBlockingActive;
106 |
107 | for (let [abbreviation, key] of abbreviations)
108 | search.push(abbreviation + "=" + encodeURIComponent(params[key]));
109 |
110 | browser.runtime.setUninstallURL(Prefs.getDocLink("uninstalled") + "&" +
111 | search.join("&"));
112 | };
113 |
114 | filterNotifier.on("subscription.added", setUninstallURL);
115 | filterNotifier.on("subscription.disabled", setUninstallURL);
116 | filterNotifier.on("subscription.removed", setUninstallURL);
117 | Prefs.on("notificationdata", setUninstallURL);
118 |
--------------------------------------------------------------------------------
/lib/url.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | /** @module url */
19 |
20 | "use strict";
21 |
22 | /**
23 | * Gets the IDN-decoded hostname from the URL of a frame.
24 | * If the URL don't have host information (like "about:blank"
25 | * and "data:" URLs) it falls back to the parent frame.
26 | *
27 | * @param {?Frame} frame
28 | * @param {URL} [originUrl]
29 | * @return {string}
30 | */
31 | exports.extractHostFromFrame = (frame, originUrl) =>
32 | {
33 | for (; frame; frame = frame.parent)
34 | {
35 | let {hostname} = frame.url;
36 | if (hostname)
37 | return hostname;
38 | }
39 |
40 | return originUrl ? originUrl.hostname : "";
41 | };
42 |
--------------------------------------------------------------------------------
/managed-storage-schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "object",
3 | "properties": {
4 | "suppress_first_run_page": {
5 | "type": "boolean"
6 | },
7 | "additional_subscriptions": {
8 | "type": "array",
9 | "items": {
10 | "type": "string"
11 | }
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/options.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | let iframe = document.getElementById("content");
21 |
22 | iframe.onload = () =>
23 | {
24 | document.title = iframe.contentDocument.title;
25 | };
26 |
27 | browser.runtime.sendMessage({
28 | type: "app.get",
29 | what: "application"
30 | }).then(application =>
31 | {
32 | // Load the mobile version of the options page on Firefox for Android.
33 | iframe.src = iframe.getAttribute("data-src-" + application) ||
34 | iframe.getAttribute("data-src");
35 | });
36 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "adblockpluschrome",
3 | "repository": "https://gitlab.com/eyeo/adblockplus/adblockpluschrome",
4 | "license": "GPL-3.0",
5 | "engines": {
6 | "node": ">=12.17.0",
7 | "npm": ">=6"
8 | },
9 | "dependencies": {
10 | "@tensorflow/tfjs-converter": "1.3.2",
11 | "@tensorflow/tfjs-core": "1.3.2"
12 | },
13 | "devDependencies": {
14 | "argparse": "^1.0.10",
15 | "chromedriver": "^85.0.1",
16 | "del": "^5.1.0",
17 | "dmg": "^0.1.0",
18 | "eslint": "^7.9.0",
19 | "eslint-config-eyeo": "^3.2.0",
20 | "extract-zip": "^2.0.1",
21 | "geckodriver": "^1.20.0",
22 | "got": "^11.6.2",
23 | "gulp": "^4.0.2",
24 | "gulp-tar": "^3.1.0",
25 | "gulp-gzip": "^1.4.2",
26 | "gulp-vinyl-zip": "^2.2.1",
27 | "handlebars": "^4.7.6",
28 | "jimp": "^0.16.1",
29 | "jsdoc": "^3.6.5",
30 | "merge-stream": "^2.0.0",
31 | "mocha": "^8.1.3",
32 | "msedgedriver": "^83.0.0",
33 | "ncp": "^2.0.0",
34 | "selenium-webdriver": "^4.0.0-alpha.7",
35 | "semver": "^7.3.2",
36 | "through": "^2.3.8",
37 | "webpack-merge": "^5.1.4",
38 | "webpack-stream": "^6.1.0"
39 | },
40 | "scripts": {
41 | "generate-buildnum": "node --no-warnings build/utils/git.mjs",
42 | "lint": "eslint --ext .js,.mjs *.js *.mjs lib/ qunit/ ext/ test/ build/",
43 | "test-only": "mocha --unhandled-rejections=strict --delay",
44 | "test": "npm run test-only --",
45 | "postinstall": "cd adblockplusui && npm install --only=production --loglevel=error --no-audit --no-fund --no-package-lock --no-optional",
46 | "posttest": "npm run lint",
47 | "docs": "jsdoc --configure jsdoc.conf --destination docs lib",
48 | "download-test-browsers": "node test/bin/downloadBrowsers.mjs"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/polyfill.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | {
21 | let asyncAPIs = [
22 | "browserAction.setIcon",
23 | "browserAction.getPopup",
24 | "contentSettings.cookies.get",
25 | "contentSettings.javascript.get",
26 | "contextMenus.removeAll",
27 | "devtools.panels.create",
28 | "management.getAll",
29 | "management.getSelf",
30 | "notifications.clear",
31 | "notifications.create",
32 | "permissions.contains",
33 | "permissions.remove",
34 | "permissions.request",
35 | "runtime.getBrowserInfo",
36 | "runtime.openOptionsPage",
37 | "runtime.sendMessage",
38 | "runtime.setUninstallURL",
39 | "storage.local.get",
40 | "storage.local.remove",
41 | "storage.local.set",
42 | "storage.managed.get",
43 | "tabs.captureVisibleTab",
44 | "tabs.create",
45 | "tabs.executeScript",
46 | "tabs.get",
47 | "tabs.getCurrent",
48 | "tabs.insertCSS",
49 | "tabs.query",
50 | "tabs.reload",
51 | "tabs.remove",
52 | "tabs.removeCSS",
53 | "tabs.sendMessage",
54 | "tabs.update",
55 | "webNavigation.getAllFrames",
56 | "webRequest.handlerBehaviorChanged",
57 | "windows.create",
58 | "windows.update"
59 | ];
60 |
61 | // Chrome (<= 66) and Opera (<= 54) don't accept passing a callback for
62 | // browserAction.setBadgeText and browserAction.setBadgeBackgroundColor
63 | const maybeAsyncAPIs = [
64 | ["browserAction.setBadgeText", {text: ""}],
65 | ["browserAction.setBadgeBackgroundColor", {color: [0, 0, 0, 0]}]
66 | ];
67 | let syncAPIs = [];
68 |
69 | // Since we add a callback for all messaging API calls in our wrappers,
70 | // Chrome assumes we're interested in the response; when there's no response,
71 | // it sets runtime.lastError
72 | const portClosedBeforeResponseError =
73 | // Older versions of Chrome have a typo:
74 | // https://crrev.com/c33f51726eacdcc1a487b21a13611f7eab580d6d
75 | /^The message port closed before a res?ponse was received\.$/;
76 |
77 | // This is the error Firefox throws when a message listener is not a
78 | // function.
79 | const invalidMessageListenerError = "Invalid listener for runtime.onMessage.";
80 |
81 | let messageListeners = new WeakMap();
82 |
83 | function getAPIWrappables(api)
84 | {
85 | let object = browser;
86 | let path = api.split(".");
87 | let name = path.pop();
88 |
89 | for (let node of path)
90 | {
91 | object = object[node];
92 |
93 | if (!object)
94 | return;
95 | }
96 |
97 | let func = object[name];
98 | if (!func)
99 | return;
100 |
101 | return {object, name, func};
102 | }
103 |
104 | function wrapAsyncAPI(api)
105 | {
106 | let wrappables = getAPIWrappables(api);
107 |
108 | if (!wrappables)
109 | return;
110 |
111 | let {object, name, func} = wrappables;
112 |
113 | // If the property is not writable assigning it will fail, so we use
114 | // Object.defineProperty here instead. Assuming the property isn't
115 | // inherited its other attributes (e.g. enumerable) are preserved,
116 | // except for accessor attributes (e.g. get and set) which are discarded
117 | // since we're specifying a value.
118 | Object.defineProperty(object, name, {
119 | value(...args)
120 | {
121 | let resolvePromise = null;
122 | let rejectPromise = null;
123 |
124 | func.call(object, ...args, result =>
125 | {
126 | let error = browser.runtime.lastError;
127 | if (error && !portClosedBeforeResponseError.test(error.message))
128 | rejectPromise(new Error(error.message));
129 | else
130 | resolvePromise(result);
131 | });
132 |
133 | return new Promise((resolve, reject) =>
134 | {
135 | resolvePromise = resolve;
136 | rejectPromise = reject;
137 | });
138 | }
139 | });
140 | }
141 |
142 | function wrapSyncAPI(api)
143 | {
144 | let wrappables = getAPIWrappables(api);
145 |
146 | if (!wrappables)
147 | return;
148 |
149 | let {object, name, func} = wrappables;
150 |
151 | Object.defineProperty(object, name, {
152 | value(...args)
153 | {
154 | return Promise.resolve(func.call(object, ...args));
155 | }
156 | });
157 | }
158 |
159 | function wrapRuntimeOnMessage()
160 | {
161 | let {onMessage} = browser.runtime;
162 | let {addListener, removeListener} = onMessage;
163 |
164 | onMessage.addListener = function(listener)
165 | {
166 | if (typeof listener != "function")
167 | throw new Error(invalidMessageListenerError);
168 |
169 | // Don't add the same listener twice or we end up with multiple wrappers.
170 | if (messageListeners.has(listener))
171 | return;
172 |
173 | let wrapper = (message, sender, sendResponse) =>
174 | {
175 | let wait = listener(message, sender, sendResponse);
176 |
177 | if (wait instanceof Promise)
178 | {
179 | wait.then(sendResponse, reason =>
180 | {
181 | try
182 | {
183 | sendResponse();
184 | }
185 | catch (error)
186 | {
187 | // sendResponse can throw if the internal port is closed; be sure
188 | // to throw the original error.
189 | }
190 |
191 | throw reason;
192 | });
193 | }
194 |
195 | return !!wait;
196 | };
197 |
198 | addListener.call(onMessage, wrapper);
199 | messageListeners.set(listener, wrapper);
200 | };
201 |
202 | onMessage.removeListener = function(listener)
203 | {
204 | if (typeof listener != "function")
205 | throw new Error(invalidMessageListenerError);
206 |
207 | let wrapper = messageListeners.get(listener);
208 | if (wrapper)
209 | {
210 | removeListener.call(onMessage, wrapper);
211 | messageListeners.delete(listener);
212 | }
213 | };
214 |
215 | onMessage.hasListener = function(listener)
216 | {
217 | if (typeof listener != "function")
218 | throw new Error(invalidMessageListenerError);
219 |
220 | return messageListeners.has(listener);
221 | };
222 | }
223 |
224 | function shouldWrapAPIs()
225 | {
226 | try
227 | {
228 | return !(browser.storage.local.get([]) instanceof Promise);
229 | }
230 | catch (error)
231 | {
232 | }
233 |
234 | return true;
235 | }
236 |
237 | function acceptsCallback(func, args)
238 | {
239 | try
240 | {
241 | func(...args, () => {});
242 | return true;
243 | }
244 | catch (e)
245 | {
246 | return false;
247 | }
248 | }
249 |
250 | if (shouldWrapAPIs())
251 | {
252 | // Unlike Firefox, Chrome doesn't have a "browser" object, but provides
253 | // the extension API through the "chrome" namespace (non-standard).
254 | if (typeof browser == "undefined")
255 | self.browser = chrome;
256 |
257 | for (let [api, ...testArgs] of maybeAsyncAPIs)
258 | {
259 | let wrappables = getAPIWrappables(api);
260 |
261 | if (!wrappables)
262 | continue;
263 |
264 | let {func} = wrappables;
265 |
266 | (acceptsCallback(func, testArgs) ? asyncAPIs : syncAPIs).push(api);
267 | }
268 |
269 | for (let api of asyncAPIs)
270 | wrapAsyncAPI(api);
271 |
272 | for (let api of syncAPIs)
273 | wrapSyncAPI(api);
274 |
275 | wrapRuntimeOnMessage();
276 | }
277 | }
278 |
279 | // Object.values is not supported in Chrome <54.
280 | if (!("values" in Object))
281 | Object.values = obj => Object.keys(obj).map(key => obj[key]);
282 |
283 | // Firefox <56 separates the locale parts with an underscore instead of a dash.
284 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1374552
285 | let {getUILanguage} = browser.i18n;
286 | browser.i18n.getUILanguage = function()
287 | {
288 | return getUILanguage().replace("_", "-");
289 | };
290 |
291 | // Chrome <69 does not support OffscreenCanvas
292 | if (typeof OffscreenCanvas == "undefined")
293 | {
294 | self.OffscreenCanvas = function(width, height)
295 | {
296 | let canvas = document.createElement("canvas");
297 | canvas.width = width;
298 | canvas.height = height;
299 | return canvas;
300 | };
301 | }
302 |
303 | // Some Node.js modules rely on the global reference.
304 | self.global = self;
305 |
--------------------------------------------------------------------------------
/qunit/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "qunit": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/qunit/subscriptions.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "EasyList Czech and Slovak+EasyList",
4 | "url": "https://easylist-downloads.adblockplus.org/easylistczechslovak+easylist.txt",
5 | "homepage": "https://adblock.sk/",
6 | "languages": [
7 | "cs",
8 | "sk"
9 | ],
10 | "type": "ads"
11 | },
12 | {
13 | "title": "EasyList",
14 | "url": "https://easylist-downloads.adblockplus.org/easylist.txt",
15 | "homepage": "https://easylist.to/",
16 | "languages": [
17 | "en"
18 | ],
19 | "type": "ads"
20 | },
21 | {
22 | "title": "EasyList Dutch+EasyList",
23 | "url": "https://easylist-downloads.adblockplus.org/easylistdutch+easylist.txt",
24 | "homepage": "https://easylist.to/",
25 | "languages": [
26 | "nl"
27 | ],
28 | "type": "ads"
29 | },
30 | {
31 | "title": "EasyList Germany+EasyList",
32 | "url": "https://easylist-downloads.adblockplus.org/easylistgermany+easylist.txt",
33 | "homepage": "https://easylist.to/",
34 | "languages": [
35 | "de"
36 | ],
37 | "type": "ads"
38 | },
39 | {
40 | "title": "EasyList Italy+EasyList",
41 | "url": "https://easylist-downloads.adblockplus.org/easylistitaly+easylist.txt",
42 | "homepage": "https://easylist.to/",
43 | "languages": [
44 | "it"
45 | ],
46 | "type": "ads"
47 | },
48 | {
49 | "title": "Liste FR+EasyList",
50 | "url": "https://easylist-downloads.adblockplus.org/liste_fr+easylist.txt",
51 | "homepage": "https://forums.lanik.us/viewforum.php?f=91",
52 | "languages": [
53 | "fr"
54 | ],
55 | "type": "ads"
56 | },
57 | {
58 | "title": "RuAdList+EasyList",
59 | "url": "https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt",
60 | "homepage": "https://forums.lanik.us/viewforum.php?f=102",
61 | "languages": [
62 | "ru",
63 | "uk"
64 | ],
65 | "type": "ads"
66 | },
67 | {
68 | "title": "ABP filters",
69 | "url": "https://easylist-downloads.adblockplus.org/abp-filters-anti-cv.txt",
70 | "homepage": "https://github.com/abp-filters/abp-filters-anti-cv",
71 | "languages": [
72 | "de",
73 | "en"
74 | ],
75 | "type": "circumvention"
76 | },
77 | {
78 | "title": "ABP Anti-Circumvention list Fr",
79 | "url": "https://easylist-downloads.adblockplus.org/abp-filters-anti-cv-fr.txt",
80 | "homepage": "https://github.com/abp-filters/abp-filters-anti-cv",
81 | "languages": [
82 | "fr"
83 | ],
84 | "type": "circumvention"
85 | },
86 | {
87 | "title": "EasyPrivacy",
88 | "url": "https://easylist-downloads.adblockplus.org/easyprivacy.txt",
89 | "homepage": "https://easylist.to/",
90 | "type": "privacy"
91 | },
92 | {
93 | "title": "Fanboy's Social Blocking List",
94 | "url": "https://easylist-downloads.adblockplus.org/fanboy-social.txt",
95 | "homepage": "https://easylist.to/",
96 | "type": "social"
97 | }
98 | ]
99 |
--------------------------------------------------------------------------------
/qunit/tests/cssEscaping.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {Filter, ElemHideFilter} =
4 | require("../../adblockpluscore/lib/filterClasses");
5 | const {escapeCSS, quoteCSS} = require("../../lib/filterComposer");
6 |
7 | QUnit.module("CSS escaping", () =>
8 | {
9 | QUnit.test("CSS escaping", assert =>
10 | {
11 | function testSelector(opts)
12 | {
13 | let mustMatch = opts.mustMatch !== false;
14 | let doc = document.implementation.createHTMLDocument();
15 |
16 | let style = doc.createElement("style");
17 | doc.documentElement.appendChild(style);
18 | style.sheet.insertRule(opts.selector + " {}", 0);
19 |
20 | let element;
21 | try
22 | {
23 | element = doc.createElement(opts.tagName || "div");
24 | }
25 | catch (e)
26 | {
27 | // Some characters we are going to test can not occur in tag names,
28 | // but we still have to make sure that no exception is thrown when
29 | // calling .querySelector() and .insertRule()
30 | element = null;
31 | mustMatch = false;
32 | }
33 |
34 | if (element)
35 | {
36 | for (let attr in opts.attributes)
37 | element.setAttribute(attr, opts.attributes[attr]);
38 |
39 | doc.documentElement.appendChild(element);
40 | }
41 |
42 | let foundElement = doc.querySelector(opts.selector);
43 | let filter = Filter.fromText("##" + opts.selector);
44 |
45 | if (!(filter instanceof ElemHideFilter))
46 | assert.ok(false, opts.selector + " (not allowed in elemhide filters)");
47 | else if (mustMatch)
48 | assert.equal(foundElement, element, opts.selector);
49 | else
50 | assert.ok(true, opts.selector);
51 | }
52 |
53 | function testEscape(s)
54 | {
55 | testSelector({
56 | selector: escapeCSS(s),
57 | tagName: s
58 | });
59 |
60 | testSelector({
61 | selector: "#" + escapeCSS(s),
62 | attributes: {id: s}
63 | });
64 |
65 | testSelector({
66 | selector: "." + escapeCSS(s),
67 | attributes: {class: s},
68 |
69 | // Whitespace characters split the class name, hence the selector
70 | // won't match. But we still have to make sure that no exception
71 | // is thrown when calling .querySelector() and .insertRule()
72 | mustMatch: !/\s/.test(s)
73 | });
74 |
75 | testSelector({
76 | selector: "[foo=" + quoteCSS(s) + "]",
77 | attributes: {foo: s}
78 | });
79 | }
80 |
81 | for (let i = 1; i < 0x80; i++)
82 | {
83 | let chr = String.fromCharCode(i);
84 |
85 | // Make sure that all ASCII characters are correctly escaped.
86 | testEscape(chr);
87 |
88 | // Some characters are only escaped when in the first positon,
89 | // so we still have to make sure that everything is correctly escaped
90 | // in subsequent positions.
91 | testEscape("x" + chr);
92 |
93 | // Leading dashes must be escaped, when followed by certain characters.
94 | testEscape("-" + chr);
95 | }
96 |
97 | // Test some non-ASCII characters. However, those shouldn't
98 | // require escaping.
99 | testEscape("\uD83D\uDE3B\u2665\u00E4");
100 | });
101 | });
102 |
--------------------------------------------------------------------------------
/qunit/tests/prefs.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {Prefs} = require("../../lib/prefs");
4 |
5 | QUnit.module("Preferences", () =>
6 | {
7 | function afterWrite(prefKey)
8 | {
9 | return Promise.race([
10 | new Promise((resolve, reject) =>
11 | {
12 | let onChange = (changes, area) =>
13 | {
14 | if (area == "local" && prefKey in changes)
15 | {
16 | browser.storage.onChanged.removeListener(onChange);
17 | resolve();
18 | }
19 | };
20 | browser.storage.onChanged.addListener(onChange);
21 | }),
22 | // We take care to timeout after 500ms in case the onChange event doesn't
23 | // fire when we expect it to. For example, Firefox 66 has a bug[1] whereby
24 | // the event doesn't fire for falsey values.
25 | // 1 - https://bugzilla.mozilla.org/show_bug.cgi?id=1541449
26 | new Promise((resolve, reject) =>
27 | {
28 | setTimeout(() => { resolve(); }, 500);
29 | })
30 | ]);
31 | }
32 |
33 | function performStorageTests(prefName, prefKey, defaultValue, newValue, tests,
34 | assert)
35 | {
36 | let [method, whichValue] = tests.shift();
37 | let value = whichValue == "default" ? defaultValue : newValue;
38 |
39 | return browser.storage.local.get(prefKey)
40 | .then(items =>
41 | {
42 | let expectingWrite = typeof defaultValue == "object" ||
43 | prefKey in items ||
44 | whichValue == "new";
45 | let promise = expectingWrite ? afterWrite(prefKey) : Promise.resolve();
46 |
47 | if (method == "property")
48 | Prefs[prefName] = value;
49 | else
50 | Prefs.set(prefName, value);
51 |
52 | assert.deepEqual(Prefs[prefName], value,
53 | `Assigned Prefs['${prefName}'] ${whichValue} value`);
54 |
55 | return promise;
56 | }).then(() =>
57 | browser.storage.local.get(prefKey)
58 | ).then(items =>
59 | {
60 | if (whichValue == "default" && typeof defaultValue != "object")
61 | {
62 | assert.equal(prefKey in items, false,
63 | prefKey + " shouldn't be present in stoage.local");
64 | }
65 | else
66 | {
67 | assert.equal(prefKey in items, true,
68 | prefKey + " should be present in stoage.local");
69 |
70 | assert.deepEqual(items[prefKey], value,
71 | prefKey + " in storage.local should have the value " +
72 | JSON.stringify(value));
73 | }
74 |
75 | if (tests.length)
76 | {
77 | return performStorageTests(prefName, prefKey,
78 | defaultValue, newValue, tests, assert);
79 | }
80 | });
81 | }
82 |
83 | function testPrefStorage(prefName, defaultValue, newValue, assert)
84 | {
85 | let prefKey = "pref:" + prefName;
86 | let tests = [["property", "default"],
87 | ["property", "new"],
88 | ["property", "default"],
89 | ["set", "new"],
90 | ["set", "default"]];
91 |
92 | let backupValue = Prefs[prefName];
93 | return performStorageTests(prefName, prefKey, defaultValue, newValue, tests,
94 | assert)
95 | .catch(exception => { assert.ok(false, exception); })
96 | .then(() => Prefs.set(prefName, backupValue));
97 | }
98 |
99 | QUnit.test("Numerical preference", assert =>
100 | {
101 | let done = assert.async();
102 |
103 | testPrefStorage("patternsbackups", 0, 12, assert).then(done);
104 | });
105 |
106 | QUnit.test("Boolean preference", assert =>
107 | {
108 | let done = assert.async();
109 |
110 | testPrefStorage("savestats", false, true, assert).then(done);
111 | });
112 |
113 | QUnit.test("String preference", assert =>
114 | {
115 | let done = assert.async();
116 |
117 | let defaultValue = "https://notification.adblockplus.org/notification.json";
118 | let newValue = "https://notification.adblockplus.org/foo\u1234bar.json";
119 |
120 | testPrefStorage("notificationurl", defaultValue, newValue, assert)
121 | .then(done);
122 | });
123 |
124 | QUnit.test("Object preference", assert =>
125 | {
126 | let done = assert.async();
127 |
128 | testPrefStorage("notificationdata", {}, {foo: 1, bar: 2}, assert)
129 | .then(done);
130 | });
131 | });
132 |
--------------------------------------------------------------------------------
/qunit/tests/subscriptionInit.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | let {chooseFilterSubscriptions} = require("../../lib/subscriptionInit");
21 |
22 | QUnit.module("Subscription initialization", hooks =>
23 | {
24 | let subscriptions = require("../subscriptions.json");
25 | let origGetUILanguage;
26 | let language;
27 |
28 | hooks.before(() =>
29 | {
30 | origGetUILanguage = browser.i18n.getUILanguage;
31 | browser.i18n.getUILanguage = () => language;
32 | });
33 |
34 | hooks.after(() =>
35 | {
36 | browser.i18n.getUILanguage = origGetUILanguage;
37 | });
38 |
39 | QUnit.test("chooses default filter subscriptions", assert =>
40 | {
41 | language = "en";
42 |
43 | let subs = chooseFilterSubscriptions(subscriptions);
44 | assert.ok(subs);
45 |
46 | let sub1 = subs.find(sub => sub.type == "circumvention");
47 | assert.ok(sub1);
48 | let sub2 = subs.find(sub => sub.type == "ads");
49 | assert.ok(sub1);
50 |
51 | assert.deepEqual(sub1.languages, ["de", "en"]);
52 | assert.deepEqual(sub2.languages, ["en"]);
53 | });
54 |
55 | QUnit.test("falls back to default language", assert =>
56 | {
57 | language = "sl";
58 |
59 | let subs = chooseFilterSubscriptions(subscriptions);
60 | assert.ok(subs);
61 | let sub1 = subs.find(sub => sub.type == "ads");
62 | assert.ok(sub1);
63 | assert.deepEqual(sub1.languages, ["en"]);
64 | });
65 | });
66 |
--------------------------------------------------------------------------------
/qunit/tests/uninstall.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | const {analytics} = require("../../adblockpluscore/lib/analytics");
21 | const {filterStorage} = require("../../adblockpluscore/lib/filterStorage");
22 | const {Prefs} = require("../../lib/prefs");
23 | const {setUninstallURL} = require("../../lib/uninstall");
24 |
25 | const realSetUninstallURL = browser.runtime.setUninstallURL;
26 |
27 | let uninstallURL;
28 | let urlParams = () => new URL(uninstallURL).search.substr(1).split("&");
29 |
30 | QUnit.module("Uninstall URL", hooks =>
31 | {
32 | hooks.beforeEach(assert =>
33 | {
34 | browser.runtime.setUninstallURL = url => uninstallURL = url;
35 | assert.ok(true);
36 | });
37 | hooks.afterEach(assert =>
38 | {
39 | browser.runtime.setUninstalLURL = realSetUninstallURL;
40 | assert.ok(true);
41 | });
42 |
43 | QUnit.test("parameters in uninstall URL", assert =>
44 | {
45 | const info = require("info");
46 | const expectedParams = [
47 | ["an", info.addonName],
48 | ["av", info.addonVersion],
49 | ["ap", info.application],
50 | ["apv", info.applicationVersion],
51 | ["p", info.platform],
52 | ["fv", analytics.getFirstVersion()],
53 | ["pv", info.platformVersion],
54 | ["ndc", "0"],
55 | ["c", "0"],
56 | ["s", "3"]
57 | ];
58 | setUninstallURL();
59 |
60 | let params = urlParams();
61 | for (let [name, value] of expectedParams)
62 | {
63 | value = encodeURIComponent(value);
64 | assert.ok(
65 | params.includes(`${name}=${value}`),
66 | `The parameter '${name}' has the expected value '${value}'`
67 | );
68 | }
69 | });
70 |
71 | QUnit.test("uninstall URL length", assert =>
72 | {
73 | const maxLength = 255;
74 | setUninstallURL();
75 | assert.ok(
76 | uninstallURL.length <= maxLength,
77 | `uninstall URL is not longer than ${maxLength} characters`
78 | );
79 | });
80 |
81 | let initialSubscriptions;
82 |
83 | QUnit.module("subscription parameter", {
84 | beforeEach()
85 | {
86 | browser.runtime.setUninstallURL = url => uninstallURL = url;
87 | initialSubscriptions = Array.from(filterStorage.subscriptions());
88 | },
89 | afterEach()
90 | {
91 | for (let subscription of initialSubscriptions)
92 | filterStorage.addSubscription(subscription);
93 | browser.runtime.setUninstalLURL = realSetUninstallURL;
94 | }
95 | });
96 |
97 | QUnit.test("parameter s=0", assert =>
98 | {
99 | for (let subscription of initialSubscriptions)
100 | filterStorage.removeSubscription(subscription);
101 | setUninstallURL();
102 | assert.ok(
103 | urlParams().includes("s=0"),
104 | "subscription parameter 's' has the expected value '0'"
105 | );
106 | });
107 |
108 | QUnit.test("parameter s=1", assert =>
109 | {
110 | for (let subscription of initialSubscriptions)
111 | {
112 | if (subscription.type != "ads")
113 | filterStorage.removeSubscription(subscription);
114 | }
115 | setUninstallURL();
116 | assert.ok(
117 | urlParams().includes("s=1"),
118 | "subscription parameter 's' has the expected value '1'" + urlParams()
119 | );
120 | });
121 |
122 | QUnit.test("parameter s=2", assert =>
123 | {
124 | for (let subscription of initialSubscriptions)
125 | {
126 | if (subscription.url != Prefs.subscriptions_exceptionsurl)
127 | filterStorage.removeSubscription(subscription);
128 | }
129 | setUninstallURL();
130 | assert.ok(
131 | urlParams().includes("s=2"),
132 | "subscription parameter 's' has the expected value '2'" + urlParams()
133 | );
134 | });
135 | });
136 |
--------------------------------------------------------------------------------
/qunit/tests/url.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | const {extractHostFromFrame} = require("../../lib/url");
21 |
22 | QUnit.module("URL/host tools", () =>
23 | {
24 | QUnit.test("Extracting hostname from frame", assert =>
25 | {
26 | function testFrameHostname(hierarchy, expectedHostname, message)
27 | {
28 | let frame = null;
29 |
30 | for (let url of hierarchy)
31 | frame = {parent: frame, url: new URL(url)};
32 |
33 | assert.equal(extractHostFromFrame(frame), expectedHostname, message);
34 | }
35 |
36 | testFrameHostname(["http://example.com/"], "example.com", "single frame");
37 | testFrameHostname(["http://example.com/", "http://example.org/"],
38 | "example.org", "with parent frame");
39 | testFrameHostname(["http://example.com/", "data:text/plain,foo"],
40 | "example.com", "data: URL, hostname in parent");
41 | testFrameHostname(["http://example.com/", "about:blank", "about:blank"],
42 | "example.com", "about:blank, hostname in ancestor");
43 | testFrameHostname(["about:blank", "about:blank"], "",
44 | "about:blank, no hostname");
45 | testFrameHostname(["http://xn--f-1gaa.com/"], "xn--f-1gaa.com",
46 | "with punycode");
47 | testFrameHostname(["http://user:password@example.com/"], "example.com",
48 | "with auth credentials");
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/subscriptionLink.postload.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | if (document instanceof HTMLDocument)
21 | {
22 | document.addEventListener("click", event =>
23 | {
24 | // Ignore right-clicks
25 | if (event.button == 2)
26 | return;
27 |
28 | // Ignore simulated clicks.
29 | if (event.isTrusted == false)
30 | return;
31 |
32 | // Search the link associated with the click
33 | let link = event.target;
34 | while (!(link instanceof HTMLAnchorElement))
35 | {
36 | link = link.parentNode;
37 |
38 | if (!link)
39 | return;
40 | }
41 |
42 | let queryString = null;
43 | if (link.protocol == "http:" || link.protocol == "https:")
44 | {
45 | if (link.host == "subscribe.adblockplus.org" && link.pathname == "/")
46 | queryString = link.search.substr(1);
47 | }
48 | else
49 | {
50 | // Firefox 51 doesn't seem to populate the "search" property for
51 | // links with non-standard URL schemes so we need to extract the query
52 | // string manually.
53 | let match = /^abp:\/*subscribe\/*\?(.*)/i.exec(link.href);
54 | if (match)
55 | queryString = match[1];
56 | }
57 |
58 | if (!queryString)
59 | return;
60 |
61 | // This is our link - make sure the browser doesn't handle it
62 | event.preventDefault();
63 | event.stopPropagation();
64 |
65 | // Decode URL parameters
66 | let title = null;
67 | let url = null;
68 | for (let param of queryString.split("&"))
69 | {
70 | let parts = param.split("=", 2);
71 | if (parts.length != 2 || !/\S/.test(parts[1]))
72 | continue;
73 | switch (parts[0])
74 | {
75 | case "title":
76 | title = decodeURIComponent(parts[1]);
77 | break;
78 | case "location":
79 | url = decodeURIComponent(parts[1]);
80 | break;
81 | }
82 | }
83 | if (!url)
84 | return;
85 |
86 | // Default title to the URL
87 | if (!title)
88 | title = url;
89 |
90 | // Trim spaces in title and URL
91 | title = title.trim();
92 | url = url.trim();
93 | if (!/^(https?|ftp):/.test(url))
94 | return;
95 |
96 | browser.runtime.sendMessage({
97 | type: "subscriptions.add",
98 | title,
99 | url,
100 | confirm: true
101 | });
102 | }, true);
103 | }
104 |
--------------------------------------------------------------------------------
/test/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "node": true,
4 | "mocha": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/test/bin/downloadBrowsers.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import path from "path";
19 | import {loadModules} from "../misc/utils.mjs";
20 |
21 | (async() =>
22 | {
23 | for (let [module] of await loadModules(path.join("test", "browsers")))
24 | {
25 | if (!module.ensureBrowser)
26 | continue;
27 |
28 | for (let version of [module.oldestCompatibleVersion,
29 | module.getLatestVersion()])
30 | {
31 | try
32 | {
33 | let binary = await module.ensureBrowser(await version);
34 | if (module.ensureDriver)
35 | await module.ensureDriver(binary);
36 | }
37 | catch (e)
38 | {
39 | console.warn(e);
40 | }
41 | }
42 | }
43 | })();
44 |
--------------------------------------------------------------------------------
/test/browsers/chromium.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import webdriver from "selenium-webdriver";
19 | import chrome from "selenium-webdriver/chrome.js";
20 | import got from "got";
21 | import path from "path";
22 | import {exec, execFile} from "child_process";
23 | import {promisify} from "util";
24 |
25 | export {ensureChromium as ensureBrowser}
26 | from "../../adblockpluscore/test/runners/chromium_download.mjs";
27 |
28 | // We need to require the chromedriver,
29 | // otherwise on Windows the chromedriver path is not added to process.env.PATH.
30 | import "chromedriver";
31 |
32 | export let target = "chrome";
33 |
34 | // The Chromium version is a build number, quite obscure.
35 | // Chromium 63.0.3239.x is 508578
36 | // Chromium 65.0.3325.0 is 530368
37 | // We currently want Chromiun 63, as we still support it and that's the
38 | // loweset version that supports WebDriver.
39 | export let oldestCompatibleVersion = 508578;
40 | const OLDEST_DRIVER_VERSION = "2.36"; // Chromium 63
41 |
42 | export async function ensureDriver(browserBinary)
43 | {
44 | let chromedriverPath =
45 | path.resolve("adblockpluscore", "chromium-snapshots", "chromedriver");
46 | let env = {...process.env, npm_config_chromedriver_skip_download: false,
47 | npm_config_tmp: chromedriverPath};
48 | if (browserBinary)
49 | {
50 | let browserVersion;
51 | if (process.platform == "win32")
52 | {
53 | let arg = `'${browserBinary.split("'").join("''")}'`;
54 | let command = `(Get-ItemProperty ${arg}).VersionInfo.ProductVersion`;
55 | let {stdout} = await promisify(exec)(command, {shell: "powershell.exe"});
56 | browserVersion = stdout.trim();
57 | }
58 | else
59 | {
60 | let {stdout} = await promisify(execFile)(browserBinary, ["--version"]);
61 | browserVersion = stdout.trim().replace(/.*\s/, "");
62 | }
63 |
64 | let majorBrowserVersion = parseInt(browserVersion.split(".")[0], 10);
65 | env.CHROMEDRIVER_VERSION = majorBrowserVersion >= 70 ?
66 | `LATEST_${browserVersion}` : OLDEST_DRIVER_VERSION;
67 | }
68 | else
69 | {
70 | env.DETECT_CHROMEDRIVER_VERSION = true;
71 | }
72 |
73 | await promisify(execFile)(
74 | process.execPath,
75 | [path.join("node_modules", "chromedriver", "install.js")],
76 | {env}
77 | );
78 | }
79 |
80 | export async function getDriver(browserBinary, extensionPaths, insecure)
81 | {
82 | await ensureDriver(browserBinary);
83 |
84 | let options = new chrome.Options()
85 | .addArguments("--no-sandbox", "--disable-gpu")
86 | .addArguments(`load-extension=${extensionPaths.join(",")}`);
87 |
88 | if (browserBinary != null)
89 | options.setChromeBinaryPath(browserBinary);
90 | if (insecure)
91 | options.addArguments("--ignore-certificate-errors");
92 |
93 | return new webdriver.Builder()
94 | .forBrowser("chrome")
95 | .setChromeOptions(options)
96 | .build();
97 | }
98 |
99 | export async function getLatestVersion()
100 | {
101 | let os = process.platform;
102 | if (os == "win32")
103 | os = process.arch == "x64" ? "win64" : "win";
104 | else if (os == "darwin")
105 | os = "mac";
106 |
107 | let data = await got(`https://omahaproxy.appspot.com/all.json?os=${os}`).json();
108 | let version = data[0].versions.find(ver => ver.channel == "stable");
109 | let base = version.branch_base_position;
110 |
111 | if (version.true_branch.includes("_"))
112 | {
113 | // A wrong base may be caused by a mini-branch (patched) release
114 | // In that case, the base is taken from the unpatched version
115 | let cv = version.current_version.split(".");
116 | let unpatched = `${cv[0]}.${cv[1]}.${cv[2]}.0`;
117 | let unpatchedVersion = await got(`https://omahaproxy.appspot.com/deps.json?version=${unpatched}`).json();
118 | base = unpatchedVersion.chromium_base_position;
119 | }
120 |
121 | return base;
122 | }
123 |
--------------------------------------------------------------------------------
/test/browsers/edge.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import webdriver from "selenium-webdriver";
19 | import msedgedriver from "msedgedriver";
20 | import fs from "fs";
21 | import path from "path";
22 | import {exec, execFile} from "child_process";
23 | import {promisify} from "util";
24 |
25 | export let target = "chrome";
26 |
27 | const MACOS_BINARY_PATH = "/Applications/Microsoft Edge.app" +
28 | "/Contents/MacOS/Microsoft Edge";
29 |
30 | export function isBrowserInstalled()
31 | {
32 | if (process.platform == "win32")
33 | return true;
34 | if (process.platform == "darwin")
35 | return fs.existsSync(MACOS_BINARY_PATH);
36 | return false;
37 | }
38 |
39 | async function ensureDriver(browserBinary)
40 | {
41 | let version;
42 | if (process.platform == "win32")
43 | {
44 | let arg = browserBinary ?
45 | `'${browserBinary.split("'").join("''")}'` :
46 | "${Env:ProgramFiles(x86)}\\Microsoft\\Edge\\Application\\msedge.exe";
47 | let command = `(Get-ItemProperty ${arg}).VersionInfo.ProductVersion`;
48 | let {stdout} = await promisify(exec)(command, {shell: "powershell.exe"});
49 | version = stdout.trim();
50 | }
51 | else
52 | {
53 | let binary = browserBinary || MACOS_BINARY_PATH;
54 | let {stdout} = await promisify(execFile)(binary, ["--version"]);
55 | version = stdout.trim().replace(/.*\s/, "");
56 | }
57 |
58 | await promisify(execFile)(
59 | process.execPath,
60 | [path.join("node_modules", "msedgedriver", "install.js")],
61 | {env: {...process.env, EDGECHROMIUMDRIVER_VERSION: version,
62 | npm_config_edgechromiumdriver_skip_download: false}}
63 | );
64 | }
65 |
66 | export async function getDriver(browserBinary, extensionPaths, insecure)
67 | {
68 | await ensureDriver(browserBinary);
69 | await msedgedriver.start(["--silent"], true); // Starts on localhost:9515
70 |
71 | let options = {
72 | args: ["--no-sandbox", "--disable-partial-raster",
73 | `load-extension=${extensionPaths.join(",")}`]
74 | };
75 | if (browserBinary)
76 | options.binary = browserBinary;
77 |
78 | return new webdriver.Builder()
79 | .forBrowser("MicrosoftEdge")
80 | .withCapabilities({
81 | "browserName": "MicrosoftEdge",
82 | "ms:edgeChromium": true,
83 | "ms:edgeOptions": options,
84 | "acceptInsecureCerts": insecure
85 | })
86 | .usingServer("http://localhost:9515")
87 | .build();
88 | }
89 |
90 | export function shutdown()
91 | {
92 | msedgedriver.stop();
93 | }
94 |
--------------------------------------------------------------------------------
/test/browsers/firefox.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import webdriver from "selenium-webdriver";
19 | import firefox from "selenium-webdriver/firefox.js";
20 | import command from "selenium-webdriver/lib/command.js";
21 | import got from "got";
22 |
23 | export {ensureFirefox as ensureBrowser}
24 | from "../../adblockpluscore/test/runners/firefox_download.mjs";
25 |
26 | // We need to require the geckodriver,
27 | // otherwise on Windows the geckodriver path is not added to process.env.PATH.
28 | import "geckodriver";
29 |
30 | export let target = "firefox";
31 | export let oldestCompatibleVersion = "57.0";
32 |
33 | export async function getDriver(browserBinary, extensionPaths, insecure)
34 | {
35 | let options = new firefox.Options().headless();
36 | if (browserBinary != null)
37 | options.setBinary(browserBinary);
38 | if (insecure)
39 | options.set("acceptInsecureCerts", true);
40 |
41 | let driver = new webdriver.Builder()
42 | .forBrowser("firefox")
43 | .setFirefoxOptions(options)
44 | .build();
45 |
46 | for (let extensionPath of extensionPaths)
47 | {
48 | await driver.execute(
49 | new command.Command("install addon")
50 | .setParameter("path", extensionPath)
51 | .setParameter("temporary", true)
52 | );
53 | }
54 |
55 | return driver;
56 | }
57 |
58 | export async function getLatestVersion()
59 | {
60 | let data = await got("https://product-details.mozilla.org/1.0/firefox_versions.json").json();
61 | return data.LATEST_FIREFOX_VERSION;
62 | }
63 |
--------------------------------------------------------------------------------
/test/entrypoint.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | const TEST_PAGES_URL = process.env.TEST_PAGES_URL ||
19 | "https://testpages.adblockplus.org/en/";
20 | const TEST_PAGES_INSECURE = process.env.TEST_PAGES_INSECURE == "true";
21 |
22 | import path from "path";
23 | import url from "url";
24 | import {exec} from "child_process";
25 | import {promisify} from "util";
26 | import got from "got";
27 | import {checkLastError, loadModules} from "./misc/utils.mjs";
28 |
29 | function getBrowserBinaries(module, browser)
30 | {
31 | let spec = process.env[`${browser.toUpperCase()}_BINARY`];
32 | if (spec)
33 | {
34 | if (spec == "installed")
35 | return [{getPath: () => Promise.resolve(null)}];
36 | if (spec.startsWith("path:"))
37 | return [{getPath: () => Promise.resolve(spec.substr(5))}];
38 | if (spec.startsWith("download:"))
39 | {
40 | if (module.ensureBrowser)
41 | return [{getPath: () => module.ensureBrowser(spec.substr(9))}];
42 | console.warn(`WARNING: Downloading ${browser} is not supported`);
43 | }
44 | }
45 |
46 | if (!module.ensureBrowser)
47 | {
48 | if (module.isBrowserInstalled())
49 | return [{getPath: () => Promise.resolve(null)}];
50 | return [];
51 | }
52 |
53 | return [
54 | {
55 | version: "oldest",
56 | getPath: () => module.ensureBrowser(module.oldestCompatibleVersion)
57 | },
58 | {
59 | version: "latest",
60 | getPath: () => module.getLatestVersion().then(module.ensureBrowser)
61 | }
62 | ];
63 | }
64 |
65 | async function createDevenv(target)
66 | {
67 | if (process.env.SKIP_BUILD != "true")
68 | await promisify(exec)(`gulp devenv -t ${target}`);
69 | }
70 |
71 | async function getDriver(binary, devenvCreated, module)
72 | {
73 | let extensionPaths = [
74 | path.resolve(`./devenv.${module.target}`),
75 | path.resolve("test", "helper-extension")
76 | ];
77 | let [browserBin] = await Promise.all([binary.getPath(), devenvCreated]);
78 | return module.getDriver(browserBin, extensionPaths, TEST_PAGES_INSECURE);
79 | }
80 |
81 | async function waitForExtension(driver)
82 | {
83 | let origin;
84 | let handle;
85 | await driver.wait(async() =>
86 | {
87 | for (handle of await driver.getAllWindowHandles())
88 | {
89 | try
90 | {
91 | await driver.switchTo().window(handle);
92 | origin = await driver.executeAsyncScript(`
93 | let callback = arguments[arguments.length - 1];
94 | (async() =>
95 | {
96 | if (typeof browser != "undefined")
97 | {
98 | let info = await browser.management.getSelf();
99 | if (info.optionsUrl == location.href)
100 | {
101 | callback(location.origin);
102 | return;
103 | }
104 | }
105 | callback(null);
106 | })();`);
107 | }
108 | catch (ex)
109 | {
110 | // Ignore windows that we're unable to switch to
111 | // or execute our script in
112 | }
113 |
114 | if (origin)
115 | return true;
116 | }
117 | return false;
118 | }, 5000, "options page not found");
119 |
120 | return [handle, origin];
121 | }
122 |
123 | async function getPageTests()
124 | {
125 | let options = TEST_PAGES_INSECURE ? {rejectUnauthorized: false} : {};
126 | let response;
127 |
128 | try
129 | {
130 | response = await got(TEST_PAGES_URL, options);
131 | }
132 | catch (e)
133 | {
134 | console.warn(`Warning: Test pages not parsed at "${TEST_PAGES_URL}"\n${e}`);
135 | return [];
136 | }
137 |
138 | let regexp = /"test-link" href="(.*?)"[\S\s]*?>(?:)?(.*?)
154 | {
155 | let pageTests = await getPageTests();
156 | let browsers = await loadModules(path.join("test", "browsers"));
157 | let suites = await loadModules(path.join("test", "suites"));
158 |
159 | for (let [module, browser] of browsers)
160 | {
161 | let devenvCreated = null;
162 | for (let binary of getBrowserBinaries(module, browser))
163 | {
164 | let description = browser.replace(/./, c => c.toUpperCase());
165 | if (binary.version)
166 | description += ` (${binary.version})`;
167 |
168 | describe(description, function()
169 | {
170 | this.timeout(0);
171 | this.pageTests = pageTests;
172 | this.testPagesURL = TEST_PAGES_URL;
173 |
174 | before(async function()
175 | {
176 | if (!devenvCreated)
177 | devenvCreated = createDevenv(module.target);
178 |
179 | this.driver = await getDriver(binary, devenvCreated, module);
180 |
181 | let caps = await this.driver.getCapabilities();
182 | this.browserName = caps.getBrowserName();
183 | this.browserVersion = caps.getBrowserVersion() || caps.get("version");
184 | // eslint-disable-next-line no-console
185 | console.log(`Browser: ${this.browserName} ${this.browserVersion}`);
186 |
187 | [this.extensionHandle,
188 | this.extensionOrigin] = await waitForExtension(this.driver);
189 | });
190 |
191 | beforeEach(async function()
192 | {
193 | let handles = await this.driver.getAllWindowHandles();
194 | let defaultHandle = handles.shift();
195 |
196 | for (let handle of handles)
197 | {
198 | if (handle != this.extensionHandle)
199 | {
200 | try
201 | {
202 | await this.driver.switchTo().window(handle);
203 | await this.driver.close();
204 | }
205 | catch (e) {}
206 | }
207 | }
208 |
209 | await this.driver.switchTo().window(defaultHandle);
210 | });
211 |
212 | it("extension loaded without errors", async function()
213 | {
214 | await checkLastError(this.driver, this.extensionHandle);
215 | });
216 |
217 | for (let [{default: defineSuite}] of suites)
218 | defineSuite();
219 |
220 | after(async function()
221 | {
222 | if (this.driver)
223 | await this.driver.quit();
224 |
225 | if (module.shutdown)
226 | module.shutdown();
227 | });
228 | });
229 | }
230 | }
231 | run();
232 | })();
233 |
--------------------------------------------------------------------------------
/test/helper-extension/background.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2020-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | "use strict";
19 |
20 | // The timeout allows the APB extension being ready on Firefox
21 | setTimeout(() =>
22 | {
23 | chrome.management.getAll(extensions =>
24 | {
25 | for (let extension of extensions)
26 | {
27 | if (extension.type == "extension" &&
28 | extension.installType == "development" &&
29 | extension.id != chrome.runtime.id &&
30 | extension.name != "Chrome Automation Extension")
31 | {
32 | chrome.tabs.create({url: extension.optionsUrl});
33 | return;
34 | }
35 | }
36 | });
37 | }, 1000);
38 |
--------------------------------------------------------------------------------
/test/helper-extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Helper extension",
3 | "version": "0.0.1",
4 | "manifest_version": 2,
5 | "description": "Opens the ABP options page",
6 | "background": {
7 | "scripts": ["background.js"]
8 | },
9 | "permissions": ["management"]
10 | }
11 |
--------------------------------------------------------------------------------
/test/misc/utils.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import assert from "assert";
19 | import fs from "fs";
20 | import path from "path";
21 | import url from "url";
22 |
23 | export async function checkLastError(driver, handle)
24 | {
25 | await driver.switchTo().window(handle);
26 |
27 | let error = await driver.executeAsyncScript(`
28 | let callback = arguments[arguments.length - 1];
29 | browser.runtime.sendMessage({type: "debug.getLastError"}).then(callback);`);
30 |
31 | if (error != null)
32 | assert.fail("Unhandled error in background page: " + error);
33 | }
34 |
35 | export async function runWithHandle(driver, handle, callback)
36 | {
37 | let currentHandle = await driver.getWindowHandle();
38 | await driver.switchTo().window(handle);
39 | try
40 | {
41 | return await callback();
42 | }
43 | finally
44 | {
45 | await driver.switchTo().window(currentHandle);
46 | }
47 | }
48 |
49 | export async function loadModules(dirname)
50 | {
51 | let modules = [];
52 | for (let dirent of await fs.promises.readdir(dirname, {withFileTypes: true}))
53 | {
54 | let filename = path.resolve(dirname, dirent.name);
55 | let basename = path.parse(dirent.name).name;
56 | if (dirent.isDirectory())
57 | filename = path.join(filename, "index.mjs");
58 | modules.push([await import(url.pathToFileURL(filename)), basename]);
59 | }
60 | return modules;
61 | }
62 |
--------------------------------------------------------------------------------
/test/suites/pages/index.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import assert from "assert";
19 | import webdriver from "selenium-webdriver";
20 | import {checkLastError, runWithHandle} from "../../misc/utils.mjs";
21 | import specializedTests from "./specialized.mjs";
22 | import defineSubscribeTest from "./subscribe.mjs";
23 | import defineUninstallTest from "./uninstall.mjs";
24 | import {getExpectedScreenshot, runFirstTest,
25 | getPage, isExcluded, runGenericTests} from "./utils.mjs";
26 |
27 | const {By} = webdriver;
28 |
29 | async function getFilters(driver)
30 | {
31 | let filters = new Set();
32 | for (let element of await driver.findElements(By.css("pre")))
33 | {
34 | for (let line of (await element.getText()).split("\n"))
35 | filters.add(line);
36 | }
37 | return Array.from(filters).join("\n");
38 | }
39 |
40 | async function updateFilters(driver, extensionHandle, url)
41 | {
42 | await driver.navigate().to(url);
43 | let filters = await getFilters(driver);
44 | let error = await runWithHandle(driver, extensionHandle,
45 | () => driver.executeAsyncScript(`
46 | let filters = arguments[0];
47 | let callback = arguments[arguments.length - 1];
48 | browser.runtime.sendMessage({type: "subscriptions.get",
49 | downloadable: true,
50 | special: true}).then(subs =>
51 | Promise.all(subs.map(subscription =>
52 | browser.runtime.sendMessage({type: "subscriptions.remove",
53 | url: subscription.url})
54 | ))
55 | ).then(() =>
56 | browser.runtime.sendMessage({type: "filters.importRaw",
57 | text: filters})
58 | ).then(errors => callback(errors[0]), callback);`, filters));
59 |
60 | if (error)
61 | throw error;
62 |
63 | await driver.navigate().refresh();
64 | }
65 |
66 | export default () =>
67 | {
68 | describe("Test pages", () =>
69 | {
70 | it("discovered filter test cases", function()
71 | {
72 | assert.ok(this.test.parent.parent.pageTests.length > 0);
73 | });
74 |
75 | defineSubscribeTest();
76 |
77 | describe("Filters", function()
78 | {
79 | for (let [url, pageTitle] of this.parent.parent.pageTests)
80 | {
81 | it(pageTitle, async function()
82 | {
83 | let page = getPage(url);
84 | if (isExcluded(page, this.browserName, this.browserVersion))
85 | this.skip();
86 |
87 | if (page in specializedTests)
88 | {
89 | await updateFilters(this.driver, this.extensionHandle, url);
90 | let locator = By.className("testcase-area");
91 | for (let element of await this.driver.findElements(locator))
92 | await specializedTests[page].run(element, this.extensionHandle);
93 | }
94 | else
95 | {
96 | let expectedScreenshot = await getExpectedScreenshot(this.driver,
97 | url);
98 | await updateFilters(this.driver, this.extensionHandle, url);
99 | await runGenericTests(this.driver, expectedScreenshot,
100 | this.browserName, this.browserVersion,
101 | pageTitle, url);
102 | }
103 |
104 | await checkLastError(this.driver, this.extensionHandle);
105 | });
106 | }
107 | });
108 |
109 | describe("Final checks", () =>
110 | {
111 | it("does not block unfiltered content", async function()
112 | {
113 | await assert.rejects(
114 | runFirstTest(this.driver, this.browserName, this.browserVersion,
115 | this.test.parent.parent.parent.pageTests,
116 | this.test.title, false),
117 | /Screenshots don't match/
118 | );
119 | });
120 |
121 | defineUninstallTest();
122 | });
123 | });
124 | };
125 |
--------------------------------------------------------------------------------
/test/suites/pages/specialized.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import assert from "assert";
19 | import webdriver from "selenium-webdriver";
20 | import {runWithHandle} from "../../misc/utils.mjs";
21 |
22 | const {By} = webdriver;
23 |
24 | let specialized = {};
25 |
26 | function clickButtonOrLink(element)
27 | {
28 | return element.findElement(By.css("a[href],button")).click();
29 | }
30 |
31 | async function checkRequestBlocked(driver, resource)
32 | {
33 | let removeTimestamp = s => s.replace(/\?.\d*/, "");
34 |
35 | await driver.wait(async() =>
36 | {
37 | let logs = await driver.manage().logs().get("browser");
38 | let expected =
39 | `${resource} - Failed to load resource: net::ERR_BLOCKED_BY_CLIENT`;
40 | return logs.some(l => removeTimestamp(l.message).includes(expected));
41 | }, 2000, "request wasn't blocked");
42 | }
43 |
44 | async function checkPing(element)
45 | {
46 | let driver = element.getDriver();
47 | await clickButtonOrLink(element);
48 | await checkRequestBlocked(driver, "ping");
49 | }
50 |
51 | specialized["filters/ping"] = {
52 | // ping test needs access to browser logs
53 | // https://github.com/mozilla/geckodriver/issues/284
54 | excludedBrowsers: {firefox: ""},
55 |
56 | run: checkPing
57 | };
58 |
59 | specialized["exceptions/ping"] = {
60 | excludedBrowsers: {firefox: ""},
61 |
62 | async run(element)
63 | {
64 | await assert.rejects(async() => checkPing(element), /request wasn't blocked/);
65 | }
66 | };
67 |
68 | async function getNumberOfHandles(driver)
69 | {
70 | return (await driver.getAllWindowHandles()).length;
71 | }
72 |
73 | async function checkPopup(element, extensionHandle)
74 | {
75 | let driver = element.getDriver();
76 | let nHandles = await getNumberOfHandles(driver);
77 | let token = Math.floor(Math.random() * 1e8);
78 | await runWithHandle(driver, extensionHandle, () => driver.executeScript(`
79 | self.tabCreated${token} = new Promise(resolve =>
80 | {
81 | browser.tabs.onCreated.addListener(function listener()
82 | {
83 | browser.tabs.onCreated.removeListener(listener);
84 | resolve();
85 | });
86 | });`));
87 | await clickButtonOrLink(element);
88 | await runWithHandle(driver, extensionHandle, () => driver.executeAsyncScript(`
89 | let callback = arguments[arguments.length - 1];
90 | self.tabCreated${token}.then(callback);`));
91 | await driver.sleep(1000);
92 | return await getNumberOfHandles(driver) > nHandles;
93 | }
94 |
95 | specialized["filters/popup"] = {
96 | async run(element, extensionHandle)
97 | {
98 | let hasPopup = await checkPopup(element, extensionHandle);
99 | assert.ok(!hasPopup, "popup was closed");
100 | }
101 | };
102 |
103 | specialized["exceptions/popup"] = {
104 | async run(element, extensionHandle)
105 | {
106 | let hasPopup = await checkPopup(element, extensionHandle);
107 | assert.ok(hasPopup, "popup remained open");
108 | }
109 | };
110 |
111 | specialized["filters/other"] = {
112 | // other test needs access to browser logs
113 | // https://github.com/mozilla/geckodriver/issues/284
114 | excludedBrowsers: {firefox: ""},
115 |
116 | async run(element)
117 | {
118 | let driver = element.getDriver();
119 | await checkRequestBlocked(driver, "other/image.png");
120 | }
121 | };
122 |
123 | export {specialized as default};
124 |
--------------------------------------------------------------------------------
/test/suites/pages/subscribe.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import assert from "assert";
19 | import webdriver from "selenium-webdriver";
20 | import {checkLastError} from "../../misc/utils.mjs";
21 | import {runFirstTest, takeScreenshot, writeScreenshotFile} from "./utils.mjs";
22 |
23 | const {By} = webdriver;
24 |
25 | async function addSubscription(driver, extensionHandle)
26 | {
27 | await driver.switchTo().window((await driver.getAllWindowHandles())[0]);
28 | await driver.findElement(By.id("subscribe-button")).click();
29 |
30 | await driver.switchTo().window(extensionHandle);
31 | await driver.switchTo().frame(0);
32 | let dialog = driver.findElement(By.id("dialog-content-predefined"));
33 | await driver.wait(async() =>
34 | {
35 | let [displayed, title] = await Promise.all([
36 | dialog.isDisplayed(),
37 | dialog.findElement(By.css(".title span")).getText()
38 | ]);
39 | return displayed && title == "ABP Testcase Subscription";
40 | }, 2000, "subscribe dialog not shown");
41 | await dialog.findElement(By.css(".default-focus")).click();
42 | }
43 |
44 | async function checkSubscriptionAdded(driver, url)
45 | {
46 | let [added, err] = await driver.executeAsyncScript(`
47 | let callback = arguments[arguments.length - 1];
48 | browser.runtime.sendMessage({type: "subscriptions.get",
49 | ignoreDisabled: true,
50 | downloadable: true}).then(subs =>
51 | subs.some(s =>
52 | s.url == "${url}abp-testcase-subscription.txt"
53 | )
54 | ).then(
55 | res => callback([res, null]),
56 | err => callback([null, err])
57 | );`);
58 | if (err)
59 | throw err;
60 | assert.ok(added, "subscription added");
61 | }
62 |
63 | export default () =>
64 | {
65 | it("subscribes to a link", async function()
66 | {
67 | let {testPagesURL} = this.test.parent.parent;
68 | try
69 | {
70 | await this.driver.navigate().to(testPagesURL);
71 | await this.driver.wait(async() =>
72 | {
73 | await addSubscription(this.driver, this.extensionHandle);
74 | return true;
75 | }, 4000);
76 | await checkSubscriptionAdded(this.driver, testPagesURL);
77 | }
78 | catch (e)
79 | {
80 | let screenshot = await takeScreenshot(this.driver);
81 | let scrPath = await writeScreenshotFile(screenshot, this.browserName,
82 | this.browserVersion,
83 | this.test.title, "actual");
84 | throw new Error(`${e.message}\n${testPagesURL}\n(see ${scrPath})`);
85 | }
86 | await this.driver.switchTo().window(
87 | (await this.driver.getAllWindowHandles())[0]
88 | );
89 | await runFirstTest(this.driver, this.browserName, this.browserVersion,
90 | this.test.parent.parent.pageTests, this.test.title);
91 | await checkLastError(this.driver, this.extensionHandle);
92 | });
93 | };
94 |
--------------------------------------------------------------------------------
/test/suites/pages/uninstall.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import {runWithHandle} from "../../misc/utils.mjs";
19 |
20 | export default () =>
21 | {
22 | it("opens uninstall page when extension is uninstalled", async function()
23 | {
24 | await runWithHandle(this.driver, this.extensionHandle, () =>
25 | this.driver.executeScript("browser.management.uninstallSelf();")
26 | );
27 |
28 | await this.driver.wait(
29 | async() =>
30 | {
31 | for (let handle of await this.driver.getAllWindowHandles())
32 | {
33 | await this.driver.switchTo().window(handle);
34 | let url = await this.driver.getCurrentUrl();
35 | if (url.startsWith("https://adblockplus.org/en/uninstalled"))
36 | return true;
37 | }
38 | return false;
39 | },
40 | 2000,
41 | "uninstall page did not open"
42 | );
43 | });
44 | };
45 |
--------------------------------------------------------------------------------
/test/suites/pages/utils.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import path from "path";
19 | import Jimp from "jimp";
20 | import semver from "semver";
21 | import specializedTests from "./specialized.mjs";
22 |
23 | const SCREENSHOT_DIR = path.join("test", "screenshots");
24 |
25 | export async function takeScreenshot(driver)
26 | {
27 | // On macOS scrollbars appear and disappear overlapping
28 | // the content as scrolling occurs. So we have to hide
29 | // the scrollbars to get reproducible screenshots.
30 | await driver.executeScript(`
31 | let style = document.createElement("style");
32 | style.textContent = "html { overflow-y: scroll; }"
33 | document.head.appendChild(style);
34 | if (document.documentElement.clientWidth == window.innerWidth)
35 | style.textContent = "html::-webkit-scrollbar { display: none; }";
36 | else
37 | document.head.removeChild(style);`);
38 |
39 | let fullScreenshot = new Jimp(0, 0);
40 | while (true)
41 | {
42 | let [width, height, offset] = await driver.executeScript(`
43 | window.scrollTo(0, arguments[0]);
44 | return [document.documentElement.clientWidth,
45 | document.documentElement.scrollHeight,
46 | window.scrollY];`, fullScreenshot.bitmap.height);
47 | let data = await driver.takeScreenshot();
48 | let partialScreenshot = await Jimp.read(Buffer.from(data, "base64"));
49 | let combinedScreenshot = new Jimp(width, offset +
50 | partialScreenshot.bitmap.height);
51 | combinedScreenshot.composite(fullScreenshot, 0, 0);
52 | combinedScreenshot.composite(partialScreenshot, 0, offset);
53 | fullScreenshot = combinedScreenshot;
54 |
55 | if (fullScreenshot.bitmap.height >= height)
56 | break;
57 | }
58 | return fullScreenshot;
59 | }
60 |
61 | export function isExcluded(page, browserName, browserVersion)
62 | {
63 | let excluded;
64 | if (page in specializedTests)
65 | excluded = specializedTests[page].excludedBrowsers;
66 | // Chromium 63 doesn't have user stylesheets (required to
67 | // overrule inline styles).
68 | else if (page == "circumvention/inline-style-important")
69 | excluded = {chrome: "<64"};
70 | // Older versions of Chromium don't run content
71 | // scripts in dynamically written documents.
72 | // Firefox <67 had a bug that resets the document:
73 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1528146
74 | else if (page == "circumvention/anoniframe-documentwrite")
75 | excluded = {chrome: "<64", firefox: "<67"};
76 | // shadowing requires Firefox 63+ or 59+ with flag
77 | // dom.webcomponents.shadowdom.enabled
78 | else if (page == "snippets/hide-if-shadow-contains")
79 | excluded = {firefox: "<63"};
80 |
81 | return !!excluded && browserName in excluded &&
82 | semver.satisfies(semver.coerce(browserVersion), excluded[browserName]);
83 | }
84 |
85 | export async function getExpectedScreenshot(driver, url)
86 | {
87 | await driver.navigate().to(`${url}?expected=1`);
88 | return await takeScreenshot(driver);
89 | }
90 |
91 | export async function writeScreenshotFile(image, browserName, browserVersion,
92 | testTitle, suffix)
93 | {
94 | let title = testTitle.toLowerCase().replace(/[^a-z0-9]+/g, "_");
95 | let filename = `${browserName}_${browserVersion}_${title}_${suffix}.png`;
96 | let screenshotPath = path.join(SCREENSHOT_DIR, filename);
97 | await image.write(screenshotPath);
98 | return screenshotPath;
99 | }
100 |
101 | export async function runGenericTests(driver, expectedScreenshot,
102 | browserName, browserVersion, testTitle,
103 | url, writeScreenshots = true)
104 | {
105 | let actualScreenshot;
106 |
107 | async function compareScreenshots()
108 | {
109 | await driver.wait(async() =>
110 | {
111 | actualScreenshot = await takeScreenshot(driver);
112 | let actualBitmap = actualScreenshot.bitmap;
113 | let expectedBitmap = expectedScreenshot.bitmap;
114 | return (actualBitmap.width == expectedBitmap.width &&
115 | actualBitmap.height == expectedBitmap.height &&
116 | actualBitmap.data.compare(expectedBitmap.data) == 0);
117 | }, 5000, "Screenshots don't match", 500);
118 | }
119 |
120 | try
121 | {
122 | try
123 | {
124 | await compareScreenshots();
125 | }
126 | catch (e2)
127 | {
128 | // In newer Firefox versions (79+) CSS might be cached:
129 | // https://bugzil.la/1657575.
130 | // We execute the test in a new tab to ensure the cache isn't used.
131 | try
132 | {
133 | await driver.switchTo().newWindow("tab");
134 | await driver.navigate().to(url);
135 | }
136 | catch (e3)
137 | {
138 | // Older browsers don't support `newWindow`, fall-back to just refresh.
139 | await driver.navigate().refresh();
140 | }
141 |
142 | await compareScreenshots();
143 | }
144 | }
145 | catch (e)
146 | {
147 | if (!writeScreenshots)
148 | throw e;
149 |
150 | let paths = [];
151 | for (let [suffix, image] of [["actual", actualScreenshot],
152 | ["expected", expectedScreenshot]])
153 | {
154 | paths.push(await writeScreenshotFile(image, browserName, browserVersion,
155 | testTitle, suffix));
156 | }
157 |
158 | throw new Error(`${e.message}\n${url}\n(see ${paths})`);
159 | }
160 | }
161 |
162 | export function getPage(url)
163 | {
164 | return url.substr(url.lastIndexOf("/", url.lastIndexOf("/") - 1) + 1);
165 | }
166 |
167 | export async function runFirstTest(driver, browserName, browserVersion,
168 | pageTests, testTitle,
169 | writeScreenshots = true)
170 | {
171 | for (let [url] of pageTests)
172 | {
173 | let page = getPage(url);
174 | if (!(isExcluded(page, browserName, browserVersion) ||
175 | page in specializedTests))
176 | {
177 | let expectedScreenshot = await getExpectedScreenshot(driver, url);
178 | await driver.navigate().to(url);
179 | await runGenericTests(driver, expectedScreenshot,
180 | browserName, browserVersion,
181 | testTitle, url, writeScreenshots);
182 | return;
183 | }
184 | }
185 | throw new Error("No generic test did run");
186 | }
187 |
--------------------------------------------------------------------------------
/test/suites/qunit.mjs:
--------------------------------------------------------------------------------
1 | /*
2 | * This file is part of Adblock Plus ,
3 | * Copyright (C) 2006-present eyeo GmbH
4 | *
5 | * Adblock Plus is free software: you can redistribute it and/or modify
6 | * it under the terms of the GNU General Public License version 3 as
7 | * published by the Free Software Foundation.
8 | *
9 | * Adblock Plus is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with Adblock Plus. If not, see .
16 | */
17 |
18 | import assert from "assert";
19 | import webdriver from "selenium-webdriver";
20 | import {checkLastError} from "../misc/utils.mjs";
21 |
22 | const {By, until} = webdriver;
23 |
24 | export default () =>
25 | {
26 | it("qunit", async function()
27 | {
28 | await this.driver.navigate().to(this.extensionOrigin + "/qunit/index.html");
29 | let elem = await this.driver.wait(
30 | until.elementLocated(By.id("qunit-testresult"))
31 | );
32 | await this.driver.wait(until.elementTextContains(elem, "tests completed"));
33 |
34 | let failures = await this.driver.findElements(
35 | By.css("#qunit-tests > .fail")
36 | );
37 | let failureDescriptions = await Promise.all(failures.map(async failure =>
38 | {
39 | let messages = await failure.findElements(
40 | By.css(".module-name, .test-name, .fail > .test-message")
41 | );
42 | return (await Promise.all(messages.map(e => e.getText()))).join(", ");
43 | }));
44 |
45 | if (failureDescriptions.length > 0)
46 | {
47 | failureDescriptions.unshift("");
48 | assert.fail(failureDescriptions.join("\n - "));
49 | }
50 |
51 | await checkLastError(this.driver, this.extensionHandle);
52 | });
53 | };
54 |
--------------------------------------------------------------------------------