1
0
mirror of https://github.com/kkapsner/CanvasBlocker synced 2024-11-01 02:48:44 +01:00

Compare commits

..

335 Commits

Author SHA1 Message Date
kkapsner
5a1a318b91 Use wrapped arguments in proxy function
Fixes #714
2024-08-21 17:23:16 +02:00
kkapsner
e549267085 Do not fail if navigator is not available 2024-08-21 17:23:16 +02:00
kkapsner
cee0f75293 Code simplification 2024-08-21 17:23:16 +02:00
kkapsner
91dd283b9a
New Czech translation 2024-08-20 17:51:45 +02:00
kkapsner
af5365b4eb Added iFrame tests 2024-08-15 16:58:24 +02:00
kkapsner
1b76df7cbc "removed iframe" method needs to provide a cleanup 2024-07-03 20:58:17 +02:00
kkapsner
474e929638 Update web-ext 2024-07-03 16:42:55 +02:00
kkapsner
642d518110
New Japanese translations 2024-07-03 16:39:45 +02:00
kkapsner
4f366ed86c
New translations 2024-04-17 00:35:54 +02:00
kkapsner
00e60074c3 Don't throw target.apply is not a function
For #705
2024-04-17 00:34:19 +02:00
kkapsner
3eedc7b7dc Only check for prototype loops when there is a prototype
For #705
2024-04-17 00:07:16 +02:00
kkapsner
bebcec2139 Version 1.11 2024-04-16 14:53:11 +02:00
kkapsner
5181a55071 change "Site specific" to "Site-specific" 2024-04-10 16:06:56 +02:00
kkapsner
a8e192fa9b change "non persistent" to "nonpersistent"
For #696
2024-04-09 14:04:51 +02:00
kkapsner
0f3141ee12
New translations
For #696
2024-04-09 14:03:06 +02:00
kkapsner
e9ce4668fe
New translations 2024-04-09 14:01:35 +02:00
kkapsner
acce01bfeb Mark default value in drop downs settings
For #696
2024-04-08 00:15:55 +02:00
kkapsner
d159769997 Added :changed tag
For #696
2024-04-08 00:12:46 +02:00
kkapsner
56401048d1 Added tag search 2024-04-08 00:09:37 +02:00
kkapsner
54c625cd26 Fix function tampering detection via prototype
For #619 and #685
2024-04-08 00:05:50 +02:00
kkapsner
7bb3f00b45 Update releaseNotes.txt 2024-04-08 00:04:53 +02:00
kkapsner
b8c6115603 Not using proxy for toString broke google
For #698, #699, #700, #701
2024-04-07 23:59:49 +02:00
kkapsner
dacc578e12 Alpha versions must not break with patches 2024-04-07 02:36:20 +02:00
kkapsner
825fa42141 Fix isPointInPath and isPointInStroke
Fixes #696
2024-04-07 01:53:01 +02:00
kkapsner
40a8012ab0 Version 1.10.1 2024-04-04 23:30:20 +02:00
kkapsner
ec95cbe11b Bump min version to 100
to avoid warnings in the upload process
2024-03-30 17:27:30 +01:00
kkapsner
36b54f3ab5 Update navigator test to include storage.quota 2024-03-30 13:58:09 +01:00
kkapsner
87790c9731 Remove facebook.com from the convenience preset
Fixes #563
2024-03-30 13:54:47 +01:00
kkapsner
200f6b31f3 Do not use proxy for Function.prototype.toString
Fixes #685
2024-03-29 16:37:07 +01:00
kkapsner
1d8bf95926 Added protection for navigator.storage.estimate()
Fixes #681
2024-03-28 16:53:01 +01:00
kkapsner
c6cf48c489 Display version
Fixes #687
2024-03-28 14:43:53 +01:00
kkapsner
9a3745b366 Whitelist also data URL blocking
Fixes #665
2024-03-28 14:40:08 +01:00
kkapsner
a18e3ba37d Disable cache when reloading in the browser action 2024-03-28 14:26:20 +01:00
kkapsner
645d0ac550 dictionary update 2024-03-28 14:23:31 +01:00
kkapsner
809c1270c5
New translations 2024-03-28 14:21:13 +01:00
kkapsner
a99a0615d0 Update dev dependencies 2024-03-28 14:20:01 +01:00
kkapsner
bc13a5e2a2 Fix typos 2024-03-28 14:18:37 +01:00
kkapsner
8176ac83dc
New translations
* New Spanish translations
* New German translations
* New Portuguese translations
* New Russian translations
* New Portuguese (Brazilian) translations
2024-03-28 14:07:25 +01:00
kkapsner
4a2079bf47 Add alpha version to updates.json during build 2024-02-17 00:07:11 +01:00
kkapsner
7229133c8d Update build tool for manifest version v3 2024-02-17 00:02:10 +01:00
kkapsner
8bdedfcb3d Update package-lock.json 2024-02-16 23:48:34 +01:00
kkapsner
ae40caed09 Update package-lock.json 2023-07-19 01:22:11 +02:00
kkapsner
053ae6725e remove crowdin.yml from build 2023-05-30 19:04:40 +02:00
kkapsner
2a6c564ed8 Remove usage of deprecated browser.extension.getURL 2023-05-30 19:04:02 +02:00
kkapsner
8a84dd09f3
New translations
Lithuanian
2023-05-30 13:50:52 +02:00
kkapsner
df039b0f3c Do not use UTC for alpha version 2023-05-30 13:48:25 +02:00
kkapsner
fc5cce23ea Increase bottom padding of options page 2023-05-30 13:42:30 +02:00
kkapsner
e9f5f710e6 Replace window.open with browser.tabs.create
Fixes #661
2023-05-30 00:20:54 +02:00
kkapsner
5df98e0cf5 Settings export page did not show whole content in Firefox for Android 2023-05-30 00:11:43 +02:00
kkapsner
6ea89b6318 Improve isMobile
Fixes #658
2023-05-29 21:41:42 +02:00
kkapsner
02dfa8bd1b browser.windows.onRemoved not available on Fenix
Fix #654
2023-05-29 21:39:02 +02:00
kkapsner
8dcfac442f Added eBay and facebook.com to the convenience preset
Fixes #563
2023-05-29 14:48:56 +02:00
kkapsner
27d8d61da6 Update release notes
For #656
2023-05-29 14:44:36 +02:00
kkapsner
fb1311a842 Added showPresetsOnInstallation
Fixes #656
2023-05-29 14:05:03 +02:00
kkapsner
114b109340 Enable multiple alpha versions per day 2023-04-19 14:48:47 +02:00
kkapsner
4ce2f98b10 Fix alpha version 2023-04-19 14:48:23 +02:00
kkapsner
2c5b00a55d Always protect about:blank
Fixes #652
2023-04-19 14:34:37 +02:00
kkapsner
d6916b013e Version 1.10 2023-04-19 14:24:41 +02:00
kkapsner
3b92824d0f Update web-ext to 7.6.1 2023-04-15 20:00:07 +02:00
kkapsner
d3558f0bd7 Update release notes
was missing change for screen API
2023-04-15 16:17:59 +02:00
kkapsner
d55921ba92 Added known issue to release notes 2023-04-15 16:07:34 +02:00
kkapsner
1ca13299ec New alpha version 2023-04-15 16:07:15 +02:00
kkapsner
53ce86a21f Update release notes 2023-04-07 17:42:16 +02:00
kkapsner
269574ae17 Respect RFP in offscreenToBlobCallback
Fixes #644
2023-04-07 17:35:57 +02:00
kkapsner
3b08fdaf9b Improve github issue template 2023-04-07 10:01:29 +02:00
kkapsner
e0e6926a74 Use correct tab in browserAction (private browsing)
Fixes #649
2023-04-07 09:22:03 +02:00
kkapsner
2ac1ec277d Fix typos with firefoxVersionRV 2023-03-25 11:36:01 +01:00
kkapsner
8e463a6164 Update web-ext to 7.6.0 2023-03-22 13:04:33 +01:00
kkapsner
2cefed00a7 Fix alpha building tool 2023-03-22 13:04:09 +01:00
kkapsner
7cd4b2e9fa Updated browser presets 2023-03-18 14:45:32 +01:00
kkapsner
8d3f489d11 Added {real Firefox version - rv} to Firefox navigator preset
Fixes #641
2023-03-18 14:44:44 +01:00
kkapsner
34bc1730a1 Added dhl.de to the convenience preset
Fixes #532
2023-03-14 20:08:14 +01:00
kkapsner
f900c25900
New translations
* New Italian translations
* New Korean translations
2023-03-14 16:50:19 +01:00
kkapsner
6b172a0a4c Update manifest.json for new manifest version 2023-03-14 16:49:37 +01:00
kkapsner
46eb608f4e Update package-lock.json 2023-03-14 16:43:20 +01:00
kkapsner
b46341de90 Update web-ext 2022-09-04 15:19:03 +02:00
kkapsner
91b7dcbb11 Update readme regarding built-in fingerprinting protection
Fixes #628
2022-09-04 15:15:50 +02:00
kkapsner
582a962d8d
New translations 2022-08-28 17:47:48 +02:00
kkapsner
d9c14d7eed Use public bounce for data-URL test
Fixes #615
2022-05-04 13:30:47 +02:00
kkapsner
4bd0c0c96c Added support for wildcards (*) in domains
Fixes #613
2022-04-26 17:08:28 +02:00
kkapsner
cfb09075eb Fetch errors in URL regular expressions creation #2
Fixes #602
2022-04-24 20:50:06 +02:00
kkapsner
4443d0a117 Improve issue template 2022-04-24 20:47:58 +02:00
kkapsner
bb71b10c58
Fixed typo in other languages 2022-04-22 10:12:17 +02:00
kkapsner
4d4cda678c Fix typo 2022-04-22 10:08:03 +02:00
kkapsner
6c6012edf4
Update descriptions in all languages
For #591 and #607
2022-04-21 21:24:00 +02:00
kkapsner
05f8f936e9 Update description of the rngs
Fixes #591
2022-04-21 21:11:44 +02:00
kkapsner
1a199a5049 Updated description of webGL vendor and renderer
Fixes #607
2022-04-21 17:18:27 +02:00
kkapsner
e5cba569ba Fix npm audit 2022-04-15 13:54:27 +02:00
kkapsner
fd09e3d5cf Use inner of top window for all outer computations
For #598
2022-04-04 18:10:16 +02:00
kkapsner
6aab7f68ea
New translations
* Czech
* Chinese Simplified
* Portuguese, Brazilian
* French
* Spanish
2022-03-30 16:48:33 +02:00
kkapsner
a92373f412 Update web-ext 2022-03-30 16:46:28 +02:00
kkapsner
b4cb52df15 Fix eslint warning 2022-03-17 23:05:46 +01:00
kkapsner
d100932236 Allow clicks in url change input
Fixes #600
2022-03-17 22:44:52 +01:00
CommanderRoot
bfa355b58d
Replace deprecated String.prototype.substr() 2022-02-21 19:46:02 +01:00
kkapsner
dc593daba8 Update eslint and web-ext 2022-02-20 23:31:23 +01:00
kkapsner
029933964f Version 1.9 2022-02-18 19:45:27 +01:00
kkapsner
ffd659c7bb Update release notes 2022-02-17 17:39:35 +01:00
kkapsner
f788cd4263
New translations 2022-02-17 17:19:05 +01:00
kkapsner
1bd87e6953 Added <option1|option2|...> syntax to webGL
Fixes #493
2022-02-15 19:53:58 +01:00
kkapsner
fb231a070b Added {random vendor} and {random renderer}
For #493
2022-02-14 17:37:54 +01:00
kkapsner
740b360485 Linting 2022-02-13 14:57:36 +01:00
kkapsner
3621fef625 Fix slight code problems in svg-test 2022-02-13 14:54:48 +01:00
kkapsner
3d603b84d4 Added notice when dom.webAudio.enabled is set to false
Fixes #592
2022-01-30 17:50:59 +01:00
kkapsner
d4fc7027eb Fetch errors in URL regular expressions creation
Fixes #581
2022-01-30 13:11:44 +01:00
kkapsner
4c364a9a72
New translations 2022-01-30 11:16:54 +01:00
kkapsner
863140c1ca Added anchors to FAQ 2022-01-30 11:00:00 +01:00
kkapsner
640bd36b86 Added SVG protection
Fixes #589 and fixes #590
2022-01-30 10:59:41 +01:00
kkapsner
734e76180f Added test for SVG
For #589 and #590
2022-01-30 10:05:52 +01:00
kkapsner
78183f9efc Version 1.8 2021-11-07 16:20:39 +01:00
kkapsner
f043acf41c Release preparation 2021-11-07 11:09:53 +01:00
kkapsner
892e4d2c34
New translations 2021-11-01 13:57:01 +01:00
kkapsner
d97864436d update release notes 2021-11-01 13:54:59 +01:00
kkapsner
7f154c6cc6 Do not use browser.extension.getURL 2021-11-01 13:18:38 +01:00
kkapsner
6f5cfc1080
New Italian translations 2021-09-09 17:42:29 +02:00
kkapsner
cca81c4006 Do not add CSP headers to 304 requests
Fixes #577
2021-09-09 17:40:18 +02:00
kkapsner
2f6ca07bba Added 304 test
For #577
2021-09-08 23:09:08 +02:00
kkapsner
8e4a881288 Close browserAction when a new page is opened
Fixes #576
2021-08-17 19:01:44 +02:00
kkapsner
709093fa4d Update web-ext 2021-07-04 09:28:57 +02:00
kkapsner
b9d5eb3d27 Version 1.7 2021-06-21 23:28:36 +02:00
kkapsner
e8ee1f8e9c Verion 1.6.1
Fixes #564
2021-06-21 22:01:09 +02:00
kkapsner
4ac02003de wrapped original funtion may gets destroyed before usage
For #564
2021-06-21 20:41:33 +02:00
kkapsner
cf0243d487 Version 1.7 2021-06-20 14:42:13 +02:00
spodermenpls
34f9dfa4fd
Status Icon revision
For #535
2021-06-14 22:25:22 +02:00
kkapsner
bce494e744 Improve whitelist button
For #535
2021-06-13 21:08:28 +02:00
kkapsner
87646a152a New alpha version 2021-06-12 01:04:47 +02:00
kkapsner
de14490574
New translations 2021-06-12 01:03:47 +02:00
kkapsner
13203a905d arrayFake did not correctly compute if values were faked
Fixes #531
2021-06-12 00:58:37 +02:00
kkapsner
277bef1227 Clean up white spaces 2021-06-12 00:50:05 +02:00
kkapsner
015350c385 Add reload button to browser action
For #535
2021-06-12 00:49:10 +02:00
kkapsner
cdfe72fada Use dedicated icons for the whitelist toggle
For #535
2021-06-12 00:04:17 +02:00
kkapsner
57bf81ec3a Cleanup icons 2021-06-12 00:03:22 +02:00
kkapsner
43ea01c178 Add whitelist type selection
Fixes #538
2021-06-08 23:42:19 +02:00
kkapsner
3722263a6f Documentation 2021-06-08 15:40:15 +02:00
kkapsner
020a9c5b9a Whitelisted block mode should be "allowEverything" 2021-06-08 15:31:37 +02:00
kkapsner
698fc02e66
New French translations 2021-06-08 13:25:58 +02:00
kkapsner
7ae6e14d0d
New translations 2021-06-07 21:33:31 +02:00
kkapsner
539ddf5e46 Update web-ext to 6.1.0 2021-06-07 21:30:26 +02:00
kkapsner
70a941f5aa Added FAQ for url specific settings
Fixes #550
2021-06-07 21:04:59 +02:00
kkapsner
51e76bafce New alpha version 2021-06-06 15:01:32 +02:00
kkapsner
8dddff85cc Cleanup 2021-06-06 14:43:46 +02:00
kkapsner
0d581403c1 toBlob is not a constructor 2021-05-26 08:19:43 +02:00
kkapsner
0930928df3 detectionTest: check function code also in an iFrame 2021-05-26 08:19:43 +02:00
kkapsner
a7d02efd09 Fix toString for proxies 2021-05-26 08:19:43 +02:00
kkapsner
aa3f9d878d detectionTest: made addTest asynchronous 2021-05-26 08:19:43 +02:00
kkapsner
211d6710f6 First proxy attempt 2021-05-26 08:19:43 +02:00
kkapsner
42b19a4ba5 Added status button in browser action to see and set the whitelist status
Fixes #535
2021-05-25 16:44:20 +02:00
kkapsner
16bef43945 Update npm modules for dev 2021-05-08 21:22:23 +02:00
kkapsner
b614b84b5f Set random supply if settings were preloaded
Fixes #544
2021-05-04 22:52:37 +02:00
kkapsner
bde9d3c012 Added FAQ for PayPal problem
Fixes #532
2021-03-12 13:43:51 +01:00
kkapsner
32af464c05 Improved whitelist inspection
Fixes #501
2021-03-06 22:52:52 +01:00
kkapsner
1e1f343f28 New alpha version 2021-03-06 13:56:11 +01:00
kkapsner
7cd4ecec44
New translations 2021-03-06 11:18:59 +01:00
kkapsner
4f5b9b5f78 Linting 2021-03-06 11:17:04 +01:00
kkapsner
be55cbf983 Added paypal.com to the convenience preset
For #532
2021-03-06 11:16:20 +01:00
kkapsner
872e633025 Convenience preset did not work properly 2021-03-06 11:10:18 +01:00
kkapsner
ee87773ce2 Limit periodical rnd clearing
Periodical persistent rnd clearing does not clear in active tabs.

Fixes #518
2021-02-21 11:49:29 +01:00
kkapsner
28dd7f6819
New translations 2021-01-26 21:56:37 +01:00
kkapsner
a5558b4144 Add button to reload extension if update pending
For #522
2021-01-26 21:45:15 +01:00
kkapsner
8e5986817e Update improvements
For #522
2021-01-26 13:47:11 +01:00
kkapsner
e48710eee9 Fix message canvasBlocker-unload
For #522
2021-01-26 13:34:51 +01:00
kkapsner
1b04da40c2 Update web-ext 2021-01-18 21:50:14 +01:00
kkapsner
afd426da58 Version 1.6 2021-01-18 21:42:12 +01:00
kkapsner
0d160143c1 Update release notes 2021-01-18 21:11:14 +01:00
kkapsner
b94de2b641
New translations
* new French translations
* new Italian translations
2021-01-18 20:37:47 +01:00
kkapsner
945c2716c9 Improved input faking performance further
fixes #517
2021-01-13 20:06:03 +01:00
kkapsner
1f1d7052a1 Fix bug in webGl parameter faking
Fixes #508
2021-01-03 22:23:46 +01:00
kkapsner
e2c5dfc06e added {disabled} to UNMASKED_VENDOR_WEBGL and UNMASKED_RENDERER_WEBGL
to disabled the WEBGL_debug_renderer_info extension

Fixes #508
2021-01-03 21:11:17 +01:00
kkapsner
b4a744660b Improved performance for protected canvas part "input"
Fixes #517
2021-01-03 17:36:40 +01:00
kkapsner
fcbc622cef Revert iframe protection upon window unload
Fixes #519
2021-01-03 17:32:57 +01:00
kkapsner
f4d09e43ac added {empty}, {false} and {undefined} to webGL preference parameters
(i.e. VENDOR, RENDERER, UNMASKED_VENDOR_WEBGL and
UNMASKED_RENDERER_WEBGL)

For #508
2021-01-02 11:05:52 +01:00
kkapsner
ae526e4710
Polish translation 2020-12-12 11:08:40 +01:00
kkapsner
822218a00c Update packages 2020-12-12 11:04:05 +01:00
kkapsner
4e6f76ab75 New translations messages.json (Polish) 2020-11-25 15:39:33 +01:00
kkapsner
543365cd64 New translations messages.json (Polish) 2020-11-25 15:29:45 +01:00
kkapsner
1125d6f1a8 Minimal styling on test pages
For #106
2020-11-21 13:58:32 +01:00
kkapsner
7178b409c3 Version 1.5 2020-11-21 13:28:40 +01:00
kkapsner
b7e1b50166 New translations messages.json (Polish) 2020-11-14 00:28:31 +01:00
kkapsner
184b6bb47c New translations messages.json (Polish) 2020-11-14 00:20:04 +01:00
kkapsner
586bc97d38 New translations messages.json (Polish) 2020-11-12 23:58:58 +01:00
kkapsner
8f13588e61 New translations messages.json (Polish) 2020-11-12 23:45:23 +01:00
kkapsner
8c852e96c9 New translations messages.json (Polish) 2020-11-10 19:19:55 +01:00
kkapsner
23f2bf8b23 New translations messages.json (Polish) 2020-11-10 18:50:30 +01:00
kkapsner
83394afc90 Update release notes 2020-11-09 08:33:04 +01:00
kkapsner
0f61318c27 Style index pages 2020-11-09 08:27:45 +01:00
kkapsner
716e6aa6a9
New Crowdin updates (#492)
* New translations messages.json (Chinese Traditional)

* New translations messages.json (German)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)

* New translations messages.json (Italian)

* New translations messages.json (Portuguese)

* New translations messages.json (Portuguese)
2020-11-02 13:54:43 +01:00
kkapsner
701eb979ed Updated DOMRect protection 2020-11-02 13:49:42 +01:00
kkapsner
d5417cf807 Improved exportFunctionWithName
To reduce the need of double exports.
2020-11-02 13:11:50 +01:00
kkapsner
c16068ca85 domRectTest: update SVG tests 2020-11-02 13:07:40 +01:00
kkapsner
ef69e1dcb5 domRectTest: added IntersectionObserverEntry tests 2020-11-02 13:07:19 +01:00
kkapsner
902d31b643 Update npm packages 2020-11-02 12:01:18 +01:00
kkapsner
0a290b3521 Fix stuck Fenix
Fixes #503
2020-11-02 11:54:43 +01:00
kkapsner
e65ac94dda Added getImageData raw display test. 2020-10-26 14:17:34 +01:00
kkapsner
71c9baec0a domRectTest: added getBOXQuads test 2020-10-14 16:33:26 +02:00
kkapsner
ff98b9b9af domRectTest: added svg tests 2020-10-14 16:33:26 +02:00
kkapsner
3eea8fe7c4 domRectTest: hide detail data 2020-10-14 16:33:26 +02:00
litetex
3ab6366994
Options: Using by default json as prefered file type for loading settings (#500) 2020-10-14 16:29:36 +02:00
kkapsner
cd852a0c8b New translations messages.json (Portuguese) 2020-10-14 16:27:39 +02:00
kkapsner
864c2454e1 New translations messages.json (Portuguese) 2020-10-14 16:12:51 +02:00
kkapsner
8d7637e65b New translations messages.json (Italian) 2020-10-13 09:34:20 +02:00
kkapsner
393020630c New translations messages.json (Portuguese) 2020-10-12 12:23:21 +02:00
kkapsner
9779d14276 New translations messages.json (Portuguese) 2020-10-12 12:14:41 +02:00
kkapsner
5ab00a5584 New translations messages.json (Portuguese) 2020-10-12 12:05:12 +02:00
kkapsner
6a31bb3791 New translations messages.json (Portuguese) 2020-10-12 11:46:05 +02:00
kkapsner
beb3a969f3 New translations messages.json (Portuguese) 2020-10-12 11:33:45 +02:00
kkapsner
5964146e22 New translations messages.json (Portuguese) 2020-10-12 11:28:23 +02:00
kkapsner
679265a82f New translations messages.json (Portuguese) 2020-10-03 13:28:14 +02:00
kkapsner
d761411339 New translations messages.json (Portuguese) 2020-10-03 13:18:11 +02:00
kkapsner
1b3709bce5 New translations messages.json (German) 2020-07-27 12:53:50 +02:00
kkapsner
00e94f9ae4 Updated dev dependencies 2020-07-19 11:22:52 +02:00
kkapsner
c6bb9766f9 New translations messages.json (Chinese Traditional) 2020-07-15 16:42:09 +02:00
kkapsner
b9c9516b14
New trnaslations
* Chinese Traditional
* French
* Spanish
2020-07-07 13:17:13 +02:00
kkapsner
cb7a98d751 Updated devDependencies package.json
To remove vulnerabilities
2020-06-27 11:49:43 +02:00
kkapsner
21a950565f Fix alpha value in canvasAPI.js 2020-06-27 11:45:23 +02:00
kkapsner
d8b5ba0744 iframe test: added nested iframes test 2020-06-19 00:07:14 +02:00
kkapsner
ce1925d95f iframe test: better timing dependencies 2020-06-19 00:06:56 +02:00
kkapsner
2c1756ff07 Avoid useless URL specific setting in whitelist inspection
Fixes #483
2020-06-17 16:18:56 +02:00
kkapsner
c6e4027a7f
Added zh-TW
Fixes #479
2020-06-10 14:56:04 +02:00
kkapsner
5ac5c8cc66 Fix crowdin.yml 2020-06-10 14:46:48 +02:00
kkapsner
46bf7e04f4 Set exceptions to two letter code 2020-06-10 14:42:35 +02:00
kkapsner
10b2ef8e70 Version 1.4 2020-06-07 13:10:18 +02:00
kkapsner
ac2e074fdd Updated release notes 2020-06-07 11:32:29 +02:00
kkapsner
d9adcb4c3d
New Portuguese Brazilian translation 2020-06-03 20:03:01 +02:00
kkapsner
5b1c4bf6cc
New Crowdin translations
* New translations (Spanish)
* New translations (Italian)
* New translations (Polish)
* New translations (Russian)
* New translations (Chinese Simplified)
* New translations (Portuguese, Brazilian)
* New translations (Norwegian Bokmal)
2020-06-02 15:30:24 +02:00
kkapsner
8cf58ebe3b Updated updates.json 2020-06-01 14:42:38 +02:00
kkapsner
2e91f85d8f Added offscreen canvas protection
Fixes #467
2020-06-01 14:25:47 +02:00
kkapsner
2c141277e8 Set proper name for faked getContext 2020-06-01 14:03:32 +02:00
kkapsner
3e3dbf2698 Added noDraw parameter to webGL test. 2020-06-01 13:53:05 +02:00
kkapsner
0225f94890 Added FAQ for hidden settings 2020-05-31 13:59:28 +02:00
kkapsner
253d3f68b1 Added canvas cache to isPointInPath and isPointInStroke 2020-05-24 23:39:44 +02:00
kkapsner
da5a9f4509 webGL: added image hash tests 2020-05-21 00:11:25 +02:00
kkapsner
a79c4ec8c5 Added test for blob, offscreen canvas and offscreen canvas in workers 2020-05-19 10:48:08 +02:00
kkapsner
7c2a4edde0 Remove unnecessary CSS. 2020-05-19 10:10:38 +02:00
kkapsner
51b740632a Updated link to DOMRect test 2020-05-19 09:54:40 +02:00
kkapsner
9a7dd3c189 DOMRect test: added test without iframe 2020-05-18 13:22:33 +02:00
kkapsner
4fa91ef3ae Fix refresh bug in audio test 2020-05-01 01:11:21 +02:00
kkapsner
64adb49b81 Additional audio tests 2020-05-01 01:06:50 +02:00
kkapsner
49f3c166a2 Navigator test: added nested worker and worker from blob
Fixes #458
2020-04-15 00:25:24 +02:00
kkapsner
308d7c4005 Navigator test: added worker test
Fixes #458
2020-04-13 10:10:44 +02:00
kkapsner
87fa607d28
New Italian translations 2020-04-10 14:28:22 +02:00
kkapsner
aae0fa2f8b
New French translations 2020-04-05 15:04:41 +02:00
kkapsner
d834b7a14b Updated npm packages 2020-04-05 14:35:50 +02:00
TotalCaesar659
e2d18a143d
Update URL to HTTPS 2020-04-05 14:30:11 +02:00
kkapsner
8ee413a951
New translations 2020-03-22 16:36:14 +01:00
kkapsner
1fb7f7991b Added link to FAQ
For #106
2020-03-22 16:33:22 +01:00
kkapsner
7cfc43194a Text corrections/improvements. 2020-03-22 14:25:15 +01:00
kkapsner
e7cef53bac textMetricsTest: added exception text for FF 68
Fixes #453
2020-03-18 14:06:34 +01:00
kkapsner
a9c89ff20c Version 1.3 2020-03-17 22:41:55 +01:00
kkapsner
d9d2038f0c
New Russian translations 2020-03-17 22:16:19 +01:00
kkapsner
f2b55fe7a2 Add missing APIs to ignoredAPIs 2020-03-17 20:04:28 +01:00
kkapsner
dd6fbc6c1f textMetricsTest: insert canvas in DOM 2020-03-15 13:43:41 +01:00
kkapsner
736aeb371d
New translations 2020-03-14 13:16:52 +01:00
kkapsner
ec128796e3 Added TextMetrics protection
Fixes #448
2020-03-14 12:54:23 +01:00
kkapsner
692b4616e2 Switch from .local to .localhost to simplify https setup 2020-03-14 11:05:02 +01:00
kkapsner
895f1a6f66 Avoid mixed content in test 2020-03-12 23:01:42 +01:00
kkapsner
73e5f5737b Added warning if some features of a API are disabled 2020-03-06 12:06:40 +01:00
kkapsner
d449e5f0c3 Added FAQ for reCAPTCHA
For #398
2020-02-27 17:30:58 +01:00
kkapsner
9614786406 New beta version 2020-02-25 08:44:01 +01:00
kkapsner
0f1aa1cc32 Always open options page in new tab
Fixes #434 and effects #445
2020-02-24 23:33:02 +01:00
kkapsner
13b061c395 Removed unnecessary activeTab permission 2020-02-23 16:43:17 +01:00
kkapsner
00f69b73ab
New Russian translations 2020-02-23 16:42:16 +01:00
kkapsner
1cfdcdb120 Added FAQs
For #106
2020-02-16 19:57:11 +01:00
kkapsner
73694ab129
Added Norwegian 2020-02-15 14:52:55 +01:00
kkapsner
59d78c8e68 New translations
New Italian and Chinese translations.
2020-02-09 23:49:42 +01:00
kkapsner
84e40b5eb4 Some code improvements
Fixes #439
2020-02-01 22:00:39 +01:00
kkapsner
ada2845213 Add task for alpha build 2020-02-01 22:00:13 +01:00
kkapsner
9e92e4baf2 Version 1.2 2020-01-29 10:42:52 +01:00
kkapsner
f65d73f125
New Italian translations 2020-01-29 10:41:43 +01:00
kkapsner
f03eb4a67a Clean up release notes 2020-01-28 23:17:23 +01:00
kkapsner
f674ee3328 Added custom build tool
Fixes #435
2020-01-28 23:16:40 +01:00
kkapsner
f358951022 New alpha 2020-01-26 01:51:37 +01:00
kkapsner
6fbb9339a1 Remove duplicated code 2020-01-26 01:11:18 +01:00
kkapsner
64b60c834a Importing settings file with an very old storage version did not work at all 2020-01-26 00:51:18 +01:00
kkapsner
350c7b65d4 Fix code loading order 2020-01-26 00:24:12 +01:00
kkapsner
3d881d3c7d Fix group filtering in revertProperties 2020-01-25 10:04:54 +01:00
kkapsner
8bfaf63a99
Translation update 2020-01-25 01:11:04 +01:00
kkapsner
cc8ca147b0 Added container specific navigator settings 2020-01-25 01:03:05 +01:00
kkapsner
01b63b356c Added parameters to parseTranslation 2020-01-25 00:42:16 +01:00
kkapsner
b48ad91dfe Release notes update 2020-01-25 00:38:39 +01:00
kkapsner
282fcb12de
Translation update 2020-01-24 15:49:28 +01:00
kkapsner
6c22788096 Cleanup CanvasBlocker
once Firefox supports browser.runtime.onSuspend
2020-01-23 15:36:32 +01:00
kkapsner
9353f71455 Added notice for privacy.resistFingerprinting
Fixes #427
2020-01-23 15:23:56 +01:00
kkapsner
1556ee45c2 Update release notes 2020-01-23 13:58:14 +01:00
kkapsner
f3f6df229f Undo interception in top windows
Fixes #431
2020-01-23 13:56:14 +01:00
kkapsner
e2efb727b9 Always specify correct function names 2020-01-22 13:38:24 +01:00
kkapsner
29e61ada25 Added window.open test
For #431
2020-01-21 22:45:28 +01:00
kkapsner
d547917b43 Fix dependency order
For #429
2020-01-19 17:01:14 +01:00
kkapsner
363940014d Protect exportFunction with defineAs
For #429
2020-01-19 01:27:26 +01:00
kkapsner
16f88a5daa Version 1.1 2020-01-19 00:42:15 +01:00
kkapsner
5de24ad430
New Russian translations 2020-01-18 10:02:21 +01:00
kkapsner
38e56aff31
New Russian translations 2020-01-14 13:41:40 +01:00
kkapsner
b0becc0af0 Hashes change upon reload is dependent on settings
Fixes #425
2020-01-12 13:47:17 +01:00
kkapsner
417b234a26
New Crowdin translations (#423)
* New translations messages.json (Chinese Simplified)

* New translations messages.json (French)

* New translations messages.json (Italian)

* New translations messages.json (Russian)

* New translations messages.json (Spanish)

* New translations messages.json (Chinese Simplified)

* New translations messages.json (Chinese Simplified)

* New translations messages.json (Chinese Simplified)

* New translations messages.json (Chinese Simplified)
2020-01-11 01:34:39 +01:00
kkapsner
3334fa64c1 New translations messages.json (German) 2020-01-06 21:16:17 +01:00
kkapsner
accdb3cec1 Formatting locales files 2020-01-06 21:09:13 +01:00
kkapsner
e50e9deca4 Added testAPI and canvasAPI 2020-01-06 15:15:04 +01:00
kkapsner
4337ccbf33 Send origin to server. 2020-01-06 15:07:58 +01:00
kkapsner
ade99bbb8a await also works with normal return values. 2020-01-06 15:05:37 +01:00
kkapsner
e1e313cb96 Added test for appendChild 2020-01-06 14:56:49 +01:00
kkapsner
16fb042335 Fixed origin display in blobIframe 2020-01-06 14:55:11 +01:00
kkapsner
4020ba1ebd Added link to translation issue 2020-01-06 14:53:49 +01:00
kkapsner
6312914ba3 Update Crowdin configuration file 2020-01-06 11:51:07 +01:00
kkapsner
9628497277 Better sentences I should make 2020-01-03 10:14:01 +01:00
kkapsner
db3cb3be44 Typos 2020-01-02 12:54:00 +01:00
kkapsner
0e8938ba6e Updated web-ext 2020-01-02 12:03:00 +01:00
kkapsner
d09340e84f Fix fields hosted on braintree when window API is protected
Fixes #417
2020-01-02 11:41:13 +01:00
kkapsner
b361733c73 Importing settings file with an older storage version did not work properly 2020-01-01 16:04:44 +01:00
kkapsner
85c88ad3d8 window.name protection was detectable 2020-01-01 00:30:16 +01:00
kkapsner
ccabfc2a6f 1.0RC3 2019-12-30 00:24:20 +01:00
kkapsner
cc3127b0a4 Updated release notes 2019-12-29 23:44:21 +01:00
kkapsner
4601dd25af Allow cross origin arguments and remove apply(..., Array.from)
Fixes #415
2019-12-29 23:40:39 +01:00
kkapsner
8506757c62 Code cleanup: improve modifiedCanvasAPI 2019-12-29 11:46:47 +01:00
kkapsner
e3182b562b Code cleanup: improve webgl.js 2019-12-29 00:45:20 +01:00
kkapsner
abdb95b815 Code cleanup: reduce block nesting 2019-12-29 00:18:05 +01:00
kkapsner
10413a89c3 Switch to asych/await where useful 2019-12-28 23:23:55 +01:00
kkapsner
372ee755f7 Added iframeAPI script for tests 2019-12-24 00:54:17 +01:00
kkapsner
3dc39e11a5 eslint cleanup 2019-12-24 00:32:50 +01:00
kkapsner
627ee6d21e Improved navigator test 2019-12-21 23:47:07 +01:00
kkapsner
09286644d8 Update release notes 2019-12-18 00:11:09 +01:00
kkapsner
26e0d21b23 Grammar 2019-12-17 08:09:16 +01:00
kkapsner
993e72d6c8 Add reCAPTCHA preset
Fixes #398
2019-12-16 19:31:18 +01:00
kkapsner
ce27426ad4 Update to eslint 6.7.2 2019-12-16 19:28:05 +01:00
kkapsner
0d0e3e30ec test updates 2019-12-16 19:27:28 +01:00
kkapsner
717e1d3e3a It's time to face it
... actually we are way beyond 1.0
2019-12-15 00:08:07 +01:00
kkapsner
a9ed208505 Added CSP test 2019-12-14 21:22:18 +01:00
kkapsner
ddcaf5a2a9 Updated min version to match ESR 2019-12-14 15:08:19 +01:00
kkapsner
6fb7622fec Always use exportFunctionWithName 2019-12-13 17:34:14 +01:00
kkapsner
af1dfe755c Simplified code structure 2019-12-12 23:44:02 +01:00
kkapsner
0d331d91a6 Fixed output in checkPropertyDescriptor() 2019-12-12 23:27:21 +01:00
kkapsner
15c537cd1f Fixed statement order to catch api changes 2019-12-12 17:44:14 +01:00
kkapsner
3ab687f45b Improved settings loading test
to include force loading detection check.
2019-12-12 00:10:46 +01:00
kkapsner
3668f48247 Adjusted codebeat settings 2019-12-12 00:10:08 +01:00
kkapsner
14a4d1cdc2 Get rid of eval. 2019-12-12 00:09:53 +01:00
kkapsner
32f9ea7447 Remove code duplications and cleanup 2019-12-10 15:07:22 +01:00
kkapsner
8e414becf0 Proper screen api whitelisting 2019-12-09 00:31:08 +01:00
kkapsner
bf757a5431 Added mail.google.com to the convenience preset
Fixes #413
2019-12-06 23:45:28 +01:00
168 changed files with 35525 additions and 9715 deletions

View File

@ -1,6 +1,6 @@
{ {
"JAVASCRIPT": { "JAVASCRIPT": {
"LOC": [70, 80, 100, 120], "LOC": [80, 90, 100, 120],
"BLOCK_NESTING": [4, 5, 6, 7] "BLOCK_NESTING": [4, 5, 6, 7]
} }
} }

View File

@ -1,5 +1,7 @@
Dieses Add-on ermöglicht es Nutzern, Webseiten davon abzuhalten, sie über Javascript APIs zu identifizieren. Nutzer können auswählen, ob die APIs komplett auf bestimmten oder allen Seiten blockiert werden (dies wird die Funktionalität einiger Seiten beeinträchtigen) oder bei den identifikationsfreundlichen Auslese-Funktionen falsche Werte vorzutäuschen. Dieses Add-on ermöglicht es Nutzern, Webseiten davon abzuhalten, sie über Javascript APIs zu identifizieren. Nutzer können auswählen, ob die APIs komplett auf bestimmten oder allen Seiten blockiert werden (dies wird die Funktionalität einiger Seiten beeinträchtigen) oder bei den identifikationsfreundlichen Auslese-Funktionen falsche Werte vorzutäuschen.
Bei Problemen konsultieren Sie bitte zuerst <a href="https://canvasblocker.kkapsner.de/faq/">FAQ</a>. Falls Sie Fehler finden oder Verbesserungsvorschläge haben, teilen Sie mir das bitte auf https://github.com/kkapsner/CanvasBlocker/issues mit.
<b>WICHTIG</b>: Sie sollten nur ein Addon/eine Einstellung aktiv haben, die eine API beschützt. Ansonsten können Sie massive Performanceprobleme bekommen. (Z.B. EclipsedMoon für Palemoon hat 'canvas.poison', was bekanntermaßen Probleme verursacht: https://github.com/kkapsner/CanvasBlocker/issues/253#issuecomment-459499290) <b>WICHTIG</b>: Sie sollten nur ein Addon/eine Einstellung aktiv haben, die eine API beschützt. Ansonsten können Sie massive Performanceprobleme bekommen. (Z.B. EclipsedMoon für Palemoon hat 'canvas.poison', was bekanntermaßen Probleme verursacht: https://github.com/kkapsner/CanvasBlocker/issues/253#issuecomment-459499290)
privacy.resistFingerprinting kann aber problemlos aktiviert werden und der Fingerprinting-Schutz muss nicht deaktiviert werden. privacy.resistFingerprinting kann aber problemlos aktiviert werden und der Fingerprinting-Schutz muss nicht deaktiviert werden.
@ -11,6 +13,7 @@ Beschützte "Fingerprinting"-APIs:
<li>history</li> <li>history</li>
<li>window (standardmäßig deaktiviert)</li> <li>window (standardmäßig deaktiviert)</li>
<li>DOMRect</li> <li>DOMRect</li>
<li>TextMetrics</li>
<li>navigator (standardmäßig deaktiviert)</li> <li>navigator (standardmäßig deaktiviert)</li>
<li>screen</li> <li>screen</li>
</ul> </ul>
@ -40,5 +43,3 @@ Die verschiedenen Blockiermodi sind:
<li>nur Einträge der Blacklist blockieren: Blockiere die beschützten APIs nur auf den Seiten der Blacklist.</li> <li>nur Einträge der Blacklist blockieren: Blockiere die beschützten APIs nur auf den Seiten der Blacklist.</li>
<li>alles erlauben: Ignoriere alle Listen und erlaube die beschützten APIs auf allen Webseiten.</li> <li>alles erlauben: Ignoriere alle Listen und erlaube die beschützten APIs auf allen Webseiten.</li>
</ul> </ul>
Falls Sie Fehler finden oder Verbesserungsvorschläge haben, teilen Sie mir das bitte auf https://github.com/kkapsner/CanvasBlocker/issues mit.

View File

@ -1,5 +1,7 @@
This add-on allows users to prevent websites from using some Javascript APIs to fingerprint them. Users can choose to block the APIs entirely on some or all websites (which may break some websites) or fake its fingerprinting-friendly readout API. This add-on allows users to prevent websites from using some Javascript APIs to fingerprint them. Users can choose to block the APIs entirely on some or all websites (which may break some websites) or fake its fingerprinting-friendly readout API.
If you encounter any problems please check the <a href="https://canvasblocker.kkapsner.de/faq/">FAQ</a> first. Please report issues and feature requests at https://github.com/kkapsner/CanvasBlocker/issues
<b>IMPORTANT</b>: you should only have ONE addon/setting set that protects an API. Otherwise you could face massive performance issues. (E.g. EclipsedMoon for Palemoon has 'canvas.poison' which is known to cause issues: https://github.com/kkapsner/CanvasBlocker/issues/253#issuecomment-459499290) <b>IMPORTANT</b>: you should only have ONE addon/setting set that protects an API. Otherwise you could face massive performance issues. (E.g. EclipsedMoon for Palemoon has 'canvas.poison' which is known to cause issues: https://github.com/kkapsner/CanvasBlocker/issues/253#issuecomment-459499290)
But setting privacy.resistFingerprinting to true and/or using the new fingerprinting protection introduced with Firefox 67 is fine. But setting privacy.resistFingerprinting to true and/or using the new fingerprinting protection introduced with Firefox 67 is fine.
@ -11,6 +13,7 @@ Protected "fingerprinting" APIs:
<li>history</li> <li>history</li>
<li>window (disabled by default)</li> <li>window (disabled by default)</li>
<li>DOMRect</li> <li>DOMRect</li>
<li>TextMetrics</li>
<li>navigator (disabled by default)</li> <li>navigator (disabled by default)</li>
<li>screen</li> <li>screen</li>
</ul> </ul>
@ -41,5 +44,3 @@ The different block modes are:
<li> block only black list: Block the protected APIs only for websites on the black list.</li> <li> block only black list: Block the protected APIs only for websites on the black list.</li>
<li> allow everything: Ignore all lists and allow the protected APIs on all websites.</li> <li> allow everything: Ignore all lists and allow the protected APIs on all websites.</li>
</ul> </ul>
Please report issues and feature requests at https://github.com/kkapsner/CanvasBlocker/issues

View File

@ -0,0 +1,12 @@
body {
margin: 30px auto;
max-width: 680px;
font-size: 13pt;
line-height: 1.4em;
color: #444444;
padding: 0;
}
h1, h2, h3{
line-height: 1.2em;
}

View File

@ -0,0 +1,9 @@
How is CanvasBlocker funded?
-------
There is no steady monetary funding of CanvasBlocker. Donations are accepted and help to cover some expenses. But since these are not that high it is not sure if a steady funding with any obligations will be accepted.
There is also no plan to monetize CanvasBlocker in any way.
All the development work is done by kkapsner in their spare time and no salary or compensation is paid for it.
So it's all done voluntarily for fun and free.

View File

@ -0,0 +1,5 @@
How can I support CanvasBlocker?
------
The best way to support CanvasBlocker is to give feedback. If something is not working like it should or something could be improved please open an [issue](https://github.com/kkapsner/CanvasBlocker/issues/new). Reviews at [addons.mozilla.org](https://addons.mozilla.org/firefox/addon/canvasblocker/reviews/) can also help to increase the user base but it is a very bad platform to communicate issues and improvements.
If you want to contribute with your own spare time you can help to improve the [translations](https://github.com/kkapsner/CanvasBlocker/issues/420). Code contributions can also be done in form of pull requests but will be reviewed very thoroughly.

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>
CanvasBlocker FAQs
</title>
<link rel="stylesheet" href="../default.css" type="text/css">
</head>
<body>
<header><h1>CanvasBlocker FAQs</h1></header>
<?php
include_once("../../kamiKatze/autoload.php");
$parser = new MarkdownParser();
foreach (new DirectoryIterator("./") as $file){
if (!$file->isDot() && !$file->isDir() && $file->getExtension() === "md"){
$markdown = $parser->parse(file_get_contents($file->getPathname()));
echo preg_replace("/<h2/", "<h2 id=\"" . str_replace(".md", "", $file->getFilename()) . "\"", $markdown->view("html"), 1);
}
}
?>
</body>
</html>

View File

@ -0,0 +1,9 @@
I do not find setting X.
------
Some settings are not always visible. There are three reasons why settings might be hidden:
* Some require other settings to be a certain value to be displayed because they are not useful without the other setting. E.g. the whole "Asking" section is only displayed when the block mode is set to "ask for permission".
* Other settings are hidden behind the "expert mode" because they need a certain level of understanding of CanvasBlocker to be used correctly.
* Finally you can hide settings. To show these settings you have to enable "display hidden settings".
So if you search for a setting and do not find it you should first enable the "expert mode" and "display hidden settings". If you then still do not find it it's not relevant for your current setup of CanvasBlocker.

View File

@ -0,0 +1,3 @@
PayPal transactions started with "Buy now" button not working!
-----
It's a known fact that the window API protection hinders the completion of all transaction processes initiated via PayPal's "Buy now" button. You have to whitelist paypal.com for that API for it to work properly.

View File

@ -0,0 +1,9 @@
Why does CanvasBlocker need permission X?
------
Here is the list of permission that CanvasBlocker needs and the reason why it's needed:
* <all_urls> and tabs: CanvasBlocker needs to be able to interact with all possible urls and tabs as fingerprinting attempts could be done everywhere.
* storage: to store the settings the storage.local API is used.
* webRequest and webRequestBlocking: to insert the CSR headers in a request in order to protect the data-URLs. Once [this bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1475831) has been fixed I can completely remove the data-URI protection (see [here](https://github.com/kkapsner/CanvasBlocker/issues/208) for further information).
* contextualIdentities and cookies: for support of browser containers. I would like to make this optional for only the people that use containers but I cannot (see [here](https://github.com/kkapsner/CanvasBlocker/issues/381) for further information).
* privacy: this permission is needed to read if the user has privacy.resistFingerprinting enabled. A notice about a slightly changed behaviour of CanvasBlocker is displayed in the settings page in that case.

View File

@ -0,0 +1,5 @@
reCAPTCHA is not working!
-----
It's a known fact that the window API protection breaks reCAPTCHAs. They use the window.name API to store information about the captcha. The protection is designed to mitigate exactly such techniques of passing information from one domain to another. But in this case the information is shared with an embedded HTML page (an <iframe> tag). As the information gets lost when the top level page navigates somewhere the tracking potential is quite limited in such a scenario.
So in conclusion you can enable "Allow window.name in frames" to make reCAPTCHA work and still don't have to worry too much about tracking with window.name.

View File

@ -0,0 +1,5 @@
How do I remove URL specific settings?
------
If one URL specific setting is set for a URL it shows up at every setting that can have URL specific values. So if you click on the little "x" to reset the value the line for the URL may not disappear. But the input field will appear a little bit gray to indicate that the "normal" value is still used for this URL.
To get rid of this URL line you have to remove all specific settings for this URL. The easiest way to get to know which settings are still set you can go to General > Settings > Export settings > Inspect. There the entry "urlSettings" stores the url specific values.

View File

@ -0,0 +1,10 @@
Page X claims my fingerprint is unique.
------
Having a unique fingerprint is fine as long as it changes. With the default settings of CanvasBlocker the fingerprint should change all the time. But also with other settings (e.g. the stealth preset) that do not change the fingerprint all the time the fingerprint should be unique per domain and therefore prevent tracking. To test this you can check the different fingerprints on [canvasblocker.kkapsner.de](https://canvasblocker.kkapsner.de/test/) and [canvasblocker2.kkapsner.de](https://canvasblocker2.kkapsner.de/test/).
My fingerprint does not change when I reload page X.
------
Some pages do not recalculate the fingerprint upon reload. Make sure you force the recomputation.
But also some CanvasBlocker settings make it to not change the fingerprint upon reload (e.g. the stealth preset).
If you have privacy.resistFingerprinting enabled the fingerprints also may stay the same. But in this case you are not trackable as the fingerprint does not leak any information about your system. See [here](https://github.com/kkapsner/CanvasBlocker/issues/158) and [here](https://github.com/ghacksuserjs/ghacks-user.js/issues/767) for further information.

View File

@ -32,7 +32,7 @@
"consistent-return": "error", "consistent-return": "error",
"constructor-super": "warn", "constructor-super": "warn",
"eqeqeq": "error", "eqeqeq": "error",
"eslint-comments/no-use": ["error", {"allow": ["eslint-disable-next-line"]}], "eslint-comments/no-use": ["error", {"allow": ["eslint-disable-next-line", "globals"]}],
"indent": ["error", "tab", {"SwitchCase": 1}], "indent": ["error", "tab", {"SwitchCase": 1}],
"max-depth": ["warn", 4], "max-depth": ["warn", 4],
"max-len": ["warn", {"code": 120, "tabWidth": 4}], "max-len": ["warn", {"code": 120, "tabWidth": 4}],
@ -50,8 +50,11 @@
"no-unreachable": "warn", "no-unreachable": "warn",
"no-unused-vars": "off", "no-unused-vars": "off",
"no-use-before-define": ["error", {"functions": false}], "no-use-before-define": ["error", {"functions": false}],
"no-useless-rename": "warn",
"no-useless-return": "warn",
"no-var": "error", "no-var": "error",
"quotes": ["error", "double"], "quotes": ["error", "double"],
"require-atomic-updates": "off",
"semi": ["error", "always"], "semi": ["error", "always"],
"space-in-parens": ["error", "never"], "space-in-parens": ["error", "never"],
"strict": ["error", "function"], "strict": ["error", "function"],
@ -67,7 +70,6 @@
{ {
"files": ["test/*"], "files": ["test/*"],
"rules": { "rules": {
"no-var": "off",
"no-console": "off" "no-console": "off"
} }
}, },

View File

@ -2,13 +2,13 @@
## Description ## Description
<!--- Provide a more detailed introduction to the issue itself. --> <!--- Provide a more detailed introduction to the issue itself. -->
<!--- Why you consider it to be a bug or a usefull change/improvement? --> <!--- Why you consider it to be a bug or a useful change/improvement? -->
## Expected Behavior ## Expected Behaviour
<!--- If you're describing a bug, tell us what should happen. --> <!--- If you're describing a bug, tell us what should happen. -->
<!--- If you're suggesting a change/improvement, tell us how it should work. --> <!--- If you're suggesting a change/improvement, tell us how it should work. -->
## Current Behavior ## Current Behaviour
<!--- If describing a bug, tell us what happens instead of the expected behavior. --> <!--- If describing a bug, tell us what happens instead of the expected behavior. -->
<!--- If suggesting a change/improvement, explain the difference from current behavior. --> <!--- If suggesting a change/improvement, explain the difference from current behavior. -->
@ -18,7 +18,7 @@
## Steps to Reproduce (for bugs) ## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. --> <!--- Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. -->
1. 1. create a fresh Firefox profile
2. 2.
3. 3.
4. 4.
@ -36,6 +36,8 @@
* Installed addons: * Installed addons:
## Your Settings ## Your Settings
~~~ json
<!--- Copy your CanvasBlocker settings here. --> <!--- Copy your CanvasBlocker settings here. -->
<!-- They can be retrieved by checking the expert mode and going to export settings. --> <!-- They can be retrieved by checking the expert mode and going to export settings. -->
<!--- You may consider deleting personal data - especially the "persistentRndStorage". --> <!--- You may consider deleting personal data - especially the "persistentRndStorage". -->
~~~

125
.tools/build.js Normal file
View File

@ -0,0 +1,125 @@
const child_process = require("child_process");
const path = require("path");
const yargs = require("yargs");
const args = yargs
.options("type", {
alias: "t",
describe: "Type of the build",
choices: ["alpha", "rc", "release"],
default: "alpha",
})
.help()
.alias("help", "h")
.argv;
const fs = require("fs");
const versionsPath = path.join(__dirname, "..", "versions");
function getXPIFileName(id, version){
"use strict";
return `${id}-${version}.xpi`;
}
async function addAlphaVersionToUpdatesJSON(version){
"use strict";
const updatesPath = path.join(versionsPath, "updates.json");
const data = JSON.parse(await fs.promises.readFile(updatesPath));
const versions = data.addons["CanvasBlocker-Beta@kkapsner.de"].updates;
if (versions.some(function(entry){
return entry.version === version;
})){
return;
}
versions.push({
version,
update_link: `https://canvasblocker.kkapsner.de/versions/${getXPIFileName("canvasblocker_beta", version)}`
});
await fs.promises.writeFile(updatesPath, JSON.stringify(data, undefined, "\t"));
}
async function getAlphaVersion(manifest){
"use strict";
function f(n){
if (n < 10) return "0" + n.toString(10);
return n.toString(10);
}
const now = new Date();
const date = `${now.getFullYear()}${f(now.getMonth() + 1)}${f(now.getDate())}`;
const versionParts = manifest.version.split(".");
while (versionParts.length > 2){
versionParts.pop();
}
const baseVersion = `${versionParts.join(".")}.${date}`;
if (!fs.existsSync(path.join(versionsPath, getXPIFileName("canvasblocker_beta", baseVersion)))){
return baseVersion;
}
let dayTry = 1;
while (fs.existsSync(path.join(versionsPath, getXPIFileName("canvasblocker_beta", `${baseVersion}.${dayTry}`)))){
dayTry += 1;
}
return `${baseVersion}.${dayTry}`;
}
function getRCVersion(manifest){
"use strict";
throw "not implemented";
}
function getReleaseVersion(manifest){
"use strict";
return manifest.version.replace(/^([\d.]+).*$/, "$1");
}
async function run(){
"use strict";
const manifestPath = path.join(__dirname, "../manifest.json");
const oldManifest = await fs.promises.readFile(manifestPath);
const manifest = require(manifestPath);
if (args.type === "alpha" || args.type === "rc"){
manifest.name = "CanvasBlocker-Beta";
["gecko", "gecko_android"].forEach(function(browserType){
if (!manifest.browser_specific_settings[browserType]) return;
manifest.browser_specific_settings[browserType].id = "CanvasBlocker-Beta@kkapsner.de";
});
}
else {
manifest.name = "CanvasBlocker";
["gecko", "gecko_android"].forEach(function(browserType){
if (!manifest.browser_specific_settings[browserType]) return;
manifest.browser_specific_settings[browserType].id = "CanvasBlocker@kkapsner.de";
delete manifest.browser_specific_settings[browserType].update_url;
});
}
if (args.type === "alpha"){
manifest.version = await getAlphaVersion(manifest);
addAlphaVersionToUpdatesJSON(manifest.version);
}
else if (args.type === "rc"){
manifest.version = getRCVersion(manifest);
}
else {
manifest.version = getReleaseVersion(manifest);
}
await fs.promises.writeFile(manifestPath, JSON.stringify(manifest, null, "\t"));
const childArgs = [
"build",
"--overwrite-dest",
"--ignore-files",
"test",
"--ignore-files",
"versions",
"--ignore-files",
"crowdin.yml",
"--ignore-files",
"package*"
];
const child = child_process.spawn("web-ext", childArgs, {stdio: "inherit"});
child.on("close", function(){
fs.promises.writeFile(manifestPath, oldManifest);
});
}
run();

View File

@ -0,0 +1,164 @@
const data = require("./chromeVendors.json");
function addString(string, currentTree){
"use strict";
if (string.length <= 1){
const nextTree = currentTree[string] || {};
currentTree[string] = nextTree;
}
else {
const firstChar = string.substring(0, 1);
const nextTree = currentTree[firstChar] || {};
currentTree[firstChar] = nextTree;
const nextString = string.substring(1);
addString(nextString, nextTree);
}
}
function output(tree){
"use strict";
const keys = Object.keys(tree);
switch (keys.length){
case 0:
return "";
case 1:
return keys[0] + output(tree[keys[0]]);
default:
return "<" + keys.map(key => key + output(tree[key])).join("|") + ">";
}
}
// every string ends with a ) and does not contain <, >, $ or |
data.forEach(function(string){
"use strict";
if (
!string.endsWith(")") ||
string.match(/[<>|$]/)
){
throw string;
}
});
const tree1 = {};
const tree2 = {};
const tree3 = {};
data.every(function(string){
"use strict";
string = string.substring(0, string.length - 1);
const parts = string.split(" Direct", 2);
const parts2 = parts[0].split(" (Microsoft Corporation", 2);
addString(parts2[0], tree1);
if (parts2.length > 1){
addString(" (Microsoft Corporation" + parts2[1], tree2);
}
else {
addString("", tree2);
}
if (parts.length > 1){
addString(" Direct" + parts[1], tree3);
}
else {
addString("", tree3);
}
return true;
});
// const compressed = output(tree1) + output(tree2) + output(tree3) + ")";
const compressed = output(tree1) + output(tree2) +
"< Direct3D<11 vs_<4_<0 ps_4_0|1 ps_4_1>|5_0 ps_5_0>|9<Ex|> vs_<0_0 ps_<2_0|3_0>|2_0 ps_2_0|3_0 ps_3_0>>|>" + ")";
console.log("compressed length", compressed.length);
// console.log(compressed);
function countWords(string){
"use strict";
const words = {};
string.split(/[^0-9a-z]+/i).filter(word => word.length > 2).forEach(function(word){
const wordStats = words[word] || {count: 0};
words[word] = wordStats;
wordStats.count += 1;
});
return words;
}
function wordCompressor(string){
"use strict";
const words = countWords(string);
const duplicatedWord = Object.keys(words)
.map(function(word){
return {
word,
count: words[word].count
};
})
.filter(word => word.count > 1)
.sort((a, b) => b.word.length*b.count - a.word.length*a.count);
let compressed = string;
let index = 0;
const usedWords = [];
for (let i = 0; i < duplicatedWord.length; i += 1){
const replacement = "$" + index.toString(36);
const word = duplicatedWord[i].word;
if (
replacement.length < word.length &&
(word.length - replacement.length) * duplicatedWord[i].count > word.length + 1
){
compressed = compressed.replace(new RegExp("\\b" + word + "\\b", "g"), replacement);
index += 1;
usedWords.push(word);
}
}
return {compressed, usedWords};
}
const {compressed: compressed2, usedWords} = wordCompressor(compressed);
console.log("compressed 2 length", compressed2.length);
console.log(compressed2);
console.log(usedWords.join("|"));
function decompress(string, words){
"use strict";
return string.replace(/\$([0-9a-z]+)/gi, function(m, index){
return words[parseInt(index, 36)];
});
}
const decompressed2 = decompress(compressed2, usedWords);
console.log("test: ", compressed === decompressed2);
for (let start = 0; start < compressed.length; start += 100){
if (compressed.substring(start, start + 100) !== decompressed2.substring(start, start + 100)){
console.log(start);
console.log(compressed.substring(start, start + 100));
console.log(decompressed2.substring(start, start + 100));
}
}
function pickOne(string){
"use strict";
const options = [];
let cumulate = "";
let index = 0;
for (const l = string.length; index < l; index += 1){
const char = string.charAt(index);
if (char === "|"){
options.push(cumulate);
cumulate = "";
}
else if (char === "<"){
const subPick = pickOne(string.substring(index + 1));
cumulate += subPick.value;
index += 1 + subPick.endIndex;
}
else if (char === ">"){
break;
}
else {
cumulate += char;
}
}
options.push(cumulate);
return {value: options[Math.floor(Math.random() * options.length)], endIndex: index};
}
console.log(pickOne(compressed).value);

693
.tools/chromeVendors.json Normal file
View File

@ -0,0 +1,693 @@
[
"ANGLE (AMD (ATI) FirePro M8900 (FireGL) Mobility Pro Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD 760G (Microsoft Corporation WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD 760G Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD 760G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD FirePro 2270 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD FirePro W5000 (FireGL V) Graphics Adapter Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD M880G with ATI Mobility Radeon HD 4200 )",
"ANGLE (AMD M880G with ATI Mobility Radeon HD 4200 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD M880G with ATI Mobility Radeon HD 4250 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 5450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 5500 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 5570 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 5670 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 5700 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 5800 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6250 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6250M )",
"ANGLE (AMD Radeon HD 6290 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6290 Graphics (Microsoft Corporation- WDDM v1.20) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6290 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6300 series Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6310 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6310 Graphics )",
"ANGLE (AMD Radeon HD 6310 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6310M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6320 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6320 Graphic Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6320 Graphics )",
"ANGLE (AMD Radeon HD 6320 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6320 Graphics)",
"ANGLE (AMD Radeon HD 6320 series Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6320M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6350 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6370D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6370M)",
"ANGLE (AMD Radeon HD 6400M Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6410D Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6410D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6450 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6450A Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6470M )",
"ANGLE (AMD Radeon HD 6470M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6470M)",
"ANGLE (AMD Radeon HD 6480G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6500 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6500M/5600/5700 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6510 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6520G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6530D Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6550A Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6570 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6570 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6570)",
"ANGLE (AMD Radeon HD 6620G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6630M)",
"ANGLE (AMD Radeon HD 6650A Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6670 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6700 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6730M)",
"ANGLE (AMD Radeon HD 6800 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6800 Series)",
"ANGLE (AMD Radeon HD 6900 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 6900M Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7000 series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7290 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7300 Series Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7300 Series)",
"ANGLE (AMD Radeon HD 7310 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7310 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7310M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7340 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7340 Graphics (Microsoft Corporation - WDDM v1.20) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7340 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7340G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7340M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7400M Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7420G Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7420G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7450 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7470M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7480D Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7480D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7500 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7500/7600 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7500G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7500M/7600M Series (Microsoft Corporation - WDDM v1.3) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7520G + 7670M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7520G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7540D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7550M/7650M Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7550M/7650M Graphics)",
"ANGLE (AMD Radeon HD 7560D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7570 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7570 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7570M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7580D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7600G + 6400M Dual Graphics)",
"ANGLE (AMD Radeon HD 7600M Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7610M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7610M)",
"ANGLE (AMD Radeon HD 7640G + 7600M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7640G + 7670M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7640G + 8750M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7640G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7650M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7660D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7660D)",
"ANGLE (AMD Radeon HD 7660G + 7600M Dual Graphics)",
"ANGLE (AMD Radeon HD 7660G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7670M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7700 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7700 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7800 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 7800 Series)",
"ANGLE (AMD Radeon HD 7900 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8210 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8240 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8250 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8330 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8350 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8400 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8450G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8470 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8470D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8510G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8550G + 8570M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8550G + HD 8600/8700M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8550G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8570D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8610G + 8500M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8610G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8650G + 8670M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8650G + 8750M Dual Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD 8670D Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD6370D Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD6410D Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD7700 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon HD7770 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon R7 200 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon R9 200 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon(TM) HD 6380G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon(TM) HD 6480G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon(TM) HD 6520G )",
"ANGLE (AMD Radeon(TM) HD 6520G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon(TM) HD 6620G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (AMD Radeon(TM) HD 7450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS EAH4350 series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS EAH5450 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS EAH5670 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ASUS EAH5770 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS EAH6450 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS EAH6670 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS EAH6970 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS HD7770 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ASUS R9 270 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI FirePro 2450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI FirePro V3700 (FireGL) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI FirePro V3800 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI FirePro V4800 (FireGL) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon 9600/9700 Series)",
"ANGLE (ATI Mobility Radeon HD 2400 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 2400 XT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 2600 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 3400 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 3430 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 3450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 3470 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 3650 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4200 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4200 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4250 )",
"ANGLE (ATI Mobility Radeon HD 4250 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4250 Graphics Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4250 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4300 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4300/4500 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4330 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4330)",
"ANGLE (ATI Mobility Radeon HD 4500 Series (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4500 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4500/5100 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4530 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4570 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4570)",
"ANGLE (ATI Mobility Radeon HD 4650 (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 4650 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5000 Series (Microsoft Corporation - WDDM v1.20) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5000 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5145 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5400 Series )",
"ANGLE (ATI Mobility Radeon HD 5400 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5430)",
"ANGLE (ATI Mobility Radeon HD 5450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 545v Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5470 )",
"ANGLE (ATI Mobility Radeon HD 5470 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5470 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5470)",
"ANGLE (ATI Mobility Radeon HD 5650 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon HD 5650)",
"ANGLE (ATI Mobility Radeon HD 6370)",
"ANGLE (ATI Mobility Radeon HD 6550 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon X1300 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon X1600 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Mobility Radeon X1600)",
"ANGLE (ATI Radeon 2100 (Microsoft Corporation - WDDM) Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon 2100 Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon 2100)",
"ANGLE (ATI Radeon 3000 Graphics (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon 3000 Graphics Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon 3000 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon 3000 Graphics)",
"ANGLE (ATI Radeon 3100 Graphics (Microsoft Corporation WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon 3100 Graphics)",
"ANGLE (ATI Radeon HD 2350 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 Pro )",
"ANGLE (ATI Radeon HD 2400 Pro Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 PRO Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 Pro Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 PRO Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 XT (Microsoft Corporation WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 XT )",
"ANGLE (ATI Radeon HD 2400 XT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 XT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2400 XT)",
"ANGLE (ATI Radeon HD 2600 Pro )",
"ANGLE (ATI Radeon HD 2600 PRO)",
"ANGLE (ATI Radeon HD 2600 XT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 2600 XT)",
"ANGLE (ATI Radeon HD 3200 Graphics (Microsoft Corporation WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3200 Graphics Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3200 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3300 Graphics Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3300 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3400 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3400 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3450 - Dell Optiplex Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3450 - Dell Optiplex Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3450 (Microsoft Corporation WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3470 - Dell Optiplex Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3600 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3600 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3650 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 3800 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4200 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4200 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4200)",
"ANGLE (ATI Radeon HD 4250 )",
"ANGLE (ATI Radeon HD 4250 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4250 Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4250)",
"ANGLE (ATI Radeon HD 4300 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4300/4500 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4350 (Microsoft Corporation WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4350 )",
"ANGLE (ATI Radeon HD 4350 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4350 Series)",
"ANGLE (ATI Radeon HD 4350)",
"ANGLE (ATI Radeon HD 4550 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4600 Series (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4600 Series )",
"ANGLE (ATI Radeon HD 4600 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4600 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4600 Serisi (Microsoft Corporation- WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4650 (Microsoft Corporation WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4650 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4670)",
"ANGLE (ATI Radeon HD 4770 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4800 Series (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4800 Series (Microsoft Corporation WDDM 1.1) )",
"ANGLE (ATI Radeon HD 4800 Series Direct3D11 vs_4_1 ps_4_1)",
"ANGLE (ATI Radeon HD 4800 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4800 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 4800 Series)",
"ANGLE (ATI Radeon HD 4870 X2 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5400 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5450 (Microsoft Corporation - WDDM v1.20) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5450 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5450)",
"ANGLE (ATI Radeon HD 5570 (Microsoft Corporation - WDDM v1.20) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5570 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5600 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5670 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5700 Series (Microsoft Corporation - WDDM v1.20) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5700 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5700 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 5800 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 6230 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD 6350 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon HD4670 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (ATI Radeon X1050 Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X1050 Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X1200 Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X1200 Series (Microsoft Corporation - WDDM) Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X1200 Series Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X1200 Series Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X1200 Series)",
"ANGLE (ATI Radeon X1250 Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X1270 Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X300/X550/X1050 Series Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X300/X550/X1050 Series Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon X300/X550/X1050 Series)",
"ANGLE (ATI Radeon Xpress 1100 Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon Xpress 1150 Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon Xpress 1150 Series Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon Xpress 1150 Series Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon Xpress 1200 Series Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon Xpress 1250 Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (ATI Radeon Xpress 200 Series Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (Intel(R) 4 Series Internal Chipset Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Intel(R) 4 Series Internal Chipset Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) 82915G/GV/910GL Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) 82945G Express Chipset Family (Microsoft Corporation - WDDM 1.0) Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) 82945G Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) 82945G Express Chipset Family Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) 82945G Express Chipset Family)",
"ANGLE (Intel(R) 946GZ Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) 946GZ Express Chipset Family Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) B43 Express Chipset Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) G33/G31 Express Chipset Family (Microsoft Corporation - WDDM 1.0) Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) G33/G31 Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) G33/G31 Express Chipset Family Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) G33/G31 Express Chipset Family)",
"ANGLE (Intel(R) G41 Express Chipset (Microsoft Corporation - WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) G41 Express Chipset (Microsoft Corporation - WDDM 1.1))",
"ANGLE (Intel(R) G41 Express Chipset Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Intel(R) G41 Express Chipset Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) G41 Express Chipset)",
"ANGLE (Intel(R) G45/G43 Express Chipset (Microsoft Corporation - WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) G45/G43 Express Chipset Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) G965 Express Chipset Family Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) Graphics Media Accelerator 3150 (Microsoft Corporation - WDDM 1.0) Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Graphics Media Accelerator 3150 Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Graphics Media Accelerator 3150 Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Graphics Media Accelerator 3150)",
"ANGLE (Intel(R) Graphics Media Accelerator 3600 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) Graphics Media Accelerator HD )",
"ANGLE (Intel(R) Graphics Media Accelerator HD Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics 3000 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics 3000)",
"ANGLE (Intel(R) HD Graphics 4000 (Microsoft Corporation - WDDM 1.2) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics 4000 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics 4000)",
"ANGLE (Intel(R) HD Graphics 4400 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics 4600 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics Family Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics Family Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) HD Graphics Family)",
"ANGLE (Intel(R) HD Graphics)",
"ANGLE (Intel(R) Q33 Express Chipset Family (Microsoft Corporation - WDDM 1.0) Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q33 Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q33 Express Chipset Family Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q35 Express Chipset Family (Microsoft Corporation - WDDM 1.0) Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q35 Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q35 Express Chipset Family Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q45/Q43 Express Chipset (Microsoft Corporation - WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) Q45/Q43 Express Chipset (Microsoft Corporation - WDDM 1.1))",
"ANGLE (Intel(R) Q45/Q43 Express Chipset Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Intel(R) Q45/Q43 Express Chipset Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Intel(R) Q45/Q43 Express Chipset)",
"ANGLE (Intel(R) Q965/Q963 Express Chipset Family (Microsoft Corporation - WDDM 1.0) Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q965/Q963 Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q965/Q963 Express Chipset Family Direct3D9Ex vs_0_0 ps_2_0)",
"ANGLE (Intel(R) Q965/Q963 Express Chipset Family)",
"ANGLE (Microsoft Basic Render Driver Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) - famiglia Express Chipset 45 (Microsoft Corporation - WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 4 Series Express Chipset Family (Microsoft Corporation - WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 4 Series Express Chipset Family Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 4 Series Express Chipset Family Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 4 Series Express Chipset Family)",
"ANGLE (Mobile Intel(R) 45 Express Chipset Family (Microsoft Corporation - WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 915GM/GMS,910GML Express Chipset Family Direct3D9 vs_0_0 ps_2_0)",
"ANGLE (Mobile Intel(R) 915GM/GMS,910GML Express Chipset Family)",
"ANGLE (Mobile Intel(R) 945 Express Chipset Family)",
"ANGLE (Mobile Intel(R) 945GM Express Chipset Family)",
"ANGLE (Mobile Intel(R) 965 Express Chipset Family (Microsoft Corporation - WDDM 1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 965 Express Chipset Family Direct3D9 vs_0_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 965 Express Chipset Family Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 965 Express Chipset Family Direct3D9Ex vs_0_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 965 Express Chipset Family Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) 965 Express Chipset Family)",
"ANGLE (Mobile Intel(R) HD Graphics Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Mobile Intel(R) HD Graphics)",
"ANGLE (NVIDIA GeForce 210 )",
"ANGLE (NVIDIA GeForce 210 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 210 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 210)",
"ANGLE (NVIDIA GeForce 310 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 310M (Microsoft Corporation - WDDM v1.2) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 310M )",
"ANGLE (NVIDIA GeForce 310M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 310M)",
"ANGLE (NVIDIA GeForce 315 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 315M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 405 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 405)",
"ANGLE (NVIDIA GeForce 405M)",
"ANGLE (NVIDIA GeForce 410M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 605 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6100 nForce 405 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6100 nForce 405 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6100)",
"ANGLE (NVIDIA GeForce 610M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6150 LE (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6150 LE Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6150SE nForce 430 (Microsoft Corporation - WDDM v1.2) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6150SE nForce 430 (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6150SE nForce 430 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6150SE nForce 430 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6150SE nForce 430)",
"ANGLE (NVIDIA GeForce 6200 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6200 TurboCache(TM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6500 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6600 (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6600 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 6600 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7000M / nForce 610M Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7000M / nForce 610M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7025 / NVIDIA nForce 630a (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7025 / NVIDIA nForce 630a )",
"ANGLE (NVIDIA GeForce 7025 / NVIDIA nForce 630a Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7025 / NVIDIA nForce 630a Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7025 / NVIDIA nForce 630a)",
"ANGLE (NVIDIA GeForce 7050 / NVIDIA nForce 620i Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7050 PV / NVIDIA nForce 630a Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7100 / NVIDIA nForce 630i Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7100 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7150M / nForce 630M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 GS Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 GT (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 LE (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 LE Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 SE/7200 GS (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 SE/7200 GS Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7300 SE/7200 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 7900 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8200 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8200M G Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8200M G)",
"ANGLE (NVIDIA GeForce 8300 GS )",
"ANGLE (NVIDIA GeForce 8300 GS Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8300 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8400 GS (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8400 GS Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8400 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8400 GS)",
"ANGLE (NVIDIA GeForce 8400GS Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8400GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8400M GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8400M GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8500 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8500 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600 GT (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600 GT )",
"ANGLE (NVIDIA GeForce 8600 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600 GT)",
"ANGLE (NVIDIA GeForce 8600 GTS (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600 GTS Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600 GTS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600GS (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600M GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8600M GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8800 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8800 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8800 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8800 GTS 512 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 8800 GTS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9100 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9200 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9200M GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9300 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9300 GE (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9300 GE Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9300 GE Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9300M GS (Microsoft Corporation - WDDM v1.1))",
"ANGLE (NVIDIA GeForce 9300M GS )",
"ANGLE (NVIDIA GeForce 9300M GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9400 GT (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9400 GT )",
"ANGLE (NVIDIA GeForce 9400 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9400 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9400 GT)",
"ANGLE (NVIDIA GeForce 9400M )",
"ANGLE (NVIDIA GeForce 9500 GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9500 GS)",
"ANGLE (NVIDIA GeForce 9500 GT (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9500 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9500 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9500 GT)",
"ANGLE (NVIDIA GeForce 9500M GS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9600 GSO 512 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9600 GT (Microsoft Corporation - WDDM v1.1))",
"ANGLE (NVIDIA GeForce 9600 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9600 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9600 GT)",
"ANGLE (NVIDIA GeForce 9600M GS)",
"ANGLE (NVIDIA GeForce 9600M GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9600M GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9650M GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9700M GTS Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9800 GT (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9800 GT (Microsoft Corporation - WDDM v1.1))",
"ANGLE (NVIDIA GeForce 9800 GT )",
"ANGLE (NVIDIA GeForce 9800 GT Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9800 GT Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce 9800 GTX/9800 GTX+ Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce FX 5200 Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (NVIDIA GeForce G 103M )",
"ANGLE (NVIDIA GeForce G 105M )",
"ANGLE (NVIDIA GeForce G 105M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce G100 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce G100)",
"ANGLE (NVIDIA GeForce G102M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce G105M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce G210 )",
"ANGLE (NVIDIA GeForce G210 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce G210)",
"ANGLE (NVIDIA GeForce G210M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce Go 7300 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 120M)",
"ANGLE (NVIDIA GeForce GT 220 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 220 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 220)",
"ANGLE (NVIDIA GeForce GT 230 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 230M )",
"ANGLE (NVIDIA GeForce GT 230M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 240 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 240 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 240)",
"ANGLE (NVIDIA GeForce GT 240M )",
"ANGLE (NVIDIA GeForce GT 240M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 320 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 320M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 330 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 330M )",
"ANGLE (NVIDIA GeForce GT 330M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 335M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 420 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 420M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 425M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 430 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 430 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 430)",
"ANGLE (NVIDIA GeForce GT 440 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 440 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 440)",
"ANGLE (NVIDIA GeForce GT 520 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 520 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 520)",
"ANGLE (NVIDIA GeForce GT 520M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 520M)",
"ANGLE (NVIDIA GeForce GT 525M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 530 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 545 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 555M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 610 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 610)",
"ANGLE (NVIDIA GeForce GT 620 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 620)",
"ANGLE (NVIDIA GeForce GT 625 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 630 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 630 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 630M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 635 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 640 )",
"ANGLE (NVIDIA GeForce GT 640 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 640)",
"ANGLE (NVIDIA GeForce GT 640M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 650M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 740M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GT 755M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTS 240 Direct3D11 vs_4_0 ps_4_0)",
"ANGLE (NVIDIA GeForce GTS 250 (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTS 250 (Microsoft Corporation - WDDM v1.1))",
"ANGLE (NVIDIA GeForce GTS 250 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTS 250 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTS 250)",
"ANGLE (NVIDIA GeForce GTS 350M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTS 450 )",
"ANGLE (NVIDIA GeForce GTS 450 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTS 450 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTS 450)",
"ANGLE (NVIDIA GeForce GTX 260 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 275 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 285 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 295 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 460 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 460 SE Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 460)",
"ANGLE (NVIDIA GeForce GTX 460M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 480 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 550 Ti Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 550 Ti)",
"ANGLE (NVIDIA GeForce GTX 560 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 560 Ti )",
"ANGLE (NVIDIA GeForce GTX 560 Ti Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 560M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 570 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 580 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 580M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 650 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 650 Ti BOOST Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 650 Ti Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 650)",
"ANGLE (NVIDIA GeForce GTX 660 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 660 Ti Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 660M )",
"ANGLE (NVIDIA GeForce GTX 660M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 670 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 670)",
"ANGLE (NVIDIA GeForce GTX 675M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 680 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 690 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 760 (192-bit) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 760 Direct3D11 vs_5_0 ps_5_0)",
"ANGLE (NVIDIA GeForce GTX 760 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 770 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA GeForce GTX 780 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA ION Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA MCP67M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA nForce 750a SLI Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA NVS 300 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA NVS 3100M )",
"ANGLE (NVIDIA NVS 3100M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA NVS 4200M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA NVS 5100M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA NVS 5200M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA NVS 5400M )",
"ANGLE (NVIDIA Quadro 1000M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro 2000M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro 600 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 1500M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 1700 (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 1700 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 1800 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 2500M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 2700M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 3700 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 570 (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 570 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 580 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 770M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro FX 880M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro K3000M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro K600 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro NVS 110M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro NVS 135M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro NVS 140M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro NVS 140M)",
"ANGLE (NVIDIA Quadro NVS 160M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro NVS 160M)",
"ANGLE (NVIDIA Quadro NVS 285 Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro NVS 290 (Microsoft Corporation - WDDM v1.1) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (NVIDIA Quadro NVS 290 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon (TM) HD 6470M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon (TM) HD 6490M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon HD 6470M Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1300/X1550 Series (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1300/X1550 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Radeon X1300/X1550 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1550 64-bit (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1550 Series (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1650 SE Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1650 Series (Microsoft Corporation - WDDM) Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1650 Series )",
"ANGLE (Radeon X1650 Series Direct3D9 vs_3_0 ps_3_0)",
"ANGLE (Radeon X1650 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X1950 Series Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Radeon X300/X550/X1050 Series (Microsoft Corporation - WDDM) Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (Radeon X300/X550/X1050 Series Direct3D9 vs_2_0 ps_2_0)",
"ANGLE (Radeon X800 GTO (Microsoft Corporation - WDDM) Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (RDPDD Chained DD Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (Royal BNA Driver Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (RS880 Direct3D9Ex vs_3_0 ps_3_0)",
"ANGLE (SiS Mirage 3 Graphics Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (SiS Mirage 3 Graphics)",
"ANGLE (VIA Chrome9 HC IGP Family WDDM Direct3D9Ex vs_2_0 ps_2_0)",
"ANGLE (WinFast GT 640(NVIDIA) Direct3D9Ex vs_3_0 ps_3_0)"
]

View File

@ -29,7 +29,6 @@ async function getMessagesInFile(path){
return getMessagesInContent(content); return getMessagesInContent(content);
} }
else { else {
// eslint-disable-next-line no-console
console.log("file does not exist:", path); console.log("file does not exist:", path);
return []; return [];
} }

View File

@ -83,12 +83,10 @@ async function translate(language){
return data; return data;
} }
translate(language).then(function(data){ (async function(){
"use strict"; "use strict";
return saveTranslation(language, data); const data = await translate(language);
}).catch(function(error){
"use strict";
console.error(error); saveTranslation(language, data);
}); }());

65
.vscode/settings.json vendored
View File

@ -1,55 +1,63 @@
{ {
"cSpell.words": [ "cSpell.words": [
"arial",
"Benachrichtigungsdetails", "Benachrichtigungsdetails",
"benachrichtigungsicon",
"Blockiermodi", "Blockiermodi",
"Blockiermodus", "Blockiermodus",
"canvasblocker",
"Captcha", "Captcha",
"checkmark",
"collapser",
"Coord", "Coord",
"darkgreen",
"dont",
"fakeable",
"Fenix",
"fragmenter",
"Funktionalitätstest", "Funktionalitätstest",
"Funktionalitätstests",
"graytext",
"Hyrp", "Hyrp",
"iframe",
"ignorelist",
"Ignorierliste", "Ignorierliste",
"KHTML", "KHTML",
"Krasnaya", "Krasnaya",
"lightgray",
"Maleficient", "Maleficient",
"mediump",
"micrococo",
"monero",
"monospace",
"Nachfrageverweigerungsmodus", "Nachfrageverweigerungsmodus",
"nocanvas",
"Oakenpants", "Oakenpants",
"PDFs", "onedrive",
"onloaded",
"oscpu",
"Palemoon", "Palemoon",
"paypal",
"PDFs",
"Ploshchad", "Ploshchad",
"prefs",
"promisify",
"recaptcha",
"Rect", "Rect",
"Rects", "Rects",
"ruleset",
"spodermenpls",
"Spoofer", "Spoofer",
"statechange",
"Strg", "Strg",
"SVGAPI",
"Thorin", "Thorin",
"Tiie", "Tiie",
"unticking",
"Vortäuschaktion", "Vortäuschaktion",
"Vortäuschgröße", "Vortäuschgröße",
"Vortäuschrate", "Vortäuschrate",
"Waterfox", "Waterfox",
"arial",
"benachrichtigungsicon",
"canvasblocker",
"collapser",
"darkgreen",
"dont",
"fakeable",
"fragmenter",
"graytext",
"iframe",
"ignorelist",
"lightgray",
"mediump",
"micrococo",
"monero",
"nocanvas",
"onedrive",
"onloaded",
"oscpu",
"prefs",
"promisify",
"ruleset",
"spodermenpls",
"unticking",
"webgl", "webgl",
"whitelisted", "whitelisted",
"writeln", "writeln",
@ -63,13 +71,14 @@
"**/vscode-extension/**", "**/vscode-extension/**",
"**/.git/objects/**", "**/.git/objects/**",
".vscode", ".vscode",
".eslintrc.json" ".eslintrc.json",
".tools/chromeVendors.json"
], ],
"eslint.validate": [ "eslint.validate": [
"javascript", "javascript",
"php", "php",
"html" "html"
], ],
"eslint.options": {"--ext": ".js,.html,.php"}, "eslint.lintTask.enable": true,
"eslint.lintTask.enable": true "cSpell.enabled": true
} }

35
.vscode/tasks.json vendored
View File

@ -45,7 +45,7 @@
"-f", "-f",
"nightly", "nightly",
"--url", "--url",
"http://canvasblocker.local/test/" "http://canvasblocker.localhost/test/"
], ],
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -70,7 +70,7 @@
"args": [ "args": [
"run", "run",
"--url", "--url",
"http://canvasblocker.local/test/" "http://canvasblocker.localhost/test/"
], ],
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -97,7 +97,7 @@
"-f", "-f",
"firefox-esr", "firefox-esr",
"--url", "--url",
"http://canvasblocker.local/test/" "http://canvasblocker.localhost/test/"
], ],
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -124,7 +124,7 @@
"-f", "-f",
"firefox-beta", "firefox-beta",
"--url", "--url",
"http://canvasblocker.local/test/" "http://canvasblocker.localhost/test/"
], ],
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -154,6 +154,8 @@
"--ignore-files", "--ignore-files",
"versions", "versions",
"--ignore-files", "--ignore-files",
"crowdin.yml",
"--ignore-files",
"package*" "package*"
], ],
"presentation": { "presentation": {
@ -164,6 +166,31 @@
}, },
"problemMatcher": [] "problemMatcher": []
}, },
{
"label": "build alpha",
"type": "shell",
"windows": {
"command": "node"
},
"linux": {
"command": "node"
},
"osx": {
"command": "node"
},
"args": [
".tools/build.js",
"--type",
"alpha"
],
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
},
"problemMatcher": []
},
{ {
"label": "web-ext lint", "label": "web-ext lint",
"type": "shell", "type": "shell",

View File

@ -4,7 +4,7 @@ CanvasBlocker [![codebeat badge](https://codebeat.co/badges/0edd6c9f-250a-4f1e-9
This add-on allows users to prevent websites from using some Javascript APIs to fingerprint them. Users can choose to block the APIs entirely on some or all websites (which may break some websites) or just block or fake its fingerprinting-friendly readout API. This add-on allows users to prevent websites from using some Javascript APIs to fingerprint them. Users can choose to block the APIs entirely on some or all websites (which may break some websites) or just block or fake its fingerprinting-friendly readout API.
**IMPORTANT**: you should only have ONE addon/setting set that protects an API. Otherwise you could face massive performance issues. (E.g. EclipsedMoon for Palemoon has 'canvas.poison' which is known to cause issues: https://github.com/kkapsner/CanvasBlocker/issues/253#issuecomment-459499290) **IMPORTANT**: you should only have ONE addon/setting set that protects an API. Otherwise you could face massive performance issues. (E.g. EclipsedMoon for Palemoon has 'canvas.poison' which is known to cause issues: https://github.com/kkapsner/CanvasBlocker/issues/253#issuecomment-459499290)
But setting privacy.resistFingerprinting to true is fine. But setting privacy.resistFingerprinting to true and/or using the new fingerprinting protection introduced with Firefox 67 is fine.
----- -----
@ -15,11 +15,13 @@ Protected "fingerprinting" APIs:
* history * history
* window (disabled by default) * window (disabled by default)
* DOMRect * DOMRect
* SVG
* TextMetrics
* navigator (disabled by default) * navigator (disabled by default)
* screen * screen
More information on fingerprinting can be found at: More information on fingerprinting can be found at:
* &lt;canvas&gt;: http://www.browserleaks.com/canvas * &lt;canvas&gt;: https://www.browserleaks.com/canvas
* audio: * audio:
* https://audiofingerprint.openwpm.com/ (very poorly written = slow) * https://audiofingerprint.openwpm.com/ (very poorly written = slow)
* https://webtransparency.cs.princeton.edu/webcensus/#audio-fp * https://webtransparency.cs.princeton.edu/webcensus/#audio-fp
@ -44,6 +46,8 @@ The different block modes are:
----- -----
You can contribute to CanvasBlocker by translating it and/or improving the translations. For further instructions go to https://github.com/kkapsner/CanvasBlocker/issues/420.
Special thanks to: Special thanks to:
* spodermenpls for finding all the typos * spodermenpls for finding all the typos
* Thorin-Oakenpants for the icon idea * Thorin-Oakenpants for the icon idea

1670
_locales/cs/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
"description": "" "description": ""
}, },
"addon_description": { "addon_description": {
"message": "Verändert einige JS-APIs um Fingerprinting zu verhindern.", "message": "Verändert einige JS-APIs, um Fingerprinting zu verhindern.",
"description": "" "description": ""
}, },
"browserAction_title_default": { "browserAction_title_default": {
@ -20,7 +20,15 @@
"description": "" "description": ""
}, },
"browserAction_title_protectedAPIs": { "browserAction_title_protectedAPIs": {
"message": "\n \u00B7 {api}", "message": "\n · {api}",
"description": ""
},
"browserAction_status_on": {
"message": "CanvasBlocker an",
"description": ""
},
"browserAction_status_off": {
"message": "CanvasBlocker aus",
"description": "" "description": ""
}, },
"more": { "more": {
@ -64,7 +72,7 @@
"description": "" "description": ""
}, },
"options_title": { "options_title": {
"message": "CanvasBlocker Einstellungen", "message": "CanvasBlocker-Einstellungen",
"description": "" "description": ""
}, },
"optionsIntroduction": { "optionsIntroduction": {
@ -83,10 +91,22 @@
"message": "Bei Aktualisierung nicht wieder anzeigen.", "message": "Bei Aktualisierung nicht wieder anzeigen.",
"description": "" "description": ""
}, },
"resistFingerprintingNotice": {
"message": "Sie haben privacy.resistFingerprinting aktiviert. Dies verändert das Verhalten von CanvasBlocker ein wenig. Weitere Informationen finden Sie {link:hier:https://github.com/kkapsner/CanvasBlocker/issues/158} und {link:hier:https://github.com/ghacksuserjs/ghacks-user.js/issues/767}.",
"description": ""
},
"settingsNotice.dom.webAudio.enabled": {
"message": "Sie haben dom.webAudio.enabled deaktiviert. Da sehr wenige das tun, macht Sie das mehr nachverfolgbar.",
"description": ""
},
"openInTab": { "openInTab": {
"message": "In separatem Tab öffnen", "message": "In separatem Tab öffnen",
"description": "" "description": ""
}, },
"labelForDefaultOption": {
"message": " (Standard)",
"description": ""
},
"group_general": { "group_general": {
"message": "Allgemein", "message": "Allgemein",
"description": "" "description": ""
@ -96,7 +116,7 @@
"description": "" "description": ""
}, },
"group_misc": { "group_misc": {
"message": "Vermischtes", "message": "Sonstiges",
"description": "" "description": ""
}, },
"section_asking": { "section_asking": {
@ -116,7 +136,7 @@
"description": "" "description": ""
}, },
"section_misc": { "section_misc": {
"message": "Vermischtes", "message": "Sonstiges",
"description": "" "description": ""
}, },
"section_settings": { "section_settings": {
@ -143,6 +163,14 @@
"message": "DOMRect-API", "message": "DOMRect-API",
"description": "" "description": ""
}, },
"section_SVG-api": {
"message": "SVG-API",
"description": ""
},
"section_TextMetrics-api": {
"message": "TextMetrics-API",
"description": ""
},
"section_Navigator-api": { "section_Navigator-api": {
"message": "Navigator-API", "message": "Navigator-API",
"description": "" "description": ""
@ -167,6 +195,26 @@
"message": "Zeigt die Beschreibungen der Einstellungen an.", "message": "Zeigt die Beschreibungen der Einstellungen an.",
"description": "" "description": ""
}, },
"disruptSessionOnUpdate_title": {
"message": "Aktualisierung unterbricht Sitzung",
"description": ""
},
"disruptSessionOnUpdate_description": {
"message": "Die Erweiterung wird sofort aktualisiert, sobald ein Update verfügbar ist, wenn aktiviert. Dies könnte einige Tabs unbrauchbar machen, die derzeit geöffnet sind.",
"description": ""
},
"reloadExtension_title": {
"message": "Erweiterung neu laden",
"description": ""
},
"reloadExtension_description": {
"message": "Führt eine ausstehende Aktualisierung aus.",
"description": ""
},
"reloadExtension_label": {
"message": "Neu laden",
"description": ""
},
"hideSetting": { "hideSetting": {
"message": "Hier klicken, um diese Einstellung zu verbergen.", "message": "Hier klicken, um diese Einstellung zu verbergen.",
"description": "" "description": ""
@ -263,6 +311,30 @@
"message": "Wollen Sie das Auslesen über die DOMRect-API erlauben?", "message": "Wollen Sie das Auslesen über die DOMRect-API erlauben?",
"description": "" "description": ""
}, },
"askForSVGPermission": {
"message": "Wollen Sie die SVG-API erlauben?",
"description": ""
},
"askForSVGInputPermission": {
"message": "Wollen Sie das Schreiben über die SVG-API erlauben?",
"description": ""
},
"askForSVGReadoutPermission": {
"message": "Wollen Sie das Auslesen über die SVG-API erlauben?",
"description": ""
},
"askForTextMetricsPermission": {
"message": "Wollen Sie die TextMetrics-API erlauben?",
"description": ""
},
"askForTextMetricsInputPermission": {
"message": "Wollen Sie das Schreiben über die TextMetrics-API erlauben?",
"description": ""
},
"askForTextMetricsReadoutPermission": {
"message": "Wollen Sie das Auslesen über die TextMetrics-API erlauben?",
"description": ""
},
"askForNavigatorPermission": { "askForNavigatorPermission": {
"message": "Wollen Sie die Navigator-API erlauben?", "message": "Wollen Sie die Navigator-API erlauben?",
"description": "" "description": ""
@ -436,7 +508,7 @@
"description": "" "description": ""
}, },
"rng_description": { "rng_description": {
"message": "nichts (komplett weiß): es wird immer ein weißes Bild zurückgegeben. Hierbei sollte die Option \"Alpha-Kanal auch vortäuschen\" aktiviert werden. ACHTUNG: Nicht im Modus \"Bei Ausgabe vortäuschen\" verwenden.\n\nnicht persistent: die Zufallszahlen werden bei jeder Vortäuschaktion neu bestimmt.\n\nkonstant: innerhalb einer Webseite wird eine Farbe immer gleich verändert.\n\npersistent: für jede Domain werden die Zufallszahlen nur einmal bestimmt.", "message": "nichts (komplett weiß): es wird immer ein weißes Bild zurückgegeben. Hierbei sollte die Option \"Alpha-Kanal auch vortäuschen\" aktiviert werden. ACHTUNG: Nicht im Modus \"Bei Ausgabe vortäuschen\" verwenden.\n\nnichtpersistent: die Zufallszahlen werden bei jeder Vortäuschaktion neu bestimmt. Beachten Sie, dass für viele APIs ein Zwischenspeicher verwendet wird, um eine Detektion zu verhindern.\n\nkonstant: Variante von nichtpersistent. Wenn die Daten eines Canvas verändert werden, haben gleichfarbige Pixel danach auch die gleiche Farbe.\n\npersistent: für jede Domain werden die Zufallszahlen nur einmal bestimmt.",
"description": "" "description": ""
}, },
"rng_options.persistent": { "rng_options.persistent": {
@ -448,7 +520,7 @@
"description": "" "description": ""
}, },
"rng_options.nonPersistent": { "rng_options.nonPersistent": {
"message": "nicht persistent", "message": "nichtpersistent",
"description": "" "description": ""
}, },
"rng_options.white": { "rng_options.white": {
@ -572,7 +644,7 @@
"description": "" "description": ""
}, },
"webGLVendor_description": { "webGLVendor_description": {
"message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"vendor\" verwendet. Leer lassen, um den Originalwert zu verwenden.", "message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"vendor\" verwendet. Spezielle Werte:\nLeer lassen, um den Originalwert zu verwenden\n\n{undefined}: gibt undefined zurück (#508)\n\n{false}: gibt den Wahrheitsert falsch zurück (#508)\n\n{empty}: gibt eine leere Zeichenkette zurück(#508)\n\n{disabled}: gibt null zurück (#508)\n\n{random vendor}: gibt einen zufälligen \"vendor\" aus der eingebauten Liste zurück (#493)\n\n{random renderer}: gibt einen zufälligen \"renderer\" aus der eingebauten Liste zurück (#493)\n\n<xxx|yyy|zzz>: wählt zufällig eine der Optionen xxx, yyy oder zzz aus (beliebige Anzahl von Optionen) (#493)",
"description": "" "description": ""
}, },
"webGLRenderer_title": { "webGLRenderer_title": {
@ -580,7 +652,7 @@
"description": "" "description": ""
}, },
"webGLRenderer_description": { "webGLRenderer_description": {
"message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"renderer\" verwendet. Leer lassen, um den Originalwert zu verwenden.", "message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"renderer\" verwendet. Spezielle Werte:\nLeer lassen, um den Originalwert zu verwenden\n\n{undefined}: gibt undefined zurück (#508)\n\n{false}: gibt den Wahrheitsert falsch zurück (#508)\n\n{empty}: gibt eine leere Zeichenkette zurück(#508)\n\n{disabled}: gibt null zurück (#508)\n\n{random vendor}: gibt einen zufälligen \"vendor\" aus der eingebauten Liste zurück (#493)\n\n{random renderer}: gibt einen zufälligen \"renderer\" aus der eingebauten Liste zurück (#493)\n\n<xxx|yyy|zzz>: wählt zufällig eine der Optionen xxx, yyy oder zzz aus (beliebige Anzahl von Optionen) (#493)",
"description": "" "description": ""
}, },
"webGLUnmaskedVendor_title": { "webGLUnmaskedVendor_title": {
@ -588,7 +660,7 @@
"description": "" "description": ""
}, },
"webGLUnmaskedVendor_description": { "webGLUnmaskedVendor_description": {
"message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"unmasked vendor\" verwendet. Leer lassen, um den Originalwert zu verwenden.", "message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"unmasked vendor\" verwendet. Spezielle Werte:\nLeer lassen, um den Originalwert zu verwenden\n\n{undefined}: gibt undefined zurück (#508)\n\n{false}: gibt den Wahrheitsert falsch zurück (#508)\n\n{empty}: gibt eine leere Zeichenkette zurück(#508)\n\n{disabled}: gibt null zurück (#508)\n\n{random vendor}: gibt einen zufälligen \"vendor\" aus der eingebauten Liste zurück (#493)\n\n{random renderer}: gibt einen zufälligen \"renderer\" aus der eingebauten Liste zurück (#493)\n\n<xxx|yyy|zzz>: wählt zufällig eine der Optionen xxx, yyy oder zzz aus (beliebige Anzahl von Optionen) (#493)",
"description": "" "description": ""
}, },
"webGLUnmaskedRenderer_title": { "webGLUnmaskedRenderer_title": {
@ -596,7 +668,7 @@
"description": "" "description": ""
}, },
"webGLUnmaskedRenderer_description": { "webGLUnmaskedRenderer_description": {
"message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"unmasked renderer\" verwendet. Leer lassen, um den Originalwert zu verwenden.", "message": "Dieser Wert wird in der webGL-Funktion \"getParameter\" für \"unmasked renderer\" verwendet. Spezielle Werte:\nLeer lassen, um den Originalwert zu verwenden\n\n{undefined}: gibt undefined zurück (#508)\n\n{false}: gibt den Wahrheitsert falsch zurück (#508)\n\n{empty}: gibt eine leere Zeichenkette zurück(#508)\n\n{disabled}: gibt null zurück (#508)\n\n{random vendor}: gibt einen zufälligen \"vendor\" aus der eingebauten Liste zurück (#493)\n\n{random renderer}: gibt einen zufälligen \"renderer\" aus der eingebauten Liste zurück (#493)\n\n<xxx|yyy|zzz>: wählt zufällig eine der Optionen xxx, yyy oder zzz aus (beliebige Anzahl von Optionen) (#493)",
"description": "" "description": ""
}, },
"useCanvasCache_title": { "useCanvasCache_title": {
@ -667,6 +739,14 @@
"message": "DOMRect-Auslese vorgetäuscht auf {url}", "message": "DOMRect-Auslese vorgetäuscht auf {url}",
"description": "" "description": ""
}, },
"fakedSVGReadout": {
"message": "SVG-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedTextMetricsReadout": {
"message": "TextMetrics-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedNavigatorReadout": { "fakedNavigatorReadout": {
"message": "Navigator-Auslese vorgetäuscht auf {url}", "message": "Navigator-Auslese vorgetäuscht auf {url}",
"description": "" "description": ""
@ -759,6 +839,10 @@
"message": "Was soll erlaubt werden?", "message": "Was soll erlaubt werden?",
"description": "" "description": ""
}, },
"selectWhitelistType": {
"message": "Wie soll erlaubt werden?",
"description": ""
},
"whitelistOnlyAPI": { "whitelistOnlyAPI": {
"message": "Erlaube nur die {api}", "message": "Erlaube nur die {api}",
"description": "" "description": ""
@ -1068,7 +1152,19 @@
"description": "" "description": ""
}, },
"protectWindow_askReCaptchaException": { "protectWindow_askReCaptchaException": {
"message": "Wenn die Window-API beschützt wird, funktioniert reCAPTCHA nicht mehr. Wollen Sie dafür eine Ausnahme hinzufügen?", "message": "Wenn die Window-API beschützt wird, funktioniert reCAPTCHA nicht mehr. Wollen Sie die window.name-API in eingebetteten Seiten erlauben, damit es wieder funktioniert?",
"description": ""
},
"allowWindowNameInFrames_title": {
"message": "Erlaube window.name in Frames",
"description": ""
},
"allowWindowNameInFrames_description": {
"message": "Die window.name-API ist im Kontext eingebetteter Seiten nicht besonders gefährlich und wird dort für legitime Anwendungen (z.B. reCAPTCHA) verwendet. Diese Einstellung erlaubt die Benutzung dort.",
"description": ""
},
"allowWindowNameInFrames_urlSpecific": {
"message": "Um dies nur für bestimmte Seiten zu erlauben, klicken Sie auf den schwarzen Pfeil um das Menü zu öffnen, fügen Sie die gewünschte Domain oder URL mit einem Klick auf \"+\" hinzu und setzen Sie das zugehörige Häkchen.",
"description": "" "description": ""
}, },
"protectDOMRect_title": { "protectDOMRect_title": {
@ -1091,6 +1187,30 @@
"message": "Ein Bruchteil eines Pixels kann durch CSS kontrolliert werden. Eigenschaften eines DOMRect, die multipliziert mit diesem Faktor eine ganze Zahl ergeben, dürfen nicht verändert werden um eine Detektion zu verhindern.", "message": "Ein Bruchteil eines Pixels kann durch CSS kontrolliert werden. Eigenschaften eines DOMRect, die multipliziert mit diesem Faktor eine ganze Zahl ergeben, dürfen nicht verändert werden um eine Detektion zu verhindern.",
"description": "" "description": ""
}, },
"protectSVG_title": {
"message": "SVG-API beschützen",
"description": ""
},
"protectSVG_description": {
"message": "Dies schützt vor Fingerprinting, das SVGs verwendet.",
"description": ""
},
"protectSVG_urlSpecific": {
"message": "Um bestimmte Seiten von diesem Schutz auszuschließen, klicken Sie auf den schwarzen Pfeil um das Menü zu öffnen, fügen Sie die gewünschte Domain oder URL mit einem Klick auf \"+\" hinzu und entfernen Sie das zugehörige Häkchen.",
"description": ""
},
"protectTextMetrics_title": {
"message": "TextMetrics-API beschützen",
"description": ""
},
"protectTextMetrics_description": {
"message": "Beschützt vor dem \"measureText()\" Fingerprint. Dieser kann verwendet werden um die DOMRect-Werte zu überprüfen.",
"description": ""
},
"protectTextMetrics_urlSpecific": {
"message": "Um bestimmte Seiten von diesem Schutz auszuschließen, klicken Sie auf den schwarzen Pfeil um das Menü zu öffnen, fügen Sie die gewünschte Domain oder URL mit einem Klick auf \"+\" hinzu und entfernen Sie das zugehörige Häkchen.",
"description": ""
},
"protectNavigator_title": { "protectNavigator_title": {
"message": "Navigator-API beschützen", "message": "Navigator-API beschützen",
"description": "" "description": ""
@ -1127,6 +1247,10 @@
"message": "ACHTUNG: der tatsächlich verwendete Browser kann nicht komplett vorgetäuscht werden, da es eine Vielzahl von Möglichkeiten gibt, diesen zu detektieren. Z.B. kann er immer über Funktionalitätstests und die browser-spezifische Darstellung von HTML-Elementen bestimmt werden.", "message": "ACHTUNG: der tatsächlich verwendete Browser kann nicht komplett vorgetäuscht werden, da es eine Vielzahl von Möglichkeiten gibt, diesen zu detektieren. Z.B. kann er immer über Funktionalitätstests und die browser-spezifische Darstellung von HTML-Elementen bestimmt werden.",
"description": "" "description": ""
}, },
"navigatorSettings_contextualIdentities": {
"message": "Es werden die Einstellungen der Tab-Umgebung {select} gezeigt.",
"description": ""
},
"navigatorSettings_presetSection.os": { "navigatorSettings_presetSection.os": {
"message": "Betriebssystemvoreinstellungen", "message": "Betriebssystemvoreinstellungen",
"description": "" "description": ""
@ -1164,7 +1288,7 @@
"description": "" "description": ""
}, },
"screenSize_urlSpecific": { "screenSize_urlSpecific": {
"message": "Um für bestimmte Seiten spezifische Werte zu verwenden, klicken Sie auf den schwarzen Pfeil um das Menü zu öffnen, fügen Sie die gewünsche Domain oder URL mit einem Klick auf \"+\" hinzu und geben sie den gewünschten Wert ein.", "message": "Um für bestimmte Seiten spezifische Werte zu verwenden, klicken Sie auf den schwarzen Pfeil um das Menü zu öffnen, fügen Sie die gewünschte Domain oder URL mit einem Klick auf \"+\" hinzu und geben sie den gewünschten Wert ein.",
"description": "" "description": ""
}, },
"fakeMinimalScreenSize_title": { "fakeMinimalScreenSize_title": {
@ -1335,6 +1459,10 @@
"message": "Einstellungen", "message": "Einstellungen",
"description": "" "description": ""
}, },
"browserAction_faq": {
"message": "FAQ",
"description": ""
},
"browserAction_test": { "browserAction_test": {
"message": "Testen", "message": "Testen",
"description": "" "description": ""
@ -1395,6 +1523,10 @@
"message": "Alle Funktionen der {api} sind deaktiviert, aber der Schutz ist eingeschaltet.", "message": "Alle Funktionen der {api} sind deaktiviert, aber der Schutz ist eingeschaltet.",
"description": "" "description": ""
}, },
"sanitation_error.disabledSomeFeatures": {
"message": "Einige Funktionen der {api} sind deaktiviert. Dies sollte nur zu Testzwecken passieren oder wenn Sie genau wissen, was diese Funktionen tun.",
"description": ""
},
"sanitation_resolution.disableMainFlag": { "sanitation_resolution.disableMainFlag": {
"message": "Hauptschalter deaktivieren", "message": "Hauptschalter deaktivieren",
"description": "" "description": ""
@ -1428,7 +1560,7 @@
"description": "" "description": ""
}, },
"sanitation_resolution.switchToNonPersistentRng": { "sanitation_resolution.switchToNonPersistentRng": {
"message": "wechsle zu \"nicht persistent\"", "message": "wechsle zu \"nichtpersistent\"",
"description": "" "description": ""
}, },
"sanitation_error.fakeEverythingInCanvas": { "sanitation_error.fakeEverythingInCanvas": {
@ -1471,6 +1603,10 @@
"message": "CanvasBlocker Erlaubnisse ansehen", "message": "CanvasBlocker Erlaubnisse ansehen",
"description": "" "description": ""
}, },
"whitelist_inspection_description": {
"message": "Anzeige des API-Schutzes für eine bestimmte Webseite. Wenn man das Häkchen bei einer API entfernt, wird diese für die ausgewählte Webseite nicht mehr beschützt.",
"description": ""
},
"whitelist_all_apis": { "whitelist_all_apis": {
"message": "Alle APIs", "message": "Alle APIs",
"description": "" "description": ""
@ -1520,7 +1656,15 @@
"description": "" "description": ""
}, },
"preset_max_protection_description": { "preset_max_protection_description": {
"message": "Maximiert den Schutz gegen die Fingerprint-Extraktion. Diese Einstellungen werden einige Seiten unbenutzbar machen, können den Browser etwas verlangsamen und es Seiten ermöglichen die Verwendung von CanvasBlocker zu detektieren.", "message": "Maximiert den Schutz gegen die Fingerprint-Extraktion. Diese Einstellungen werden einige Seiten unbenutzbar machen, können den Browser etwas verlangsamen und es Seiten ermöglichen die Verwendung von CanvasBlocker zu detektieren. Wenn Sie diese Voreinstellung angewendet haben, sollten Sie in Betracht ziehen auch die reCAPTCHA-Voreinstellung zu verwenden.",
"description": ""
},
"preset_recaptcha_title": {
"message": "reCAPTCHA-Ausnahme",
"description": ""
},
"preset_recaptcha_description": {
"message": "Der Window-API-Schutz macht reCAPTCHA unbenutzbar. Diese Voreinstellung erlaubt die Benutzung der window.name-API in eingebetteten Seiten, wodurch es wieder funktioniert.",
"description": "" "description": ""
} }
} }

View File

@ -24,6 +24,14 @@
"message": "\n \u00B7 {api}", "message": "\n \u00B7 {api}",
"description": "" "description": ""
}, },
"browserAction_status_on": {
"message": "CanvasBlocker on",
"description": ""
},
"browserAction_status_off": {
"message": "CanvasBlocker off",
"description": ""
},
"more": { "more": {
"message": "more", "message": "more",
@ -84,15 +92,28 @@
"message": "CanvasBlocker was updated. If you want to be able to access this page in the future and have not bookmarked it yet, please bookmark it.", "message": "CanvasBlocker was updated. If you want to be able to access this page in the future and have not bookmarked it yet, please bookmark it.",
"description": "" "description": ""
}, },
"dontShowOptionsOnUpdate":{ "dontShowOptionsOnUpdate": {
"message": "Don't show up again after update.", "message": "Don't show up again after update.",
"description": "" "description": ""
}, },
"resistFingerprintingNotice": {
"message": "You have privacy.resistFingerprinting enabled. This slightly changes the behaviour of CanvasBlocker. See further information {link:here:https://github.com/kkapsner/CanvasBlocker/issues/158} and {link:here:https://github.com/ghacksuserjs/ghacks-user.js/issues/767}.",
"description": ""
},
"settingsNotice.dom.webAudio.enabled": {
"message": "You have dom.webAudio.enabled disabled. This makes you more trackable as very few people do this.",
"description": ""
},
"openInTab": { "openInTab": {
"message": "Open in separate tab", "message": "Open in separate tab",
"description": "" "description": ""
}, },
"labelForDefaultOption": {
"message": " (default)",
"description": ""
},
"group_general": { "group_general": {
"message": "General", "message": "General",
"description": "" "description": ""
@ -130,31 +151,39 @@
"message": "Settings", "message": "Settings",
"description": "" "description": ""
}, },
"section_canvas-api":{ "section_canvas-api": {
"message": "Canvas API", "message": "Canvas API",
"description": "" "description": ""
}, },
"section_audio-api":{ "section_audio-api": {
"message": "Audio API", "message": "Audio API",
"description": "" "description": ""
}, },
"section_history-api":{ "section_history-api": {
"message": "History API", "message": "History API",
"description": "" "description": ""
}, },
"section_window-api":{ "section_window-api": {
"message": "Window API", "message": "Window API",
"description": "" "description": ""
}, },
"section_DOMRect-api":{ "section_DOMRect-api": {
"message": "DOMRect API", "message": "DOMRect API",
"description": "" "description": ""
}, },
"section_Navigator-api":{ "section_SVG-api": {
"message": "SVG API",
"description": ""
},
"section_TextMetrics-api": {
"message": "TextMetrics API",
"description": ""
},
"section_Navigator-api": {
"message": "Navigator API", "message": "Navigator API",
"description": "" "description": ""
}, },
"section_Screen-api":{ "section_Screen-api": {
"message": "Screen API", "message": "Screen API",
"description": "" "description": ""
}, },
@ -177,6 +206,28 @@
"description": "" "description": ""
}, },
"disruptSessionOnUpdate_title": {
"message": "Disrupt session on update",
"description": ""
},
"disruptSessionOnUpdate_description": {
"message": "If set to true the extension will update as soon as the update is available. This might break some tabs that are currently open.",
"description": ""
},
"reloadExtension_title": {
"message": "Reload extension",
"description": ""
},
"reloadExtension_description": {
"message": "Perform a pending update.",
"description": ""
},
"reloadExtension_label": {
"message": "Reload",
"description": ""
},
"hideSetting": { "hideSetting": {
"message": "Click here to hide this setting.", "message": "Click here to hide this setting.",
"description": "" "description": ""
@ -275,6 +326,30 @@
"message": "Do you want to allow DOMRect API readout?", "message": "Do you want to allow DOMRect API readout?",
"description": "" "description": ""
}, },
"askForSVGPermission": {
"message": "Do you want to allow the SVG API?",
"description": ""
},
"askForSVGInputPermission": {
"message": "Do you want to allow SVG API input?",
"description": ""
},
"askForSVGReadoutPermission": {
"message": "Do you want to allow SVG API readout?",
"description": ""
},
"askForTextMetricsPermission": {
"message": "Do you want to allow the TextMetrics API?",
"description": ""
},
"askForTextMetricsInputPermission": {
"message": "Do you want to allow TextMetrics API input?",
"description": ""
},
"askForTextMetricsReadoutPermission": {
"message": "Do you want to allow TextMetrics API readout?",
"description": ""
},
"askForNavigatorPermission": { "askForNavigatorPermission": {
"message": "Do you want to allow the navigator API?", "message": "Do you want to allow the navigator API?",
"description": "" "description": ""
@ -304,7 +379,7 @@
"description": "" "description": ""
}, },
"askOnlyOnce_description": { "askOnlyOnce_description": {
"message": "When CanvasBlocker's block mode is set to 'ask permission' or 'ask permission for readout API', a confirm message will appear every time a page tries to access the API or readout API. This setting tries to display the confirm message only once for each page regardless of how many times the page tries to access the API. Nevertheless, multiple confirm messages may still be displayed on some pages.\n\nNo: asking every time\n\nIndividual: each API-type (context, input, readout) has to be confirmed separately\n\ncombined: all API-types get confirmed together", "message": "When CanvasBlocker's block mode is set to 'ask permission' or 'ask permission for readout API', a confirm message will appear every time a page tries to access the API or readout API. This setting tries to display the confirm message only once for each page regardless of how many times the page tries to access the API. Nevertheless, multiple confirm messages may still be displayed on some pages.\n\nNo: asking every time\n\nIndividual: each API type (context, input, readout) has to be confirmed separately\n\nCombined: all API types get confirmed together",
"description": "" "description": ""
}, },
"askOnlyOnce_options.no": { "askOnlyOnce_options.no": {
@ -319,32 +394,32 @@
"message": "combined", "message": "combined",
"description": "" "description": ""
}, },
"askDenyMode_title":{ "askDenyMode_title": {
"message": "Ask deny mode", "message": "Ask deny mode",
"description": "" "description": ""
}, },
"askDenyMode_description":{ "askDenyMode_description": {
"message": "Which mode shall be used when the permission is denied.", "message": "Which mode shall be used when the permission is denied.",
"description": "" "description": ""
}, },
"askDenyMode_options.block":{ "askDenyMode_options.block": {
"message": "block", "message": "block",
"description": "" "description": ""
}, },
"askDenyMode_options.fake":{ "askDenyMode_options.fake": {
"message": "fake", "message": "fake",
"description": "" "description": ""
}, },
"showCanvasWhileAsking_title":{ "showCanvasWhileAsking_title": {
"message": "Show canvas content", "message": "Show canvas content",
"description": "" "description": ""
}, },
"showCanvasWhileAsking_description":{ "showCanvasWhileAsking_description": {
"message": "Shows the content of the canvas for which the permission is asked for, if possible.", "message": "Shows the content of the canvas for which the permission is asked for, if possible.",
"description": "" "description": ""
}, },
"showCanvasWhileAsking_message":{ "showCanvasWhileAsking_message": {
"message": "The webpage wants to read the content of the following canvas:", "message": "The webpage wants to read the content of the following canvas:",
"description": "" "description": ""
}, },
@ -425,7 +500,7 @@
}, },
"urlSettings_title": { "urlSettings_title": {
"message": "Site specific values", "message": "Site-specific values",
"description": "" "description": ""
}, },
"urlSettings_description": { "urlSettings_description": {
@ -457,7 +532,7 @@
}, },
"rng_description": { "rng_description": {
"message": "none (completely white): a completely white image is returned. The option \"Fake the alpha channel\" should be activated with this. CAUTION: Do not use this with the \"fake at input\" mode.\n\nnon persistent: the random numbers will be determined freshly for each faking action.\n\nconstant: within one web page a color will always be faked to the same color.\n\npersistent: the random number will only be determined once for every domain.", "message": "none (completely white): a completely white image is returned. The option \"Fake the alpha channel\" should be activated with this. CAUTION: Do not use this with the \"fake at input\" mode.\n\nnonpersistent: the random numbers will be determined freshly for each faking action. Keep in mind that many API protections have caches in place to prevent detection.\n\nconstant: variation of nonpersistent. But when altering canvas data same colored pixels also share the same color afterwards.\n\npersistent: the random number will only be determined once for every domain.",
"description": "" "description": ""
}, },
"rng_options.persistent": { "rng_options.persistent": {
@ -469,7 +544,7 @@
"description": "" "description": ""
}, },
"rng_options.nonPersistent": { "rng_options.nonPersistent": {
"message": "non persistent", "message": "nonpersistent",
"description": "" "description": ""
}, },
"rng_options.white": { "rng_options.white": {
@ -602,7 +677,7 @@
"description": "" "description": ""
}, },
"webGLVendor_description": { "webGLVendor_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"vendor\". Leave empty to use the original value.", "message": "Value to be reported in the webGL function \"getParameter\" for the \"vendor\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": "" "description": ""
}, },
"webGLRenderer_title": { "webGLRenderer_title": {
@ -610,7 +685,7 @@
"description": "" "description": ""
}, },
"webGLRenderer_description": { "webGLRenderer_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"renderer\". Leave empty to use the original value.", "message": "Value to be reported in the webGL function \"getParameter\" for the \"renderer\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": "" "description": ""
}, },
"webGLUnmaskedVendor_title": { "webGLUnmaskedVendor_title": {
@ -618,7 +693,7 @@
"description": "" "description": ""
}, },
"webGLUnmaskedVendor_description": { "webGLUnmaskedVendor_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked vendor\". Leave empty to use the original value.", "message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked vendor\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": "" "description": ""
}, },
"webGLUnmaskedRenderer_title": { "webGLUnmaskedRenderer_title": {
@ -626,7 +701,7 @@
"description": "" "description": ""
}, },
"webGLUnmaskedRenderer_description": { "webGLUnmaskedRenderer_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked renderer\". Leave empty to use the original value.", "message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked renderer\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": "" "description": ""
}, },
@ -701,6 +776,14 @@
"message": "Faked DOMRect readout on {url}", "message": "Faked DOMRect readout on {url}",
"description": "" "description": ""
}, },
"fakedSVGReadout": {
"message": "Faked SVG readout on {url}",
"description": ""
},
"fakedTextMetricsReadout": {
"message": "Faked TextMetrics readout on {url}",
"description": ""
},
"fakedNavigatorReadout": { "fakedNavigatorReadout": {
"message": "Faked navigator readout on {url}", "message": "Faked navigator readout on {url}",
"description": "" "description": ""
@ -795,6 +878,10 @@
"message": "What is the scope of the whitelisting?", "message": "What is the scope of the whitelisting?",
"description": "" "description": ""
}, },
"selectWhitelistType": {
"message": "What is the type of the whitelisting?",
"description": ""
},
"whitelistOnlyAPI": { "whitelistOnlyAPI": {
"message": "Whitelist only the {api}", "message": "Whitelist only the {api}",
"description": "" "description": ""
@ -1111,7 +1198,20 @@
"description": "" "description": ""
}, },
"protectWindow_askReCaptchaException": { "protectWindow_askReCaptchaException": {
"message": "Protecting the window API breaks reCAPTCHA. Do you want to add an exception for it?", "message": "Protecting the window API breaks reCAPTCHA. Do you want to allow the window.name API in embedded pages which will make it work again?",
"description": ""
},
"allowWindowNameInFrames_title": {
"message": "Allow window.name in frames",
"description": ""
},
"allowWindowNameInFrames_description": {
"message": "The window.name API is not that dangerous in the context of embedded pages and it is used there for legitimate reasons (e.g. reCAPTCHA). This setting will allow these usages.",
"description": ""
},
"allowWindowNameInFrames_urlSpecific": {
"message": "To allow this only for specific websites, click on the black arrow to open the menu, add the domain or URL by clicking on \"+\" and set its checkmark.",
"description": "" "description": ""
}, },
@ -1137,6 +1237,32 @@
"description": "" "description": ""
}, },
"protectSVG_title": {
"message": "Protect SVG API",
"description": ""
},
"protectSVG_description": {
"message": "This protects against fingerprinting using SVGs.",
"description": ""
},
"protectSVG_urlSpecific": {
"message": "To exclude specific websites from this protection, click on the black arrow to open the menu, add the domain or URL by clicking on \"+\" and remove its checkmark.",
"description": ""
},
"protectTextMetrics_title": {
"message": "Protect TextMetrics API",
"description": ""
},
"protectTextMetrics_description": {
"message": "This protects against the \"measureText()\" fingerprinting which can be used to cross validate DOMRect values.",
"description": ""
},
"protectTextMetrics_urlSpecific": {
"message": "To exclude specific websites from this protection, click on the black arrow to open the menu, add the domain or URL by clicking on \"+\" and remove its checkmark.",
"description": ""
},
"protectNavigator_title": { "protectNavigator_title": {
"message": "Protect navigator API", "message": "Protect navigator API",
"description": "" "description": ""
@ -1175,6 +1301,10 @@
"message": "CAUTION: the actual browser in use cannot be faked entirely as there is a multitude of ways to detect it. E.g. feature tests and browser specific rendering of HTML elements will always leak.", "message": "CAUTION: the actual browser in use cannot be faked entirely as there is a multitude of ways to detect it. E.g. feature tests and browser specific rendering of HTML elements will always leak.",
"description": "" "description": ""
}, },
"navigatorSettings_contextualIdentities": {
"message": "Settings for the container {select} are shown.",
"description": ""
},
"navigatorSettings_presetSection.os": { "navigatorSettings_presetSection.os": {
"message": "Operating system presets", "message": "Operating system presets",
"description": "" "description": ""
@ -1394,6 +1524,10 @@
"message": "Settings", "message": "Settings",
"description": "" "description": ""
}, },
"browserAction_faq": {
"message": "FAQ",
"description": ""
},
"browserAction_test": { "browserAction_test": {
"message": "Test", "message": "Test",
"description": "" "description": ""
@ -1455,6 +1589,10 @@
"message": "All features of {api} are disabled but the protection is enabled.", "message": "All features of {api} are disabled but the protection is enabled.",
"description": "" "description": ""
}, },
"sanitation_error.disabledSomeFeatures": {
"message": "Some features of {api} are disabled. This should only be done for testing or if you really know what the features are doing.",
"description": ""
},
"sanitation_resolution.disableMainFlag": { "sanitation_resolution.disableMainFlag": {
"message": "disable main flag", "message": "disable main flag",
"description": "" "description": ""
@ -1488,7 +1626,7 @@
"description": "" "description": ""
}, },
"sanitation_resolution.switchToNonPersistentRng": { "sanitation_resolution.switchToNonPersistentRng": {
"message": "switch to \"non persistent\" rng", "message": "switch to \"nonpersistent\" rng",
"description": "" "description": ""
}, },
"sanitation_error.fakeEverythingInCanvas": { "sanitation_error.fakeEverythingInCanvas": {
@ -1532,6 +1670,10 @@
"message": "CanvasBlocker whitelist inspection", "message": "CanvasBlocker whitelist inspection",
"description": "" "description": ""
}, },
"whitelist_inspection_description": {
"message": "Shows which API protections are active for a given site. If you remove a checkmark for an API this API will be not protected for the selected site.",
"description": ""
},
"whitelist_all_apis": { "whitelist_all_apis": {
"message": "All APIs", "message": "All APIs",
"description": "" "description": ""
@ -1583,7 +1725,15 @@
"description": "" "description": ""
}, },
"preset_max_protection_description": { "preset_max_protection_description": {
"message": "Maximizes the protection against fingerprint extraction. This settings will break some pages, might slow down the browser a little bit and might enable sites to detect that CanvasBlocker is used.", "message": "Maximizes the protection against fingerprint extraction. These settings will break some pages, might slow down the browser a little bit and might enable sites to detect that CanvasBlocker is used. After applying this preset you should consider applying the reCAPTCHA preset as well.",
"description": ""
},
"preset_recaptcha_title": {
"message": "reCAPTCHA exception",
"description": ""
},
"preset_recaptcha_description": {
"message": "Protecting the window API breaks reCAPTCHA. This preset allows the usage of the window.name API in embedded pages which will make it work again.",
"description": "" "description": ""
} }
} }

View File

@ -7,7 +7,6 @@
"message": "Altera algunas API de JS para evitar la huella digital.", "message": "Altera algunas API de JS para evitar la huella digital.",
"description": "" "description": ""
}, },
"browserAction_title_default": { "browserAction_title_default": {
"message": "CanvasBlocker", "message": "CanvasBlocker",
"description": "" "description": ""
@ -21,10 +20,17 @@
"description": "" "description": ""
}, },
"browserAction_title_protectedAPIs": { "browserAction_title_protectedAPIs": {
"message": "\n \u00B7 {api}", "message": "\n · {api}",
"description": ""
},
"browserAction_status_on": {
"message": "CanvasBlocker activado",
"description": ""
},
"browserAction_status_off": {
"message": "CanvasBlocker desactivado",
"description": "" "description": ""
}, },
"more": { "more": {
"message": "más", "message": "más",
"description": "" "description": ""
@ -41,17 +47,26 @@
"message": "Buscar", "message": "Buscar",
"description": "" "description": ""
}, },
"ok": {
"message": "Aceptar",
"description": ""
},
"apply": {
"message": "Aplicar",
"description": ""
},
"cancel": {
"message": "Cancelar",
"description": ""
},
"input": { "input": {
"message": "entrada", "message": "entrada",
"description": "" "description": ""
}, },
"readout": { "readout": {
"message": "lectura", "message": "lectura",
"description": "" "description": ""
}, },
"options": { "options": {
"message": "Configuración", "message": "Configuración",
"description": "" "description": ""
@ -69,18 +84,41 @@
"description": "" "description": ""
}, },
"updateNotice": { "updateNotice": {
"message": "Se ha actualizado CanvasBlocker. Si quiere poder acceder a está página más adelante y no la tiene en los marcadores, añádala.", "message": "Se ha actualizado CanvasBlocker. Si quieres poder acceder a está página más adelante y no la tienes en los marcadores, añádela.",
"description": "" "description": ""
}, },
"dontShowOptionsOnUpdate":{ "dontShowOptionsOnUpdate": {
"message": "No mostrar de nuevo al actualizar.", "message": "No mostrar de nuevo al actualizar.",
"description": "" "description": ""
}, },
"resistFingerprintingNotice": {
"message": "Tienes habilitado privacy.resistFingerprinting. Esto cambia ligeramente el comportamiento de CanvasBlocker. Más información {link:aquí:https://github.com/kkapsner/CanvasBlocker/issues/158} y {link:aquí:https://github.com/ghacksuserjs/ghacks-user.js/issues/767}.",
"description": ""
},
"settingsNotice.dom.webAudio.enabled": {
"message": "Tiene dom.webAudio.enabled desactivado. Esto le hace más rastreable ya que muy poca gente lo hace.",
"description": ""
},
"openInTab": { "openInTab": {
"message": "Abrir en una pestaña separada", "message": "Abrir en una pestaña separada",
"description": "" "description": ""
}, },
"labelForDefaultOption": {
"message": " (default)",
"description": ""
},
"group_general": {
"message": "General",
"description": ""
},
"group_APIs": {
"message": "API",
"description": ""
},
"group_misc": {
"message": "Varios",
"description": ""
},
"section_asking": { "section_asking": {
"message": "Preguntando", "message": "Preguntando",
"description": "" "description": ""
@ -98,38 +136,49 @@
"description": "" "description": ""
}, },
"section_misc": { "section_misc": {
"message": "Miscelánea", "message": "Varios",
"description": "" "description": ""
}, },
"section_settings": { "section_settings": {
"message": "Configuración", "message": "Configuración",
"description": "" "description": ""
}, },
"section_canvas-api":{ "section_canvas-api": {
"message": "API de canvas", "message": "API de canvas",
"description": "" "description": ""
}, },
"section_audio-api":{ "section_audio-api": {
"message": "API de audio", "message": "API de audio",
"description": "" "description": ""
}, },
"section_history-api":{ "section_history-api": {
"message": "API de history", "message": "API history",
"description": "" "description": ""
}, },
"section_window-api":{ "section_window-api": {
"message": "API de window", "message": "API de window",
"description": "" "description": ""
}, },
"section_DOMRect-api":{ "section_DOMRect-api": {
"message": "API de DOMRect", "message": "API de DOMRect",
"description": "" "description": ""
}, },
"section_Navigator-api":{ "section_SVG-api": {
"message": "API de navigator", "message": "SVG API",
"description": ""
},
"section_TextMetrics-api": {
"message": "API TextMetrics",
"description": ""
},
"section_Navigator-api": {
"message": "API navigator",
"description": ""
},
"section_Screen-api": {
"message": "API pantalla",
"description": "" "description": ""
}, },
"displayAdvancedSettings_title": { "displayAdvancedSettings_title": {
"message": "Modo para expertos", "message": "Modo para expertos",
"description": "" "description": ""
@ -138,7 +187,6 @@
"message": "Muestra opciones adicionales.", "message": "Muestra opciones adicionales.",
"description": "" "description": ""
}, },
"displayDescriptions_title": { "displayDescriptions_title": {
"message": "Mostrar las descripciones", "message": "Mostrar las descripciones",
"description": "" "description": ""
@ -147,12 +195,30 @@
"message": "Muestra las descripciones de las opciones.", "message": "Muestra las descripciones de las opciones.",
"description": "" "description": ""
}, },
"disruptSessionOnUpdate_title": {
"message": "Interrumpir sesión al actualizar",
"description": ""
},
"disruptSessionOnUpdate_description": {
"message": "Si se establece en true la extensión se actualizará tan pronto como la actualización esté disponible. Esto podría romper algunas pestañas que están actualmente abiertas.",
"description": ""
},
"reloadExtension_title": {
"message": "Recargar extensión",
"description": ""
},
"reloadExtension_description": {
"message": "Realizar una actualización pendiente.",
"description": ""
},
"reloadExtension_label": {
"message": "Recargar",
"description": ""
},
"hideSetting": { "hideSetting": {
"message": "Haga clic aquí para ocultar esta opción.", "message": "Haga clic aquí para ocultar esta opción.",
"description": "" "description": ""
}, },
"displayHiddenSettings_title": { "displayHiddenSettings_title": {
"message": "Mostrar las opciones ocultas", "message": "Mostrar las opciones ocultas",
"description": "" "description": ""
@ -161,7 +227,6 @@
"message": "Activar para mostrar las opciones ocultas.", "message": "Activar para mostrar las opciones ocultas.",
"description": "" "description": ""
}, },
"askForInvisiblePermission": { "askForInvisiblePermission": {
"message": "¿Quiere permitir los <canvas> invisibles?", "message": "¿Quiere permitir los <canvas> invisibles?",
"description": "" "description": ""
@ -246,6 +311,30 @@
"message": "¿Quiere permitir la lectura para la API de DOMRect?", "message": "¿Quiere permitir la lectura para la API de DOMRect?",
"description": "" "description": ""
}, },
"askForSVGPermission": {
"message": "¿Quiere permitir la API SVG?",
"description": ""
},
"askForSVGInputPermission": {
"message": "¿Quieres permitir la entrada de la API SVG?",
"description": ""
},
"askForSVGReadoutPermission": {
"message": "¿Quieres permitir la lectura de la API SVG?",
"description": ""
},
"askForTextMetricsPermission": {
"message": "¿Quiere permitir la API TextMetrics?",
"description": ""
},
"askForTextMetricsInputPermission": {
"message": "¿Quiere permitir la entrada de la API de TextMetrics?",
"description": ""
},
"askForTextMetricsReadoutPermission": {
"message": "¿Quiere permitir la lectura de la API de TextMetrics?",
"description": ""
},
"askForNavigatorPermission": { "askForNavigatorPermission": {
"message": "¿Quiere permitir la API de navigator?", "message": "¿Quiere permitir la API de navigator?",
"description": "" "description": ""
@ -258,6 +347,18 @@
"message": "¿Quiere permitir la lectura para la API de navigator?", "message": "¿Quiere permitir la lectura para la API de navigator?",
"description": "" "description": ""
}, },
"askForScreenPermission": {
"message": "¿Quiere permitir la API de pantalla?",
"description": ""
},
"askForScreenInputPermission": {
"message": "¿Quiere permitir la entrada de la API de pantalla?",
"description": ""
},
"askForScreenReadoutPermission": {
"message": "¿Quiere permitir la lectura de la API de pantalla?",
"description": ""
},
"askOnlyOnce_title": { "askOnlyOnce_title": {
"message": "Pregunar una sola vez", "message": "Pregunar una sola vez",
"description": "" "description": ""
@ -278,36 +379,34 @@
"message": "combinado", "message": "combinado",
"description": "" "description": ""
}, },
"askDenyMode_title":{ "askDenyMode_title": {
"message": "Preguntar por el modo de denegación", "message": "Preguntar por el modo de denegación",
"description": "" "description": ""
}, },
"askDenyMode_description":{ "askDenyMode_description": {
"message": "Qué modo se debe usar cuando se deniega un permiso.", "message": "Qué modo se debe usar cuando se deniega un permiso.",
"description": "" "description": ""
}, },
"askDenyMode_options.block":{ "askDenyMode_options.block": {
"message": "bloquear", "message": "bloquear",
"description": "" "description": ""
}, },
"askDenyMode_options.fake":{ "askDenyMode_options.fake": {
"message": "falsear", "message": "falsear",
"description": "" "description": ""
}, },
"showCanvasWhileAsking_title": {
"showCanvasWhileAsking_title":{
"message": "Mostrar el contenido del «canvas»", "message": "Mostrar el contenido del «canvas»",
"description": "" "description": ""
}, },
"showCanvasWhileAsking_description":{ "showCanvasWhileAsking_description": {
"message": "Muestra el contenido del «canvas» para el que se ha pedido permiso, si es posible.", "message": "Muestra el contenido del «canvas» para el que se ha pedido permiso, si es posible.",
"description": "" "description": ""
}, },
"showCanvasWhileAsking_message":{ "showCanvasWhileAsking_message": {
"message": "La página web quiere leer el contenido del siguiente «canvas»:", "message": "La página web quiere leer el contenido del siguiente «canvas»:",
"description": "" "description": ""
}, },
"blackList_description": { "blackList_description": {
"message": "Los dominios o URL donde bloquear siempre todas las API. Para añadir varias entradas, sepárelas con comas.", "message": "Los dominios o URL donde bloquear siempre todas las API. Para añadir varias entradas, sepárelas con comas.",
"description": "" "description": ""
@ -316,7 +415,6 @@
"message": "Lista negra", "message": "Lista negra",
"description": "" "description": ""
}, },
"blockMode_description": { "blockMode_description": {
"message": "", "message": "",
"description": "" "description": ""
@ -353,7 +451,6 @@
"message": "Modo de bloqueo", "message": "Modo de bloqueo",
"description": "" "description": ""
}, },
"protectedCanvasPart_title": { "protectedCanvasPart_title": {
"message": "Parte protegida de la API de canvas", "message": "Parte protegida de la API de canvas",
"description": "" "description": ""
@ -382,7 +479,6 @@
"message": "todo", "message": "todo",
"description": "" "description": ""
}, },
"urlSettings_title": { "urlSettings_title": {
"message": "Valores específicos para sitios", "message": "Valores específicos para sitios",
"description": "" "description": ""
@ -391,12 +487,10 @@
"message": "", "message": "",
"description": "" "description": ""
}, },
"inputURL": { "inputURL": {
"message": "Introduzca un dominio o «RegExp» de URL:", "message": "Introduzca un dominio o «RegExp» de URL:",
"description": "" "description": ""
}, },
"minFakeSize_description": { "minFakeSize_description": {
"message": "Los «canvas» con un área menor o igual que este número no se falsean. Es un parámetro para prevenir la detección.\nPRECAUCIÓN: esto reduce la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 100.", "message": "Los «canvas» con un área menor o igual que este número no se falsean. Es un parámetro para prevenir la detección.\nPRECAUCIÓN: esto reduce la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 100.",
"description": "" "description": ""
@ -405,7 +499,6 @@
"message": "Tamaño mínimo para falsear", "message": "Tamaño mínimo para falsear",
"description": "" "description": ""
}, },
"maxFakeSize_description": { "maxFakeSize_description": {
"message": "Los «canvas» con un área mayor que este número no se falsean (introduzca un cero para deshabilitarlo). Es un parámetro de rendimiento que puede prevenir bloqueos del navegador y debe ajustarse a la potencia de cálculo del dispositivo.\nPRECAUCIÓN: esto reduce la seguridad del complemento, por lo que es muy recomendable no darle un valor menor que 1000000.", "message": "Los «canvas» con un área mayor que este número no se falsean (introduzca un cero para deshabilitarlo). Es un parámetro de rendimiento que puede prevenir bloqueos del navegador y debe ajustarse a la potencia de cálculo del dispositivo.\nPRECAUCIÓN: esto reduce la seguridad del complemento, por lo que es muy recomendable no darle un valor menor que 1000000.",
"description": "" "description": ""
@ -414,9 +507,8 @@
"message": "Tamaño máximo para falsear", "message": "Tamaño máximo para falsear",
"description": "" "description": ""
}, },
"rng_description": { "rng_description": {
"message": "ninguno (totalmente blanco): se devuelve una imagen totalmente blanca. La opción «Falsear el canal alfa» debe activarse junto a esta. PRECAUCIÓN: no usar con el modo «falsear a la entrada».\nno persistente: los números aleatorios se vuelven a calcular para cada acción de falseado.\nconstante: en una página web, un color se falsea siempre por el mismo color.\npersistente: el número aleatorio solo se calcula una vez por cada dominio.", "message": "none (completely white): a completely white image is returned. The option \"Fake the alpha channel\" should be activated with this. CAUTION: Do not use this with the \"fake at input\" mode.\n\nnonpersistent: the random numbers will be determined freshly for each faking action. Keep in mind that many API protections have caches in place to prevent detection.\n\nconstant: variation of nonpersistent. But when altering canvas data same colored pixels also share the same color afterwards.\n\npersistent: the random number will only be determined once for every domain.",
"description": "" "description": ""
}, },
"rng_options.persistent": { "rng_options.persistent": {
@ -439,7 +531,6 @@
"message": "Generador de números aleatorios", "message": "Generador de números aleatorios",
"description": "" "description": ""
}, },
"persistentRndStorage_title": { "persistentRndStorage_title": {
"message": "Almacenamiento persistente", "message": "Almacenamiento persistente",
"description": "" "description": ""
@ -448,7 +539,6 @@
"message": "Almacena los datos para el generador de números aleatorios persistente para usarlos tras reiniciar.", "message": "Almacena los datos para el generador de números aleatorios persistente para usarlos tras reiniciar.",
"description": "" "description": ""
}, },
"storePersistentRnd_title": { "storePersistentRnd_title": {
"message": "Almacenar los datos persistentes", "message": "Almacenar los datos persistentes",
"description": "" "description": ""
@ -457,7 +547,6 @@
"message": "Si se deben almacenar los datos para el generador de números aleatorios persistente. De lo contrario se descartan al cerrar el navegador.", "message": "Si se deben almacenar los datos para el generador de números aleatorios persistente. De lo contrario se descartan al cerrar el navegador.",
"description": "" "description": ""
}, },
"persistentRndClearInterval_title": { "persistentRndClearInterval_title": {
"message": "Intervalo de borrado de los datos persistentes", "message": "Intervalo de borrado de los datos persistentes",
"description": "" "description": ""
@ -494,7 +583,6 @@
"message": "años", "message": "años",
"description": "" "description": ""
}, },
"clearPersistentRnd_title": { "clearPersistentRnd_title": {
"message": "Limpiar el almacenamiento aleatorio persistente", "message": "Limpiar el almacenamiento aleatorio persistente",
"description": "" "description": ""
@ -507,7 +595,14 @@
"message": "Limpiar", "message": "Limpiar",
"description": "" "description": ""
}, },
"clearPersistentRndForContainer_label": {
"message": "Limpiar contenedor",
"description": ""
},
"clearPersistentRndForContainer_title": {
"message": "Seleccionar contenedor",
"description": ""
},
"sharePersistentRndBetweenDomains_title": { "sharePersistentRndBetweenDomains_title": {
"message": "Compartir la aleatoriedad persistente entre dominios", "message": "Compartir la aleatoriedad persistente entre dominios",
"description": "" "description": ""
@ -520,7 +615,6 @@
"message": "¿Seguro que quiere compartir la aleatoriedad persistente entre dominios?\nPRECAUCIÓN: esto hace al nevegador 100% rastreable y es, por tanto, una amenaza para su privacidad.", "message": "¿Seguro que quiere compartir la aleatoriedad persistente entre dominios?\nPRECAUCIÓN: esto hace al nevegador 100% rastreable y es, por tanto, una amenaza para su privacidad.",
"description": "" "description": ""
}, },
"ignoreFrequentColors_title": { "ignoreFrequentColors_title": {
"message": "Ignorar los colores más frecuentes", "message": "Ignorar los colores más frecuentes",
"description": "" "description": ""
@ -529,7 +623,6 @@
"message": "Número de colores que no deben falsearse por «canvas». Es un parámetro para evitar la detección.\nPRECAUCIÓN: esto reduce el rendimiento del complemento porque las estadísticas de color deben calcularse para cada imagen. Además, puede reducir la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 3.", "message": "Número de colores que no deben falsearse por «canvas». Es un parámetro para evitar la detección.\nPRECAUCIÓN: esto reduce el rendimiento del complemento porque las estadísticas de color deben calcularse para cada imagen. Además, puede reducir la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 3.",
"description": "" "description": ""
}, },
"minColors_title": { "minColors_title": {
"message": "Número mínimo de colores", "message": "Número mínimo de colores",
"description": "" "description": ""
@ -538,7 +631,6 @@
"message": "Número de colores que debe falsearse en un «canvas».\nPRECAUCIÓN: esto puede reducir la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 10.", "message": "Número de colores que debe falsearse en un «canvas».\nPRECAUCIÓN: esto puede reducir la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 10.",
"description": "" "description": ""
}, },
"fakeAlphaChannel_title": { "fakeAlphaChannel_title": {
"message": "Falsear el canal alfa", "message": "Falsear el canal alfa",
"description": "" "description": ""
@ -547,7 +639,38 @@
"message": "Activa la falsificación del canal alfa (transparencia).", "message": "Activa la falsificación del canal alfa (transparencia).",
"description": "" "description": ""
}, },
"webGLVendor_title": {
"message": "Proveedor webGL reportado",
"description": ""
},
"webGLVendor_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"vendor\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": ""
},
"webGLRenderer_title": {
"message": "Renderizador webGL reportado",
"description": ""
},
"webGLRenderer_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"renderer\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": ""
},
"webGLUnmaskedVendor_title": {
"message": "Proveedor webGL desenmascarado reportado",
"description": ""
},
"webGLUnmaskedVendor_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked vendor\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": ""
},
"webGLUnmaskedRenderer_title": {
"message": "Renderizador webGL desenmascarado reportado",
"description": ""
},
"webGLUnmaskedRenderer_description": {
"message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked renderer\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
"description": ""
},
"useCanvasCache_title": { "useCanvasCache_title": {
"message": "Usar la caché para «canvas»", "message": "Usar la caché para «canvas»",
"description": "" "description": ""
@ -556,7 +679,6 @@
"message": "Activa la caché para «canvas». Puede prevenir la detección y aumenta el rendimiento cuando se leen varias veces «canvas» pequeños, pero lo reduce para los «canvas» grandes.", "message": "Activa la caché para «canvas». Puede prevenir la detección y aumenta el rendimiento cuando se leen varias veces «canvas» pequeños, pero lo reduce para los «canvas» grandes.",
"description": "" "description": ""
}, },
"protectedAPIFeatures_title": { "protectedAPIFeatures_title": {
"message": "Funciones protegidas de la API", "message": "Funciones protegidas de la API",
"description": "" "description": ""
@ -565,7 +687,6 @@
"message": "Lista de funciones protegidas de la API. Al desmarcar una casilla, la función de la API queda desprotegida.", "message": "Lista de funciones protegidas de la API. Al desmarcar una casilla, la función de la API queda desprotegida.",
"description": "" "description": ""
}, },
"disableNotifications": { "disableNotifications": {
"message": "deshabilitar las notificaciones", "message": "deshabilitar las notificaciones",
"description": "" "description": ""
@ -590,7 +711,6 @@
"message": "Usar la lista blanca de archivos específicos", "message": "Usar la lista blanca de archivos específicos",
"description": "" "description": ""
}, },
"preBlock": { "preBlock": {
"message": "API bloqueada en {url} porque la configuración de CanvasBlocker no se ha cargado a tiempo.", "message": "API bloqueada en {url} porque la configuración de CanvasBlocker no se ha cargado a tiempo.",
"description": "" "description": ""
@ -619,15 +739,26 @@
"message": "Lectura de DOMRect falseada en {url}", "message": "Lectura de DOMRect falseada en {url}",
"description": "" "description": ""
}, },
"fakedSVGReadout": {
"message": "Lectura SVG falseada en {url}",
"description": ""
},
"fakedTextMetricsReadout": {
"message": "Lectura de TextMetrics falseada en {url}",
"description": ""
},
"fakedNavigatorReadout": { "fakedNavigatorReadout": {
"message": "Lectura de navigator falseada en {url}", "message": "Lectura de navigator falseada en {url}",
"description": "" "description": ""
}, },
"fakedScreenReadout": {
"message": "Lectura de screen falseada en {url}",
"description": ""
},
"fakedInput": { "fakedInput": {
"message": "Falseado a la entrada en {url}", "message": "Falseado a la entrada en {url}",
"description": "" "description": ""
}, },
"ignoreList_description": { "ignoreList_description": {
"message": "Los dominios o URL donde no mostrar notificaciones. Para añadir varias entradas, sepárelas con comas.", "message": "Los dominios o URL donde no mostrar notificaciones. Para añadir varias entradas, sepárelas con comas.",
"description": "" "description": ""
@ -636,7 +767,6 @@
"message": "Lista de ignorados", "message": "Lista de ignorados",
"description": "" "description": ""
}, },
"ignoredAPIs_title": { "ignoredAPIs_title": {
"message": "API ignoradas", "message": "API ignoradas",
"description": "" "description": ""
@ -645,6 +775,10 @@
"message": "No se mostrarán notificaciones para las API seleccionadas.", "message": "No se mostrarán notificaciones para las API seleccionadas.",
"description": "" "description": ""
}, },
"localFile": {
"message": "archivo local",
"description": ""
},
"ignorelistDomain": { "ignorelistDomain": {
"message": "silenciar el dominio", "message": "silenciar el dominio",
"description": "" "description": ""
@ -705,6 +839,10 @@
"message": "¿Cuál es el alcance de la lista blanca?", "message": "¿Cuál es el alcance de la lista blanca?",
"description": "" "description": ""
}, },
"selectWhitelistType": {
"message": "¿Cuál es el tipo de lista blanca?",
"description": ""
},
"whitelistOnlyAPI": { "whitelistOnlyAPI": {
"message": "Permitir solo {api}", "message": "Permitir solo {api}",
"description": "" "description": ""
@ -849,7 +987,6 @@
"message": "inspeccionar la lista blanca", "message": "inspeccionar la lista blanca",
"description": "" "description": ""
}, },
"sessionWhiteList_title": { "sessionWhiteList_title": {
"message": "Lista blanca de la sesión", "message": "Lista blanca de la sesión",
"description": "" "description": ""
@ -858,7 +995,6 @@
"message": "Los dominios o URL a los que se permite usar todas las API en la sesión actual. Para añadir varias entradas, sepárelas con comas.", "message": "Los dominios o URL a los que se permite usar todas las API en la sesión actual. Para añadir varias entradas, sepárelas con comas.",
"description": "" "description": ""
}, },
"whitelistDomainTemporarily": { "whitelistDomainTemporarily": {
"message": "permitir el dominio temporalmente", "message": "permitir el dominio temporalmente",
"description": "" "description": ""
@ -867,7 +1003,6 @@
"message": "permitir el URL temporalmente", "message": "permitir el URL temporalmente",
"description": "" "description": ""
}, },
"storeNotificationData_title": { "storeNotificationData_title": {
"message": "Guardar datos detallados de las notificaciones", "message": "Guardar datos detallados de las notificaciones",
"description": "" "description": ""
@ -876,7 +1011,6 @@
"message": "", "message": "",
"description": "" "description": ""
}, },
"storeImageForInspection_title": { "storeImageForInspection_title": {
"message": "Guardar la imagen para inspeccionarla", "message": "Guardar la imagen para inspeccionarla",
"description": "" "description": ""
@ -885,9 +1019,8 @@
"message": "Habilita el almacenamiento del contenido del «canvas» falseado.\nPRECAUCIÓN: esto puede usar mucha memoria.", "message": "Habilita el almacenamiento del contenido del «canvas» falseado.\nPRECAUCIÓN: esto puede usar mucha memoria.",
"description": "" "description": ""
}, },
"protectAudio_title": { "protectAudio_title": {
"message": "Proteger la API de audio", "message": "Proteger la API audio",
"description": "" "description": ""
}, },
"protectAudio_description": { "protectAudio_description": {
@ -923,19 +1056,19 @@
"description": "" "description": ""
}, },
"audioFakeRate_options.0.1%": { "audioFakeRate_options.0.1%": {
"message": "0.1% de los valores", "message": "0,1% de los valores",
"description": "" "description": ""
}, },
"audioFakeRate_options.1%": { "audioFakeRate_options.1%": {
"message": "1% de los valores", "message": "1% de los valores",
"description": "" "description": ""
}, },
"audioFakeRate_options.10%": { "audioFakeRate_options.10%": {
"message": "10% de los valores", "message": "10% de los valores",
"description": "" "description": ""
}, },
"audioFakeRate_options.100%": { "audioFakeRate_options.100%": {
"message": "100% de los valores", "message": "100 % de los valores",
"description": "" "description": ""
}, },
"audioNoiseLevel_title": { "audioNoiseLevel_title": {
@ -975,7 +1108,7 @@
"description": "" "description": ""
}, },
"useAudioCache_urlSpecific": { "useAudioCache_urlSpecific": {
"message": "Algunas páginas usan guiones para crear huellas digitales muy mal escritos que ralentizan Firefox. Para deshabilitar la caché para sitios web específicos, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casilla.", "message": "Algunas páginas usan scripts muy mal escritos con audio para crear huella digital que ralentizan Firefox. Para deshabilitar la caché para sitios web específicos, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": "" "description": ""
}, },
"audioUseFixedIndices_title": { "audioUseFixedIndices_title": {
@ -994,7 +1127,6 @@
"message": "Los índices que siempre se falsean. Para añadir varias entradas, sepárelas con comas.", "message": "Los índices que siempre se falsean. Para añadir varias entradas, sepárelas con comas.",
"description": "" "description": ""
}, },
"historyLengthThreshold_title": { "historyLengthThreshold_title": {
"message": "Tamaño máximo del historial", "message": "Tamaño máximo del historial",
"description": "" "description": ""
@ -1008,7 +1140,7 @@
"description": "" "description": ""
}, },
"protectWindow_title": { "protectWindow_title": {
"message": "Proteger la API de window", "message": "Proteger la API window",
"description": "" "description": ""
}, },
"protectWindow_description": { "protectWindow_description": {
@ -1016,27 +1148,37 @@
"description": "" "description": ""
}, },
"protectWindow_urlSpecific": { "protectWindow_urlSpecific": {
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casilla.", "message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": "" "description": ""
}, },
"protectWindow_askReCaptchaException": { "protectWindow_askReCaptchaException": {
"message": "La protección de la API de window rompe los reCAPTCHA. ¿Quiere añadir una excepción para eso?", "message": "La protección de la API window rompe reCAPTCHA. ¿Quieres permitir la API window.name en las páginas integradas para que vuelva a funcionar?",
"description": ""
},
"allowWindowNameInFrames_title": {
"message": "Permitir window.name en frames",
"description": ""
},
"allowWindowNameInFrames_description": {
"message": "La API window.name no es tan peligrosa en el contexto de las páginas incrustadas y se utiliza allí por razones legítimas (por ejemplo, reCAPTCHA). Esta configuración permitirá estos usos.",
"description": ""
},
"allowWindowNameInFrames_urlSpecific": {
"message": "Para permitir esto solo para sitios web específicos, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y marca la casilla.",
"description": "" "description": ""
}, },
"protectDOMRect_title": { "protectDOMRect_title": {
"message": "Proteger la API de DOMRect", "message": "Proteger la API DOMRect",
"description": "" "description": ""
}, },
"protectDOMRect_description": { "protectDOMRect_description": {
"message": "Protege contra la huella digital mediante «getClientRects()» y varios métodos similares.", "message": "Esto protege contra la huella digital mediante «getClientRects()» y varios métodos similares.",
"description": "" "description": ""
}, },
"protectDOMRect_urlSpecific": { "protectDOMRect_urlSpecific": {
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casilla.", "message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": "" "description": ""
}, },
"domRectIntegerFactor_title": { "domRectIntegerFactor_title": {
"message": "Factor de número entero de DOMRect", "message": "Factor de número entero de DOMRect",
"description": "" "description": ""
@ -1045,20 +1187,42 @@
"message": "Alguna fracción de un píxel puede ser controlada con CSS. Para prevenir la detección, los valores de un DOMRect que multiplicados por este factor sean números enteros no serán modificados.", "message": "Alguna fracción de un píxel puede ser controlada con CSS. Para prevenir la detección, los valores de un DOMRect que multiplicados por este factor sean números enteros no serán modificados.",
"description": "" "description": ""
}, },
"protectSVG_title": {
"message": "Proteger API SVG",
"description": ""
},
"protectSVG_description": {
"message": "Esto protege contra la toma de huellas dactilares usando SVGs.",
"description": ""
},
"protectSVG_urlSpecific": {
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o la URL haciendo clic en \"+\" y quite su marca de verificación.",
"description": ""
},
"protectTextMetrics_title": {
"message": "Proteger la API TextMetrics",
"description": ""
},
"protectTextMetrics_description": {
"message": "Esto protege contra la huella digital mediante «measureText()» que se pueden usar para validar de forma cruzada los valores DOMRect.",
"description": ""
},
"protectTextMetrics_urlSpecific": {
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": ""
},
"protectNavigator_title": { "protectNavigator_title": {
"message": "Proteger la API de navigator", "message": "Proteger la API navigator",
"description": "" "description": ""
}, },
"protectNavigator_description": { "protectNavigator_description": {
"message": "Esta página permite cambios en la API de navigator. Habilitar esta protección no cambia nada por defecto. Abra la configuración de navigator para especificar allí los cambios que desee.", "message": "Esta página permite cambios en la API navigator. Habilitar esta protección no cambia nada por defecto. Abre la configuración de navigator para especificar allí los cambios que desees.",
"description": "" "description": ""
}, },
"protectNavigator_urlSpecific": { "protectNavigator_urlSpecific": {
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casila.", "message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": "" "description": ""
}, },
"openNavigatorSettings_title": { "openNavigatorSettings_title": {
"message": "Configuración de navigator", "message": "Configuración de navigator",
"description": "" "description": ""
@ -1071,19 +1235,22 @@
"message": "Abrir", "message": "Abrir",
"description": "" "description": ""
}, },
"navigatorSettings_title": { "navigatorSettings_title": {
"message": "Configuración de CanvasBlocker para navigator", "message": "Configuración de CanvasBlocker para navigator",
"description": "" "description": ""
}, },
"navigatorSettings_description": { "navigatorSettings_description": {
"message": "En esta página puede cambiar la configuración de navigator. Si usa un preajuste, debe usar siempre uno para sistema operativo y otro para el navegador. Después de seleccionarlos, todavía puede hacer modificaciones.", "message": "En esta página puedes cambiar la configuración de navigator. Si usas un preajuste, siempre debes usar uno para el sistema operativo y otro para el navegador. Después de seleccionarlos, todavía puede hacer modificaciones.",
"description": "" "description": ""
}, },
"navigatorSettings_disclaimer": { "navigatorSettings_disclaimer": {
"message": "PRECAUCIÓN: el navegador en uso no puede falsearse por completo ya que hay multitud de formas para detectarlo. Por ejemplo, la detección de características y representación específica del navegador de elementos de HTML siempre van a dejar escapar información.", "message": "PRECAUCIÓN: el navegador en uso no puede falsearse por completo ya que hay multitud de formas para detectarlo. Por ejemplo, la detección de características y representación específica del navegador de elementos de HTML siempre van a dejar escapar información.",
"description": "" "description": ""
}, },
"navigatorSettings_contextualIdentities": {
"message": "Se muestra la configuración del contenedor {select}.",
"description": ""
},
"navigatorSettings_presetSection.os": { "navigatorSettings_presetSection.os": {
"message": "Preajustes del sistema operativo", "message": "Preajustes del sistema operativo",
"description": "" "description": ""
@ -1097,10 +1264,45 @@
"description": "" "description": ""
}, },
"navigatorSettings_reset": { "navigatorSettings_reset": {
"message": "Reiniciar", "message": "Restablecer",
"description": ""
},
"protectScreen_title": {
"message": "Proteger la API screen",
"description": ""
},
"protectScreen_description": {
"message": "Esto protege contra los intentos de crear huella digital, incluido el tamaño de la pantalla.",
"description": ""
},
"protectScreen_urlSpecific": {
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": ""
},
"screenSize_title": {
"message": "Tamaño de pantalla",
"description": ""
},
"screenSize_description": {
"message": "Si se establece con un valor \"...x...\" las dimensiones especificadas serán reportadas como el tamaño de la pantalla.",
"description": ""
},
"screenSize_urlSpecific": {
"message": "Para proporcionar tamaños específicos para ciertos sitios web, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» e ingresa el valor deseado.",
"description": ""
},
"fakeMinimalScreenSize_title": {
"message": "Tamaño mínimo falso de la pantalla",
"description": ""
},
"fakeMinimalScreenSize_description": {
"message": "Utilice un tamaño de pantalla mínimo del siguiente conjunto que pueda ajustarse a las dimensiones de la ventana interior. Tamaños de pantalla: 1366x768, 1440x900, 1600x900, 1920x1080, 4096x2160, 8192x4320",
"description": ""
},
"fakeMinimalScreenSize_urlSpecific": {
"message": "Para excluir sitios web específicos del falseado, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": "" "description": ""
}, },
"theme_title": { "theme_title": {
"message": "Tema", "message": "Tema",
"description": "" "description": ""
@ -1114,7 +1316,7 @@
"description": "" "description": ""
}, },
"theme_options.default": { "theme_options.default": {
"message": "por defecto", "message": "predeterminado",
"description": "" "description": ""
}, },
"theme_options.light": { "theme_options.light": {
@ -1133,7 +1335,6 @@
"message": "ninguno", "message": "ninguno",
"description": "" "description": ""
}, },
"blockDataURLs_title": { "blockDataURLs_title": {
"message": "Bloquear las páginas de URL de datos", "message": "Bloquear las páginas de URL de datos",
"description": "" "description": ""
@ -1143,10 +1344,9 @@
"description": "" "description": ""
}, },
"blockDataURLs_urlSpecific": { "blockDataURLs_urlSpecific": {
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casila.", "message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
"description": "" "description": ""
}, },
"showReleaseNotes_title": { "showReleaseNotes_title": {
"message": "Notas de la versión", "message": "Notas de la versión",
"description": "" "description": ""
@ -1159,7 +1359,6 @@
"message": "Mostrar", "message": "Mostrar",
"description": "" "description": ""
}, },
"logLevel_title": { "logLevel_title": {
"message": "Nivel de registro", "message": "Nivel de registro",
"description": "" "description": ""
@ -1192,21 +1391,19 @@
"message": "detallado", "message": "detallado",
"description": "" "description": ""
}, },
"exportSettings_title": { "exportSettings_title": {
"message": "Exportar la configuración", "message": "Exportar configuración",
"description": "" "description": ""
}, },
"exportSettings_description": { "exportSettings_description": {
"message": "", "message": "",
"description": "" "description": ""
}, },
"settingControlling_title": {
"openSettingSanitation_title": { "message": "Control de configuración",
"message": "Saneado de la configuración",
"description": "" "description": ""
}, },
"openSettingSanitation_description": { "settingControlling_description": {
"message": "", "message": "",
"description": "" "description": ""
}, },
@ -1214,7 +1411,22 @@
"message": "Abrir", "message": "Abrir",
"description": "" "description": ""
}, },
"inspectWhitelist_label": {
"message": "Inspeccionar la lista blanca",
"description": ""
},
"openSettingPresets_title": {
"message": "Preajustes",
"description": ""
},
"openSettingPresets_description": {
"message": "Esto abre la página de preajustes que se mostró en la instalación. Los preajustes son colecciones de valores de configuración para algunos casos de uso común de CanvasBlocker.",
"description": ""
},
"openSettingPresets_label": {
"message": "Abrir",
"description": ""
},
"inspectSettings_label": { "inspectSettings_label": {
"message": "Inspeccionar", "message": "Inspeccionar",
"description": "" "description": ""
@ -1227,13 +1439,8 @@
"message": "Cargar", "message": "Cargar",
"description": "" "description": ""
}, },
"inspectWhitelist_label": {
"message": "Inspeccionar la lista blanca",
"description": ""
},
"resetSettings_title": { "resetSettings_title": {
"message": "Reiniciar la configuración", "message": "Restablecer configuración",
"description": "" "description": ""
}, },
"resetSettings_description": { "resetSettings_description": {
@ -1241,18 +1448,21 @@
"description": "" "description": ""
}, },
"resetSettings_label": { "resetSettings_label": {
"message": "Reiniciar", "message": "Restablecer",
"description": "" "description": ""
}, },
"resetSettings_confirm": { "resetSettings_confirm": {
"message": "¿Seguro que quiere reiniciar toda la configuración?", "message": "¿Seguro que quiere reiniciar toda la configuración?",
"description": "" "description": ""
}, },
"browserAction_settings": { "browserAction_settings": {
"message": "Configuración", "message": "Configuración",
"description": "" "description": ""
}, },
"browserAction_faq": {
"message": "Preguntas frecuentes",
"description": ""
},
"browserAction_test": { "browserAction_test": {
"message": "Probar", "message": "Probar",
"description": "" "description": ""
@ -1265,7 +1475,6 @@
"message": "Notificar problemas", "message": "Notificar problemas",
"description": "" "description": ""
}, },
"sanitation_title": { "sanitation_title": {
"message": "Saneado de la configuración", "message": "Saneado de la configuración",
"description": "" "description": ""
@ -1314,6 +1523,10 @@
"message": "Todas las opciones de {api} están deshabilitadas pero la protección está habilitada.", "message": "Todas las opciones de {api} están deshabilitadas pero la protección está habilitada.",
"description": "" "description": ""
}, },
"sanitation_error.disabledSomeFeatures": {
"message": "Algunas características de {api} están deshabilitadas. Esto sólo debe hacerse para probar o si realmente sabe lo que las características están haciendo.",
"description": ""
},
"sanitation_resolution.disableMainFlag": { "sanitation_resolution.disableMainFlag": {
"message": "deshabilitar la opción principal", "message": "deshabilitar la opción principal",
"description": "" "description": ""
@ -1379,16 +1592,79 @@
"description": "" "description": ""
}, },
"sanitation_error.doNotSharePersistentRndBetweenDomains": { "sanitation_error.doNotSharePersistentRndBetweenDomains": {
"message": "No compartir la aleatoriedad persistente entre dominios porque hace al navegador 100% rastreable.", "message": "No compartir la aleatoriedad persistente entre dominios porque hace al navegador 100 % rastreable.",
"description": ""
},
"sanitation_error.customScreenSize": {
"message": "No utilice un tamaño de pantalla personalizado, ya que hace que el navegador sea más rastreable.",
"description": "" "description": ""
}, },
"whitelist_inspection_title": { "whitelist_inspection_title": {
"message": "Inspección de la lista blanca de CanvasBlocker", "message": "Inspección de la lista blanca de CanvasBlocker",
"description": "" "description": ""
}, },
"whitelist_inspection_description": {
"message": "Muestra las protecciones de las API que están activas para un sitio determinado. Si eliminas una marca de verificación para una API, esta API no estará protegida para el sitio seleccionado.",
"description": ""
},
"whitelist_all_apis": { "whitelist_all_apis": {
"message": "Todas las API", "message": "Todas las API",
"description": "" "description": ""
},
"presets": {
"message": "Preajustes",
"description": ""
},
"presets_title": {
"message": "Preajustes de CanvasBlocker",
"description": ""
},
"presets_installNotice": {
"message": "{image:../icons/icon.svg}Gracias por instalar CanvasBlocker. Para ajustarlo a tus necesidades puedes aplicar algunos preajustes. Para una mayor personalización puedes abrir la página de {link:opciones:options.html}. Allí también puedes abrir esta página de preajustes más tarde.",
"description": ""
},
"presets_introduction": {
"message": "Estos son algunos preajustes de configuración para adaptarse a las necesidades de diferentes personas. Los valores entre paréntesis son el estado actual de la configuración. Puedes aplicar múltiples preajustes de configuración uno tras otro, pero alguna configuración puede ser sobrescrita por el que se aplique más tarde.",
"description": ""
},
"preset_default_title": {
"message": "Configuración predeterminada",
"description": ""
},
"preset_default_description": {
"message": "No se han aplicado ajustes especiales.",
"description": ""
},
"preset_convenience_title": {
"message": "Configuración cómoda",
"description": ""
},
"preset_convenience_description": {
"message": "Aplica algunas configuraciones para que la experiencia de navegación sea lo más cómoda posible. Esto incluye la lista blanca de algunos sitios.",
"description": ""
},
"preset_stealth_title": {
"message": "Ajustes de sigilo",
"description": ""
},
"preset_stealth_description": {
"message": "Configura CanvasBlocker para que sea difícil de detectar. Esto puede llevar a un alto uso de la CPU y puede ralentizar el navegador.",
"description": ""
},
"preset_max_protection_title": {
"message": "Máxima protección",
"description": ""
},
"preset_max_protection_description": {
"message": "Maximiza la protección contra la extracción de huella dagital. Esta configuración romperá algunas páginas, podría ralentizar un poco el navegador y podría permitir a los sitios detectar si utilizas CanvasBlocker. Después de aplicar este preajuste, deberías considerar aplicar también el preajuste reCAPTCHA.",
"description": ""
},
"preset_recaptcha_title": {
"message": "Excepción de reCAPTCHA",
"description": ""
},
"preset_recaptcha_description": {
"message": "La protección de la API window rompe reCAPTCHA. Este preajuste permite el uso de la API window.name en páginas integradas, lo que hará que vuelva a funcionar.",
"description": ""
} }
} }

File diff suppressed because it is too large Load Diff

1670
_locales/hi/messages.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1670
_locales/ja/messages.json Normal file

File diff suppressed because it is too large Load Diff

1670
_locales/ko/messages.json Normal file

File diff suppressed because it is too large Load Diff

1670
_locales/lt/messages.json Normal file

File diff suppressed because it is too large Load Diff

1670
_locales/nb/messages.json Normal file

File diff suppressed because it is too large Load Diff

1670
_locales/pl/messages.json Normal file

File diff suppressed because it is too large Load Diff

1670
_locales/pt/messages.json Normal file

File diff suppressed because it is too large Load Diff

1670
_locales/pt_BR/messages.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1670
_locales/zh_TW/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -20,3 +20,52 @@ div {
.action.search { .action.search {
padding-left: calc(0.5em + 19px + 0.25em); padding-left: calc(0.5em + 19px + 0.25em);
} }
#headerActions {
display: grid;
grid-template-columns: 0.5fr 50px 0.5fr;
grid-template-rows: auto;
}
#addonStatus {
grid-row: 1 / 2;
grid-column: 2 / 3;
border: none;
display: block;
margin: 5px auto;
width: 40px;
min-width: 0;
height: 40px;
background: none;
background-position: 50%;
background-size: 100%;
cursor: pointer;
}
#addonStatus.unknown {
background-image: radial-gradient(black, rgba(0, 0, 0, 0), transparent);;
}
#addonStatus.off {
background-image: url(../icons/browserAction-CBoff.svg);
}
#addonStatus.on {
background-image: url(../icons/browserAction-CBon.svg);
}
#reload {
grid-row: 1 / 2;
grid-column: 3 / 4;
cursor: pointer;
height: 19px;
width: 19px;
align-self: center;
justify-self: left;
margin-left: 1em;
background-color: currentColor;
mask-size: 100%;
mask-image: url(../icons/browserAction-reload.svg);
}
#reload.hidden {
display: none;
}

View File

@ -7,14 +7,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head> </head>
<body> <body>
<div id="headerActions">
<button id="addonStatus" class="undefined"></button>
<div id="reload" class="hidden"></div>
</div>
<div id="actions" class="stackedInputs"></div> <div id="actions" class="stackedInputs"></div>
<script src="../lib/require.js"></script> <div id="version" class="versionDisplay"></div>
<script src="../lib/extension.js"></script> <script src="../lib/require.js"></script>
<script src="../lib/logging.js"></script> <script src="../lib/logging.js"></script>
<script src="../lib/settingDefinitions.js"></script> <script src="../lib/extension.js"></script>
<script src="../lib/settingContainers.js"></script> <script src="../lib/settingDefinitions.js"></script>
<script src="../lib/settings.js"></script> <script src="../lib/settingContainers.js"></script>
<script src="../lib/theme.js"></script> <script src="../lib/settings.js"></script>
<script src="../lib/lists.js"></script>
<script src="../lib/theme.js"></script>
<script src="browserAction.js"></script> <script src="browserAction.js"></script>
</body> </body>
</html> </html>

View File

@ -7,48 +7,116 @@
const extension = require("../lib/extension"); const extension = require("../lib/extension");
const logging = require("../lib/logging"); const logging = require("../lib/logging");
const settings = require("../lib/settings"); const settings = require("../lib/settings");
const settingContainers = require("../lib/settingContainers");
const lists = require("../lib/lists");
require("../lib/theme").init(); require("../lib/theme").init();
logging.message("Opened browser action"); logging.message("Opened browser action");
settings.onloaded(function(){
const actions = document.getElementById("actions");
[ browser.tabs.query({active: true, currentWindow: true}).then(async function([currentTab]){
{ function isWhitelisted(url){
label: "settings", if (!(url instanceof URL)){
icon: browser.extension.getURL("icons/pageAction-showOptions.svg"), url = new URL(url);
action: function(){ }
if (browser.runtime && browser.runtime.openOptionsPage){ return lists.get("white").match(url) ||
browser.runtime.openOptionsPage(); settings.get("blockMode", url).startsWith("allow");
} }
else {
window.open(browser.extension.getURL("options/options.html"), "_blank"); const currentURL = new URL(currentTab.url);
} const reloadButton = document.getElementById("reload");
reloadButton.addEventListener("click", async function(){
await browser.tabs.reload(currentTab.id, {bypassCache: true});
window.close();
});
const addonStatus = document.getElementById("addonStatus");
addonStatus.addEventListener("click", async function(){
reloadButton.classList.toggle("hidden");
if (isWhitelisted(currentURL)){
settingContainers.resetUrlValue("blockMode", currentURL);
if (settings.get("blockMode").startsWith("allow")){
settings.set("blockMode", "fake", currentURL.host);
} }
}, if (settings.get("blockDataURLs")){
{ settingContainers.resetUrlValue("blockDataURLs", currentURL);
label: "test",
advanced: true,
icon: browser.extension.getURL("icons/browserAction-test.svg"),
action: function(){
window.open("https://canvasblocker.kkapsner.de/test", "_blank");
} }
}, const entries = lists.get("white").filter(e => e.match(currentURL)).map(e => e.value);
{ await Promise.all([
label: "review", lists.removeFrom("white", entries),
icon: browser.extension.getURL("icons/browserAction-review.svg"), lists.removeFrom("sessionWhite", entries)
action: function(){ ]);
window.open("https://addons.mozilla.org/firefox/addon/canvasblocker/reviews/", "_blank"); }
else {
settings.set("blockMode", "allowEverything", currentURL.hostname);
if (settings.get("blockDataURLs")){
settings.set("blockDataURLs", false, currentURL.hostname);
} }
}, }
{ update();
label: "reportIssue", });
icon: browser.extension.getURL("icons/browserAction-reportIssue.svg"), function update(){
action: function(){ if (isWhitelisted(currentURL)){
window.open("https://github.com/kkapsner/CanvasBlocker/issues", "_blank"); addonStatus.className = "off";
addonStatus.title = extension.getTranslation("browserAction_status_off");
}
else {
addonStatus.className = "on";
addonStatus.title = extension.getTranslation("browserAction_status_on");
}
}
return settings.onloaded(update);
}).catch(function(){});
const actionDefinitions = [
{
label: "settings",
icon: extension.getURL("icons/pageAction-showOptions.svg"),
action: function(){
if (browser.runtime && browser.runtime.openOptionsPage){
browser.runtime.openOptionsPage();
} }
}, else {
].forEach(function(action){ browser.tabs.create({url: extension.getURL("options/options.html")});
}
window.close();
}
},
{
label: "faq",
icon: extension.getURL("icons/browserAction-faq.svg"),
action: function(){
browser.tabs.create({url: "https://canvasblocker.kkapsner.de/faq/"});
window.close();
}
},
{
label: "test",
advanced: true,
icon: extension.getURL("icons/browserAction-test.svg"),
action: function(){
browser.tabs.create({url: "https://canvasblocker.kkapsner.de/test"});
window.close();
}
},
{
label: "review",
icon: extension.getURL("icons/browserAction-review.svg"),
action: function(){
browser.tabs.create({url: "https://addons.mozilla.org/firefox/addon/canvasblocker/reviews/"});
window.close();
}
},
{
label: "reportIssue",
icon: extension.getURL("icons/browserAction-reportIssue.svg"),
action: function(){
browser.tabs.create({url: "https://github.com/kkapsner/CanvasBlocker/issues"});
window.close();
}
},
];
settings.onloaded(async function(){
const actions = document.getElementById("actions");
actionDefinitions.forEach(function(action){
logging.verbose("Action", action); logging.verbose("Action", action);
if (action.advanced && !settings.displayAdvancedSettings){ if (action.advanced && !settings.displayAdvancedSettings){
logging.verbose("Hiding advanced action"); logging.verbose("Hiding advanced action");
@ -59,16 +127,23 @@
const icon = document.createElement("span"); const icon = document.createElement("span");
icon.className = "icon"; icon.className = "icon";
icon.style.maskImage = "url(" + action.icon + ")"; function setIcon(url){
icon.style.maskImage = "url(" + url + ")";
}
setIcon(action.icon);
actionButton.appendChild(icon); actionButton.appendChild(icon);
actionButton.appendChild( const textNode = document.createTextNode("");
document.createTextNode( function setLabel(label){
extension.getTranslation("browserAction_" + action.label) || action.label textNode.nodeValue = extension.getTranslation("browserAction_" + label) || label;
) }
); setLabel(action.label);
actionButton.addEventListener("click", action.action);
actionButton.appendChild(textNode);
actionButton.addEventListener("click", function(){
action.action.call(this, {setIcon, setLabel});
});
actions.appendChild(actionButton); actions.appendChild(actionButton);
}); });
@ -80,13 +155,17 @@
search.addEventListener("keypress", function(event){ search.addEventListener("keypress", function(event){
if ([10, 13].indexOf(event.keyCode) !== -1){ if ([10, 13].indexOf(event.keyCode) !== -1){
window.open(browser.extension.getURL( browser.tabs.create({url: extension.getURL(
"options/options.html" + "options/options.html" +
"?search=" + "?search=" +
encodeURIComponent(this.value) encodeURIComponent(this.value)
)); )});
window.close(); window.close();
} }
}); });
}); });
window.addEventListener("load", async function(){
extension.displayVersion("version", 250);
});
}()); }());

8
crowdin.yml Normal file
View File

@ -0,0 +1,8 @@
files:
- source: /_locales/en/*.json
translation: /_locales/%two_letters_code%/%original_file_name%
languages_mapping:
two_letters_code:
zh-CN: zh_CN
pt-BR: pt_BR
zh-TW: zh_TW

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
id="svg2" inkscape:version="0.92.2 2405546, 2018-03-11" sodipodi:docname="browserAction-whitelisted.svg" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="96px" height="96px"
viewBox="0 0 96 96" enable-background="new 0 0 96 96" xml:space="preserve">
<sodipodi:namedview id="namedview9" inkscape:pageshadow="2" inkscape:window-width="1600" inkscape:window-height="841" inkscape:current-layer="svg2" inkscape:window-x="0" inkscape:window-y="0" inkscape:pageopacity="0" gridtolerance="10" inkscape:cx="-19.539638" objecttolerance="10" inkscape:zoom="3.4766083" borderopacity="1" showgrid="false" bordercolor="#666666" guidetolerance="10" pagecolor="#ffffff" inkscape:cy="53.605992" inkscape:window-maximized="1">
</sodipodi:namedview>
<path id="rect3826" fill="#F5F5F5" d="M23.808,2h48.385C84.236,2,94,11.764,94,23.808v48.385C94,84.236,84.236,94,72.191,94H23.808
C11.764,94,2,84.236,2,72.191V23.808C2,11.764,11.764,2,23.808,2z"/>
<path id="path4138" inkscape:connector-curvature="0" fill="#5C5C5C" d="M30.141,82.826c0.456,0.027,1.107-0.164,1.411-0.412
c0.139-0.113,0.567-0.551,0.953-0.971c1.881-2.055,3.544-2.898,4.942-2.512c1.059,0.295,2.344,2.311,3.471,5.438
c0.671,1.862,1.018,2.272,2.078,2.461c0.646,0.106,1.577-0.211,1.949-0.677c0.786-0.982,0.744-1.581-0.297-4.195
c-1.14-2.859-2.118-4.486-3.483-5.796c-0.557-0.533-0.688-0.608-1.792-1.093c-1.115-0.48-1.25-0.521-2.083-0.574
c-1.22-0.079-1.971,0.035-3.196,0.482c-1.626,0.603-3.177,1.717-4.734,3.402c-0.836,0.906-1.096,1.307-1.206,1.854
C27.903,81.493,28.871,82.75,30.141,82.826z M22.995,76.758c0.63-0.013,2.712-0.607,4.185-1.197c2.96-1.186,5.259-2.843,6.942-5.006
c1.732-2.223,2.766-5.514,2.534-8.063c-0.14-1.537-1.171-2.426-2.56-2.204c-1.155,0.185-1.846,1.142-1.794,2.487
c0.05,1.305-0.112,2.123-0.675,3.406c-0.385,0.877-0.617,1.291-0.996,1.769c-0.619,0.78-1.767,1.784-2.752,2.403
c-0.96,0.604-3.115,1.506-4.52,1.896c-1.711,0.474-2.105,0.747-2.419,1.679C20.476,75.309,21.548,76.785,22.995,76.758
L22.995,76.758z M51.635,88.271c0.777,0.125,1.611-0.181,2.063-0.742c0.357-0.449,0.572-1.219,0.482-1.725
c-0.047-0.264-0.535-1.229-1.246-2.457C47.646,74.152,43.52,64.589,41.913,57.8c-0.611-2.579-0.907-5.196-0.801-7.098
c0.148-2.679,0.969-4.377,2.944-6.095c1.762-1.532,3.848-1.911,5.826-1.057c1.743,0.751,3.076,2.407,3.571,4.432
c0.115,0.471,0.252,1.868,0.331,3.375c0.401,7.607,1.776,12.148,5.877,19.398c1.379,2.438,4.983,8.372,5.26,8.658
c0.401,0.418,1.349,0.727,1.919,0.621c1.196-0.217,2.012-1.457,1.729-2.645c-0.047-0.197-0.654-1.31-1.354-2.465
c-4.361-7.22-5.924-10.104-7.027-13.035c-1.252-3.308-1.752-5.979-2.022-10.822c-0.16-2.871-0.322-3.906-0.828-5.317
c-1.099-3.067-3.728-5.678-6.591-6.551c-2.245-0.684-4.219-0.622-6.348,0.198c-2.368,0.913-4.308,2.545-5.829,4.908
c-0.639,0.992-1.221,2.44-1.507,3.751c-0.764,3.5-0.163,8.708,1.692,14.667c1.689,5.424,4.678,12.258,8.348,19.088
c1.315,2.444,2.89,5.198,3.224,5.629C50.621,87.842,51.201,88.205,51.635,88.271L51.635,88.271z M16.767,68.199
c1.446,0.063,3.005-0.068,4.184-0.352c3.642-0.882,7.026-3.611,9.745-7.875c1.634-2.563,2.318-4.521,3.509-10.057
c1.172-5.442,2.109-7.967,3.715-10.011c1.779-2.264,4.521-3.784,7.851-4.353c2.52-0.429,4.891-0.146,7.23,0.864
c2.353,1.015,4.129,2.524,5.588,4.745c1.854,2.821,2.699,5.881,2.933,10.604c0.065,1.353,0.201,2.918,0.307,3.479
c0.688,3.884,2.863,8.895,5.92,13.621c0.916,1.419,1.207,1.744,1.771,1.988c1.646,0.711,3.463-0.881,2.918-2.563
c-0.076-0.231-0.453-0.908-0.842-1.498c-2.794-4.276-4.837-8.867-5.479-12.313c-0.08-0.438-0.163-1.318-0.185-1.963
c-0.133-4.199-0.59-6.895-1.646-9.699c-1.692-4.485-4.479-7.75-8.464-9.903c-3.773-2.044-8.651-2.458-13.146-1.115
c-5.404,1.616-9.096,5.196-11.024,10.692c-0.607,1.731-1.005,3.23-1.692,6.383c-0.791,3.628-1.068,4.646-1.708,6.272
c-0.496,1.265-0.673,1.619-1.226,2.455c-1.98,2.989-3.854,4.75-5.994,5.623c-1.19,0.48-2.282,0.647-4.036,0.611
c-1.452-0.027-1.799,0.037-2.299,0.449c-0.739,0.607-1,1.592-0.647,2.441C14.468,67.754,15.168,68.129,16.767,68.199z M59.887,85.92
c0.512-0.016,0.977-0.203,1.449-0.588c0.332-0.27,0.672-1.158,0.635-1.663c-0.039-0.546-0.158-0.802-1.128-2.423
c-4.798-8.023-8.554-16.719-10.219-23.664c-0.656-2.736-0.978-4.822-1.11-7.225c-0.078-1.395-0.127-1.67-0.354-2.042
c-0.219-0.346-0.375-0.48-0.848-0.716c-0.381-0.191-0.708-0.283-0.942-0.294c-1.189-0.013-2.147,0.904-2.231,2.132
c-0.043,0.636,0.108,2.902,0.277,4.158c1.131,8.401,5.262,19.021,11.536,29.646c0.567,0.964,1.172,1.891,1.34,2.059
C58.714,85.734,59.246,85.938,59.887,85.92L59.887,85.92z M14.583,61.125c0.586-0.031,2.742-0.874,4.285-1.674
c1.912-0.99,3.742-2.354,4.875-3.629c0.981-1.105,1.427-1.824,2.097-3.387c0.732-1.707,0.892-2.229,2.144-7.027
c1.272-4.877,1.599-5.944,2.381-7.763c0.754-1.753,1.157-2.384,2.252-3.528c0.693-0.725,2.023-1.881,2.895-2.519
c2.495-1.823,5.927-3.274,8.839-3.737c6.736-1.071,14.553,1.754,18.793,6.792c1.271,1.517,2.273,3.206,3.144,5.3
c1,2.419,1.549,4.663,2.187,8.936c0.336,2.241,0.695,3.818,1.256,5.479c0.899,2.672,2.705,5.885,3.646,6.496
c0.739,0.479,1.938,0.368,2.604-0.236c0.18-0.161,0.406-0.493,0.521-0.754c0.354-0.813,0.248-1.536-0.35-2.337
c-0.592-0.796-1.432-2.38-1.93-3.637c-0.8-2.012-1.113-3.368-1.652-7.07c-0.938-6.443-2.918-11.259-6.196-15.068
c-2.198-2.553-4.604-4.357-7.858-5.901c-5.721-2.709-11.805-3.296-17.592-1.696c-4.545,1.257-9.443,4.404-12.359,7.941
c-0.997,1.21-1.276,1.7-2.223,3.907c-1.008,2.352-1.2,2.962-2.501,7.973c-1.507,5.809-2,7.213-2.963,8.454
c-1.16,1.496-3.207,2.865-6.102,4.084c-1.705,0.722-2.043,0.949-2.319,1.588C11.812,59.604,12.948,61.215,14.583,61.125
L14.583,61.125z M16.24,52.795c0.483,0.055,1.019-0.072,1.441-0.348c0.606-0.393,0.871-0.975,1.224-2.689
c1.364-6.627,3.764-12.456,6.843-16.617c0.599-0.81,0.96-1.228,1.792-2.076c1.003-1.021,1.213-1.806,0.752-2.811
c-0.614-1.339-2.258-1.645-3.459-0.643c-0.925,0.771-2.451,2.626-3.626,4.407c-2.578,3.906-4.743,9.102-6.082,14.591
c-0.455,1.863-0.815,3.732-0.801,4.146C14.359,51.752,15.23,52.678,16.24,52.795L16.24,52.795z M77.986,50.193
c0.961,0.14,1.797-0.314,2.26-1.229c0.326-0.641,0.309-1-0.189-3.746c-0.914-5.066-2.465-10.016-4.086-13.047
c-2.645-4.948-6.426-8.803-11.617-11.844c-1.438-0.847-4.939-2.44-6.442-2.938c-6.185-2.042-12.656-2.134-19.49-0.278
c-2.937,0.798-7.896,2.919-9.62,4.115c-0.965,0.67-1.278,1.718-0.809,2.71c0.53,1.121,1.613,1.56,2.716,1.102
c0.315-0.131,1.1-0.529,1.743-0.884c1.418-0.784,5.004-2.234,6.736-2.724c7.333-2.072,14.103-1.64,20.314,1.298
c5.229,2.475,8.858,5.515,11.537,9.662c2.168,3.365,3.647,7.662,4.71,13.635c0.521,2.965,0.521,2.974,0.88,3.377
C77.049,49.893,77.426,50.111,77.986,50.193L77.986,50.193z M81.521,35.5c1.049-0.005,1.99-0.758,2.176-1.744
c0.117-0.623-0.105-1.528-0.82-3.338c-2.076-5.25-5.73-10.019-10.551-13.767c-4.316-3.356-9.973-5.997-15.771-7.365
C52.813,8.404,48.351,8.09,44.511,8.44c-1.835,0.167-4.559,0.642-6.361,1.109c-1.521,0.395-2.014,0.709-2.351,1.499
c-0.576,1.351,0.384,2.922,1.839,3.013c0.196,0.012,1.036-0.144,1.867-0.347c3.635-0.888,6.941-1.216,10.265-1.017
c7.313,0.437,15.016,3.396,20.502,7.883c4.207,3.437,7.521,8.089,8.979,12.609c0.271,0.842,0.504,1.368,0.694,1.58
C80.284,35.141,81.063,35.502,81.521,35.5L81.521,35.5z"/>
<linearGradient id="rect3826-9_1_" gradientUnits="userSpaceOnUse" x1="-2884.5107" y1="2237.3203" x2="-2884.0027" y2="2328.032" gradientTransform="matrix(-1.0612 0 0 1 -3035.2104 -2234.4375)">
<stop offset="0" style="stop-color:#808080;stop-opacity:0"/>
<stop offset="0.6455" style="stop-color:#808080;stop-opacity:0.498"/>
<stop offset="0.8787" style="stop-color:#808080"/>
<stop offset="1" style="stop-color:#808080"/>
</linearGradient>
<path id="rect3826-9" inkscape:connector-curvature="0" opacity="0.6" fill="url(#rect3826-9_1_)" enable-background="new " d="
M48,3v90H26.146C13.326,93,3,83.27,3,71.188V24.813C3,12.731,13.326,3,26.146,3H48z"/>
<path id="rect3826-4" inkscape:connector-curvature="0" opacity="0.6" fill="#808080" enable-background="new " d="M71.188,3
C83.27,3,93,12.731,93,24.813v46.375C93,83.27,83.27,93,71.188,93H48V3H71.188z"/>
<path id="rect3826-0" fill="none" stroke="#000000" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M23.808,2
h48.385C84.236,2,94,11.764,94,23.808v48.385C94,84.236,84.236,94,72.191,94H23.808C11.764,94,2,84.236,2,72.191V23.808
C2,11.764,11.764,2,23.808,2z"/>
<g id="Loeschen">
<g id="XMLID_1_">
<g>
<path fill="#FFFFFF" d="M82.62,69.56c0,4.221-1.64,8.19-4.63,11.181c-2.98,2.979-6.95,4.63-11.181,4.63
c-4.22,0-8.189-1.65-11.18-4.63C52.64,77.75,51,73.78,51,69.56c0-4.229,1.64-8.199,4.63-11.18c2.98-2.99,6.96-4.64,11.18-4.64
c4.23,0,8.19,1.649,11.181,4.64c1.49,1.49,2.649,3.23,3.439,5.12C82.21,65.4,82.62,67.44,82.62,69.56z M76,64.61l-4.24-4.25
l-4.95,4.949L61.86,60.37l-4.24,4.24l4.95,4.949l-4.95,4.95l4.24,4.24l4.949-4.95l4.95,4.95L76,74.51l-4.95-4.95L76,64.61z"/>
<polygon fill="#FF0000" points="71.76,60.36 76,64.61 71.05,69.56 76,74.51 71.76,78.75 66.81,73.8 61.86,78.75 57.62,74.51
62.57,69.56 57.62,64.61 61.86,60.37 66.81,65.31 "/>
<path fill="#FF0000" d="M82.62,69.56c0-2.119-0.41-4.159-1.19-6.06c-0.79-1.89-1.949-3.63-3.439-5.12
c-2.99-2.99-6.95-4.64-11.181-4.64c-4.22,0-8.199,1.649-11.18,4.64C52.64,61.36,51,65.33,51,69.56c0,4.221,1.64,8.19,4.63,11.181
c2.99,2.979,6.96,4.63,11.18,4.63c4.23,0,8.2-1.65,11.181-4.63C80.98,77.75,82.62,73.78,82.62,69.56z M80.06,56.31
c7.32,7.311,7.32,19.181,0,26.5C76.4,86.47,71.61,88.3,66.81,88.3c-4.79,0-9.59-1.83-13.25-5.49
c-7.319-7.319-7.319-19.189,0-26.5c3.66-3.659,8.46-5.489,13.25-5.489C71.61,50.82,76.4,52.65,80.06,56.31z"/>
</g>
<g>
<polygon fill="none" stroke="#000000" stroke-width="0.5" stroke-linejoin="round" stroke-miterlimit="10" points="71.76,60.36
66.81,65.31 61.86,60.37 57.62,64.61 62.57,69.56 57.62,74.51 61.86,78.75 66.81,73.8 71.76,78.75 76,74.51 71.05,69.56
76,64.61 "/>
<path fill="none" stroke="#000000" stroke-width="0.5" stroke-linejoin="round" stroke-miterlimit="10" d="M80.06,56.31
C76.4,52.65,71.61,50.82,66.81,50.82c-4.79,0-9.59,1.83-13.25,5.489c-7.319,7.311-7.319,19.181,0,26.5
c3.66,3.66,8.46,5.49,13.25,5.49c4.801,0,9.591-1.83,13.25-5.49C87.38,75.49,87.38,63.62,80.06,56.31z"/>
<path fill="none" stroke="#000000" stroke-width="0.5" stroke-linejoin="round" stroke-miterlimit="10" d="M77.99,80.74
c-2.98,2.979-6.95,4.63-11.181,4.63c-4.22,0-8.189-1.65-11.18-4.63C52.64,77.75,51,73.78,51,69.56c0-4.229,1.64-8.199,4.63-11.18
c2.98-2.99,6.96-4.64,11.18-4.64c4.23,0,8.19,1.649,11.181,4.64c1.49,1.49,2.649,3.23,3.439,5.12c0.78,1.9,1.19,3.94,1.19,6.06
C82.62,73.78,80.98,77.75,77.99,80.74z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
id="svg2" inkscape:version="0.92.2 2405546, 2018-03-11" sodipodi:docname="browserAction-notPrinted.svg" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="96px" height="96px"
viewBox="0 0 96 96" enable-background="new 0 0 96 96" xml:space="preserve">
<sodipodi:namedview id="namedview9" inkscape:pageshadow="2" inkscape:window-width="1600" inkscape:window-height="841" inkscape:current-layer="svg2" inkscape:window-x="0" inkscape:window-y="0" inkscape:pageopacity="0" gridtolerance="10" inkscape:cx="-39.24275" objecttolerance="10" inkscape:zoom="3.4766083" borderopacity="1" showgrid="false" bordercolor="#666666" guidetolerance="10" pagecolor="#ffffff" inkscape:cy="53.605992" inkscape:window-maximized="1">
</sodipodi:namedview>
<path id="rect3826" fill="#F5F5F5" d="M23.808,2h48.385C84.236,2,94,11.764,94,23.808v48.385C94,84.236,84.236,94,72.191,94H23.808
C11.764,94,2,84.236,2,72.191V23.808C2,11.764,11.764,2,23.808,2z"/>
<path id="path4138" inkscape:connector-curvature="0" fill="#5C5C5C" d="M30.141,82.826c0.456,0.027,1.107-0.164,1.411-0.412
c0.139-0.113,0.567-0.551,0.953-0.971c1.881-2.055,3.544-2.898,4.942-2.512c1.059,0.295,2.344,2.311,3.471,5.439
c0.671,1.863,1.018,2.273,2.078,2.461c0.646,0.107,1.577-0.211,1.949-0.676c0.786-0.983,0.744-1.582-0.297-4.196
c-1.14-2.86-2.118-4.487-3.483-5.796c-0.557-0.533-0.688-0.609-1.792-1.092c-1.115-0.481-1.25-0.521-2.083-0.575
c-1.22-0.079-1.971,0.035-3.196,0.483c-1.626,0.602-3.177,1.717-4.734,3.402c-0.836,0.906-1.096,1.307-1.206,1.854
C27.903,81.493,28.871,82.75,30.141,82.826z M22.995,76.758c0.63-0.013,2.712-0.607,4.185-1.197c2.96-1.186,5.259-2.843,6.942-5.006
c1.732-2.223,2.766-5.514,2.534-8.063c-0.14-1.537-1.171-2.426-2.56-2.204c-1.155,0.185-1.846,1.142-1.794,2.487
c0.05,1.305-0.112,2.123-0.675,3.406c-0.385,0.877-0.617,1.291-0.996,1.769c-0.619,0.78-1.767,1.784-2.752,2.403
c-0.96,0.604-3.115,1.506-4.52,1.896c-1.711,0.474-2.105,0.747-2.419,1.679C20.476,75.309,21.548,76.785,22.995,76.758
L22.995,76.758z M51.635,88.271c0.777,0.125,1.611-0.18,2.063-0.742c0.357-0.449,0.572-1.219,0.482-1.725
c-0.047-0.263-0.535-1.228-1.246-2.457C47.646,74.152,43.52,64.589,41.913,57.8c-0.611-2.579-0.907-5.196-0.801-7.098
c0.148-2.679,0.969-4.377,2.944-6.095c1.762-1.532,3.848-1.911,5.826-1.057c1.743,0.751,3.076,2.407,3.571,4.432
c0.115,0.471,0.252,1.868,0.331,3.375c0.401,7.608,1.776,12.149,5.877,19.399c1.379,2.438,4.983,8.372,5.26,8.658
c0.401,0.418,1.349,0.726,1.919,0.621c1.196-0.216,2.012-1.457,1.729-2.644c-0.047-0.197-0.655-1.31-1.354-2.465
c-4.362-7.22-5.924-10.104-7.028-13.035c-1.252-3.308-1.752-5.979-2.022-10.822c-0.16-2.872-0.322-3.907-0.828-5.318
c-1.099-3.067-3.728-5.678-6.591-6.551c-2.245-0.684-4.219-0.622-6.348,0.198c-2.368,0.913-4.308,2.545-5.829,4.908
c-0.639,0.992-1.221,2.44-1.507,3.751c-0.764,3.5-0.163,8.708,1.692,14.667c1.689,5.424,4.678,12.258,8.348,19.088
c1.315,2.445,2.89,5.199,3.224,5.629C50.621,87.842,51.201,88.205,51.635,88.271L51.635,88.271z M16.767,68.199
c1.446,0.063,3.005-0.068,4.184-0.352c3.642-0.881,7.026-3.611,9.745-7.875c1.634-2.563,2.318-4.521,3.509-10.056
c1.172-5.442,2.109-7.967,3.715-10.011c1.779-2.264,4.521-3.784,7.851-4.352c2.52-0.429,4.891-0.146,7.23,0.864
c2.353,1.015,4.129,2.524,5.588,4.745c1.854,2.821,2.699,5.881,2.933,10.603c0.065,1.353,0.201,2.918,0.306,3.479
c0.688,3.884,2.864,8.895,5.92,13.621c0.916,1.419,1.207,1.744,1.771,1.988c1.646,0.711,3.463-0.881,2.918-2.563
c-0.076-0.231-0.453-0.908-0.842-1.498c-2.793-4.276-4.836-8.867-5.479-12.313c-0.08-0.438-0.163-1.319-0.184-1.963
c-0.133-4.199-0.59-6.895-1.646-9.699c-1.692-4.485-4.479-7.75-8.463-9.903c-3.774-2.044-8.652-2.458-13.146-1.115
c-5.404,1.616-9.096,5.196-11.024,10.692c-0.607,1.731-1.005,3.23-1.692,6.383c-0.791,3.628-1.068,4.646-1.708,6.272
c-0.496,1.265-0.673,1.619-1.226,2.455c-1.98,2.99-3.854,4.75-5.994,5.623c-1.19,0.481-2.282,0.648-4.036,0.612
c-1.452-0.028-1.799,0.037-2.299,0.449c-0.739,0.608-1,1.592-0.647,2.442C14.468,67.754,15.168,68.129,16.767,68.199z M59.887,85.92
c0.512-0.016,0.977-0.203,1.449-0.588c0.332-0.27,0.672-1.158,0.635-1.663c-0.039-0.546-0.158-0.802-1.128-2.423
c-4.798-8.023-8.554-16.719-10.219-23.664c-0.656-2.736-0.978-4.822-1.11-7.225c-0.078-1.394-0.127-1.67-0.354-2.042
c-0.219-0.346-0.375-0.48-0.848-0.716c-0.381-0.191-0.708-0.283-0.942-0.294c-1.189-0.013-2.147,0.904-2.231,2.133
c-0.043,0.635,0.108,2.902,0.277,4.158c1.131,8.401,5.262,19.02,11.536,29.646c0.567,0.964,1.172,1.891,1.34,2.059
C58.714,85.734,59.246,85.938,59.887,85.92L59.887,85.92z M14.583,61.125c0.586-0.031,2.742-0.874,4.285-1.674
c1.912-0.99,3.742-2.354,4.875-3.629c0.981-1.105,1.427-1.824,2.097-3.387c0.732-1.707,0.892-2.229,2.144-7.027
c1.272-4.877,1.599-5.944,2.381-7.763c0.754-1.753,1.157-2.384,2.252-3.528c0.693-0.725,2.023-1.881,2.895-2.519
c2.495-1.823,5.927-3.274,8.839-3.737c6.736-1.071,14.553,1.754,18.793,6.792c1.272,1.517,2.274,3.206,3.144,5.3
c1,2.419,1.549,4.663,2.187,8.936c0.336,2.241,0.695,3.818,1.256,5.479c0.899,2.672,2.705,5.885,3.646,6.496
c0.739,0.479,1.938,0.368,2.604-0.236c0.179-0.161,0.406-0.493,0.521-0.754c0.354-0.813,0.248-1.536-0.35-2.337
c-0.592-0.796-1.432-2.38-1.93-3.637c-0.799-2.012-1.113-3.368-1.652-7.07c-0.938-6.443-2.918-11.259-6.196-15.068
c-2.198-2.553-4.604-4.357-7.858-5.901c-5.721-2.709-11.805-3.296-17.592-1.696c-4.545,1.257-9.443,4.404-12.359,7.941
c-0.997,1.21-1.276,1.7-2.223,3.907c-1.008,2.352-1.2,2.962-2.501,7.973c-1.507,5.809-2,7.213-2.963,8.454
c-1.16,1.496-3.207,2.865-6.102,4.084c-1.705,0.721-2.043,0.949-2.319,1.588C11.812,59.604,12.948,61.215,14.583,61.125
L14.583,61.125z M16.24,52.795c0.483,0.055,1.019-0.072,1.441-0.348c0.606-0.393,0.871-0.975,1.224-2.689
c1.364-6.627,3.764-12.456,6.843-16.617c0.599-0.81,0.96-1.228,1.792-2.076c1.003-1.021,1.213-1.806,0.752-2.811
c-0.614-1.339-2.258-1.645-3.459-0.643c-0.925,0.771-2.451,2.626-3.626,4.407c-2.578,3.906-4.743,9.102-6.082,14.591
c-0.455,1.863-0.815,3.732-0.801,4.146C14.359,51.752,15.23,52.678,16.24,52.795L16.24,52.795z M77.986,50.193
c0.961,0.14,1.797-0.314,2.26-1.229c0.326-0.641,0.309-1-0.189-3.746c-0.914-5.066-2.465-10.016-4.086-13.047
c-2.645-4.948-6.426-8.803-11.617-11.844c-1.439-0.847-4.94-2.44-6.443-2.938c-6.184-2.042-12.656-2.134-19.49-0.278
c-2.937,0.798-7.896,2.919-9.62,4.115c-0.965,0.67-1.278,1.718-0.809,2.71c0.53,1.121,1.613,1.56,2.716,1.102
c0.315-0.131,1.1-0.529,1.743-0.884c1.418-0.784,5.004-2.234,6.736-2.724c7.333-2.072,14.103-1.64,20.314,1.298
c5.23,2.475,8.859,5.515,11.537,9.662c2.168,3.365,3.648,7.662,4.71,13.635c0.521,2.965,0.522,2.974,0.88,3.377
C77.049,49.893,77.426,50.111,77.986,50.193L77.986,50.193z M81.521,35.5c1.049-0.005,1.99-0.758,2.176-1.744
c0.117-0.623-0.105-1.528-0.82-3.338c-2.076-5.25-5.73-10.019-10.551-13.767c-4.316-3.356-9.973-5.997-15.771-7.365
C52.813,8.404,48.351,8.09,44.511,8.44c-1.835,0.167-4.559,0.642-6.361,1.109c-1.521,0.395-2.014,0.709-2.351,1.499
c-0.576,1.351,0.384,2.922,1.839,3.013c0.196,0.012,1.036-0.144,1.867-0.347c3.635-0.888,6.941-1.216,10.265-1.017
c7.313,0.437,15.015,3.396,20.502,7.883c4.207,3.437,7.521,8.089,8.979,12.609c0.271,0.842,0.504,1.368,0.694,1.58
C80.284,35.141,81.063,35.502,81.521,35.5L81.521,35.5z"/>
<linearGradient id="rect3826-9_1_" gradientUnits="userSpaceOnUse" x1="-2392.2759" y1="-1420.481" x2="-2391.7678" y2="-1511.1927" gradientTransform="matrix(-1.0612 0 0 -1 -2512.8511 -1417.5977)">
<stop offset="0" style="stop-color:#0050BF;stop-opacity:0"/>
<stop offset="0.6455" style="stop-color:#0050BF;stop-opacity:0.498"/>
<stop offset="0.8787" style="stop-color:#0050BF"/>
<stop offset="1" style="stop-color:#0050BF"/>
</linearGradient>
<path id="rect3826-9" inkscape:connector-curvature="0" opacity="0.5" fill="url(#rect3826-9_1_)" enable-background="new " d="
M48,3v90H26.146C13.326,93,3,83.27,3,71.188V24.813C3,12.731,13.326,3,26.146,3H48z"/>
<path id="rect3826-4" inkscape:connector-curvature="0" opacity="0.5" fill="#004FBD" enable-background="new " d="M71.188,3
C83.27,3,93,12.731,93,24.813v46.375C93,83.27,83.27,93,71.188,93H48V3H71.188z"/>
<path id="rect3826-0" fill="none" stroke="#000000" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M23.808,2
h48.385C84.236,2,94,11.764,94,23.808v48.385C94,84.236,84.236,94,72.191,94H23.808C11.764,94,2,84.236,2,72.191V23.808
C2,11.764,11.764,2,23.808,2z"/>
<g id="Haekchen">
<g id="XMLID_1_">
<g>
<path fill="#FFFFFF" d="M78.01,58.39c6.16,6.17,6.16,16.19,0,22.36c-2.99,2.98-6.96,4.62-11.18,4.62s-8.19-1.64-11.17-4.62
c-6.16-6.17-6.16-16.19,0-22.36c2.979-2.979,6.95-4.62,11.17-4.62S75.02,55.41,78.01,58.39z M77.78,65l-3.11-3.16l-10.4,8.63
l-3.859-4.2l-4.271,4.28l8.04,8.04L77.78,65z"/>
<polygon fill="#58E617" points="74.67,61.84 77.78,65 64.18,78.59 56.14,70.55 60.41,66.27 64.27,70.47 "/>
<path fill="#58E617" d="M78.01,80.75c6.16-6.17,6.16-16.19,0-22.36c-2.99-2.979-6.96-4.62-11.18-4.62s-8.19,1.641-11.17,4.62
c-6.16,6.17-6.16,16.19,0,22.36c2.979,2.98,6.95,4.62,11.17,4.62S75.02,83.73,78.01,80.75z M80.08,56.33
c7.31,7.31,7.31,19.17,0,26.479c-3.66,3.66-8.46,5.49-13.25,5.49s-9.58-1.83-13.24-5.49c-7.32-7.31-7.32-19.17,0-26.479
c3.66-3.66,8.45-5.49,13.24-5.49S76.42,52.67,80.08,56.33z"/>
</g>
<g>
<path fill="none" stroke="#000000" stroke-width="0.5" stroke-linejoin="round" stroke-miterlimit="10" d="M80.08,56.33
c-3.66-3.66-8.46-5.49-13.25-5.49s-9.58,1.83-13.24,5.49c-7.32,7.31-7.32,19.17,0,26.479c3.66,3.66,8.45,5.49,13.24,5.49
s9.59-1.83,13.25-5.49C87.39,75.5,87.39,63.64,80.08,56.33z"/>
<path fill="none" stroke="#000000" stroke-width="0.5" stroke-linejoin="round" stroke-miterlimit="10" d="M78.01,80.75
c-2.99,2.98-6.96,4.62-11.18,4.62s-8.19-1.64-11.17-4.62c-6.16-6.17-6.16-16.19,0-22.36c2.979-2.979,6.95-4.62,11.17-4.62
s8.189,1.641,11.18,4.62C84.17,64.56,84.17,74.58,78.01,80.75z"/>
<polygon fill="none" stroke="#000000" stroke-width="0.5" stroke-linejoin="round" stroke-miterlimit="10" points="64.27,70.47
60.41,66.27 56.14,70.55 64.18,78.59 77.78,65 74.67,61.84 "/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

100
icons/browserAction-faq.svg Normal file
View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
width="38"
height="38"
viewBox="0 0 38 38"
sodipodi:docname="browserAction-faq.svg"
inkscape:export-filename="Fingerprint.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1600"
inkscape:window-height="841"
id="namedview4"
showgrid="false"
inkscape:zoom="8.8685035"
inkscape:cx="-5.5444943"
inkscape:cy="22.071382"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true">
<inkscape:grid
type="xygrid"
id="grid3725" />
</sodipodi:namedview>
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#919191;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 19,0 C 8.5272756,0 0,8.5272748 0,19 0,29.472725 8.5272756,38 19,38 29.472724,38 38,29.472725 38,19 38,8.5272748 29.472724,0 19,0 Z m 0,3.5 c 8.581184,0 15.5,6.918815 15.5,15.5 0,8.581185 -6.918816,15.5 -15.5,15.5 C 10.418816,34.5 3.5,27.581185 3.5,19 3.5,10.418815 10.418816,3.5 19,3.5 Z"
id="path819"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#bbbbbb;fill-opacity:1;stroke:none"
x="10.982422"
y="33.580078"
id="text823"><tspan
sodipodi:role="line"
id="tspan821"
x="10.982422"
y="68.970703"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:AkrutiMal2;-inkscape-font-specification:AkrutiMal2" /></text>
<circle
style="opacity:1;fill:#919191;fill-opacity:1;stroke:#bbbbbb;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path831"
cx="19"
cy="27.772472"
r="3.9999993" />
<path
style="fill:none;stroke:#919191;stroke-width:6.30000019;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 12.290687,12.178281 c 0,0 2.914851,-4.0593089 6.314481,-4.0593088 3.382758,0 5.57748,1.9006158 5.750689,4.2848258 0.142504,1.961563 -3.177558,2.475312 -4.172068,4.172069 -0.770156,1.313982 -1.240343,2.139595 -1.240344,4.397584 l -1e-6,1.691379"
id="path817"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csaasc" />
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
id="svg2"
version="1.1"
inkscape:version="1.1 (c4e8f9ed74, 2021-05-24)"
width="38"
height="38"
viewBox="0 0 38 38"
sodipodi:docname="browserAction-reload.svg"
inkscape:export-filename="Fingerprint.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1600"
inkscape:window-height="841"
id="namedview4"
showgrid="false"
inkscape:zoom="8.8685035"
inkscape:cx="0.62017228"
inkscape:cy="22.044305"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
showguides="true"
inkscape:pagecheckerboard="0">
<inkscape:grid
type="xygrid"
id="grid3725" />
</sodipodi:namedview>
<path
id="path819"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#919191;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.5;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 27.558376,2.036681 C 18.208263,-2.6806621 6.7540244,1.0915089 2.036681,10.441624 c -4.7173433,9.350114 -0.9451714,20.804352 8.404943,25.521695 9.350113,4.717343 20.804352,0.945172 25.521695,-8.404943 1.490789,-2.95485 2.131192,-6.119738 2.017806,-9.215679 l -3.48287,0.09573 c 0.09126,2.528509 -0.436066,5.11796 -1.659758,7.543405 -3.865316,7.661336 -13.158996,10.72198 -20.82033,6.856664 C 4.3568317,28.973181 1.2961865,19.679502 5.1615029,12.018167 9.0268193,4.3568308 18.320499,1.2961869 25.981833,5.1615029 27.491656,5.9232425 28.81983,6.8983886 29.954505,8.027729 L 32.30738,5.4367801 C 30.943863,4.0966388 29.353902,2.942564 27.558376,2.036681 Z" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#bbbbbb;fill-opacity:1;stroke:none"
x="10.982422"
y="33.580078"
id="text823"><tspan
sodipodi:role="line"
id="tspan821"
x="10.982422"
y="68.970703"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:AkrutiMal2;-inkscape-font-specification:AkrutiMal2" /></text>
<g
id="g180"
transform="matrix(-0.67598944,-0.06825165,0.06825165,-0.67598944,43.19324,21.507957)"
style="stroke-width:1.47183">
<rect
style="fill:#919191;fill-opacity:1;stroke:#919191;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect829"
width="5.999999"
height="16.195862"
x="15.780028"
y="-28.064632"
transform="rotate(82.446816)" />
<rect
style="fill:#919191;fill-opacity:1;stroke:#919191;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect829-3"
width="5.999999"
height="16.195862"
x="-18.228109"
y="-31.699099"
transform="rotate(173.76303)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -67,26 +67,5 @@
d="m 14.012442,1035.5221 c -2.335232,-2.4003 -6.181514,-2.4678 -8.563982,-0.1499 -2.382415,2.3178 -2.409607,6.1539 -0.07438,8.5542 2.083652,2.1417 5.370014,2.4455 7.746402,0.8391 l 6.980535,7.1749 1.507034,-1.4662 -6.980536,-7.1749 c 1.672964,-2.3332 1.468573,-5.6355 -0.615078,-7.7772 z m -1.048412,1.02 c 1.790326,1.8402 1.762992,4.7471 -0.04629,6.5074 -1.809231,1.7602 -4.704915,1.6971 -6.495242,-0.1431 -1.790325,-1.8402 -1.763281,-4.7256 0.04595,-6.4858 1.809283,-1.7603 4.705256,-1.7186 6.495583,0.1215 z" d="m 14.012442,1035.5221 c -2.335232,-2.4003 -6.181514,-2.4678 -8.563982,-0.1499 -2.382415,2.3178 -2.409607,6.1539 -0.07438,8.5542 2.083652,2.1417 5.370014,2.4455 7.746402,0.8391 l 6.980535,7.1749 1.507034,-1.4662 -6.980536,-7.1749 c 1.672964,-2.3332 1.468573,-5.6355 -0.615078,-7.7772 z m -1.048412,1.02 c 1.790326,1.8402 1.762992,4.7471 -0.04629,6.5074 -1.809231,1.7602 -4.704915,1.6971 -6.495242,-0.1431 -1.790325,-1.8402 -1.763281,-4.7256 0.04595,-6.4858 1.809283,-1.7603 4.705256,-1.7186 6.495583,0.1215 z"
id="path2985" id="path2985"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
<rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="rect3955-51"
width="10"
height="2"
x="-24.180271"
y="1033.5027" />
<rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="rect3955-4-7"
width="10"
height="2"
x="-24.180271"
y="1030.5026" />
<rect
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="rect3955-5-1"
width="10"
height="2"
x="-24.180271"
y="1027.5026" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -52,18 +52,6 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1033.3622)"> transform="translate(0,-1033.3622)">
<text
xml:space="preserve"
style="font-size:144px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="-4.8717484"
y="1008.9256"
id="text3755"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3757"
x="-4.8717484"
y="1008.9256"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Times New Roman;-inkscape-font-specification:Times New Roman">www.</tspan></text>
<path <path
style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846" d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -52,18 +52,6 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1033.3622)"> transform="translate(0,-1033.3622)">
<text
xml:space="preserve"
style="font-size:144px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="-4.8717484"
y="1008.9256"
id="text3755"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3757"
x="-4.8717484"
y="1008.9256"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Times New Roman;-inkscape-font-specification:Times New Roman">www.</tspan></text>
<path <path
style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846" d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -52,17 +52,6 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1033.3622)"> transform="translate(0,-1033.3622)">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
x="-4.8717484"
y="1008.9256"
id="text3755"><tspan
sodipodi:role="line"
id="tspan3757"
x="-4.8717484"
y="1008.9256"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:1.25;font-family:'Times New Roman';-inkscape-font-specification:'Times New Roman';text-align:start;text-anchor:start">www.</tspan></text>
<path <path
style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846" d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846"

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -52,17 +52,6 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1033.3622)"> transform="translate(0,-1033.3622)">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
x="-4.8717484"
y="1008.9256"
id="text3755"><tspan
sodipodi:role="line"
id="tspan3757"
x="-4.8717484"
y="1008.9256"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:1.25;font-family:'Times New Roman';-inkscape-font-specification:'Times New Roman';text-align:start;text-anchor:start">www.</tspan></text>
<path <path
style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846" d="m 4.1668789,1040.8717 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.3550471,-9.0615 7.0710681,-13.3846"

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -52,18 +52,6 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1033.3622)"> transform="translate(0,-1033.3622)">
<text
xml:space="preserve"
style="font-size:144px;font-style:normal;font-weight:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
x="4.5984316"
y="1006.9256"
id="text3755"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3757"
x="4.5984316"
y="1006.9256"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Times New Roman;-inkscape-font-specification:Times New Roman">https:</tspan></text>
<path <path
style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 4.167,1040.8719 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.355047,-9.0615 7.071068,-13.3846" d="m 4.167,1040.8719 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.355047,-9.0615 7.071068,-13.3846"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -52,17 +52,6 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1033.3622)"> transform="translate(0,-1033.3622)">
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
x="4.5984316"
y="1006.9256"
id="text3755"><tspan
sodipodi:role="line"
id="tspan3757"
x="4.5984316"
y="1006.9256"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:1.25;font-family:'Times New Roman';-inkscape-font-specification:'Times New Roman';text-align:start;text-anchor:start">https:</tspan></text>
<path <path
style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" style="fill:none;stroke:#00be00;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 4.167,1040.8719 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.355047,-9.0615 7.071068,-13.3846" d="m 4.167,1040.8719 c 2.65165,3.1567 3.661803,3.788 4.293148,8.5863 0.883884,-3.7881 2.355047,-9.0615 7.071068,-13.3846"

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -88,7 +88,8 @@
audio: _("askForAudioPermission"), audio: _("askForAudioPermission"),
history: _("askForHistoryPermission"), history: _("askForHistoryPermission"),
window: _("askForWindowPermission"), window: _("askForWindowPermission"),
domRect: _("askForDOMRectPermission") domRect: _("askForDOMRectPermission"),
svg: _("askForSVGPermission"),
}, },
askStatus: { askStatus: {
alreadyAsked: {}, alreadyAsked: {},
@ -103,7 +104,8 @@
audio: _("askForAudioInputPermission"), audio: _("askForAudioInputPermission"),
history: _("askForHistoryInputPermission"), history: _("askForHistoryInputPermission"),
window: _("askForWindowInputPermission"), window: _("askForWindowInputPermission"),
domRect: _("askForDOMRectInputPermission") domRect: _("askForDOMRectInputPermission"),
svg: _("askForSVGInputPermission"),
}, },
askStatus: { askStatus: {
alreadyAsked: {}, alreadyAsked: {},
@ -118,7 +120,8 @@
audio: _("askForAudioReadoutPermission"), audio: _("askForAudioReadoutPermission"),
history: _("askForHistoryReadoutPermission"), history: _("askForHistoryReadoutPermission"),
window: _("askForWindowReadoutPermission"), window: _("askForWindowReadoutPermission"),
domRect: _("askForDOMRectReadoutPermission") domRect: _("askForDOMRectReadoutPermission"),
svg: _("askForSVGReadoutPermission"),
}, },
askStatus: { askStatus: {
alreadyAsked: {}, alreadyAsked: {},

View File

@ -52,7 +52,7 @@
logging.message("check url %s for block mode %s", url, blockMode); logging.message("check url %s for block mode %s", url, blockMode);
switch (url.protocol){ switch (url.protocol){
case "about:": case "about:":
if (url.href === "about:blank"){ if (url.pathname === "blank"){
logging.message("use regular mode on about:blank"); logging.message("use regular mode on about:blank");
break; break;
} }

View File

@ -47,7 +47,10 @@
scope.init = function(){ scope.init = function(){
function listener(details){ function listener(details){
const headers = details.responseHeaders; const headers = details.responseHeaders;
if (settings.get("blockDataURLs", new URL(details.url))){ if (
details.statusCode !== 304 &&
settings.get("blockDataURLs", new URL(details.url))
){
const cspMatch = (blockBlob? "": "blob: ") + "filesystem: *"; const cspMatch = (blockBlob? "": "blob: ") + "filesystem: *";
logging.verbose("Adding CSP header to", details); logging.verbose("Adding CSP header to", details);
setHeader(headers, { setHeader(headers, {

View File

@ -13,6 +13,7 @@
} }
const browserAvailable = typeof browser !== "undefined"; const browserAvailable = typeof browser !== "undefined";
const logging = require("./logging");
scope.inBackgroundScript = !!( scope.inBackgroundScript = !!(
browserAvailable && browserAvailable &&
@ -26,7 +27,56 @@
return id; return id;
}; };
scope.extensionID = browserAvailable? browser.extension.getURL(""): "extensionID"; scope.parseTranslation = function parseTranslation(message, parameters = {}){
const container = document.createDocumentFragment();
message.split(/(\{[^}]+\})/).forEach(function(part){
if (part.startsWith("{") && part.endsWith("}")){
part = part.substring(1, part.length - 1);
const args = part.split(":");
switch (args[0]){
case "image": {
const image = document.createElement("img");
image.className = "noticeImage";
image.src = args.slice(1).join(":");
container.appendChild(image);
break;
}
case "link": {
const link = document.createElement("a");
link.target = "_blank";
link.textContent = args[1];
link.href = args.slice(2).join(":");
container.appendChild(link);
break;
}
default:
if (parameters[args[0]]){
const parameter = parameters[args[0]];
if ((typeof parameter) === "function"){
container.appendChild(parameter(args.slice(1).join(":")));
}
else {
container.appendChild(document.createTextNode(parameter));
}
}
else {
container.appendChild(document.createTextNode(part));
}
}
}
else {
container.appendChild(document.createTextNode(part));
}
});
return container;
};
scope.getURL = function getURL(str){
return browser.runtime.getURL(str);
};
scope.extensionID = browserAvailable? scope.getURL(""): "extensionID";
scope.inIncognitoContext = browserAvailable? browser.extension.inIncognitoContext: false; scope.inIncognitoContext = browserAvailable? browser.extension.inIncognitoContext: false;
@ -44,5 +94,238 @@
}; };
Object.seal(scope.message); Object.seal(scope.message);
scope.getWrapped = function getWrapped(obj){
return obj && (obj.wrappedJSObject || obj);
};
scope.exportFunctionWithName = function exportFunctionWithName(func, context, name){
const targetObject = scope.getWrapped(context).Object.create(null);
const exportedTry = exportFunction(func, targetObject, {allowCrossOriginArguments: true, defineAs: name});
if (exportedTry.name === name){
return exportedTry;
}
else {
if (func.name === name){
logging.message(
"FireFox bug: Need to change name in exportFunction from",
exportedTry.name,
"(originally correct) to",
name
);
}
else {
logging.error("Wrong name specified for", name, new Error());
}
const wrappedContext = scope.getWrapped(context);
const options = {
allowCrossOriginArguments: true,
defineAs: name
};
const oldDescriptor = Object.getOwnPropertyDescriptor(wrappedContext, name);
if (oldDescriptor && !oldDescriptor.configurable){
logging.error(
"Unable to export function with the correct name", name,
"instead we have to use", exportedTry.name
);
return exportedTry;
}
const exported = exportFunction(func, context, options);
if (oldDescriptor){
Object.defineProperty(wrappedContext, name, oldDescriptor);
}
else {
delete wrappedContext[name];
}
return exported;
}
};
const proxies = new Map();
const changedWindowsForProxies = new WeakMap();
function setupWindowForProxies(window){
if (changedWindowsForProxies.get(window)){
return;
}
const wrappedWindow = scope.getWrapped(window);
const functionPrototype = wrappedWindow.Function.prototype;
const originalToString = functionPrototype.toString;
changedWindowsForProxies.set(window, originalToString);
const alteredToString = scope.createProxyFunction(
window,
originalToString,
function toString(){
if (proxies.has(this)){
return proxies.get(this).string;
}
return originalToString.call(scope.getWrapped(this));
}
);
scope.changeProperty(window, "toString", {
object: functionPrototype,
name: "toString",
type: "value",
changed: alteredToString
});
const wrappedReflect = wrappedWindow.Reflect;
const originalReflectSetPrototypeOf = wrappedReflect.setPrototypeOf;
const alteredReflectSetPrototypeOf = scope.exportFunctionWithName(
function setPrototypeOf(target, prototype){
target = scope.getWrapped(target);
if (proxies.has(target)){
target = proxies.get(target).wrappedOriginal;
}
if (proxies.has(prototype)){
prototype = proxies.get(prototype).wrappedOriginal;
}
if (prototype){
const grandPrototype = wrappedReflect.getPrototypeOf(prototype);
if (proxies.has(grandPrototype)){
const testPrototype = wrappedWindow.Object.create(proxies.get(grandPrototype).wrappedOriginal);
const value = originalReflectSetPrototypeOf.call(wrappedReflect, target, testPrototype);
if (!value){
return false;
}
}
}
const value = originalReflectSetPrototypeOf.call(wrappedReflect, target, scope.getWrapped(prototype));
return value;
}, window, "setPrototypeOf"
);
scope.changeProperty(window, "toString", {
object: wrappedWindow.Reflect,
name: "setPrototypeOf",
type: "value",
changed: alteredReflectSetPrototypeOf
});
}
scope.createProxyFunction = function createProxyFunction(window, original, replacement){
setupWindowForProxies(window);
const wrappedObject = scope.getWrapped(window).Object;
const handler = wrappedObject.create(null);
handler.apply = scope.exportFunctionWithName(function(target, thisArg, args){
args = scope.getWrapped(args);
try {
return args.length?
replacement.call(thisArg, ...args):
replacement.call(thisArg);
}
catch (error){
try {
return original.apply(thisArg, args);
}
catch (error){
return scope.getWrapped(target).apply(thisArg, args);
}
}
}, window, "");
handler.setPrototypeOf = scope.exportFunctionWithName(function(target, prototype){
target = scope.getWrapped(target);
if (proxies.has(target)){
target = proxies.get(target).wrappedOriginal;
}
if (proxies.has(prototype)){
prototype = proxies.get(prototype).wrappedOriginal;
}
if (prototype){
const grandPrototype = wrappedObject.getPrototypeOf(prototype);
if (proxies.has(grandPrototype)){
const testPrototype = wrappedObject.create(proxies.get(grandPrototype).wrappedOriginal);
wrappedObject.setPrototypeOf(target, testPrototype);
}
}
return wrappedObject.setPrototypeOf(target, scope.getWrapped(prototype));
}, window, "");
const proxy = new window.Proxy(original, handler);
const proxyData = {
original: original,
wrappedOriginal: scope.getWrapped(original),
string: changedWindowsForProxies.get(window).call(original),
};
proxies.set(proxy, proxyData);
proxies.set(scope.getWrapped(proxy), proxyData);
return scope.getWrapped(proxy);
};
const changedPropertiesByWindow = new WeakMap();
scope.changeProperty = function(window, group, {object, name, type, changed}){
let changedProperties = changedPropertiesByWindow.get(scope.getWrapped(window));
if (!changedProperties){
changedProperties = [];
changedPropertiesByWindow.set(scope.getWrapped(window), changedProperties);
}
const descriptor = Object.getOwnPropertyDescriptor(object, name);
const original = descriptor[type];
descriptor[type] = changed;
Object.defineProperty(object, name, descriptor);
changedProperties.push({group, object, name, type, original});
};
scope.revertProperties = function(window, group){
window = scope.getWrapped(window);
let changedProperties = changedPropertiesByWindow.get(window);
if (!changedProperties){
return;
}
if (group){
const remainingProperties = changedProperties.filter(function(changedProperty){
return changedProperty.group !== group;
});
changedPropertiesByWindow.set(window, remainingProperties);
changedProperties = changedProperties.filter(function(changedProperty){
return changedProperty.group === group;
});
}
else {
changedPropertiesByWindow.delete(window);
}
for (let i = changedProperties.length - 1; i >= 0; i -= 1){
const {object, name, type, original} = changedProperties[i];
logging.verbose("reverting", name, "on", object);
const descriptor = Object.getOwnPropertyDescriptor(object, name);
descriptor[type] = original;
Object.defineProperty(object, name, descriptor);
}
};
scope.waitSync = function waitSync(reason = "for no reason"){
logging.message(`Starting synchronous request ${reason}.`);
try {
let xhr = new XMLHttpRequest();
xhr.open("GET", "https://[::]", false);
xhr.send();
xhr = null;
}
catch (error){
logging.verbose("Error in XHR:", error);
}
};
scope.displayVersion = async function displayVersion(node, displayRefresh = false){
if ("string" === typeof node){
node = document.getElementById(node);
}
if (!node){
throw "display node not found";
}
fetch(scope.getURL("manifest.json")).then(function(response){
return response.json();
}).then(function(manifest){
node.textContent = "Version " + manifest.version;
return manifest.version;
}).catch(function(error){
node.textContent = "Unable to get version: " + error;
});
if (displayRefresh){
// Workaround to hide the scroll bars
window.setTimeout(function(){
node.style.display = "none";
node.style.display = "";
}, displayRefresh);
}
};
Object.seal(scope); Object.seal(scope);
}()); }());

View File

@ -9,7 +9,6 @@
const {ask} = require("./askForPermission"); const {ask} = require("./askForPermission");
const {sha256String: hashing} = require("./hash"); const {sha256String: hashing} = require("./hash");
const {check: originalCheck, checkStack: originalCheckStack} = require("./check"); const {check: originalCheck, checkStack: originalCheckStack} = require("./check");
const {getWrapped} = require("./modifiedAPIFunctions");
const extension = require("./extension"); const extension = require("./extension");
const iframeProtection = require("./iframeProtection"); const iframeProtection = require("./iframeProtection");
@ -88,6 +87,8 @@
logging.notice("my tab cookie store id is", data.cookieStoreId); logging.notice("my tab cookie store id is", data.cookieStoreId);
const {persistent: persistentRnd} = require("./randomSupplies"); const {persistent: persistentRnd} = require("./randomSupplies");
persistentRnd.setCookieStoreId(data.cookieStoreId); persistentRnd.setCookieStoreId(data.cookieStoreId);
const modifiedNavigatorAPI = require("./modifiedNavigatorAPI");
modifiedNavigatorAPI.setCookieStoreId(data.cookieStoreId);
} }
const persistentRndName = "persistent" + (extension.inIncognitoContext? "Incognito": "") + "Rnd"; const persistentRndName = "persistent" + (extension.inIncognitoContext? "Incognito": "") + "Rnd";
if (data.hasOwnProperty(persistentRndName)){ if (data.hasOwnProperty(persistentRndName)){
@ -126,14 +127,12 @@
return settings.get(...args); return settings.get(...args);
} }
const interceptedWindows = new WeakMap(); const interceptedWindows = new WeakMap();
function interceptWindow(window){ function interceptWindow(window){
let wrappedTry; let wrappedTry;
try { try {
// eslint-disable-next-line no-unused-vars
const href = window.location.href; const href = window.location.href;
wrappedTry = getWrapped(window); wrappedTry = extension.getWrapped(window);
} }
catch (error){ catch (error){
// we are unable to read the location due to SOP // we are unable to read the location due to SOP
@ -146,9 +145,15 @@
if (!enabled || interceptedWindows.get(wrappedWindow)){ if (!enabled || interceptedWindows.get(wrappedWindow)){
return false; return false;
} }
if (wrappedWindow.matchMedia(extensionSecret[0]) === extensionSecret[1]){ const canvasBlockerData = wrappedWindow.matchMedia(extensionSecret[0]);
interceptedWindows.set(wrappedWindow, true); if (canvasBlockerData.secret === extensionSecret[1]){
return false; if (wrappedWindow.top === wrappedWindow){
canvasBlockerData.undoIntercept(extension.extensionID);
}
else {
interceptedWindows.set(wrappedWindow, true);
return false;
}
} }
logging.message("intercepting window", window); logging.message("intercepting window", window);
@ -170,17 +175,28 @@
const matchMediaDescriptor = Object.getOwnPropertyDescriptor(wrappedWindow, "matchMedia"); const matchMediaDescriptor = Object.getOwnPropertyDescriptor(wrappedWindow, "matchMedia");
const originalMatchMedia = matchMediaDescriptor.value; const originalMatchMedia = matchMediaDescriptor.value;
matchMediaDescriptor.value = exportFunction(function matchMedia(query){ extension.changeProperty(window, "matchMedia", {
if (query === extensionSecret[0]){ object: wrappedWindow,
return extensionSecret[1]; name: "matchMedia",
} type: "value",
else { changed: extension.exportFunctionWithName(function matchMedia(query){
return arguments.length > 1? if (query === extensionSecret[0]){
originalMatchMedia.apply(this, wrappedWindow.Array.from(arguments)): return {
originalMatchMedia.call(this, query); secret: extensionSecret[1],
} undoIntercept: function(token){
}, window); if (token === extension.extensionID){
Object.defineProperty(wrappedWindow, "matchMedia", matchMediaDescriptor); extension.revertProperties(window);
}
}
};
}
else {
return arguments.length > 1?
originalMatchMedia.call(this, ...arguments):
originalMatchMedia.call(this, query);
}
}, window, originalMatchMedia.name)
});
interceptedWindows.set(wrappedWindow, true); interceptedWindows.set(wrappedWindow, true);
return true; return true;
@ -189,6 +205,10 @@
logging.message("register listener for messages from background script"); logging.message("register listener for messages from background script");
extension.message.on(function(data){ extension.message.on(function(data){
if (data["canvasBlocker-unload"]){ if (data["canvasBlocker-unload"]){
extension.revertProperties(window);
for (let frameIndex = 0; frameIndex < window.length; frameIndex += 1){
extension.revertProperties(window[frameIndex]);
}
enabled = false; enabled = false;
} }
if ( if (

View File

@ -13,6 +13,7 @@
} }
const settings = require("./settings"); const settings = require("./settings");
const extension = require("./extension");
const lists = require("./lists"); const lists = require("./lists");
function isWhitelisted(url){ function isWhitelisted(url){
@ -23,23 +24,15 @@
function changeProperty(object, name, type, changed){ function changeProperty(object, name, type, changed){
const descriptor = Object.getOwnPropertyDescriptor(object, name); const descriptor = Object.getOwnPropertyDescriptor(object, name);
const original = descriptor[type]; const original = descriptor[type];
descriptor[type] = changed; if ((typeof changed) === "function"){
Object.defineProperty(object, name, descriptor); changed = extension.createProxyFunction(window, original, changed);
registerChangedProperty(object, name, descriptor, type, original); }
} extension.changeProperty(window, "iframeProtection", {object, name, type, changed});
const changedProperties = [];
// eslint-disable-next-line max-params
function registerChangedProperty(object, name, descriptor, type, original){
changedProperties.push({object, name, descriptor, type, original});
} }
if (settings.isStillDefault){ if (settings.isStillDefault){
settings.onloaded(function(){ settings.onloaded(function(){
if (isWhitelisted(window.location)){ if (isWhitelisted(window.location)){
changedProperties.forEach(function({object, name, descriptor, type, original}){ extension.revertProperties(window, "iframeProtection");
descriptor[type] = original;
Object.defineProperty(object, name, descriptor);
});
changedProperties.length = 0;
} }
}); });
} }
@ -48,6 +41,9 @@
return false; return false;
} }
} }
window.addEventListener("unload", function(){
extension.revertProperties(window, "iframeProtection");
});
return changeProperty; return changeProperty;
} }
@ -70,10 +66,9 @@
return window; return window;
} }
}; };
changeProperty(wrappedConstructor.prototype, "contentWindow", "get", exportFunction( changeProperty(wrappedConstructor.prototype, "contentWindow", "get",
Object.getOwnPropertyDescriptor(contentWindowTemp, "contentWindow").get, Object.getOwnPropertyDescriptor(contentWindowTemp, "contentWindow").get
window );
));
const contentDocumentDescriptor = Object.getOwnPropertyDescriptor( const contentDocumentDescriptor = Object.getOwnPropertyDescriptor(
constructor.prototype, constructor.prototype,
@ -89,10 +84,9 @@
return document; return document;
} }
}; };
changeProperty(wrappedConstructor.prototype, "contentDocument", "get", exportFunction( changeProperty(wrappedConstructor.prototype, "contentDocument", "get",
Object.getOwnPropertyDescriptor(contentDocumentTemp, "contentDocument").get, Object.getOwnPropertyDescriptor(contentDocumentTemp, "contentDocument").get
window );
));
}); });
} }
@ -129,41 +123,42 @@
protectionDefinition.methods.forEach(function(method){ protectionDefinition.methods.forEach(function(method){
const descriptor = Object.getOwnPropertyDescriptor(object, method); const descriptor = Object.getOwnPropertyDescriptor(object, method);
const original = descriptor.value; const original = descriptor.value;
changeProperty(object, method, "value", exportFunction(eval(`(function ${method}(){ changeProperty(object, method, "value", class {
const value = arguments.length? [method](){
original.apply(this, window.Array.from(arguments)): const value = arguments.length?
original.call(this); original.call(this, ...arguments):
allCallback(); original.call(this);
return value; allCallback();
})`), window)); return value;
}
}.prototype[method]);
}); });
protectionDefinition.getters.forEach(function(property){ protectionDefinition.getters.forEach(function(property){
const temp = eval(`({ const temp = {
get ${property}(){ get [property](){
const ret = this.${property}; const ret = this[property];
allCallback(); allCallback();
return ret; return ret;
} }
})`); };
changeProperty(object, property, "get", exportFunction( changeProperty(object, property, "get",
Object.getOwnPropertyDescriptor(temp, property).get, Object.getOwnPropertyDescriptor(temp, property).get
window );
));
}); });
protectionDefinition.setters.forEach(function(property){ protectionDefinition.setters.forEach(function(property){
const descriptor = Object.getOwnPropertyDescriptor(object, property); const descriptor = Object.getOwnPropertyDescriptor(object, property);
const setter = descriptor.set; const setter = descriptor.set;
const temp = eval(`({ const temp = {
set ${property}(value){ set [property](value){
const ret = setter.call(this, value); const ret = setter.call(this, value);
// const ret = this.${property} = value; // const ret = this.${property} = value;
allCallback(); allCallback();
return ret; return ret;
} }
})`); };
changeProperty(object, property, "set", exportFunction( changeProperty(object, property, "set",
Object.getOwnPropertyDescriptor(temp, property).set, window Object.getOwnPropertyDescriptor(temp, property).set
)); );
}); });
}); });
} }
@ -204,8 +199,7 @@
documentWriteDescriptorOnHTMLDocument? documentWriteDescriptorOnHTMLDocument?
wrappedWindow.HTMLDocument.prototype: wrappedWindow.HTMLDocument.prototype:
wrappedWindow.Document.prototype, wrappedWindow.Document.prototype,
// eslint-disable-next-line no-unused-vars "write", "value", function write(markup){
"write", "value", exportFunction(function write(markup){
for (let i = 0, l = arguments.length; i < l; i += 1){ for (let i = 0, l = arguments.length; i < l; i += 1){
const str = "" + arguments[i]; const str = "" + arguments[i];
// weird problem with waterfox and google docs // weird problem with waterfox and google docs
@ -223,7 +217,7 @@
} }
} }
} }
}, window) }
); );
const documentWritelnDescriptorOnHTMLDocument = Object.getOwnPropertyDescriptor( const documentWritelnDescriptorOnHTMLDocument = Object.getOwnPropertyDescriptor(
@ -239,26 +233,22 @@
documentWritelnDescriptorOnHTMLDocument? documentWritelnDescriptorOnHTMLDocument?
wrappedWindow.HTMLDocument.prototype: wrappedWindow.HTMLDocument.prototype:
wrappedWindow.Document.prototype, wrappedWindow.Document.prototype,
"writeln", "value", exportFunction( "writeln", "value", function writeln(markup){
// eslint-disable-next-line no-unused-vars for (let i = 0, l = arguments.length; i < l; i += 1){
function writeln(markup){ const str = "" + arguments[i];
for (let i = 0, l = arguments.length; i < l; i += 1){ const parts = str.split(/(?=<)/);
const str = "" + arguments[i]; const length = parts.length;
const parts = str.split(/(?=<)/); const scripts = window.document.getElementsByTagName("script");
const length = parts.length; for (let i = 0; i < length; i += 1){
const scripts = window.document.getElementsByTagName("script"); documentWrite.call(this, parts[i]);
for (let i = 0; i < length; i += 1){ allCallback();
documentWrite.call(this, parts[i]); if (scripts.length && scripts[scripts.length - 1].src){
allCallback(); observe();
if (scripts.length && scripts[scripts.length - 1].src){
observe();
}
} }
} }
documentWriteln.call(this, ""); }
}, documentWriteln.call(this, "");
window }
)
); );
} }
@ -274,9 +264,9 @@
).get; ).get;
changeProperty( changeProperty(
wrappedWindow, wrappedWindow,
"open", "value", exportFunction(function open(){ "open", "value", function open(){
const newWindow = arguments.length? const newWindow = arguments.length?
windowOpen.apply(this, window.Array.from(arguments)): windowOpen.call(this, ...arguments):
windowOpen.call(this); windowOpen.call(this);
if (newWindow){ if (newWindow){
// if we use windowOpen from the normal window we see some SOP errors // if we use windowOpen from the normal window we see some SOP errors
@ -284,7 +274,7 @@
singleCallback(getDocument.call(newWindow).defaultView); singleCallback(getDocument.call(newWindow).defaultView);
} }
return newWindow; return newWindow;
}, window) }
); );
} }

View File

@ -14,7 +14,6 @@
const {changedFunctions, changedGetters, setRandomSupply} = require("./modifiedAPI"); const {changedFunctions, changedGetters, setRandomSupply} = require("./modifiedAPI");
const randomSupplies = require("./randomSupplies"); const randomSupplies = require("./randomSupplies");
const {getWrapped} = require("./modifiedAPIFunctions");
const logging = require("./logging"); const logging = require("./logging");
const settings = require("./settings"); const settings = require("./settings");
const extension = require("./extension"); const extension = require("./extension");
@ -40,6 +39,9 @@
settings.on("rng", function(){ settings.on("rng", function(){
setRandomSupplyByType(settings.rng); setRandomSupplyByType(settings.rng);
}); });
if (!settings.isStillDefault){
setRandomSupplyByType(settings.rng);
}
function getURL(windowToProcess){ function getURL(windowToProcess){
let href; let href;
@ -68,7 +70,7 @@
[changedFunction.object] [changedFunction.object]
).map(function(name){ ).map(function(name){
if (name){ if (name){
const constructor = getWrapped(windowToProcess)[name]; const constructor = extension.getWrapped(windowToProcess)[name];
if (constructor){ if (constructor){
return constructor.prototype; return constructor.prototype;
} }
@ -77,7 +79,7 @@
}).concat( }).concat(
changedFunction.objectGetters? changedFunction.objectGetters?
changedFunction.objectGetters.map(function(objectGetter){ changedFunction.objectGetters.map(function(objectGetter){
return objectGetter(getWrapped(windowToProcess)); return objectGetter(extension.getWrapped(windowToProcess));
}): }):
[] []
); );
@ -85,6 +87,9 @@
const forEachFunction = function(windowToProcess, callback){ const forEachFunction = function(windowToProcess, callback){
apiNames.forEach(function(name){ apiNames.forEach(function(name){
const changedFunction = changedFunctions[name]; const changedFunction = changedFunctions[name];
if (changedFunction.name){
name = changedFunction.name;
}
getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){ getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){
if (object){ if (object){
callback({name, object: object, changedFunction}); callback({name, object: object, changedFunction});
@ -96,7 +101,7 @@
changedGetters.forEach(function(changedGetter){ changedGetters.forEach(function(changedGetter){
const name = changedGetter.name; const name = changedGetter.name;
changedGetter.objectGetters.forEach(function(changedGetter){ changedGetter.objectGetters.forEach(function(changedGetter){
const object = changedGetter(getWrapped(windowToProcess)); const object = changedGetter(extension.getWrapped(windowToProcess));
if (object){ if (object){
callback({name, object, objectGetter: changedGetter}); callback({name, object, objectGetter: changedGetter});
} }
@ -144,40 +149,44 @@
} }
map.set(object, originalPropertyDescriptor); map.set(object, originalPropertyDescriptor);
const temp = class {
[`get ${name}`](){
if (forceLoad){
logging.warning("force load the settings. Calling stack:", (new Error()).stack);
undoPreIntercept();
settings.forceLoad();
doRealIntercept(windowToProcess, apis, state);
const descriptor = Object.getOwnPropertyDescriptor(object, name);
return descriptor.value || descriptor.get.call(this);
}
else {
logging.notice("API blocked (%s)", name);
const url = getURL(windowToProcess);
if (!url){
return undef;
}
const error = new Error();
apis.notify({
url,
errorStack: error.stack,
messageId: "preBlock",
timestamp: new Date(),
functionName: name,
dataURL: false
});
return undef;
}
}
[`set ${name}`](newValue){}
}.prototype;
Object.defineProperty( Object.defineProperty(
object, object,
name, name,
{ {
enumerable: true, enumerable: true,
configurable: true, configurable: true,
get: exportFunction(function(){ get: extension.exportFunctionWithName(temp[`get ${name}`], windowToProcess, `get ${name}`),
if (forceLoad){ set: extension.exportFunctionWithName(temp[`set ${name}`], windowToProcess, `set ${name}`)
logging.warning("force load the settings. Calling stack:", (new Error()).stack);
undoPreIntercept();
settings.forceLoad();
doRealIntercept(windowToProcess, apis, state);
const descriptor = Object.getOwnPropertyDescriptor(object, name);
return descriptor.value || descriptor.get.call(this);
}
else {
logging.notice("API blocked (%s)", name);
const url = getURL(windowToProcess);
if (!url){
return undef;
}
const error = new Error();
apis.notify({
url,
errorStack: error.stack,
messageId: "preBlock",
timestamp: new Date(),
functionName: name,
dataURL: false
});
return undef;
}
}, windowToProcess),
set: exportFunction(function(){}, windowToProcess)
} }
); );
}); });
@ -231,7 +240,7 @@
name, changedFunction, siteStatus, original, name, changedFunction, siteStatus, original,
window: windowToProcess, prefs, notify, checkStack, ask window: windowToProcess, prefs, notify, checkStack, ask
}){ }){
return function checker(callingDepth = 2){ return function checker(callingDepth = 3){
const errorStack = (new Error()).stack; const errorStack = (new Error()).stack;
try { try {
@ -314,42 +323,50 @@
function interceptFunctions(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){ function interceptFunctions(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){
apiNames.forEach(function(name){ apiNames.forEach(function(name){
const changedFunction = changedFunctions[name]; const changedFunction = changedFunctions[name];
if (changedFunction.name){
name = changedFunction.name;
}
const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs); const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs);
logging.verbose("status for", name, ":", functionStatus); logging.verbose("status for", name, ":", functionStatus);
if (functionStatus.active){ if (!functionStatus.active) return;
getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){
if (object){ getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){
const original = object[name]; if (!object) return;
const checker = generateChecker({
name, changedFunction, siteStatus, original, const original = object[name];
window: windowToProcess, prefs, checkStack, ask, notify const checker = generateChecker({
}); name, changedFunction, siteStatus, original,
const descriptor = Object.getOwnPropertyDescriptor(object, name); window: windowToProcess, prefs, checkStack, ask, notify
if (descriptor){ });
if (descriptor.hasOwnProperty("value")){ const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (changedFunction.fakeGenerator){ if (!descriptor) return;
descriptor.value = exportFunction( const type = descriptor.hasOwnProperty("value")? "value": "get";
changedFunction.fakeGenerator(checker, original, windowToProcess), let changed;
windowToProcess if (type ==="value"){
); if (changedFunction.fakeGenerator){
} const generated = changedFunction.fakeGenerator(checker, original, windowToProcess);
else { if ((changedFunction.exportOptions || {}).allowCallbacks){
descriptor.value = null; changed = extension.exportFunctionWithName(generated, windowToProcess, original.name);
} }
} else {
else { changed = extension.createProxyFunction(windowToProcess, original, generated);
descriptor.get = exportFunction(function(){
return exportFunction(
changedFunction.fakeGenerator(checker),
windowToProcess
);
}, windowToProcess);
}
Object.defineProperty(object, name, descriptor);
} }
} }
else {
changed = null;
}
}
else {
changed = extension.createProxyFunction(windowToProcess, original, extension.exportFunctionWithName(
changedFunction.fakeGenerator(checker),
windowToProcess,
original.name
));
}
extension.changeProperty(windowToProcess, changedFunction.api, {
object, name, type, changed
}); });
} });
}); });
} }
function interceptGetters(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){ function interceptGetters(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){
@ -357,70 +374,82 @@
const name = changedGetter.name; const name = changedGetter.name;
const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs); const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs);
logging.verbose("status for", changedGetter, ":", functionStatus); logging.verbose("status for", changedGetter, ":", functionStatus);
if (functionStatus.active){ if (!functionStatus.active) return;
changedGetter.objectGetters.forEach(function(objectGetter){
const object = objectGetter(getWrapped(windowToProcess));
if (object){
const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (descriptor && descriptor.hasOwnProperty("get")){
const original = descriptor.get;
const checker = generateChecker({
name, changedFunction: changedGetter, siteStatus, original,
window: windowToProcess, prefs, checkStack, ask, notify
});
const getter = changedGetter.getterGenerator(checker, original, windowToProcess);
descriptor.get = exportFunction(getter, windowToProcess);
if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){ changedGetter.objectGetters.forEach(function(objectGetter){
const setter = changedGetter.setterGenerator( const object = objectGetter(extension.getWrapped(windowToProcess));
windowToProcess, if (!object) return;
descriptor.set,
prefs
);
descriptor.set = exportFunction(setter, windowToProcess);
}
Object.defineProperty(object, name, descriptor); const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (!descriptor) return;
if (descriptor.hasOwnProperty("get")){
const original = descriptor.get;
const checker = generateChecker({
name, changedFunction: changedGetter, siteStatus, original,
window: windowToProcess, prefs, checkStack, ask, notify
});
const getter = changedGetter.getterGenerator(checker, original, windowToProcess);
extension.changeProperty(windowToProcess, changedGetter.api,
{
object, name, type: "get",
changed: extension.createProxyFunction(windowToProcess, original, getter)
} }
else if ( );
changedGetter.valueGenerator &&
descriptor && descriptor.hasOwnProperty("value") if (descriptor.hasOwnProperty("set") && descriptor.set && changedGetter.setterGenerator){
){ const original = descriptor.set;
const protectedAPIFeatures = prefs("protectedAPIFeatures"); const setter = changedGetter.setterGenerator(
if ( windowToProcess,
functionStatus.active && original,
( prefs
!protectedAPIFeatures.hasOwnProperty(name + " @ " + changedGetter.api) || );
protectedAPIFeatures[name + " @ " + changedGetter.api] extension.changeProperty(windowToProcess, changedGetter.api,
) {
){ object, name, type: "set",
switch (functionStatus.mode){ changed: extension.createProxyFunction(windowToProcess, original, setter)
case "ask": case "block": case "fake":
descriptor.value = changedGetter.valueGenerator({
mode: functionStatus.mode,
original: descriptor.value,
notify: function notifyCallback(messageId){
notify({
url: getURL(windowToProcess),
errorStack: (new Error()).stack,
messageId,
timestamp: new Date(),
functionName: name,
api: changedGetter.api
});
}
});
Object.defineProperty(object, name, descriptor);
break;
}
} }
} );
else {
logging.error("Try to fake non getter property:", changedGetter);
}
} }
});
} }
else if (
changedGetter.valueGenerator &&
descriptor.hasOwnProperty("value")
){
const protectedAPIFeatures = prefs("protectedAPIFeatures");
if (
protectedAPIFeatures.hasOwnProperty(name + " @ " + changedGetter.api) &&
!protectedAPIFeatures[name + " @ " + changedGetter.api]
){
return;
}
switch (functionStatus.mode){
case "ask": case "block": case "fake":
extension.changeProperty(windowToProcess, changedGetter.api, {
object, name, type: "value",
changed: changedGetter.valueGenerator({
mode: functionStatus.mode,
original: descriptor.value,
notify: function notifyCallback(messageId){
notify({
url: getURL(windowToProcess),
errorStack: (new Error()).stack,
messageId,
timestamp: new Date(),
functionName: name,
api: changedGetter.api
});
}
})
});
break;
}
}
else {
logging.error("Try to fake non getter property:", changedGetter);
}
});
}); });
} }
scope.intercept = function intercept({subject: windowToProcess}, apis){ scope.intercept = function intercept({subject: windowToProcess}, apis){

View File

@ -15,6 +15,7 @@
} }
const settings = require("./settings"); const settings = require("./settings");
const logging = require("./logging");
function getDomainRegExpList(domainList){ function getDomainRegExpList(domainList){
@ -28,14 +29,27 @@
}) })
.map(function(entry){ .map(function(entry){
let regExp; let regExp;
const domain = !!entry.match(/^[A-Za-z0-9_.-]+$/); const domain = !!entry.match(/^[A-Za-z0-9_.*-]+$/);
if (domain){ if (domain){
regExp = new RegExp("(?:^|\\.)" + entry.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$", "i"); regExp = new RegExp(
"(?:^|\\.)" + entry.replace(/([\\+?[^\]$(){}=!|.])/g, "\\$1").replace(/\*/g, ".+") + "\\.?$",
"i"
);
} }
else { else {
regExp = new RegExp(entry, "i"); try {
regExp = new RegExp(entry, "i");
}
catch (error){
logging.error("Error in regular expression", entry, error);
regExp = new RegExp(
"(?:^|\\.)" + entry.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$",
"i"
);
}
} }
return { return {
value: entry,
match: function(url){ match: function(url){
if (domain){ if (domain){
return (url.hostname || "").match(regExp); return (url.hostname || "").match(regExp);
@ -114,15 +128,28 @@
} }
return lists[type]; return lists[type];
}; };
scope.appendTo = function appendToList(type, entry){ scope.appendTo = async function appendToList(type, entry){
const oldValue = settings[type + "List"]; const oldValue = settings[type + "List"];
return settings.set(type + "List", oldValue + (oldValue? ",": "") + entry).then(function(){ await settings.set(type + "List", oldValue + (oldValue? ",": "") + entry);
return updateList(type); return updateList(type);
}); };
scope.removeFrom = async function removeFromList(type, entry){
const oldValue = settings[type + "List"];
const filter = entry.forEach? v => entry.indexOf(v) === -1: v => v !== entry;
await settings.set(
type + "List",
oldValue
.split(",")
.map(v => v.replace(/^\s+|\s+$/, ""))
.filter(filter)
.join(",")
);
return updateList(type);
}; };
scope.update = updateList; scope.update = updateList;
scope.updateAll = function updateAllLists(){ scope.updateAll = function updateAllLists(){
updateList("white"); updateList("white");
updateList("sessionWhite");
updateList("ignore"); updateList("ignore");
updateList("black"); updateList("black");
updateStackList(settings.stackList); updateStackList(settings.stackList);

View File

@ -13,10 +13,11 @@
} }
let settings = false; let settings = false;
scope.setSettings = function(realSettings){ scope.setSettings = async function(realSettings){
if (!settings){ if (!settings){
settings = realSettings; settings = realSettings;
return settings.loaded.then(scope.clearQueue); await settings.loaded;
return scope.clearQueue();
} }
else { else {
warning("logging: Settings can only be set once."); warning("logging: Settings can only be set once.");
@ -77,10 +78,10 @@
} }
} }
function error (...args){performLog(1, args);} function error (...args){performLog(1, args);}
function warning(...args){performLog(25, args);} function warning(...args){performLog(25, args);}
function message(...args){performLog(50, args);} function message(...args){performLog(50, args);}
function notice (...args){performLog(75, args);} function notice (...args){performLog(75, args);}
function verbose(...args){performLog(100, args);} function verbose(...args){performLog(100, args);}
function metaLog(...args){performLog(999, args);} function metaLog(...args){performLog(999, args);}

View File

@ -10,11 +10,12 @@
const persistentRndStorage = require("./persistentRndStorage"); const persistentRndStorage = require("./persistentRndStorage");
const notification = require("./notification"); const notification = require("./notification");
const mobile = require("./mobile"); const mobile = require("./mobile");
const extension = require("./extension");
const registerSettingsContentScript = (function(){ const registerSettingsContentScript = (function(){
let unregisterSettingsContentScript = function(){}; let unregisterSettingsContentScript = function(){};
let lastRegistering; let lastRegistering;
return function registerSettingsContentScript(){ return async function registerSettingsContentScript(){
logging.message("Register content script for the settings."); logging.message("Register content script for the settings.");
logging.verbose("Unregister old content script, if present."); logging.verbose("Unregister old content script, if present.");
unregisterSettingsContentScript(); unregisterSettingsContentScript();
@ -23,7 +24,7 @@
data[def.name] = def.get(); data[def.name] = def.get();
}); });
lastRegistering = data; lastRegistering = data;
browser.contentScripts.register({ const api = await browser.contentScripts.register({
matches: ["<all_urls>"], matches: ["<all_urls>"],
matchAboutBlank: true, matchAboutBlank: true,
allFrames: true, allFrames: true,
@ -52,19 +53,16 @@
} }
}(${JSON.stringify(data)}))` }(${JSON.stringify(data)}))`
}] }]
}).then(function(api){
logging.verbose("Content script registered.");
if (data !== lastRegistering){
logging.verbose("Multiple content scripts registered at once. Remove unnecessary one.");
api.unregister();
}
else {
unregisterSettingsContentScript = api.unregister;
}
return;
}).catch(function(error){
logging.warning("Unable to register content script:", error);
}); });
logging.verbose("Content script registered.");
if (data !== lastRegistering){
logging.verbose("Multiple content scripts registered at once. Remove unnecessary one.");
api.unregister();
}
else {
unregisterSettingsContentScript = api.unregister;
}
}; };
}()); }());
@ -79,7 +77,7 @@
persistentRndStorage.init(); persistentRndStorage.init();
logging.message("register non port message listener"); logging.message("register non port message listener");
browser.runtime.onMessage.addListener(function(data){ browser.runtime.onMessage.addListener(async function(data){
logging.notice("got data without port", data); logging.notice("got data without port", data);
const keys = Object.keys(data); const keys = Object.keys(data);
if (data["canvasBlocker-new-domain-rnd"]){ if (data["canvasBlocker-new-domain-rnd"]){
@ -93,7 +91,7 @@
} }
} }
if (data["canvasBlocker-clear-domain-rnd"]){ if (data["canvasBlocker-clear-domain-rnd"]){
persistentRndStorage.clear(); persistentRndStorage.clear(data["canvasBlocker-clear-domain-rnd"] === "force");
if (keys.length === 1){ if (keys.length === 1){
return; return;
} }
@ -105,13 +103,9 @@
} }
} }
logging.notice("pass the message to the tabs"); logging.notice("pass the message to the tabs");
browser.tabs.query({}).then(function(tabs){ const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){ tabs.forEach(function(tab){
browser.tabs.sendMessage(tab.id, data); browser.tabs.sendMessage(tab.id, data);
});
return;
}).catch(function(error){
logging.warning("Unable to get browser tabs:", error);
}); });
}); });
@ -127,7 +121,7 @@
logging.verbose("send back the persistent random seeds", persistentRndStorage.persistentRnd); logging.verbose("send back the persistent random seeds", persistentRndStorage.persistentRnd);
port.postMessage({ port.postMessage({
tabId: port.sender.tab.id, tabId: port.sender.tab.id,
cookieStoreId: port.sender.tab.cookieStoreId, cookieStoreId: port.sender.tab.cookieStoreId || "",
persistentRnd: persistentRndStorage.persistentRnd, persistentRnd: persistentRndStorage.persistentRnd,
persistentIncognitoRnd: persistentRndStorage.persistentIncognitoRnd persistentIncognitoRnd: persistentRndStorage.persistentIncognitoRnd
}); });
@ -168,7 +162,7 @@
!browser.pageAction.openPopup !browser.pageAction.openPopup
){ ){
browser.tabs.create({ browser.tabs.create({
url: browser.extension.getURL("options/options.html?notice=" + reason) url: extension.getURL("options/options.html?notice=" + reason)
}); });
} }
} }
@ -176,8 +170,12 @@
case "install": case "install":
logging.message("CanvasBlocker installed"); logging.message("CanvasBlocker installed");
openOptions(details.reason); openOptions(details.reason);
browser.tabs.create({ settings.onloaded(function(){
url: browser.extension.getURL("options/presets.html?notice=" + details.reason) if (settings.showPresetsOnInstallation){
browser.tabs.create({
url: extension.getURL("options/presets.html?notice=" + details.reason)
});
}
}); });
break; break;
case "update": case "update":
@ -190,12 +188,45 @@
} }
// mobile default settings // mobile default settings
mobile.ifMobile(function(){ mobile.ifMobile(async function(){
return browser.storage.local.get().then(mobile.applyMobileDefaults).catch(function(error){ const settings = await browser.storage.local.get();
logging.error("Unable to set mobile default values:", error); mobile.applyMobileDefaults(settings);
});
}); });
}); });
if (browser.runtime.onSuspend){
browser.runtime.onSuspend.addListener(async function(){
logging.message("Suspending CanvasBlocker");
(await browser.tabs.query({})).forEach(function(tab){
browser.tabs.sendMessage(tab.id, {
"canvasBlocker-unload": true
});
});
});
}
if (browser.runtime.onUpdateAvailable){
browser.runtime.onUpdateAvailable.addListener(async function(details){
logging.message("Update available", details);
if (settings.disruptSessionOnUpdate){
await Promise.all((await browser.tabs.query({})).map(async function(tab){
try{
await browser.tabs.sendMessage(tab.id, {
"canvasBlocker-unload": true
});
}
catch(error){
logging.verbose("error while unloading", tab, ":", error);
}
}));
window.setTimeout(function(){
logging.verbose("Reload extension after one second");
browser.runtime.reload();
}, 1000);
}
else {
settings.updatePending = true;
}
});
}
logging.message("end"); logging.message("end");
}()); }());

View File

@ -15,31 +15,33 @@
const settings = require("./settings"); const settings = require("./settings");
const settingDefinitions = require("./settingDefinitions"); const settingDefinitions = require("./settingDefinitions");
scope.isMobile = function isMobile(){ scope.isMobile = async function isMobile(){
const platformInfo = await browser.runtime.getPlatformInfo();
if (platformInfo && platformInfo.os === "android"){
return true;
}
// todo: proper mobile check (e.g. over browser.runtime.getBrowserInfo()) and no feature check // todo: proper mobile check (e.g. over browser.runtime.getBrowserInfo()) and no feature check
return Promise.resolve( return !browser.pageAction ||
!browser.pageAction ||
!browser.pageAction.show || !browser.pageAction.show ||
!browser.pageAction.openPopup !browser.pageAction.openPopup
); ;
}; };
scope.ifMobile = function ifMobile(ifCallback, elseCallback){ scope.ifMobile = async function ifMobile(ifCallback, elseCallback){
return scope.isMobile().then(function(isMobile){ const isMobile = await scope.isMobile();
if (isMobile){ if (isMobile){
return ifCallback(); return ifCallback();
} }
else if (elseCallback){ else if (elseCallback){
return elseCallback(); return elseCallback();
} }
else { else {
return false; return false;
} }
});
}; };
scope.applyMobileDefaults = function applyMobileDefaults(storage = false){ scope.applyMobileDefaults = async function applyMobileDefaults(storage = false){
return Promise.all(settingDefinitions.filter(function(definition){ await Promise.all(settingDefinitions.filter(function(definition){
return definition.hasOwnProperty("mobileDefaultValue") && ( return definition.hasOwnProperty("mobileDefaultValue") && (
!storage || !storage ||
!storage.hasOwnProperty(definition.name) !storage.hasOwnProperty(definition.name)

View File

@ -39,6 +39,8 @@
appendModified(require("./modifiedHistoryAPI")); appendModified(require("./modifiedHistoryAPI"));
appendModified(require("./modifiedWindowAPI")); appendModified(require("./modifiedWindowAPI"));
appendModified(require("./modifiedDOMRectAPI")); appendModified(require("./modifiedDOMRectAPI"));
appendModified(require("./modifiedSVGAPI"));
appendModified(require("./modifiedTextMetricsAPI"));
appendModified(require("./modifiedNavigatorAPI")); appendModified(require("./modifiedNavigatorAPI"));
appendModified(require("./modifiedScreenAPI")); appendModified(require("./modifiedScreenAPI"));
}()); }());

View File

@ -12,16 +12,12 @@
scope = require.register("./modifiedAPIFunctions", {}); scope = require.register("./modifiedAPIFunctions", {});
} }
scope.getWrapped = function getWrapped(obj){
return obj && (obj.wrappedJSObject || obj);
};
scope.checkerWrapper = function checkerWrapper(checker, object, args, callback){ scope.checkerWrapper = function checkerWrapper(checker, object, args, callback){
const check = checker.call(object); const check = checker.call(object);
if (check.allow){ if (check.allow){
if (check.allow === true){ if (check.allow === true){
return args.length? return args.length?
check.original.apply(object, check.window.Array.from(args)): check.original.call(object, ...args):
check.original.call(object); check.original.call(object);
} }
return callback.call(object, args, check); return callback.call(object, args, check);
@ -30,6 +26,14 @@
return undefined; return undefined;
}; };
scope.getStatusByFlag = function getStatusByFlag(flag){
return function getStatus(obj, status, prefs){
status = Object.create(status);
status.active = prefs(flag, status.url);
return status;
};
};
scope.setFunctionProperties = function setFunctionProperties(functions, data){ scope.setFunctionProperties = function setFunctionProperties(functions, data){
Object.keys(functions).forEach(function(key){ Object.keys(functions).forEach(function(key){
const func = functions[key]; const func = functions[key];

View File

@ -13,7 +13,7 @@
} }
const {sha256String: hashing} = require("./hash"); const {sha256String: hashing} = require("./hash");
const {checkerWrapper} = require("./modifiedAPIFunctions"); const {checkerWrapper, setFunctionProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
let randomSupply = null; let randomSupply = null;
@ -156,26 +156,23 @@
randomSupply = supply; randomSupply = supply;
}; };
function getStatus(obj, status, prefs){
status = Object.create(status);
status.active = prefs("protectAudio", status.url);
return status;
}
const getChannelDataAlreadyFakedArrays = new WeakMap(); const getChannelDataAlreadyFakedArrays = new WeakMap();
function fakeArrayCheckerCallback(array, fakeFunction, args, check){
const {prefs, notify, window, original} = check;
notify("fakedAudioReadout");
const ret = original.call(this, ...args);
fakeFunction(array, window, prefs);
return ret;
}
// changed functions and their fakes // changed functions and their fakes
scope.changedFunctions = { scope.changedFunctions = {
getFloatFrequencyData: { getFloatFrequencyData: {
object: ["AnalyserNode"], object: ["AnalyserNode"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getFloatFrequencyData(array){ return function getFloatFrequencyData(array){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments,
const {prefs, notify, window, original} = check; fakeArrayCheckerCallback.bind(this, array, fakeFloat32Array)
notify("fakedAudioReadout"); );
const ret = original.apply(this, window.Array.from(args));
fakeFloat32Array(array, window, prefs);
return ret;
});
}; };
} }
}, },
@ -183,13 +180,9 @@
object: ["AnalyserNode"], object: ["AnalyserNode"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getByteFrequencyData(array){ return function getByteFrequencyData(array){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments,
const {prefs, notify, window, original} = check; fakeArrayCheckerCallback.bind(this, array, fakeUint8Array)
notify("fakedAudioReadout"); );
const ret = original.apply(this, window.Array.from(args));
fakeUint8Array(array, window, prefs);
return ret;
});
}; };
} }
}, },
@ -197,13 +190,9 @@
object: ["AnalyserNode"], object: ["AnalyserNode"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getFloatTimeDomainData(array){ return function getFloatTimeDomainData(array){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments,
const {prefs, notify, window, original} = check; fakeArrayCheckerCallback.bind(this, array, fakeFloat32Array)
notify("fakedAudioReadout"); );
const ret = original.apply(this, window.Array.from(args));
fakeFloat32Array(array, window, prefs);
return ret;
});
}; };
} }
}, },
@ -211,24 +200,19 @@
object: ["AnalyserNode"], object: ["AnalyserNode"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getByteTimeDomainData(array){ return function getByteTimeDomainData(array){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments,
const {prefs, notify, window, original} = check; fakeArrayCheckerCallback.bind(this, array, fakeUint8Array)
notify("fakedAudioReadout"); );
const ret = original.apply(this, window.Array.from(args));
fakeUint8Array(array, window, prefs);
return ret;
});
}; };
} }
}, },
getChannelData: { getChannelData: {
object: ["AudioBuffer"], object: ["AudioBuffer"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function getChannelData(channel){ return function getChannelData(channel){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
const ret = original.apply(this, window.Array.from(args)); const ret = original.call(this, ...args);
if (!getChannelDataAlreadyFakedArrays.get(ret)){ if (!getChannelDataAlreadyFakedArrays.get(ret)){
notify("fakedAudioReadout"); notify("fakedAudioReadout");
fakeFloat32Array(ret, window, prefs); fakeFloat32Array(ret, window, prefs);
@ -242,7 +226,6 @@
copyFromChannel: { copyFromChannel: {
object: ["AudioBuffer"], object: ["AudioBuffer"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function copyFromChannel(destination, channelNumber, startInChannel){ return function copyFromChannel(destination, channelNumber, startInChannel){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
@ -252,7 +235,7 @@
fakeFloat32Array(channelData, window, prefs); fakeFloat32Array(channelData, window, prefs);
getChannelDataAlreadyFakedArrays.set(channelData, true); getChannelDataAlreadyFakedArrays.set(channelData, true);
} }
const ret = original.apply(this, window.Array.from(args)); const ret = original.call(this, ...args);
return ret; return ret;
}); });
}; };
@ -261,12 +244,11 @@
getFrequencyResponse: { getFrequencyResponse: {
object: ["BiquadFilterNode", "IIRFilterNode"], object: ["BiquadFilterNode", "IIRFilterNode"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function getFrequencyResponse(frequencyArray, magResponseOutput, phaseResponseOutput){ return function getFrequencyResponse(frequencyArray, magResponseOutput, phaseResponseOutput){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
notify("fakedAudioReadout"); notify("fakedAudioReadout");
const ret = original.apply(this, window.Array.from(args)); const ret = original.call(this, ...args);
fakeFloat32Array(magResponseOutput, window, prefs); fakeFloat32Array(magResponseOutput, window, prefs);
fakeFloat32Array(phaseResponseOutput, window, prefs); fakeFloat32Array(phaseResponseOutput, window, prefs);
return ret; return ret;
@ -275,9 +257,10 @@
} }
}, },
}; };
Object.keys(scope.changedFunctions).forEach(function(key){
scope.changedFunctions[key].type = "readout"; setFunctionProperties(scope.changedFunctions, {
scope.changedFunctions[key].getStatus = getStatus; type: "readout",
scope.changedFunctions[key].api = "audio"; getStatus: getStatusByFlag("protectAudio"),
api: "audio"
}); });
}()); }());

View File

@ -7,8 +7,9 @@
const scope = ((typeof exports) !== "undefined")? exports: require.register("./modifiedCanvasAPI"); const scope = ((typeof exports) !== "undefined")? exports: require.register("./modifiedCanvasAPI");
const colorStatistics = require("./colorStatistics"); const colorStatistics = require("./colorStatistics");
const logging = require("./logging"); const logging = require("./logging");
const {copyCanvasToWebgl} = require("./webgl"); const extension = require("./extension");
const {getWrapped, checkerWrapper} = require("./modifiedAPIFunctions"); const webgl = require("./webgl");
const {checkerWrapper} = require("./modifiedAPIFunctions");
let randomSupply = null; let randomSupply = null;
@ -23,8 +24,8 @@
let imageData; let imageData;
let source; let source;
if ((context.canvas.width || 0) * (context.canvas.height || 0) === 0){ if ((context.canvas.width || 0) * (context.canvas.height || 0) === 0){
imageData = new (getWrapped(window).ImageData)(0, 0); imageData = new (extension.getWrapped(window).ImageData)(0, 0);
source = new (getWrapped(window).ImageData)(0, 0); source = new (extension.getWrapped(window).ImageData)(0, 0);
} }
else if (context instanceof window.CanvasRenderingContext2D){ else if (context instanceof window.CanvasRenderingContext2D){
imageData = window.CanvasRenderingContext2D.prototype.getImageData.call( imageData = window.CanvasRenderingContext2D.prototype.getImageData.call(
@ -35,7 +36,7 @@
source = imageData.data; source = imageData.data;
} }
else { else {
imageData = new (getWrapped(window).ImageData)(context.canvas.width, context.canvas.height); imageData = new (extension.getWrapped(window).ImageData)(context.canvas.width, context.canvas.height);
source = new Uint8Array(imageData.data.length); source = new Uint8Array(imageData.data.length);
( (
context instanceof window.WebGLRenderingContext? context instanceof window.WebGLRenderingContext?
@ -181,99 +182,134 @@
scope.setRandomSupply = function(supply){ scope.setRandomSupply = function(supply){
randomSupply = supply; randomSupply = supply;
webgl.setRandomSupply(supply);
}; };
const canvasContextType = new WeakMap();
function getNumber(originalValue, max, index, window){ function createGetStatus(protectedPart){
const bitLength = Math.floor(Math.log2(max) + 1); if (protectedPart === "readout+"){
const rng = randomSupply.getBitRng(bitLength, window); return function(obj, status, prefs){
let value = 0; const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
for (let i = 0; i < bitLength; i += 1){ status = Object.create(status);
value <<= 1; status.active = protectedPartChecker("readout");
value ^= rng(originalValue, index + i); if (!status.active && protectedPartChecker("input")){
const contextType = canvasContextType.get(obj);
status.active = contextType !== "2d";
}
return status;
};
} }
return value; return function getStatus(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker(protectedPart);
return status;
};
} }
const parameterFakeTypes = { function useFakeCanvasCallback(args, check){
decimal: function(originalValue, definition, window){ const {prefs, notify, window, original} = check;
const int = Math.floor(originalValue); if (canvasSizeShouldBeFaked(this, prefs)){
if (int !== originalValue){ const fakeCanvas = getFakeCanvas(window, this, prefs);
const decimal = originalValue - int; if (fakeCanvas !== this){
const rng = randomSupply.getRng(1, window); notify("fakedReadout");
const newDecimal = decimal * (rng(definition.pname) / 0xFFFFFFFF);
return int + newDecimal;
} }
else { return original.call(fakeCanvas, ...args);
return originalValue;
}
},
shift: function(originalValue, definition, window){
const value = getNumber(originalValue, definition.max, definition.pname, window);
return originalValue >>> value;
},
"-": function(originalValue, definition, window){
const value = getNumber(originalValue, definition.max, definition.pname, window) *
(definition.factor || 1);
if (value > originalValue){
return 0;
}
return originalValue - value;
} }
}; else {
const parameterChangeDefinition = { return original.call(this, ...args);
2928: {name: "DEPTH_RANGE", type: "decimal", isArray: true}, }
3379: {name: "MAX_TEXTURE_SIZE", type: "shift", max: 1}, }
3386: {name: "MAX_VIEWPORT_DIMS", type: "shift", max: 1, isArray: true},
32883: {name: "MAX_3D_TEXTURE_SIZE", type: "shift", max: 1}, function mixOnInputCallback(args, check){
33000: {name: "MAX_ELEMENTS_VERTICES", type: "-", max: 3, factor: 50}, const {prefs, notify, window, original} = check;
33001: {name: "MAX_ELEMENTS_INDICES", type: "-", max: 3, factor: 50}, if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
33901: {name: "ALIASED_POINT_SIZE_RANGE", type: "decimal", isArray: true}, notify("fakedInput");
33902: {name: "ALIASED_LINE_WIDTH_RANGE", type: "decimal", isArray: true}, let oldImageData;
34024: {name: "MAX_RENDERBUFFER_SIZE", type: "shift", max: 1}, let x, y, width, height;
34045: {name: "MAX_TEXTURE_LOD_BIAS", type: "-", max: 1, factor: 1}, const getImageData = window.CanvasRenderingContext2D.prototype.getImageData;
34076: {name: "MAX_CUBE_MAP_TEXTURE_SIZE", type: "shift", max: 1}, const border = 4;
34921: {name: "MAX_VERTEX_ATTRIBS", type: "shift", max: 1}, try {
34930: {name: "MAX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1}, // "this" is not trustable - it may be not a context
35071: {name: "MAX_ARRAY_TEXTURE_LAYERS", type: "shift", max: 1}, const measurement = window.CanvasRenderingContext2D.prototype.measureText.call(this, args[0]);
35371: {name: "MAX_VERTEX_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1}, const left = Math.max(0, measurement.actualBoundingBoxLeft);
35373: {name: "MAX_FRAGMENT_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1}, const top = Math.max(0, measurement.actualBoundingBoxAscent);
35374: {name: "MAX_COMBINED_UNIFORM_BLOCKS", type: "-", max: 3, factor: 1}, width = Math.ceil(measurement.actualBoundingBoxRight + left) + 2 * border;
35375: {name: "MAX_UNIFORM_BUFFER_BINDINGS", type: "-", max: 3, factor: 1}, height = Math.ceil(measurement.actualBoundingBoxDescent + top) + 2 * border;
35376: {name: "MAX_UNIFORM_BLOCK_SIZE", type: "shift", max: 1}, x = args[1] - border - left;
35377: {name: "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10}, y = args[2] - border - top;
35379: {name: "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10}, oldImageData = getImageData.call(this, x, y, width, height);
35657: {name: "MAX_FRAGMENT_UNIFORM_COMPONENTS", type: "shift", max: 1}, }
35658: {name: "MAX_VERTEX_UNIFORM_COMPONENTS", type: "shift", max: 1}, catch (error){
35659: {name: "MAX_VARYING_COMPONENTS", type: "shift", max: 1}, // nothing to do here
35660: {name: "MAX_VERTEX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1}, }
35661: {name: "MAX_COMBINED_TEXTURE_IMAGE_UNITS", type: "-", max: 1, factor: 2}, // if "this" is not a correct context the next line will throw an error
35968: {name: "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", type: "shift", max: 1}, const ret = original.call(this, ...args);
35978: {name: "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", type: "shift", max: 1}, const newImageData = getImageData.call(this, x, y, width, height);
36203: {name: "MAX_ELEMENT_INDEX", type: "-", max: 15, factor: 1}, this.putImageData(randomMixImageData(window, oldImageData, newImageData), x, y);
36347: {name: "MAX_VERTEX_UNIFORM_VECTORS", type: "shift", max: 1}, return ret;
36348: {name: "MAX_VARYING_VECTORS", type: "shift", max: 1}, }
36349: {name: "MAX_FRAGMENT_UNIFORM_VECTORS", type: "shift", max: 1}, else {
37154: {name: "MAX_VERTEX_OUTPUT_COMPONENTS", type: "shift", max: 1}, return original.call(this, ...args);
37157: {name: "MAX_FRAGMENT_INPUT_COMPONENTS", type: "shift", max: 1}, }
7936: {name: "VENDOR", fake: function(originalValue, window, prefs){ }
const settingValue = prefs("webGLVendor") || originalValue;
return {value: settingValue, faked: settingValue === originalValue}; function offscreenToBlobCallback(args, check){
}}, const {prefs, notify, window, original} = check;
7937: {name: "RENDERER", fake: function(originalValue, window, prefs){ if (canvasSizeShouldBeFaked(this, prefs)){
const settingValue = prefs("webGLRenderer") || originalValue; try {
return {value: settingValue, faked: settingValue === originalValue}; const canvas = window.document.createElement("canvas");
}}, canvas.width = this.width;
37445: {name: "UNMASKED_VENDOR_WEBGL", fake: function(originalValue, window, prefs){ canvas.height = this.height;
const settingValue = prefs("webGLUnmaskedVendor") || originalValue; const context = canvas.getContext("2d");
return {value: settingValue, faked: settingValue === originalValue}; context.drawImage(this.transferToImageBitmap(), 0, 0);
}}, const fakeCanvas = getFakeCanvas(window, canvas, prefs);
37446: {name: "UNMASKED_RENDERER_WEBGL", fake: function(originalValue, window, prefs){ if (fakeCanvas !== canvas){
const settingValue = prefs("webGLUnmaskedRenderer") || originalValue; notify("fakedReadout");
return {value: settingValue, faked: settingValue === originalValue}; }
}}
}; const fakeContext = getContext(window, fakeCanvas);
const canvasContextType = new WeakMap(); const {imageData} = getImageData(window, fakeContext);
const fakeOffscreenCanvas = new window.OffscreenCanvas(this.width, this.height);
const offscreenContext = fakeOffscreenCanvas.getContext("2d");
offscreenContext.putImageData(imageData, 0, 0);
return original.call(fakeOffscreenCanvas, ...args);
}
catch (error){
logging.warning("Error while faking:", error);
return original.call(this, ...args);
}
}
else {
return original.call(this, ...args);
}
}
const isPointCache = Object.create(null);
function getIsPointCacheIndex(x, y, values){
return String.fromCodePoint(...values, x, y);
}
function getIsPointValue({func, x, y, index, originalValue, window, prefs}){
const useCanvasCache = prefs("useCanvasCache");
let cacheIndex;
const values = [originalValue, func(x^1, y), func(x, y^1), func(x^1, y^1)];
if (useCanvasCache){
cacheIndex = getIsPointCacheIndex(x, y, values);
const cached = isPointCache[cacheIndex];
if ((typeof cached) === "boolean"){
return cached;
}
}
const rng = randomSupply.getIndexRng(1, 4, window);
const res = values[rng(index)];
if (useCanvasCache){
isPointCache[cacheIndex] = res;
}
return res;
}
// changed functions and their fakes // changed functions and their fakes
scope.changedFunctions = { scope.changedFunctions = {
getContext: { getContext: {
@ -301,123 +337,57 @@
}, },
object: "HTMLCanvasElement", object: "HTMLCanvasElement",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars return function getContext(context, contextAttributes){
return function(context, contextAttributes){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {window, original} = check; const {original} = check;
canvasContextType.set(this, context); canvasContextType.set(this, context);
return original.apply(this, window.Array.from(args)); return original.call(this, ...args);
}); });
}; };
} }
}, },
toDataURL: { toDataURL: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("readout+"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
if (!status.active && protectedPartChecker("input")){
const contextType = canvasContextType.get(obj);
status.active = contextType !== "2d";
}
return status;
},
object: "HTMLCanvasElement", object: "HTMLCanvasElement",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function toDataURL(){ return function toDataURL(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, useFakeCanvasCallback);
const {prefs, notify, window, original} = check;
if (canvasSizeShouldBeFaked(this, prefs)){
const fakeCanvas = getFakeCanvas(window, this, prefs);
if (fakeCanvas !== this){
notify("fakedReadout");
}
return original.apply(fakeCanvas, window.Array.from(args));
}
else {
return original.apply(this, window.Array.from(args));
}
});
}; };
} }
}, },
toBlob: { toBlob: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("readout+"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
if (!status.active && protectedPartChecker("input")){
const contextType = canvasContextType.get(obj);
status.active = contextType !== "2d";
}
return status;
},
object: "HTMLCanvasElement", object: "HTMLCanvasElement",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function toBlob(callback){ return function toBlob(callback){
return checkerWrapper(checker, this, arguments, function(args, check){ if (this instanceof toBlob){
const {prefs, notify, window, original} = check; throw new extension.getWrapped(window).TypeError(
if (canvasSizeShouldBeFaked(this, prefs)){ "HTMLCanvasElement.prototype.toBlob is not a constructor"
const fakeCanvas = getFakeCanvas(window, this, prefs); );
if (fakeCanvas !== this){ }
notify("fakedReadout"); return checkerWrapper(checker, this, arguments, useFakeCanvasCallback);
}
return original.apply(fakeCanvas, window.Array.from(args));
}
else {
return original.apply(this, window.Array.from(args));
}
});
}; };
}, },
exportOptions: {allowCallbacks: true} exportOptions: {allowCallbacks: true}
}, },
mozGetAsFile: { mozGetAsFile: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("readout+"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
if (!status.active && protectedPartChecker("input")){
const contextType = canvasContextType.get(obj);
status.active = contextType !== "2d";
}
return status;
},
object: "HTMLCanvasElement", object: "HTMLCanvasElement",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function mozGetAsFile(callback){ return function mozGetAsFile(callback){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, useFakeCanvasCallback);
const {prefs, notify, window, original} = check;
if (canvasSizeShouldBeFaked(this, prefs)){
const fakeCanvas = getFakeCanvas(window, this, prefs);
if (fakeCanvas !== this){
notify("fakedReadout");
}
return original.apply(fakeCanvas, window.Array.from(args));
}
else {
return original.apply(this, window.Array.from(args));
}
});
}; };
} },
exportOptions: {allowCallbacks: true}
}, },
getImageData: { getImageData: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("readout"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
return status;
},
object: "CanvasRenderingContext2D", object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function getImageData(sx, sy, sw, sh){ return function getImageData(sx, sy, sw, sh){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
@ -434,10 +404,10 @@
"2d" "2d"
); );
} }
return original.apply(context, window.Array.from(args)); return original.call(context, ...args);
} }
else { else {
return original.apply(this, window.Array.from(args)); return original.call(this, ...args);
} }
}); });
}; };
@ -445,23 +415,21 @@
}, },
isPointInPath: { isPointInPath: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("readout"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
return status;
},
object: "CanvasRenderingContext2D", object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function isPointInPath(x, y){ return function isPointInPath(x, y){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {notify, window, original} = check; const {prefs, notify, window, original} = check;
const rng = randomSupply.getValueRng(1, window); const originalValue = original.call(this, ...args);
const originalValue = original.apply(this, window.Array.from(args));
if ((typeof originalValue) === "boolean"){ if ((typeof originalValue) === "boolean"){
notify("fakedReadout"); notify("fakedReadout");
const index = x + this.width * y; return getIsPointValue({
return original.call(this, rng(x, index), rng(y, index), args[2]); func: (x, y) => original.call(this, x, y, args[2]),
x, y,
index: x + this.canvas.width * y,
originalValue, window, prefs
});
} }
else { else {
return originalValue; return originalValue;
@ -472,32 +440,31 @@
}, },
isPointInStroke: { isPointInStroke: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("readout"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
return status;
},
object: "CanvasRenderingContext2D", object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function isPointInStroke(x, y){ return function isPointInStroke(x, y){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {notify, window, original} = check; const {prefs, notify, window, original} = check;
const rng = randomSupply.getValueRng(1, window); const originalValue = original.call(this, ...args);
const originalValue = original.apply(this, window.Array.from(args));
if ((typeof originalValue) === "boolean"){ if ((typeof originalValue) === "boolean"){
notify("fakedReadout"); notify("fakedReadout");
let func;
if (x instanceof window.Path2D){ if (x instanceof window.Path2D){
let path = x; let path = x;
x = y; x = y;
y = args[2]; y = args[2];
let index = x + this.width * y; func = (x, y) => original.call(this, path, x, y);
return original.call(this, path, rng(x, index), rng(y, index));
} }
else { else {
let index = x + this.width * y; func = (x, y) => original.call(this, x, y);
return original.call(this, rng(x, index), rng(y, index));
} }
return getIsPointValue({
func,
x, y,
index: x + this.canvas.width * y,
originalValue, window, prefs
});
} }
else { else {
return originalValue; return originalValue;
@ -508,104 +475,45 @@
}, },
fillText: { fillText: {
type: "input", type: "input",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("input"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("input");
return status;
},
object: "CanvasRenderingContext2D", object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function fillText(str, x, y){ return function fillText(str, x, y){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, mixOnInputCallback);
const {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
notify("fakedInput");
let oldImageData;
try {
// "this" is not trustable - it may be not a context
oldImageData = getImageData(window, this).imageData;
}
catch (error){
// nothing to do here
}
// if "this" is not a correct context the next line will throw an error
const ret = original.apply(this, window.Array.from(args));
const newImageData = getImageData(window, this).imageData;
this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0);
return ret;
}
else {
return original.apply(this, window.Array.from(args));
}
});
}; };
} }
}, },
strokeText: { strokeText: {
type: "input", type: "input",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus("input"),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("input");
return status;
},
object: "CanvasRenderingContext2D", object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line no-unused-vars
return function strokeText(str, x, y){ return function strokeText(str, x, y){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, mixOnInputCallback);
const {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
notify("fakedInput");
let oldImageData;
try {
// "this" is not trustable - it may be not a context
oldImageData = getImageData(window, this).imageData;
}
catch (error){
// nothing to do here
}
// if "this" is not a correct context the next line will throw an error
const ret = original.apply(this, window.Array.from(args));
const newImageData = getImageData(window, this).imageData;
this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0);
return ret;
}
else {
return original.apply(this, window.Array.from(args));
}
});
}; };
} }
}, },
readPixels: { readPixels: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus(["readout", "input"]),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker(["readout", "input"]);
return status;
},
object: ["WebGLRenderingContext", "WebGL2RenderingContext"], object: ["WebGLRenderingContext", "WebGL2RenderingContext"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
// eslint-disable-next-line max-params, no-unused-vars // eslint-disable-next-line max-params
return function readPixels(x, y, width, height, format, type, pixels){ return function readPixels(x, y, width, height, format, type, pixels){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
notify("fakedReadout"); notify("fakedReadout");
const fakeCanvas = getFakeCanvas(window, this.canvas, prefs); const fakeCanvas = getFakeCanvas(window, this.canvas, prefs);
const {context} = copyCanvasToWebgl( const {context} = webgl.copyCanvasToWebgl(
window, window,
fakeCanvas, fakeCanvas,
this instanceof window.WebGLRenderingContext? "webgl": "webgl2" this instanceof window.WebGLRenderingContext? "webgl": "webgl2"
); );
return original.apply(context, window.Array.from(args)); return original.call(context, ...args);
} }
else { else {
return original.apply(this, window.Array.from(args)); return original.call(this, ...args);
} }
}); });
}; };
@ -613,59 +521,16 @@
}, },
getParameter: { getParameter: {
type: "readout", type: "readout",
getStatus: function(obj, status, prefs){ getStatus: createGetStatus(["readout", "input"]),
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker(["readout", "input"]);
return status;
},
object: ["WebGLRenderingContext", "WebGL2RenderingContext"], object: ["WebGLRenderingContext", "WebGL2RenderingContext"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
Object.keys(parameterChangeDefinition).forEach(function(parameterName){ webgl.initializeParameterDefinitions();
const definition = parameterChangeDefinition[parameterName];
definition.pname = parameterName;
if (!definition.fake){
definition.fake = definition.isArray?
function fake(originalValue, window){
let faked = false;
let fakedValue = [];
for (let i = 0; i < originalValue.length; i += 1){
fakedValue[i] = parameterFakeTypes[this.type](originalValue[i], this, window);
faked |= originalValue[i] === fakedValue[i];
originalValue[i] = fakedValue[i];
}
this.fake = function(originalValue){
if (faked){
for (let i = 0; i < originalValue.length; i += 1){
originalValue[i] = fakedValue[i];
}
}
return {
value: originalValue,
faked
};
};
return {
value: originalValue,
faked
};
}:
function fake(originalValue, window){
let value = parameterFakeTypes[this.type](originalValue, this, window);
let faked = value === originalValue;
this.fake = function(){
return {value, faked};
};
return {value, faked};
};
}
});
return function getParameter(pname){ return function getParameter(pname){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args)); const originalValue = original.call(this, ...args);
if (parameterChangeDefinition[pname]){ if (webgl.parameterChangeDefinition[pname]){
const definition = parameterChangeDefinition[pname]; const definition = webgl.parameterChangeDefinition[pname];
const {value, faked} = definition.fake(originalValue, window, prefs); const {value, faked} = definition.fake(originalValue, window, prefs);
if (faked){ if (faked){
notify("fakedReadout"); notify("fakedReadout");
@ -678,7 +543,70 @@
}); });
}; };
} }
} },
getExtension: {
type: "readout",
getStatus: createGetStatus(["readout", "input"]),
object: ["WebGLRenderingContext", "WebGL2RenderingContext"],
fakeGenerator: function(checker){
return function getExtension(extensionName){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.call(this, ...args);
if (
extensionName === "WEBGL_debug_renderer_info" &&
originalValue &&
webgl.parameterChangeDefinition[originalValue.UNMASKED_VENDOR_WEBGL] &&
webgl.parameterChangeDefinition[originalValue.UNMASKED_RENDERER_WEBGL]
){
const {value: vendorValue, faked: vendorFaked} = webgl
.parameterChangeDefinition[originalValue.UNMASKED_VENDOR_WEBGL]
.fake(this.getParameter(originalValue.UNMASKED_VENDOR_WEBGL), window, prefs);
const {value: rendererValue, faked: rendererFaked} = webgl
.parameterChangeDefinition[originalValue.UNMASKED_RENDERER_WEBGL]
.fake(this.getParameter(originalValue.UNMASKED_RENDERER_WEBGL), window, prefs);
if (
vendorFaked && vendorValue === null &&
rendererFaked && rendererValue === null
){
const value = null;
if (originalValue !== value){
notify("fakedReadout");
}
return value;
}
else {
return originalValue;
}
}
else {
return originalValue;
}
});
};
}
},
convertToBlob: {
type: "readout",
getStatus: createGetStatus("readout"),
object: ["OffscreenCanvas"],
fakeGenerator: function(checker){
return function convertToBlob(){
return checkerWrapper(checker, this, arguments, offscreenToBlobCallback);
};
}
},
offscreenToBlob: {
name: "toBlob",
type: "readout",
getStatus: createGetStatus("readout"),
object: ["OffscreenCanvas"],
fakeGenerator: function(checker){
return function toBlob(){
return checkerWrapper(checker, this, arguments, offscreenToBlobCallback);
};
}
},
}; };
Object.keys(scope.changedFunctions).forEach(function(key){ Object.keys(scope.changedFunctions).forEach(function(key){
scope.changedFunctions[key].api = "canvas"; scope.changedFunctions[key].api = "canvas";

View File

@ -12,7 +12,8 @@
scope = require.register("./modifiedDOMRectAPI", {}); scope = require.register("./modifiedDOMRectAPI", {});
} }
const {getWrapped, checkerWrapper, setProperties: setProperties} = require("./modifiedAPIFunctions"); const extension = require("./extension");
const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const {byteArrayToString: hash} = require("./hash"); const {byteArrayToString: hash} = require("./hash");
@ -30,7 +31,7 @@
const registeredRects = new WeakMap(); const registeredRects = new WeakMap();
function registerDOMRect(domRect, notify, window, prefs){ function registerDOMRect(domRect, notify, window, prefs){
registeredRects.set(getWrapped(domRect), { registeredRects.set(extension.getWrapped(domRect), {
notify: function(){ notify: function(){
let done = false; let done = false;
return function(message){ return function(message){
@ -45,47 +46,104 @@
}); });
} }
function getDOMRectRegistration(domRect){ function getDOMRectRegistration(domRect){
return registeredRects.get(getWrapped(domRect)); return registeredRects.get(extension.getWrapped(domRect));
} }
const cache = {}; const cache = {};
const valueCache = [{}, {}, {}, {}]; const valueCache = [{}, {}, {}, {}, {}, {}, {}];
scope.cache = {
valueCache,
X: 0,
Y: 1,
WIDTH: 2,
HEIGHT: 3,
OTHER: 4,
Z: 5,
W: 6,
};
function getFakeValue(value, i, {window, prefs, rng}){
const valueHash = getValueHash(value);
const cache = valueCache[i];
let cachedValue = cache[valueHash];
if (typeof cachedValue === "number"){
return cachedValue;
}
if ((value * prefs("domRectIntegerFactor", window.location)) % 1 === 0){
cache[valueHash] = value;
return value;
}
else {
const fakedValue = value + 0.01 * (rng(i) / 0xffffffff - 0.5);
const fakedHash = getValueHash(fakedValue);
cache[valueHash] = fakedValue;
cache[fakedHash] = fakedValue;
return fakedValue;
}
}
scope.getFakeValue = getFakeValue;
function getFakeDomRect(window, domRect, prefs, notify){ function getFakeDomRect(window, domRect, prefs, notify){
const hash = getHash(domRect); const hash = getHash(domRect);
let cached = cache[hash]; let cached = cache[hash];
if (!cached){ if (!cached){
notify("fakedDOMRectReadout"); notify("fakedDOMRectReadout");
const rng = randomSupply.getRng(4, window); const rng = randomSupply.getRng(4, window);
const getFakeValue = function getFakeValue(value, i){ const env = {window, prefs, rng};
const valueHash = getValueHash(value); cached = new (domRect instanceof window.SVGRect? window.DOMRectReadOnly: domRect.constructor)(
const cache = valueCache[i]; getFakeValue(domRect.x, 0, env),
let cachedValue = cache[valueHash]; getFakeValue(domRect.y, 1, env),
if (typeof cachedValue === "number"){ getFakeValue(domRect.width, 2, env),
return cachedValue; getFakeValue(domRect.height, 3, env)
}
if ((value * prefs("domRectIntegerFactor", window.location)) % 1 === 0){
cache[valueHash] = value;
return value;
}
else {
const fakedValue = value + 0.01 * (rng(i) / 0xffffffff - 0.5);
const fakedHash = getValueHash(fakedValue);
cache[valueHash] = fakedValue;
cache[fakedHash] = fakedValue;
return fakedValue;
}
};
cached = new domRect.constructor(
getFakeValue(domRect.x, 0),
getFakeValue(domRect.y, 1),
getFakeValue(domRect.width, 2),
getFakeValue(domRect.height, 3)
); );
cache[hash] = cached; cache[hash] = cached;
cache[getHash(cached)] = cached; cache[getHash(cached)] = cached;
} }
return cached; return cached;
} }
function getFakeDOMPoint(window, domPoint, prefs){
const env = {window, prefs, rng: randomSupply.getRng(7, window)};
return new domPoint.constructor(
getFakeValue(domPoint.x, 0, env),
getFakeValue(domPoint.y, 1, env),
getFakeValue(domPoint.z, 5, env),
getFakeValue(domPoint.w, 6, env)
);
}
function getFakeSVGPoint(window, svgPoint, prefs){
const env = {window, prefs, rng: randomSupply.getRng(2, window)};
svgPoint.x = getFakeValue(svgPoint.x, 0, env);
svgPoint.y = getFakeValue(svgPoint.y, 1, env);
return svgPoint;
}
function getFakeDOMQuad(window, domQuad, prefs, notify){
notify("fakedDOMRectReadout");
return new domQuad.constructor(
getFakeDOMPoint(window, domQuad.p1, prefs),
getFakeDOMPoint(window, domQuad.p2, prefs),
getFakeDOMPoint(window, domQuad.p3, prefs),
getFakeDOMPoint(window, domQuad.p4, prefs)
);
}
function registerCallback(args, check){
const {prefs, notify, window, original} = check;
const originalValue = args.length?
original.call(this, ...args):
original.call(this);
registerDOMRect(originalValue, notify, window, prefs);
return originalValue;
}
function fakePointCallback(args, check){
const {prefs, notify, window, original} = check;
const ret = args.length? original.call(this, ...args): original.call(this);
notify("fakedDOMRectReadout");
if (ret instanceof window.SVGPoint){
return getFakeSVGPoint(window, ret, prefs);
}
else {
return getFakeDOMPoint(window, ret, prefs);
}
}
scope.changedFunctions = { scope.changedFunctions = {
getClientRects: { getClientRects: {
@ -94,7 +152,7 @@
return function getClientRects(){ return function getClientRects(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
const ret = args.length? original.apply(this, window.Array.from(args)): original.call(this); const ret = args.length? original.call(this, ...args): original.call(this);
for (let i = 0; i < ret.length; i += 1){ for (let i = 0; i < ret.length; i += 1){
registerDOMRect(ret[i], notify, window, prefs); registerDOMRect(ret[i], notify, window, prefs);
} }
@ -107,52 +165,74 @@
object: ["Range", "Element"], object: ["Range", "Element"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getBoundingClientRect(){ return function getBoundingClientRect(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, registerCallback);
const {prefs, notify, window, original} = check;
const ret = args.length? original.apply(this, window.Array.from(args)): original.call(this);
registerDOMRect(ret, notify, window, prefs);
return ret;
});
}; };
} }
}, },
getBounds: { getBoxQuads: {
object: ["DOMQuad"], object: ["Document", "Element", "Text", "CSSPseudoElement"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getBounds(){ return function getBoxQuads(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
const ret = args.length? original.apply(this, window.Array.from(args)): original.call(this); const ret = args.length? original.call(this, ...args): original.call(this);
registerDOMRect(ret, notify, window, prefs); for (let i = 0; i < ret.length; i += 1){
ret[i] = getFakeDOMQuad(window, ret[i], prefs, notify);
}
return ret; return ret;
}); });
}; };
} }
}, },
// It seems only getBoxQuads creates a DOMQuad and this method is behind a flag.
// So the only way to create one is manually by the constructor and then no fingerprinting is possible.
// getBounds: {
// object: ["DOMQuad"],
// fakeGenerator: function(checker){
// return function getBounds(){
// return checkerWrapper(checker, this, arguments, registerCallback);
// };
// }
// },
getBBox: { getBBox: {
object: ["SVGGraphicsElement"], object: ["SVGGraphicsElement"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getBBox(){ return function getBBox(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, registerCallback);
const {prefs, notify, window, original} = check; };
const ret = args.length? original.apply(this, window.Array.from(args)): original.call(this); }
registerDOMRect(ret, notify, window, prefs); },
return ret;
}); getStartPositionOfChar: {
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getStartOfChar(){
return checkerWrapper(checker, this, arguments, fakePointCallback);
};
}
},
getEndPositionOfChar: {
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getEndOfChar(){
return checkerWrapper(checker, this, arguments, fakePointCallback);
}; };
} }
}, },
getExtentOfChar: { getExtentOfChar: {
object: ["SVGTextContentElement"], object: ["SVGTextContentElement"],
fakeGenerator: function(checker){ fakeGenerator: function(checker){
return function getBBox(){ return function getExtentOfChar(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, registerCallback);
const {prefs, notify, window, original} = check; };
const ret = args.length? original.apply(this, window.Array.from(args)): original.call(this); }
registerDOMRect(ret, notify, window, prefs); },
return ret; getPointAtLength: {
}); object: ["SVGGeometryElement", "SVGPathElement"],
fakeGenerator: function(checker){
return function getPointAtLength(){
return checkerWrapper(checker, this, arguments, fakePointCallback);
}; };
} }
}, },
@ -166,12 +246,13 @@
]: ]:
[ [
function(window){return window.DOMRect && window.DOMRect.prototype;}, function(window){return window.DOMRect && window.DOMRect.prototype;},
function(window){return window.SVGRect && window.SVGRect.prototype;},
function(window){return window.DOMRectReadOnly && window.DOMRectReadOnly.prototype;} function(window){return window.DOMRectReadOnly && window.DOMRectReadOnly.prototype;}
], ],
name: property, name: property,
getterGenerator: function(){ getterGenerator: function(){
const temp = eval(`({ const temp = {
get ${property}(){ get [property](){
const registration = getDOMRectRegistration(this); const registration = getDOMRectRegistration(this);
if (registration){ if (registration){
return getFakeDomRect( return getFakeDomRect(
@ -179,24 +260,24 @@
this, this,
registration.prefs, registration.prefs,
registration.notify registration.notify
).${property}; )[property];
} }
return this.${property}; return this[property];
} }
})`); };
return Object.getOwnPropertyDescriptor(temp, property).get; return Object.getOwnPropertyDescriptor(temp, property).get;
} }
}; };
if (!readonly){ if (!readonly){
changedGetter.setterGenerator = function(window, original, prefs){ changedGetter.setterGenerator = function(window, original, prefs){
const temp = eval(`({ const temp = {
set ${property}(newValue){ set [property](newValue){
const registration = getDOMRectRegistration(this); const registration = getDOMRectRegistration(this);
if (registration){ if (registration){
const fakeDomRect = getFakeDomRect(window, this, prefs, registration.notify); const fakeDomRect = getFakeDomRect(window, this, prefs, registration.notify);
registeredRects.delete(getWrapped(this)); registeredRects.delete(extension.getWrapped(this));
["x", "y", "width", "height"].forEach((prop) => { ["x", "y", "width", "height"].forEach((prop) => {
if (prop === "${property}"){ if (prop === property){
this[prop] = newValue; this[prop] = newValue;
} }
else { else {
@ -205,10 +286,10 @@
}); });
} }
else { else {
original.apply(this, window.Array.from(arguments)); original.call(this, ...arguments);
} }
} }
})`); };
return Object.getOwnPropertyDescriptor(temp, property).set; return Object.getOwnPropertyDescriptor(temp, property).set;
}; };
} }
@ -234,14 +315,7 @@
getterGenerator: function(checker){ getterGenerator: function(checker){
const temp = { const temp = {
get intersectionRect(){ get intersectionRect(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, registerCallback);
const {prefs, notify, window, original} = check;
const originalValue = args.length?
original.apply(this, window.Array.from(args)):
original.call(this);
registerDOMRect(originalValue, notify, window, prefs);
return originalValue;
});
} }
}; };
return Object.getOwnPropertyDescriptor(temp, "intersectionRect").get; return Object.getOwnPropertyDescriptor(temp, "intersectionRect").get;
@ -257,14 +331,7 @@
getterGenerator: function(checker){ getterGenerator: function(checker){
const temp = { const temp = {
get boundingClientRect(){ get boundingClientRect(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, registerCallback);
const {prefs, notify, window, original} = check;
const originalValue = args.length?
original.apply(this, window.Array.from(args)):
original.call(this);
registerDOMRect(originalValue, notify, window, prefs);
return originalValue;
});
} }
}; };
return Object.getOwnPropertyDescriptor(temp, "boundingClientRect").get; return Object.getOwnPropertyDescriptor(temp, "boundingClientRect").get;
@ -280,14 +347,7 @@
getterGenerator: function(checker){ getterGenerator: function(checker){
const temp = { const temp = {
get rootBounds(){ get rootBounds(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, registerCallback);
const {prefs, notify, window, original} = check;
const originalValue = args.length?
original.apply(this, window.Array.from(args)):
original.call(this);
registerDOMRect(originalValue, notify, window, prefs);
return originalValue;
});
} }
}; };
return Object.getOwnPropertyDescriptor(temp, "rootBounds").get; return Object.getOwnPropertyDescriptor(temp, "rootBounds").get;
@ -295,15 +355,9 @@
} }
]; ];
function getStatus(obj, status, prefs){
status = Object.create(status);
status.active = prefs("protectDOMRect", status.url);
return status;
}
setProperties(scope.changedFunctions, scope.changedGetters, { setProperties(scope.changedFunctions, scope.changedGetters, {
type: "readout", type: "readout",
getStatus: getStatus, getStatus: getStatusByFlag("protectDOMRect"),
api: "domRect" api: "domRect"
}); });
}()); }());

View File

@ -12,7 +12,7 @@
scope = require.register("./modifiedHistoryAPI", {}); scope = require.register("./modifiedHistoryAPI", {});
} }
const {checkerWrapper} = require("./modifiedAPIFunctions"); const {checkerWrapper, setGetterProperties} = require("./modifiedAPIFunctions");
scope.changedGetters = [ scope.changedGetters = [
{ {
@ -23,7 +23,7 @@
get length(){ get length(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
const originalLength = original.apply(this, window.Array.from(args)); const originalLength = original.call(this, ...args);
const threshold = prefs("historyLengthThreshold", window.location); const threshold = prefs("historyLengthThreshold", window.location);
if (originalLength > threshold){ if (originalLength > threshold){
notify("fakedHistoryReadout"); notify("fakedHistoryReadout");
@ -47,9 +47,9 @@
return status; return status;
} }
scope.changedGetters.forEach(function(changedGetter){ setGetterProperties(scope.changedGetters, {
changedGetter.type = "readout"; type: "readout",
changedGetter.getStatus = getStatus; getStatus: getStatus,
changedGetter.api = "history"; api: "history"
}); });
}()); }());

View File

@ -12,41 +12,85 @@
scope = require.register("./modifiedNavigatorAPI", {}); scope = require.register("./modifiedNavigatorAPI", {});
} }
const {checkerWrapper} = require("./modifiedAPIFunctions"); const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const extension = require("./extension");
const navigator = require("./navigator"); const navigator = require("./navigator");
let cookieStoreId = false;
scope.setCookieStoreId = function(newCookieStoreId){
if (typeof newCookieStoreId === "string"){
cookieStoreId = (
newCookieStoreId !== "" &&
newCookieStoreId !== "firefox-default"
)? newCookieStoreId: "";
}
};
function getCookieStoreId(){
while (cookieStoreId === false){
extension.waitSync("to wait for cookie store id");
}
return cookieStoreId;
}
scope.changedGetters = navigator.allProperties.map(function(property){ scope.changedGetters = navigator.allProperties.map(function(property){
return { return {
objectGetters: [function(window){return window.Navigator && window.Navigator.prototype;}], objectGetters: [function(window){return window.Navigator && window.Navigator.prototype;}],
name: property, name: property,
getterGenerator: function(checker){ getterGenerator: function(checker){
const temp = eval(`({ const temp = {
get ${property}(){ get [property](){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {notify, window, original} = check; const {notify, original} = check;
const originalValue = original.apply(this, window.Array.from(args)); const originalValue = original.call(this, ...args);
const returnValue = navigator.getNavigatorValue("${property}"); const returnValue = navigator.getNavigatorValue(property, getCookieStoreId);
if (originalValue !== returnValue){ if (originalValue !== returnValue){
notify("fakedNavigatorReadout"); notify("fakedNavigatorReadout");
} }
return returnValue; return returnValue;
}); });
} }
})`); };
return Object.getOwnPropertyDescriptor(temp, property).get; return Object.getOwnPropertyDescriptor(temp, property).get;
} }
}; };
}); });
function getStatus(obj, status, prefs){ scope.changedFunctions = {
status = Object.create(status); estimate: {
status.active = prefs("protectNavigator", status.url); objectGetters: [function(window){return window.StorageManager && window.StorageManager.prototype;}],
return status; fakeGenerator: function(checker){
} const quota = 10 * 1024 * 1024 * 1024;
return function estimate(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {notify, original, window} = check;
const This = this;
return new window.Promise(async function(resolve, reject){
try {
const originalValue = await original.call(This, ...args);
if (originalValue.quota !== quota){
originalValue.usage = Math.min(
quota,
Math.max(0, quota - (originalValue.quota - originalValue.usage))
);
originalValue.quota = quota;
scope.changedGetters.forEach(function(changedGetter){ notify("fakedNavigatorReadout");
changedGetter.type = "readout"; }
changedGetter.getStatus = getStatus; resolve(originalValue);
changedGetter.api = "navigator"; }
catch (error){
reject(error);
}
});
});
};
}
}
};
setProperties(scope.changedFunctions, scope.changedGetters, {
type: "readout",
getStatus: getStatusByFlag("protectNavigator"),
api: "navigator"
}); });
}()); }());

88
lib/modifiedSVGAPI.js Normal file
View File

@ -0,0 +1,88 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
"use strict";
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
else {
scope = require.register("./modifiedSVGAPI", {});
}
const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const {byteArrayToString: hash} = require("./hash");
let randomSupply = null;
scope.setRandomSupply = function(supply){
randomSupply = supply;
};
function getValueHash(value){
return hash(new Float32Array([value]));
}
const cache = {};
function getFakeValue(value, window){
const valueHash = getValueHash(value);
let cachedValue = cache[valueHash];
if (typeof cachedValue === "number"){
return cachedValue;
}
else {
const rng = randomSupply.getRng(1, window);
const fakedValue = value + 0.01 * (rng(0) / 0xffffffff - 0.5);
const fakedHash = getValueHash(fakedValue);
cache[valueHash] = fakedValue;
cache[fakedHash] = fakedValue;
return fakedValue;
}
}
scope.getFakeValue = getFakeValue;
function getFakeValueCallback(args, check){
const {notify, window, original} = check;
const ret = args.length? original.call(this, ...args): original.call(this);
notify("fakedSVGReadout");
return getFakeValue(ret, window);
}
scope.changedFunctions = {
getTotalLength: {
object: ["SVGGeometryElement"],
fakeGenerator: function(checker){
return function getTotalLength(){
return checkerWrapper(checker, this, arguments, getFakeValueCallback);
};
}
},
getComputedTextLength: {
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getComputedTextLength(){
return checkerWrapper(checker, this, arguments, getFakeValueCallback);
};
}
},
getSubStringLength: {
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getSubStringLength(charnum, nchars){
return checkerWrapper(checker, this, arguments, getFakeValueCallback);
};
}
},
};
scope.changedGetters = [];
setProperties(scope.changedFunctions, scope.changedGetters, {
type: "readout",
getStatus: getStatusByFlag("protectSVG"),
api: "svg"
});
}());

View File

@ -12,7 +12,7 @@
scope = require.register("./modifiedScreenAPI", {}); scope = require.register("./modifiedScreenAPI", {});
} }
const {checkerWrapper} = require("./modifiedAPIFunctions"); const {checkerWrapper, setGetterProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const physical = { const physical = {
width: Math.round(window.screen.width * window.devicePixelRatio), width: Math.round(window.screen.width * window.devicePixelRatio),
@ -92,7 +92,7 @@
function getFaker(dimension){ function getFaker(dimension){
return function fake(args, check){ return function fake(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args)); const originalValue = original.call(this, ...args);
const returnValue = (typeof dimension) === "function"? const returnValue = (typeof dimension) === "function"?
dimension(window): dimension(window):
dimension? dimension?
@ -184,7 +184,7 @@
getterGenerator: function(checker){ getterGenerator: function(checker){
const temp = { const temp = {
get outerWidth(){ get outerWidth(){
return checkerWrapper(checker, this, arguments, getFaker(window => window.innerWidth)); return checkerWrapper(checker, this, arguments, getFaker(window => window.top.innerWidth));
} }
}; };
return Object.getOwnPropertyDescriptor(temp, "outerWidth").get; return Object.getOwnPropertyDescriptor(temp, "outerWidth").get;
@ -196,7 +196,7 @@
getterGenerator: function(checker){ getterGenerator: function(checker){
const temp = { const temp = {
get outerHeight(){ get outerHeight(){
return checkerWrapper(checker, this, arguments, getFaker(window => window.innerHeight)); return checkerWrapper(checker, this, arguments, getFaker(window => window.top.innerHeight));
} }
}; };
return Object.getOwnPropertyDescriptor(temp, "outerHeight").get; return Object.getOwnPropertyDescriptor(temp, "outerHeight").get;
@ -206,11 +206,55 @@
objectGetters: [function(window){return window.MediaQueryList && window.MediaQueryList.prototype;}], objectGetters: [function(window){return window.MediaQueryList && window.MediaQueryList.prototype;}],
name: "matches", name: "matches",
getterGenerator: function(checker){ getterGenerator: function(checker){
function getAlteredMedia(originalMedia, prefs, window){
const dimensions = getScreenDimensions(prefs, window);
return originalMedia.replace(
/\(\s*(?:(min|max)-)?device-(width|height):\s+(\d+\.?\d*)px\s*\)/,
function(m, type, dimension, value){
value = parseFloat(value);
let newCompareValue;
switch (type){
case "min":
if (value <= dimensions[dimension]){
newCompareValue = 0;
}
else {
newCompareValue = 2 * physical[dimension];
}
break;
case "max":
if (value >= dimensions[dimension]){
newCompareValue = 2 * physical[dimension];
}
else {
newCompareValue = 0;
}
break;
default:
if (
Math.round(value * 100) ===
Math.round(dimensions[dimension] * 100)
){
newCompareValue = physical[dimension];
}
else {
newCompareValue = 0;
}
}
return "(" + (type? type + "-": "") +
"device-" + dimension + ": " +
(
newCompareValue /
window.devicePixelRatio
) + "px)";
}
);
}
const temp = { const temp = {
get matches(){ get matches(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args)); const originalValue = original.call(this, ...args);
const screenSize = prefs("screenSize", window.location); const screenSize = prefs("screenSize", window.location);
if ( if (
( (
@ -219,49 +263,8 @@
) && ) &&
this.media.match(/device-(width|height)/) this.media.match(/device-(width|height)/)
){ ){
const dimensions = getScreenDimensions(prefs, window);
const originalMedia = this.media; const originalMedia = this.media;
const alteredMedia = this.media.replace( const alteredMedia = getAlteredMedia(originalMedia, prefs, window);
/\(\s*(?:(min|max)-)?device-(width|height):\s+(\d+\.?\d*)px\s*\)/,
function(m, type, dimension, value){
value = parseFloat(value);
let newCompareValue = value;
switch (type){
case "min":
if (value <= dimensions[dimension]){
newCompareValue = 0;
}
else {
newCompareValue = 2 * physical[dimension];
}
break;
case "max":
if (value >= dimensions[dimension]){
newCompareValue = 2 * physical[dimension];
}
else {
newCompareValue = 0;
}
break;
default:
if (
Math.round(value * 100) ===
Math.round(dimensions[dimension] * 100)
){
newCompareValue = physical[dimension];
}
else {
newCompareValue = 0;
}
}
return "(" + (type? type + "-": "") +
"device-" + dimension + ": " +
(
newCompareValue /
window.devicePixelRatio
) + "px)";
}
);
if (alteredMedia !== originalMedia){ if (alteredMedia !== originalMedia){
const alteredQuery = window.matchMedia(alteredMedia); const alteredQuery = window.matchMedia(alteredMedia);
const fakedValue = original.call(alteredQuery); const fakedValue = original.call(alteredQuery);
@ -280,15 +283,9 @@
}, },
]; ];
function getStatus(obj, status, prefs){ setGetterProperties(scope.changedGetters, {
status = Object.create(status); type: "readout",
status.active = prefs("protectScreen", status.url); getStatus: getStatusByFlag("protectScreen"),
return status; api: "screen"
}
scope.changedGetters.forEach(function(changedGetter){
changedGetter.type = "readout";
changedGetter.getStatus = getStatus;
changedGetter.api = "screen";
}); });
}()); }());

View File

@ -0,0 +1,96 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
"use strict";
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
else {
scope = require.register("./modifiedTextMetricsAPI", {});
}
const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const {byteArrayToString: hash} = require("./hash");
const {cache} = require("./modifiedDOMRectAPI");
const valueCache = cache.valueCache;
function getValueHash(value){
return hash(new Float32Array([value]));
}
let randomSupply = null;
scope.setRandomSupply = function(supply){
randomSupply = supply;
};
function getFakeValue(window, value, i, prefs){
const valueHash = getValueHash(value);
const cache = valueCache[i];
let cachedValue = cache[valueHash];
if (typeof cachedValue === "number"){
return cachedValue;
}
if ((value * prefs("domRectIntegerFactor", window.location)) % 1 === 0){
cache[valueHash] = value;
return value;
}
else {
const rng = randomSupply.getRng(5, window);
const fakedValue = value + 0.01 * (rng(i) / 0xffffffff - 0.5);
const fakedHash = getValueHash(fakedValue);
cache[valueHash] = fakedValue;
cache[fakedHash] = fakedValue;
return fakedValue;
}
}
function generateChangedTextMetricsPropertyGetter(property, cacheIndex){
const changedGetter = {
objectGetters: [
function(window){return window.TextMetrics && window.TextMetrics.prototype;}
],
name: property,
getterGenerator: function(checker){
const temp = {
get [property](){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.call(this, ...args);
const returnValue = getFakeValue(window, originalValue, cacheIndex, prefs);
if (originalValue !== returnValue){
notify("fakedTextMetricsReadout");
}
return returnValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, property).get;
}
};
return changedGetter;
}
scope.changedGetters = [
generateChangedTextMetricsPropertyGetter("width", cache.WIDTH),
generateChangedTextMetricsPropertyGetter("actualBoundingBoxAscent", cache.OTHER),
generateChangedTextMetricsPropertyGetter("actualBoundingBoxDescent", cache.OTHER),
generateChangedTextMetricsPropertyGetter("actualBoundingBoxLeft", cache.OTHER),
generateChangedTextMetricsPropertyGetter("actualBoundingBoxRight", cache.OTHER),
generateChangedTextMetricsPropertyGetter("alphabeticBaseline", cache.OTHER),
generateChangedTextMetricsPropertyGetter("emHeightAscent", cache.OTHER),
generateChangedTextMetricsPropertyGetter("emHeightDescent", cache.OTHER),
generateChangedTextMetricsPropertyGetter("fontBoundingBoxAscent", cache.OTHER),
generateChangedTextMetricsPropertyGetter("fontBoundingBoxDescent", cache.OTHER),
generateChangedTextMetricsPropertyGetter("hangingBaseline", cache.OTHER),
generateChangedTextMetricsPropertyGetter("ideographicBaseline", cache.OTHER),
];
setProperties({}, scope.changedGetters, {
type: "readout",
getStatus: getStatusByFlag("protectTextMetrics"),
api: "textMetrics"
});
}());

View File

@ -12,7 +12,7 @@
scope = require.register("./modifiedWindowAPI", {}); scope = require.register("./modifiedWindowAPI", {});
} }
const {checkerWrapper} = require("./modifiedAPIFunctions"); const {checkerWrapper, setGetterProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const windowNames = new WeakMap(); const windowNames = new WeakMap();
scope.changedGetters = [ scope.changedGetters = [
@ -23,11 +23,8 @@
const temp = { const temp = {
get opener(){ get opener(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {notify, original} = check;
if (!prefs("protectWindow", window.location)){ const originalOpener = original.call(this, ...args);
return original.apply(this, window.Array.from(args));
}
const originalOpener = original.apply(this, window.Array.from(args));
if (originalOpener !== null){ if (originalOpener !== null){
notify("fakedWindowReadout"); notify("fakedWindowReadout");
} }
@ -51,12 +48,15 @@
const temp = { const temp = {
get name(){ get name(){
return checkerWrapper(checker, this, arguments, function(args, check){ return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check; const {notify, original, prefs} = check;
if (!prefs("protectWindow", window.location)){ const originalName = original.call(this, ...args);
return original.apply(this, window.Array.from(args)); if (
this !== this.top &&
prefs("allowWindowNameInFrames", this.location)
){
return originalName;
} }
const originalName = original.apply(this, window.Array.from(args)); const returnedName = windowNames.get(this) || "";
const returnedName = windowNames.get(window) || "";
if (originalName !== returnedName){ if (originalName !== returnedName){
notify("fakedWindowReadout"); notify("fakedWindowReadout");
} }
@ -69,8 +69,8 @@
setterGenerator: function(window, original){ setterGenerator: function(window, original){
const temp = { const temp = {
set name(name){ set name(name){
original.apply(this, window.Array.from(arguments)); original.call(this, ...arguments);
windowNames.set(window, name); windowNames.set(this, name);
} }
}; };
return Object.getOwnPropertyDescriptor(temp, "name").set; return Object.getOwnPropertyDescriptor(temp, "name").set;
@ -78,15 +78,9 @@
} }
]; ];
function getStatus(obj, status, prefs){ setGetterProperties(scope.changedGetters, {
status = Object.create(status); type: "readout",
status.active = prefs("protectWindow", status.url); getStatus: getStatusByFlag("protectWindow"),
return status; api: "window"
}
scope.changedGetters.forEach(function(changedGetter){
changedGetter.type = "readout";
changedGetter.getStatus = getStatus;
changedGetter.api = "window";
}); });
}()); }());

View File

@ -26,6 +26,7 @@
original[property] = window.navigator[property]; original[property] = window.navigator[property];
}); });
original["real Firefox version"] = window.navigator.userAgent.replace(/^.+Firefox\//, ""); original["real Firefox version"] = window.navigator.userAgent.replace(/^.+Firefox\//, "");
original["real Firefox version - rv"] = window.navigator.userAgent.replace(/^.+; rv:([\d.]+).*$/, "$1");
let changedValues = {}; let changedValues = {};
@ -36,38 +37,63 @@
changedValues = newValue; changedValues = newValue;
}); });
const getValue = function(){
function getValue(name, stack = []){ function getChangedValues(getCookieStoreId){
if (stack.indexOf(name) !== -1){ if (changedValues.contextualIdentities){
return "[ERROR: loop in property definition]"; const cookieStoreId = getCookieStoreId();
} if (
stack.push(name); cookieStoreId !== "" &&
cookieStoreId !== "firefox-default" &&
switch (name){ changedValues.contextualIdentities[cookieStoreId]
case "original value": ){
return original[stack[stack.length - 2]]; return changedValues.contextualIdentities[cookieStoreId];
case "random":
return String.fromCharCode(Math.floor(65 + 85 * Math.random()));
default:
if (changedValues.hasOwnProperty(name)){
return parseString(changedValues[name], stack.slice());
} }
else { else {
return original[name]; return changedValues;
} }
}
else {
return changedValues;
}
} }
}
function parseString(string, stack){
if (string === "{undefined}"){
return undefined;
}
return string.replace(/{([a-z[\]_. -]*)}/ig, function(m, name){
return getValue(name, stack.slice());
});
}
scope.getNavigatorValue = function getNavigatorValue(name){ return function getValue(name, getCookieStoreId){
return getValue(name); const changedValues = getChangedValues(getCookieStoreId);
function getValueInternal(name, stack = []){
if (stack.indexOf(name) !== -1){
return "[ERROR: loop in property definition]";
}
stack.push(name);
switch (name){
case "original value":
return original[stack[stack.length - 2]];
case "random":
return String.fromCharCode(Math.floor(65 + 85 * Math.random()));
default:
if (changedValues.hasOwnProperty(name)){
return parseString(changedValues[name], stack.slice());
}
else {
return original[name];
}
}
}
function parseString(string, stack){
if (string === "{undefined}"){
return undefined;
}
return string.replace(/{([a-z[\]_. -]*)}/ig, function(m, name){
return getValueInternal(name, stack.slice());
});
}
return getValueInternal(name);
};
}();
scope.getNavigatorValue = function getNavigatorValue(name, getCookieStoreId){
return getValue(name, getCookieStoreId);
}; };
function changeHTTPHeader(details){ function changeHTTPHeader(details){
@ -82,7 +108,9 @@
){ ){
for (let header of details.requestHeaders){ for (let header of details.requestHeaders){
if (header.name.toLowerCase() === "user-agent"){ if (header.name.toLowerCase() === "user-agent"){
header.value = getValue("userAgent"); header.value = getValue("userAgent", function(){
return details.cookieStoreId;
});
} }
} }
} }
@ -118,5 +146,24 @@
scope.unregisterHeaderChange(); scope.unregisterHeaderChange();
} }
}); });
if (browser.contextualIdentities && browser.contextualIdentities.onRemoved){
logging.message("register contextual navigator identities removal");
browser.contextualIdentities.onRemoved.addListener(function(details){
logging.message("Contextual navigator identity", details.contextualIdentity.cookieStoreId, "removed.");
if (changedValues.contextualIdentities){
delete changedValues.contextualIdentities[details.contextualIdentity.cookieStoreId];
if (Object.keys(changedValues.contextualIdentities).length === 0){
delete changedValues.contextualIdentities;
}
settings.navigatorDetails = changedValues;
}
});
}
else {
logging.error(
"Old Firefox does not support browser.contextualIdentities.onRemoved"
);
}
}; };
}()); }());

View File

@ -163,16 +163,12 @@
}); });
}; };
settings.on("showNotifications", function({newValue}){ settings.on("showNotifications", async function({newValue}){
if (!newValue){ if (!newValue){
logging.message("notifications were disabled -> hide all page actions"); logging.message("notifications were disabled -> hide all page actions");
browser.tabs.query({}).then(function(tabs){ const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){ tabs.forEach(function(tab){
browser.pageAction.hide(tab.id); browser.pageAction.hide(tab.id);
});
return;
}).catch(function(error){
logging.warning("Unable to get browser tabs:", error);
}); });
} }
}); });
@ -180,20 +176,16 @@
browser.tabs.onRemoved.addListener(function(tabId){ browser.tabs.onRemoved.addListener(function(tabId){
tabsData.delete(tabId); tabsData.delete(tabId);
}); });
settings.on("displayBadge", function({newValue}){ settings.on("displayBadge", async function({newValue}){
if (!newValue){ if (!newValue){
logging.message("badge was disabled -> hide all badges"); logging.message("badge was disabled -> hide all badges");
if (browser.browserAction.setBadgeText){ if (browser.browserAction.setBadgeText){
browser.tabs.query({}).then(function(tabs){ const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){ tabs.forEach(function(tab){
browser.browserAction.setBadgeText({ browser.browserAction.setBadgeText({
tabId: tab.id, tabId: tab.id,
text: "" text: ""
});
}); });
return;
}).catch(function(error){
logging.warning("Unable to get browser tabs:", error);
}); });
} }
} }

View File

@ -89,23 +89,10 @@
}; };
}(); }();
browser.windows.onRemoved.addListener(function(){
browser.windows.getAll().then(function(windows){
if (windows.every(function(window){
return !window.incognito;
})){
clearIncognito();
}
return;
}).catch(function(error){
logging.warning("Unable to get browser windows:", error);
});
});
function registerTimeout(){ function registerTimeout(){
const interval = getInterval(); const interval = getInterval();
if (interval > 0){ if (interval > 0){
const timeout = settings.lastPersistentRndClearing + interval - Date.now(); const timeout = settings.lastPersistentRndClearing + interval - Date.now();
logging.message("registering persistent rng data clearing timeout. Clearing in ", timeout, "ms"); logging.message("registering persistent rng data clearing timeout. Clearing in ", timeout, "ms");
if (timeout > 1073741824){ if (timeout > 1073741824){
// window.setTimeout can only handle delays up to 32 bit. // window.setTimeout can only handle delays up to 32 bit.
@ -117,28 +104,24 @@
} }
} }
} }
function broadcast(data){ async function broadcast(data){
browser.tabs.query({}).then(function(tabs){ const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){ tabs.forEach(function(tab){
browser.tabs.sendMessage(tab.id, data); browser.tabs.sendMessage(tab.id, data);
});
return;
}).catch(function(error){
logging.warning("Unable to get browser tabs:", error);
}); });
} }
function clearIncognito(){ function clearIncognito(){
scope.persistentIncognitoRnd = Object.create(null); scope.persistentIncognitoRnd = Object.create(null);
settings.persistentIncognitoRndStorage = JSON.stringify(scope.persistentIncognitoRnd); settings.persistentIncognitoRndStorage = JSON.stringify(scope.persistentIncognitoRnd);
} }
function clear(){ function clear(force = false){
logging.verbose("domain rnd cleared"); logging.verbose("domain rnd cleared");
scope.persistentRnd = Object.create(null); scope.persistentRnd = Object.create(null);
settings.persistentRndStorage = JSON.stringify(scope.persistentRnd); settings.persistentRndStorage = JSON.stringify(scope.persistentRnd);
settings.lastPersistentRndClearing = Date.now(); settings.lastPersistentRndClearing = Date.now();
clearIncognito(); clearIncognito();
registerTimeout(); registerTimeout();
broadcast({"canvasBlocker-clear-domain-rnd": true}); broadcast({"canvasBlocker-clear-domain-rnd": force? "force": true});
} }
function setDomainData(domain, incognito, rnd){ function setDomainData(domain, incognito, rnd){
logging.verbose("got new domain rnd for ", domain, " (incognito:", incognito, "):", rnd); logging.verbose("got new domain rnd for ", domain, " (incognito:", incognito, "):", rnd);
@ -181,4 +164,18 @@
scope.setDomainData = setDomainData; scope.setDomainData = setDomainData;
scope.clearDomainData = clearDomainData; scope.clearDomainData = clearDomainData;
scope.clearContainerData = clearContainerData; scope.clearContainerData = clearContainerData;
try {
browser.windows.onRemoved.addListener(async function(){
const windows = await browser.windows.getAll();
if (windows.every(function(window){
return !window.incognito;
})){
clearIncognito();
}
});
}
catch (error){
logging.error("Unable to register windows.onRemoved listener", error);
}
}()); }());

View File

@ -63,7 +63,6 @@
}; };
const settings = require("./settings"); const settings = require("./settings");
const logging = require("./logging");
const extension = require("./extension"); const extension = require("./extension");
function getDomain(window){ function getDomain(window){
@ -83,6 +82,12 @@
let persistentRnd = Object.create(null); let persistentRnd = Object.create(null);
let cookieStoreId = false; let cookieStoreId = false;
function getCookieStoreId(){
while (cookieStoreId === false){
extension.waitSync("to wait for cookie store id");
}
return cookieStoreId;
}
settings.onloaded(function(){ settings.onloaded(function(){
try { try {
let storedData = JSON.parse( let storedData = JSON.parse(
@ -117,24 +122,24 @@
} }
} }
if (data["canvasBlocker-clear-domain-rnd"]){ if (data["canvasBlocker-clear-domain-rnd"]){
const domain = getCookieStoreId() + getDomain(window);
let ownPersistendRnd = data["canvasBlocker-clear-domain-rnd"] !== "force"? persistentRnd[domain]: false;
persistentRnd = Object.create(null); persistentRnd = Object.create(null);
if (ownPersistendRnd){
persistentRnd[domain] = ownPersistendRnd;
extension.message.send({
"canvasBlocker-new-domain-rnd": {
domain,
incognito: extension.inIncognitoContext,
rnd: Array.from(persistentRnd[domain])
}
});
}
} }
}); });
return function getPersistentRnd(window){ return function getPersistentRnd(window){
while (cookieStoreId === false){ const domain = getCookieStoreId() + getDomain(window);
logging.message("Starting synchronous request to wait for cookie store id.");
try {
let xhr = new XMLHttpRequest();
xhr.open("GET", "https://[::]", false);
xhr.send();
xhr = null;
}
catch (error){
logging.verbose("Error in XHR:", error);
}
}
const domain = cookieStoreId + getDomain(window);
if (!persistentRnd[domain]){ if (!persistentRnd[domain]){
// create the (sub-)domains random numbers if not existing // create the (sub-)domains random numbers if not existing
persistentRnd[domain] = new Uint8Array(128); persistentRnd[domain] = new Uint8Array(128);
@ -200,7 +205,7 @@
return function getConstantPixelRng(length, window, ignoredColors){ return function getConstantPixelRng(length, window, ignoredColors){
const rng = scope.nonPersistent.getValueRng(1024, window); const rng = scope.nonPersistent.getValueRng(1024, window);
// eslint-disable-next-line max-params, no-unused-vars // eslint-disable-next-line max-params
return function(r, g, b, a, i){ return function(r, g, b, a, i){
const index = String.fromCharCode(r, g, b, a); const index = String.fromCharCode(r, g, b, a);
if (ignoredColors[index]){ if (ignoredColors[index]){

View File

@ -19,17 +19,26 @@
texts.push({text: text.toLowerCase(), content}); texts.push({text: text.toLowerCase(), content});
}; };
scope.search = function(search){ scope.search = function(search){
const resultSets = search.toLowerCase().split(/\s+/).filter(function(term){ const resultSets = search.split(/\s+/).filter(function(term){
return term.trim(); return term.trim();
}).map(function(term){
return new RegExp(term);
}).map(function(term){ }).map(function(term){
const matching = new Set(); const matching = new Set();
texts.forEach(function(text){ if (term.match(/^:[a-z]+$/i)){
if (term.test(text.text)){ const tag = term.substring(1);
matching.add(text.content); texts.forEach(function(text){
} if (text.content.querySelector(`.${tag}`)){
}); matching.add(text.content);
}
});
}
else {
term = new RegExp(term.toLowerCase());
texts.forEach(function(text){
if (term.test(text.text)){
matching.add(text.content);
}
});
}
return matching; return matching;
}); });
if (resultSets.length){ if (resultSets.length){

View File

@ -40,6 +40,7 @@
let newEntry = {url}; let newEntry = {url};
newEntry[name] = value; newEntry[name] = value;
urlContainerValue.push(newEntry); urlContainerValue.push(newEntry);
initializeUrlSetting(newEntry);
matching = [newEntry]; matching = [newEntry];
} }
matching[0][name] = value; matching[0][name] = value;
@ -47,18 +48,17 @@
}; };
scope.resetUrlValue = function(name, url){ scope.resetUrlValue = function(name, url){
let urlContainerValue = scope.urlContainer.get(); let urlContainerValue = scope.urlContainer.get();
const matching = urlContainerValue.filter(function(urlSetting){ urlContainerValue.filter(function(urlSetting){
return urlSetting.match(url); return urlSetting.match(url);
}); }).forEach(function(match){
if (matching.length){ delete match[name];
delete matching[0][name]; if (Object.keys(match).every(function(key){return key === "url";})){
if (Object.keys(matching[0]).every(function(key){return key === "url";})){
urlContainerValue = urlContainerValue.filter(function(urlSetting){ urlContainerValue = urlContainerValue.filter(function(urlSetting){
return urlSetting !== matching[0]; return urlSetting !== match;
}); });
} }
scope.urlContainer.set(urlContainerValue); });
} scope.urlContainer.set(urlContainerValue);
}; };
function processHideContainer(settingDefinition){ function processHideContainer(settingDefinition){
@ -164,77 +164,89 @@
} }
}; };
function initializeUrlSetting(urlSetting){
let regExp;
const domain = !!urlSetting.url.match(/^[A-Za-z0-9_.*-]+$/);
if (domain){
regExp = new RegExp(
"(?:^|\\.)" + urlSetting.url.replace(/([\\+?[^\]$(){}=!|.])/g, "\\$1").replace(/\*/g, ".+") + "\\.?$",
"i"
);
}
else {
try {
regExp = new RegExp(urlSetting.url, "i");
}
catch (error){
logging.error("Error in regular expression", urlSetting.url, error);
regExp = new RegExp(
"(?:^|\\.)" + urlSetting.url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$",
"i"
);
}
}
const match = function(url){
if (!url){
return false;
}
else if (
url instanceof String ||
(typeof url) === "string"
){
return url === urlSetting.url;
}
else if (domain){
return (url.hostname || "").match(regExp);
}
else {
return url.href.match(regExp);
}
};
Object.defineProperty(
urlSetting,
"match",
{
enumerable: false,
writable: true,
configurable: true,
value: match
}
);
}
scope.initializeUrlContainer = function(eventHandler){ scope.initializeUrlContainer = function(eventHandler){
if (scope.urlContainer){ if (!scope.urlContainer){
scope.urlContainer.on(function({newValue, oldValue}){ return;
newValue.forEach(function(urlSetting){ }
let regExp; scope.urlContainer.on(function({newValue, oldValue}){
const domain = !!urlSetting.url.match(/^[A-Za-z0-9_.-]+$/); newValue.forEach(initializeUrlSetting);
if (domain){
regExp = new RegExp(
"(?:^|\\.)" + urlSetting.url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$",
"i"
);
}
else {
regExp = new RegExp(urlSetting.url, "i");
}
const match = function(url){
if (!url){
return false;
}
else if (
url instanceof String ||
(typeof url) === "string"
){
return url === urlSetting.url;
}
else if (domain){
return (url.hostname || "").match(regExp);
}
else {
return url.href.match(regExp);
}
};
Object.defineProperty(
urlSetting,
"match",
{
enumerable: false,
writable: true,
configurable: true,
value: match
}
);
});
const newUrls = newValue.map(function(entry){return entry.url;}); const newUrls = newValue.map(function(entry){return entry.url;});
const oldUrls = oldValue.map(function(entry){return entry.url;}); const oldUrls = oldValue.map(function(entry){return entry.url;});
const matching = {}; const matching = {};
newUrls.forEach(function(url, i){ newUrls.forEach(function(url, i){
matching[url] = {new: i, old: oldUrls.indexOf(url)}; matching[url] = {new: i, old: oldUrls.indexOf(url)};
}); });
oldUrls.forEach(function(url, i){ oldUrls.forEach(function(url, i){
if (!matching[url]){ if (!matching[url]){
matching[url] = {new: -1, old: i}; matching[url] = {new: -1, old: i};
} }
}); });
Object.keys(matching).forEach(function(url){ Object.keys(matching).forEach(function(url){
const oldEntry = oldValue[matching[url].old] || {}; const oldEntry = oldValue[matching[url].old] || {};
const newEntry = newValue[matching[url].new] || {}; const newEntry = newValue[matching[url].new] || {};
scope.urlContainer.entries.forEach(function(settingDefinition){ scope.urlContainer.entries.forEach(function(settingDefinition){
const name = settingDefinition.name; const name = settingDefinition.name;
const oldValue = oldEntry[name]; const oldValue = oldEntry[name];
const newValue = newEntry[name]; const newValue = newEntry[name];
if (oldValue !== newValue){ if (oldValue !== newValue){
((eventHandler[name] || {})[url] || []).forEach(function(callback){ ((eventHandler[name] || {})[url] || []).forEach(function(callback){
callback({name, newValue, oldValue, url}); callback({name, newValue, oldValue, url});
}); });
} }
});
}); });
}); });
} });
}; };
}()); }());

View File

@ -93,12 +93,14 @@
{name: "Canvas-API", level: 1}, {name: "Canvas-API", level: 1},
"getContext @ canvas", "getContext @ canvas",
{message: "readout", level: 2}, {message: "readout", level: 2},
"toDataURL @ canvas", "toBlob @ canvas", "mozGetAsFile @ canvas", "getImageData @ canvas", "toDataURL @ canvas",
"toBlob @ canvas", "convertToBlob @ canvas", "mozGetAsFile @ canvas",
"getImageData @ canvas",
"isPointInPath @ canvas", "isPointInStroke @ canvas", "isPointInPath @ canvas", "isPointInStroke @ canvas",
{message: "input", level: 2}, {message: "input", level: 2},
"fillText @ canvas", "strokeText @ canvas", "fillText @ canvas", "strokeText @ canvas",
{name: "webGL", level: 2}, {name: "webGL", level: 2},
"readPixels @ canvas", "getParameter @ canvas", "readPixels @ canvas", "getParameter @ canvas", "getExtension @ canvas",
{name: "Audio-API", level: 1}, {name: "Audio-API", level: 1},
"getFloatFrequencyData @ audio", "getByteFrequencyData @ audio", "getFloatFrequencyData @ audio", "getByteFrequencyData @ audio",
"getFloatTimeDomainData @ audio", "getByteTimeDomainData @ audio", "getFloatTimeDomainData @ audio", "getByteTimeDomainData @ audio",
@ -112,17 +114,39 @@
{name: "DOMRect-API", level: 1}, {name: "DOMRect-API", level: 1},
"getClientRects @ domRect", "getClientRects @ domRect",
"getBoundingClientRect @ domRect", "getBoundingClientRect @ domRect",
"getBoxQuads @ domRect",
"getBounds @ domRect", "getBounds @ domRect",
"getBBox @ domRect", "getBBox @ domRect",
"getStartPositionOfChar @ domRect",
"getEndPositionOfChar @ domRect",
"getExtentOfChar @ domRect", "getExtentOfChar @ domRect",
"getPointAtLength @ domRect",
"intersectionRect @ domRect", "intersectionRect @ domRect",
"boundingClientRect @ domRect", "boundingClientRect @ domRect",
"rootBounds", "rootBounds @ domRect",
{name: "SVG-API", level: 1},
"getTotalLength @ svg",
"getComputedTextLength @ svg",
"getSubStringLength @ svg",
{name: "TextMetrics-API", level: 1},
"width @ textMetrics",
"actualBoundingBoxAscent @ textMetrics",
"actualBoundingBoxDescent @ textMetrics",
"actualBoundingBoxLeft @ textMetrics",
"actualBoundingBoxRight @ textMetrics",
"alphabeticBaseline @ textMetrics",
"emHeightAscent @ textMetrics",
"emHeightDescent @ textMetrics",
"fontBoundingBoxAscent @ textMetrics",
"fontBoundingBoxDescent @ textMetrics",
"hangingBaseline @ textMetrics",
"ideographicBaseline @ textMetrics",
{name: "Navigator-API", level: 1}, {name: "Navigator-API", level: 1},
"appCodeName @ navigator", "appCodeName @ navigator",
"appName @ navigator", "appName @ navigator",
"appVersion @ navigator", "appVersion @ navigator",
"buildID @ navigator", "buildID @ navigator",
"estimate @ navigator",
"oscpu @ navigator", "oscpu @ navigator",
"platform @ navigator", "platform @ navigator",
"product @ navigator", "product @ navigator",
@ -261,7 +285,10 @@
"history", "history",
"window", "window",
"domRect", "domRect",
"svg",
"textMetrics",
"navigator", "navigator",
"screen",
], ],
defaultKeyValue: false defaultKeyValue: false
}, },
@ -321,6 +348,11 @@
defaultValue: false, defaultValue: false,
urlSpecific: true urlSpecific: true
}, },
{
name: "allowWindowNameInFrames",
defaultValue: false,
urlSpecific: true
},
{ {
name: "protectDOMRect", name: "protectDOMRect",
defaultValue: true, defaultValue: true,
@ -330,6 +362,16 @@
name: "domRectIntegerFactor", name: "domRectIntegerFactor",
defaultValue: 4 defaultValue: 4
}, },
{
name: "protectSVG",
defaultValue: true,
urlSpecific: true
},
{
name: "protectTextMetrics",
defaultValue: true,
urlSpecific: true
},
{ {
name: "blockDataURLs", name: "blockDataURLs",
defaultValue: true, defaultValue: true,
@ -373,17 +415,30 @@
defaultValue: "auto", defaultValue: "auto",
options: ["auto", "default", "light", "dark", "colorful"/*, "none"*/] options: ["auto", "default", "light", "dark", "colorful"/*, "none"*/]
}, },
{
name: "showPresetsOnInstallation",
defaultValue: true
},
{ {
name: "dontShowOptionsOnUpdate", name: "dontShowOptionsOnUpdate",
defaultValue: false defaultValue: false
}, },
{
name: "disruptSessionOnUpdate",
defaultValue: false
},
{
name: "updatePending",
resetOnStartup: true,
defaultValue: false
},
{ {
name: "isStillDefault", name: "isStillDefault",
defaultValue: true defaultValue: true
}, },
{ {
name: "storageVersion", name: "storageVersion",
defaultValue: 0.6, defaultValue: 1.0,
fixed: true fixed: true
} }
]; ];

View File

@ -16,40 +16,40 @@
scope.getMessages = function(settingDefinition){ scope.getMessages = function(settingDefinition){
const messages = []; const messages = [];
if (settingDefinition){ if (!settingDefinition){
messages.push(settingDefinition.name + "_title"); return messages;
messages.push(settingDefinition.name + "_description"); }
if (settingDefinition.urlSpecific){
messages.push(settingDefinition.name + "_urlSpecific"); messages.push(settingDefinition.name + "_title");
} messages.push(settingDefinition.name + "_description");
if (settingDefinition.options){ if (settingDefinition.urlSpecific){
settingDefinition.options.forEach(function(option){ messages.push(settingDefinition.name + "_urlSpecific");
if (option !== null){ }
messages.push(settingDefinition.name + "_options." + option); if (settingDefinition.options){
} settingDefinition.options.forEach(function(option){
}); if (option !== null){
} messages.push(settingDefinition.name + "_options." + option);
if (settingDefinition.inputs){ }
settingDefinition.inputs.forEach(function(input){ });
if (input){ }
if (input.options){ if (settingDefinition.inputs){
input.options.forEach(function(option){ settingDefinition.inputs.forEach(function(input){
if (option !== null){ if (input && input.options){
messages.push(input.name + "_options." + option); input.options.forEach(function(option){
} if (option !== null){
}); messages.push(input.name + "_options." + option);
} }
} });
}); }
} });
if (settingDefinition.action){ }
messages.push(settingDefinition.name + "_label"); if (settingDefinition.action){
} messages.push(settingDefinition.name + "_label");
if (settingDefinition.actions){ }
settingDefinition.actions.forEach(function(action){ if (settingDefinition.actions){
messages.push(action.name + "_label"); settingDefinition.actions.forEach(function(action){
}); messages.push(action.name + "_label");
} });
} }
return messages; return messages;
}; };

View File

@ -13,6 +13,7 @@
} }
const logging = require("./logging"); const logging = require("./logging");
const extension = require("./extension");
const settingDefinitions = require("./settingDefinitions"); const settingDefinitions = require("./settingDefinitions");
const settingContainers = require("./settingContainers"); const settingContainers = require("./settingContainers");
const definitionsByName = {}; const definitionsByName = {};
@ -129,24 +130,24 @@
} }
return true; return true;
}; };
const storeValue = function storeValue(newValue){ const storeValue = async function storeValue(newValue){
logging.verbose("Trying to store new value for %s", name, newValue); logging.verbose("Trying to store new value for %s", name, newValue);
settings[name] = newValue; settings[name] = newValue;
if (!settingDefinition.transient){ if (!settingDefinition.transient){
const storeObject = {}; const storeObject = {};
storeObject[name] = newValue; storeObject[name] = newValue;
const promise = browser.storage.local.set(storeObject); try {
promise.then(function(){ await browser.storage.local.set(storeObject);
logging.verbose("New value stored for %s:", name, newValue); logging.verbose("New value stored for %s:", name, newValue);
return; }
}).catch(function(error){ catch (error){
logging.error("Unable to store new value for %s:", name, newValue, error); logging.error("Unable to store new value for %s:", name, newValue, error);
}); throw error;
return promise; }
} }
else { else {
logging.warning("Transient setting %s cannot be stored.", name); logging.warning("Transient setting %s cannot be stored.", name);
return Promise.reject("Transient setting " + name + " cannot be stored."); throw "Transient setting " + name + " cannot be stored.";
} }
}; };
@ -450,16 +451,7 @@
}; };
scope.forceLoad = function(){ scope.forceLoad = function(){
while (settings.isStillDefault){ while (settings.isStillDefault){
logging.message("Starting synchronous request to wait for settings."); extension.waitSync("to wait for settings");
try {
let xhr = new XMLHttpRequest();
xhr.open("GET", "https://[::]", false);
xhr.send();
xhr = null;
}
catch (error){
logging.verbose("Error in XHR:", error);
}
logging.message("settings still default?", settings.isStillDefault); logging.message("settings still default?", settings.isStillDefault);
} }
}; };

View File

@ -15,12 +15,11 @@
const settingDefinitions = require("./settingDefinitions"); const settingDefinitions = require("./settingDefinitions");
scope.validVersions = [undefined, 0.1, 0.2, 0.3, 0.4, 0.5]; scope.validVersions = [undefined, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 1.0];
scope.transitions = { scope.transitions = {
// eslint-disable-next-line no-unused-vars "": function(){
"": function(oldStorage){
return { return {
storageVersion: 0.6 storageVersion: 1.0
}; };
}, },
0.1: function(oldStorage){ 0.1: function(oldStorage){
@ -43,42 +42,27 @@
const urlSettings = {}; const urlSettings = {};
(oldStorage.blackList || "").split(",") [
.map(function(url){return url.trim();}) {listName: "blackList", property: "blockMode", value: "block"},
.filter(function(url){return !!url;}) {listName: "whiteList", property: "blockMode", value: "allow"},
.forEach(function(url){ {listName: "ignoreList", property: "showNotifications", value: false}
let entry = urlSettings[url]; ].forEach(function(listAction){
if (!entry){ (oldStorage[listAction.listName] || "").split(",")
entry = {url, blockMode: "block"}; .map(function(url){return url.trim();})
urlSettings[url] = entry; .filter(function(url){return !!url;})
newStorage.urlSettings.push(entry); .forEach(function(url){
} let entry = urlSettings[url];
}); if (!entry){
(oldStorage.whiteList || "").split(",") entry = {url, [listAction.property]: listAction.value};
.map(function(url){return url.trim();}) urlSettings[url] = entry;
.filter(function(url){return !!url;}) newStorage.urlSettings.push(entry);
.forEach(function(url){ }
let entry = urlSettings[url]; else {
if (!entry){ entry[listAction.property] = listAction.value;
entry = {url, blockMode: "allow"}; }
urlSettings[url] = entry; });
newStorage.urlSettings.push(entry); });
}
});
(oldStorage.ignoreList || "").split(",")
.map(function(url){return url.trim();})
.filter(function(url){return !!url;})
.forEach(function(url){
let entry = urlSettings[url];
if (!entry){
entry = {url, showNotifications: false};
urlSettings[url] = entry;
newStorage.urlSettings.push(entry);
}
else {
entry.showNotifications = false;
}
});
["whiteList", "blackList", "ignoreList"].forEach(function(list){ ["whiteList", "blackList", "ignoreList"].forEach(function(list){
if (oldStorage.hasOwnProperty(list)){ if (oldStorage.hasOwnProperty(list)){
newStorage[list] = ""; newStorage[list] = "";
@ -158,6 +142,25 @@
} }
return newStorage; return newStorage;
}, },
0.6: function (oldStorage){
const newStorage = {
storageVersion: 1.0
};
if (
oldStorage.hasOwnProperty("protectWindow") &&
oldStorage.protectWindow &&
oldStorage.hasOwnProperty("urlSettings") &&
Array.isArray(oldStorage.urlSettings) &&
oldStorage.urlSettings.filter(function(entry){
return entry.url === "^https://www\\.google\\.com/recaptcha/api2/(?:b?frame|anchor).*$";
}).some(function(entry){
return entry.protectWindow === false;
})
){
newStorage.allowWindowNameInFrames = true;
}
return newStorage;
}
}; };
scope.check = function(storage, {settings, logging}){ scope.check = function(storage, {settings, logging}){

View File

@ -13,9 +13,10 @@
} }
const settings = require("./settings"); const settings = require("./settings");
const extension = require("./extension");
scope.init = function(page){ scope.init = function(page){
const basePath = browser.extension.getURL("themes"); const basePath = extension.getURL("themes");
const baseLink = document.createElement("link"); const baseLink = document.createElement("link");
baseLink.href = `${basePath}/base/layout.css`; baseLink.href = `${basePath}/base/layout.css`;

View File

@ -75,11 +75,11 @@
const positionAttributeLocation = context.getAttribLocation(program, "a_position"); const positionAttributeLocation = context.getAttribLocation(program, "a_position");
context.enableVertexAttribArray(positionAttributeLocation); context.enableVertexAttribArray(positionAttributeLocation);
const size = 2; // 2 components per iteration const size = 2; // 2 components per iteration
const type = context.FLOAT; // the data is 32bit floats const type = context.FLOAT; // the data is 32bit floats
const normalize = false; // don't normalize the data const normalize = false; // don't normalize the data
const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position const stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
const offset = 0; // start at the beginning of the buffer const offset = 0; // start at the beginning of the buffer
context.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset); context.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);
const texCoordLocation = context.getAttribLocation(program, "a_texCoord"); const texCoordLocation = context.getAttribLocation(program, "a_texCoord");
@ -109,4 +109,153 @@
return {webGlCanvas, context}; return {webGlCanvas, context};
}; };
const webglRandom = require("./webglRandom");
let randomSupply = null;
scope.setRandomSupply = function(supply){
randomSupply = supply;
webglRandom.setRandomSupply(supply);
};
function getNumber({originalValue, max, index, window}){
const bitLength = Math.floor(Math.log2(max) + 1);
const rng = randomSupply.getBitRng(bitLength, window);
let value = 0;
for (let i = 0; i < bitLength; i += 1){
value <<= 1;
value ^= rng(originalValue, index + i);
}
return value;
}
const parameterFakeTypes = {
preference: function(originalValue, definition, window, prefs){
const settingValue = prefs(definition.preferenceName) || originalValue;
switch (settingValue){
case "{undefined}":
return undefined;
case "{false}":
return false;
case "{empty}":
return "";
case "{disabled}":
return null;
case "{random vendor}":
return webglRandom.getRandomVendor(window);
case "{random renderer}":
return webglRandom.getRandomRenderer(window);
}
if (settingValue.startsWith("<") && settingValue.endsWith(">")){
return webglRandom.pickOneFromTree(settingValue, window);
}
return settingValue;
},
decimal: function(originalValue, definition, window){
const int = Math.floor(originalValue);
if (int !== originalValue){
const decimal = originalValue - int;
const rng = randomSupply.getRng(1, window);
const newDecimal = decimal * (rng(definition.pname) / 0xFFFFFFFF);
return int + newDecimal;
}
else {
return originalValue;
}
},
shift: function(originalValue, definition, window){
const value = getNumber({originalValue, max: definition.max, index: definition.pname, window});
return originalValue >>> value;
},
"-": function(originalValue, definition, window){
const value = getNumber({originalValue, max: definition.max, index: definition.pname, window}) *
(definition.factor || 1);
if (value > originalValue){
return 0;
}
return originalValue - value;
}
};
const parameterChangeDefinition = {
2928: {name: "DEPTH_RANGE", type: "decimal", isArray: true},
3379: {name: "MAX_TEXTURE_SIZE", type: "shift", max: 1},
3386: {name: "MAX_VIEWPORT_DIMS", type: "shift", max: 1, isArray: true},
32883: {name: "MAX_3D_TEXTURE_SIZE", type: "shift", max: 1},
33000: {name: "MAX_ELEMENTS_VERTICES", type: "-", max: 3, factor: 50},
33001: {name: "MAX_ELEMENTS_INDICES", type: "-", max: 3, factor: 50},
33901: {name: "ALIASED_POINT_SIZE_RANGE", type: "decimal", isArray: true},
33902: {name: "ALIASED_LINE_WIDTH_RANGE", type: "decimal", isArray: true},
34024: {name: "MAX_RENDERBUFFER_SIZE", type: "shift", max: 1},
34045: {name: "MAX_TEXTURE_LOD_BIAS", type: "-", max: 1, factor: 1},
34076: {name: "MAX_CUBE_MAP_TEXTURE_SIZE", type: "shift", max: 1},
34921: {name: "MAX_VERTEX_ATTRIBS", type: "shift", max: 1},
34930: {name: "MAX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1},
35071: {name: "MAX_ARRAY_TEXTURE_LAYERS", type: "shift", max: 1},
35371: {name: "MAX_VERTEX_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1},
35373: {name: "MAX_FRAGMENT_UNIFORM_BLOCKS", type: "-", max: 1, factor: 1},
35374: {name: "MAX_COMBINED_UNIFORM_BLOCKS", type: "-", max: 3, factor: 1},
35375: {name: "MAX_UNIFORM_BUFFER_BINDINGS", type: "-", max: 3, factor: 1},
35376: {name: "MAX_UNIFORM_BLOCK_SIZE", type: "shift", max: 1},
35377: {name: "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10},
35379: {name: "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", type: "-", max: 7, factor: 10},
35657: {name: "MAX_FRAGMENT_UNIFORM_COMPONENTS", type: "shift", max: 1},
35658: {name: "MAX_VERTEX_UNIFORM_COMPONENTS", type: "shift", max: 1},
35659: {name: "MAX_VARYING_COMPONENTS", type: "shift", max: 1},
35660: {name: "MAX_VERTEX_TEXTURE_IMAGE_UNITS", type: "shift", max: 1},
35661: {name: "MAX_COMBINED_TEXTURE_IMAGE_UNITS", type: "-", max: 1, factor: 2},
35968: {name: "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", type: "shift", max: 1},
35978: {name: "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", type: "shift", max: 1},
36203: {name: "MAX_ELEMENT_INDEX", type: "-", max: 15, factor: 1},
36347: {name: "MAX_VERTEX_UNIFORM_VECTORS", type: "shift", max: 1},
36348: {name: "MAX_VARYING_VECTORS", type: "shift", max: 1},
36349: {name: "MAX_FRAGMENT_UNIFORM_VECTORS", type: "shift", max: 1},
37154: {name: "MAX_VERTEX_OUTPUT_COMPONENTS", type: "shift", max: 1},
37157: {name: "MAX_FRAGMENT_INPUT_COMPONENTS", type: "shift", max: 1},
7936: {name: "VENDOR", type: "preference", preferenceName: "webGLVendor"},
7937: {name: "RENDERER", type: "preference", preferenceName: "webGLRenderer"},
37445: {name: "UNMASKED_VENDOR_WEBGL", type: "preference", preferenceName: "webGLUnmaskedVendor"},
37446: {name: "UNMASKED_RENDERER_WEBGL", type: "preference", preferenceName: "webGLUnmaskedRenderer"}
};
scope.initializeParameterDefinitions = function(){
function singleFake(originalValue, window, prefs){
const value = parameterFakeTypes[this.type](originalValue, this, window, prefs);
const faked = value !== originalValue;
this.fake = function(){
return {value, faked};
};
return {value, faked};
}
function arrayFake(originalValue, window, prefs){
let faked = false;
let fakedValue = [];
for (let i = 0; i < originalValue.length; i += 1){
fakedValue[i] = parameterFakeTypes[this.type](originalValue[i], this, window, prefs);
faked = faked || originalValue[i] !== fakedValue[i];
originalValue[i] = fakedValue[i];
}
this.fake = function(originalValue){
if (faked){
for (let i = 0; i < originalValue.length; i += 1){
originalValue[i] = fakedValue[i];
}
}
return {
value: originalValue,
faked
};
};
return {
value: originalValue,
faked
};
}
Object.keys(parameterChangeDefinition).forEach(function(parameterName){
const definition = parameterChangeDefinition[parameterName];
definition.pname = parameterName;
if (!definition.fake){
definition.fake = definition.isArray? arrayFake: singleFake;
}
});
};
scope.parameterChangeDefinition = parameterChangeDefinition;
}()); }());

173
lib/webglRandom.js Normal file
View File

@ -0,0 +1,173 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
"use strict";
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
else {
scope = require.register("./webglRandom", {});
}
let randomSupply = null;
scope.setRandomSupply = function(supply){
randomSupply = supply;
};
const windowHashes = new WeakMap();
function getWindowStorage(window){
let storage = windowHashes.get(window);
if (!storage){
const vendorRng = randomSupply.getIndexRng(1, scope.vendors.length, window);
const vendorIndex = vendorRng(0);
storage = {vendorIndex};
const vendor = scope.vendors[vendorIndex];
if (vendor.getRandomRenderer){
vendor.renderers = [vendor.getRandomRenderer(window)];
storage.rendererIndex = 0;
}
else {
const rendererRng = randomSupply.getIndexRng(1, vendor.renderers.length, window);
storage.rendererIndex = rendererRng(0);
}
windowHashes.set(window, storage);
}
return storage;
}
function getRandomVendorIndex(window){
return getWindowStorage(window).vendorIndex;
}
function getRandomRendererIndex(window){
return getWindowStorage(window).rendererIndex;
}
scope.getRandomVendor = function getRandomVendor(window){
return scope.vendors[getRandomVendorIndex(window)].vendor;
};
scope.getRandomRenderer = function getRandomRenderer(window){
const vendor = scope.vendors[getRandomVendorIndex(window)];
return vendor.renderers[getRandomRendererIndex(window)];
};
scope.pickOneFromTree = function pickOneFromTree(treeString, window){
function pickOne(string){
const options = [];
let cumulate = "";
let index = 0;
for (const l = string.length; index < l; index += 1){
const char = string.charAt(index);
if (char === "|"){
options.push(cumulate);
cumulate = "";
}
else if (char === "<"){
const subPick = pickOne(string.substring(index + 1));
cumulate += subPick.value;
index += 1 + subPick.endIndex;
}
else if (char === ">"){
break;
}
else {
cumulate += char;
}
}
options.push(cumulate);
const optionRng = randomSupply.getIndexRng(1, options.length, window);
return {value: options[optionRng(0)], endIndex: index};
}
return pickOne(treeString).value;
};
scope.vendors = [
{
vendor: "Chromium",
renderers: [
"Chromium",
]
},
{
vendor: "Intel Inc.",
renderers: [
"Intel GMA X3100 OpenGL Engine",
"Intel HD Graphics 3000 OpenGL Engine",
"Intel HD Graphics 4000 OpenGL Engine",
"Intel HD Graphics 5000 OpenGL Engine",
"Intel Iris OpenGL Engine",
"Intel Iris Pro OpenGL Engine",
]
},
{
vendor: "Intel Open Source Technology Center",
renderers: [
"Mesa DRI Intel(R) Haswell Mobile",
"Mesa DRI Intel(R) Ironlake Mobile",
"Mesa DRI Intel(R) Ivybridge Mobile x86/MMX/SSE2",
"Mesa DRI Intel(R) Ivybridge Mobile",
"Mesa DRI Intel(R) Sandybridge Desktop x86/MMX/SSE2",
"Mesa DRI Intel(R) Sandybridge Desktop",
"Mesa DRI Intel(R) Sandybridge Mobile x86/MMX/SSE2",
"Mesa DRI Intel(R) Sandybridge Mobile",
]
},
{
vendor: "Google Inc.",
getRandomRenderer: function(window){
const words = "Series|Graphics|Chipset|Express|Family|nForce|Dual|NVIDIA|800|600|300|FireGL|Mobility|Radeon|series|FirePro|Optiplex|WDDM|200|400|700|70M|50M|Driver|200M".split("|");
const compressed = "ANGLE (<A<MD <760G|(ATI) $f M8900 ($b) $c Pro $1|$f <2270|W5000 ($b V) $1 Adapter>|M880G with ATI $c $d HD 42<00 |50>|$d< <HD<6<370D $1|410D $1>|77<00 $0|70>| <5<450|5<00 $0|70>|670|$k $0|$8 $0>|6<2<50< $1|M >|90 $1>|3<00 $e $1|10< $1 |M>|20< <$1 |$e $1>|M>|50|70<D|M>>|4<00M $0|10D|50A $1|$l |80G>|5<00< $0|M/5600/5700 $0>|10 $0|20G|30D $1|50A|70>|6<20G|30M|50A $1|70>|7<00 $0|30M>|$8 $0|900< $0|M $0>>|7<000 $e|290 $1|3<00 $0 $1|10< $1|M>|40< $1|G|M>>|4<00M $0|20G|50 $1|$l|80D>|5<00< $0|/7600 $0|G|M/7600M $0>|20G + 7670M $6 $1|40D|$m/7650M $1|60D|70< $1|M>|80D>|6<00<G + 6400M $6 $1|M $0>|10M|40G + <76<00M $6 $1|$l $6 $1>|8750M $6 $1>|$m|60<D|G + 7600M $6 $1>|$l>|$k $0|$8 $0|900 $0>|8<2<10|40|50>|3<30|50>|4<00|50G|70D>|5<10G|50G + <8570M $6 $1|HD 8600/8700M $6 $1>|70D>|6<10G + 8500M $6 $1|50G + 8<670M $6 $1|750M $6 $1>|70D>>>>|R<7 $i $0|9 $i $0>>|(TM) HD <6<380G|480G|520G |620G>|7450>>>|SUS <EAH<4350 $e|5<450 $0|670 $0|770 $0>|6<450 $0|670 $0|970 $0>>|HD7770 $0|R9 270 $0>|TI <$f <2450|V<3<$k ($b)|$8>|4800 ($b)>>|$c $d <9600/9700 $0|HD <2<$j XT|$9>|3<4<00 $0|30|50|70>|650>|4<2<00 $0|50 $1>|3<00< $0|/4500 $0>|30>|5<00< $0|/5100 $0>|30 $0|70>|650>|5<000 $0|145|4<00 $0 |30|5<0|v>|70 >|650>|6<370|550>>|X1<$a|$9>>|$d <2100|3<000 $1|100 $1>|HD<4670| <2<350|$j <P<ro |RO>|$0|XT >|$9 <P<ro |RO>|XT>>|3<$i $1|$a $1|4<00 $0|50 - Dell $g|70 - Dell $g>|6<00 $0|50>|$8 $0>|4<2<00|50 $1>|3<00< $0|/4500 $0>|50 $0>|550|6<00 Seri<es |si>|50|70>|770|8<00 $0|70 X2>>|5<4<00 $0|50>|570|6<00 $0|70>|$k $0|$8 $0>|6<230|350>>>|X<1<050|2<00 $0|50|70>>|$a/X550/X1050 $0|press <1<1<00|50 $0>|2<00 $0|50>>|$i $0>>>>>|Intel(R) <4 $0 Internal $2|829<15G/GV/910GL $3 $2 $4|45G $3 $2 $4>|946GZ $3 $2 $4|B43 $3 $2|G<33/G31 $3 $2 $4|4<1 $3 $2|5/G43 $3 $2>|965 $3 $2 $4|raphics Media Accelerator <3<150|$9 $0>|HD >>|HD $1 <3000|4<000|$j|$9>|$4>|Q<3<3 $3 $2 $4|5 $3 $2 $4>|45/Q43 $3 $2|965/Q963 $3 $2 $4>>|M<icrosoft Basic Render $n|obile Intel(R) <4<5 $3 $2 $4| $0 $3 $2 $4>|9<15GM/GMS,910GML $3 $2 $4|45< $3 $2 $4|GM $3 $2 $4>|65 $3 $2 $4>|- famiglia $3 $2 45|HD $1>>|$7 <GeForce <210 |31<0M |5M>|4<05M|10M>|6<05|1<0<0 $5 405|M>|50< LE|SE $5 430>>|$i TurboCache(TM)|500|$9>|7<0<00M / $5 610M|25 / $7 $5 630a |50 </ $7 $5 620i|PV / $7 $5 630a>>|1<00 </ $7 $5 630i|GS>|$m / $5 630M>|$a <G<S|T>|LE|SE/7200 GS>|900 GS>|8<$o G|$a GS |$j< GS|GS|M G<S|T>>|500 GT|$9< G<S|T< |S>>|GS|M G<S|T>>|$8 G<S|TS 512>>|9<100|$o GS|$a< GE|M GS >|$j< GT |M >|500< G<S|T>|M GS>|6<00< G<SO 512|T>|M G<S|T>>|$m GT>|700M GTS|$8 GT< |X/9800 GTX+>>|FX 5200|G<10<0|2M|5M>|210< |M>| 10<3M |5M >|o 7300|T< <120M|2<20|30M |40M >|3<20M|3<0M |5M>>|4<2<0M|5M>|30|40>|5<2<0M|5M>|30|45|55M>|6<10|2<0|5>|3<0M|5>|40< |M>|$m>|7<40M|55M>>|S <2<40|50>|350M|450 >|X <2<60|75|85|95>|4<60< SE|M>|80>|5<50 Ti|60< Ti |M>|70|80M>|6<50 Ti BOOST|60< Ti|M >|7<0|5M>|80|90>|7<60 (192-bit)|70|80>>>>>|ION|MCP67M|$5 750a SLI|NVS <3<00|100M >|4200M|5<100M|$o|400M >>|Quadro <1000M|2000M|$9|FX <1<500M|$k|$8>|2<500M|700M>|3700|5<70|80>|770M|880M>|K<3000M|$9>|NVS <1<10M|35M|40M|60M>|2<85|90>>>>|R<adeon <(TM) HD 64<$l|90M>|HD 6470M|X<1<$a/X1550 $0|550 <64-bit|$0>|650 S<E|eries >|950 $0>|$a/X550/X1050 $0|$8 GTO>>|DPDD Chained DD|oyal BNA $n|S880>|SiS Mirage 3 $1|VIA Chrome9 HC IGP $4 $h|WinFast GT 640($7)><| (Microsoft Corporation< <$h 1.1) |- $h< <1.<0)|1)|2)>|v1.<1)|2<0)|)>|3)>>|)>>|- $h v1.<1)|20)>>>< Direct3D<11 vs_<4_<0 ps_4_0|1 ps_4_1>|5_0 ps_5_0>|9<Ex|> vs_<0_0 ps_<2_0|3_0>|2_0 ps_2_0|3_0 ps_3_0>>|>)".replace(
/\$([0-9a-z]+)/gi,
function(m, index){
return words[parseInt(index, 36)];
}
);
return scope.pickOneFromTree(compressed, window);
}
},
{
vendor: "NVIDIA Corporation",
renderers: [
"GeForce 8600M GT/PCIe/SSE2",
"GeForce GT 430/PCIe/SSE2",
"GeForce GT 520/PCIe/SSE2",
"GeForce GTX 650 Ti/PCIe/SSE2",
"GeForce GTX 680/PCIe/SSE2",
"GeForce GTX 770/PCIe/SSE2",
"NVIDIA GeForce 320M OpenGL Engine",
"NVIDIA GeForce 8600M GT OpenGL Engine",
"NVIDIA GeForce 8800 GS OpenGL Engine",
"NVIDIA GeForce 8800 GT OpenGL Engine",
"NVIDIA GeForce 9400 OpenGL Engine",
"NVIDIA GeForce 9400M OpenGL Engine",
"NVIDIA GeForce 9600M GT OpenGL Engine",
"NVIDIA GeForce GT 130 OpenGL Engine",
"NVIDIA GeForce GT 330M OpenGL Engine",
"NVIDIA GeForce GT 640M OpenGL Engine",
"NVIDIA GeForce GT 650M OpenGL Engine",
"NVIDIA GeForce GT 750M OpenGL Engine",
"NVIDIA GeForce GTX 660M OpenGL Engine",
"NVIDIA GeForce GTX 675MX OpenGL Engine",
"NVIDIA GeForce GTX 680MX OpenGL Engine",
"Quadro 2000/PCIe/SSE2",
"Quadro 2000M/PCIe/SSE2",
"Quadro FX 1800/PCIe/SSE2",
"Quadro K600/PCIe/SSE2",
]
},
{
vendor: "VMware, Inc.",
renderers: [
"Gallium 0.4 on i915 (chipset: Pineview M)",
"Gallium 0.4 on llvmpipe (LLVM 3.2, 128 bits)",
]
},
{
vendor: "TransGaming Inc.",
renderers: [
"SwiftShader",
]
},
];
}());

Some files were not shown because too many files have changed in this diff Show More