1
0
mirror of https://github.com/kkapsner/CanvasBlocker synced 2024-05-29 09:28:06 +02:00

Compare commits

...

385 Commits

Author SHA1 Message Date
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
kkapsner
762367a87b Added window.open protection 2019-12-02 22:57:11 +01:00
kkapsner
a181780020 Last adjustments to screen protection
Fixes #220
2019-12-02 19:17:54 +01:00
kkapsner
b7ba5c2050 Added default values for mobile 2019-12-02 19:16:32 +01:00
kkapsner
5020e0b070 Resetting the settings had undesired side effects 2019-12-02 19:13:38 +01:00
kkapsner
c62ddcc33f Typo 2019-12-01 23:33:17 +01:00
kkapsner
59ad6fc7d6 navigator.oscpu and navigator.buildID are undefined in non Gecko browsers
Fixes #411
2019-12-01 01:27:29 +01:00
kkapsner
1aff68d802 Added linting for consistent-return 2019-12-01 01:25:39 +01:00
kkapsner
635cdf8061 Initialized npm 2019-11-30 15:34:42 +01:00
kkapsner
16fc49eee3 Added missing texts 2019-11-30 14:38:53 +01:00
kkapsner
afd9aea767 Correct name for .codebeatsettings 2019-11-30 13:22:41 +01:00
kkapsner
17349dcb05 Linting of .tools and test 2019-11-30 02:05:37 +01:00
kkapsner
aef6bd3d59 Big linting 2019-11-28 01:26:35 +01:00
kkapsner
b5e6d049ce Spelling settings 2019-11-28 01:13:40 +01:00
kkapsner
83a8234b24 userAgent feature could not be disabled 2019-11-25 22:10:31 +01:00
kkapsner
d4ce6c4b4b Updated screen sizes. 2019-11-22 08:23:45 +01:00
kkapsner
cded4d369a Removed write rights for codebeat 2019-11-21 11:34:42 +01:00
kkapsner
d930de1883 Updated description 2019-11-21 11:10:38 +01:00
kkapsner
a555f3cd64 Grammar 2019-11-12 23:18:17 +01:00
kkapsner
d42a4d2372 Added screnn protection
First draft for #220
2019-11-11 23:00:39 +01:00
kkapsner
cc776b48de Improved storage of protected API features 2019-11-11 15:30:11 +01:00
kkapsner
1430b89d55 settings sanitation: added missing APIs 2019-11-08 08:44:20 +01:00
kkapsner
320dc02941 Fix export background color
Background color of the textarea in the settings export was not readable
in the dark theme when the value was invalid
2019-11-08 08:42:02 +01:00
kkapsner
5d6c2d9a47 Added ability to use objectGetters on faked functions. 2019-11-07 17:38:54 +01:00
kkapsner
73657852d3 Added screen size test 2019-11-06 14:29:53 +01:00
kkapsner
ee254b3b93 Updated descriptions
Made clear that the built in fingerprinting protection of Firefox is
fine to use.
2019-10-24 17:29:05 +02:00
kkapsner
f010c7c8fa Version 0.5.15 2019-09-24 13:11:47 +02:00
kkapsner
aa7a4e1d06 Added protection for getParameter (webGL)
Fixes #329
2019-09-19 00:47:52 +02:00
kkapsner
64d4aa3f73 Enhanced webGL test page
For #329
2019-09-19 00:41:20 +02:00
kkapsner
506f062c07 Revert to correct values when whitelisted
Fixes #397
2019-09-13 00:49:34 +02:00
kkapsner
f3d1ca80f5 Remove iframe protection from whitelisted pages
Fixes #397
2019-09-11 23:57:00 +02:00
kkapsner
069165e8d6 Added viewport meta
For #387
2019-09-11 23:49:50 +02:00
kkapsner
5a355284f3 Reevaluated logging message levels
Fixes #394
2019-09-05 01:07:05 +02:00
kkapsner
14b4bd8ac6 Improved test pages
For #390
2019-09-04 00:30:20 +02:00
kkapsner
acc171041d Improved option pages on mobile
Fixes #387
2019-08-21 14:33:29 +02:00
kkapsner
a95fae3de8 Future proof: getters my be values in other browsers
Example: window.opener is a value property in Chrome.
2019-08-16 17:34:16 +02:00
kkapsner
d666d68812 Version 0.5.13 2019-07-24 22:58:08 +02:00
kkapsner
d7d0555036 Typo 2019-07-18 20:42:56 +02:00
kkapsner
61c0a185b5 Added description for preset opening setting 2019-07-18 19:52:35 +02:00
kkapsner
209937c9bc Version 0.5.13 RC1 2019-07-17 17:19:28 +02:00
kkapsner
74f2ad7328 Setting rearrangements 2019-07-17 13:09:16 +02:00
kkapsner
adbab50952 presets: text improvements 2019-07-17 13:08:49 +02:00
kkapsner
43b2fa8b51 Added setting presets
Fixes #376
2019-07-16 18:25:30 +02:00
kkapsner
4d7b9b981c Only show "Clear container" if they are available 2019-07-16 13:40:08 +02:00
kkapsner
3861b29848 Linting 2019-07-16 13:36:54 +02:00
kkapsner
7574ee1126 Added tab icon to all option pages 2019-07-16 13:35:30 +02:00
kkapsner
6fb0f946af Display of url specific settings did not work properly
For #271
2019-07-14 10:53:23 +02:00
kkapsner
ca6d421153 Fixed chinese localization file 2019-07-07 20:59:39 +02:00
kkapsner
3452c9b58c Added thank to KrasnayaPloshchad 2019-07-07 20:58:20 +02:00
kkapsner
a269f1701f
Merge pull request #380 from KrasnayaPloshchad/patch-1
Update massages for zh_CN locale
2019-07-07 20:56:15 +02:00
Krasnaya Ploshchad’
2d86005df2
Update massages for zh_CN locale
Lots of messages were translated.
2019-07-07 14:08:40 +00:00
kkapsner
85320924fa Version 0.5.13 2019-07-06 12:51:51 +02:00
kkapsner
bd6b85784f Updated release notes 2019-07-06 11:52:58 +02:00
kkapsner
40be0ad6f5 options: moved "Settings" to "General" group
For #353
2019-07-03 13:44:38 +02:00
kkapsner
e698a6372b options: made tabs taller
Fixes #353
2019-06-30 12:50:12 +02:00
kkapsner
6083eb051f Added tabs in options page
For #353
2019-06-29 16:55:26 +02:00
kkapsner
7037804b6f Enabled whitelisting of local files
Fixes #373
2019-06-25 23:22:55 +02:00
kkapsner
7cd89dec99 Detect when browser.contextualIdentities.onRemoved is not supported
For #372
2019-06-25 08:21:34 +02:00
kkapsner
9ccaabd46a Version 0.5.12 2019-06-23 20:46:35 +02:00
187 changed files with 42423 additions and 4841 deletions

6
.codebeatsettings Normal file
View File

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

View File

@ -1,7 +1,22 @@
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)
privacy.resistFingerprinting kann aber problemlos aktiviert werden.
privacy.resistFingerprinting kann aber problemlos aktiviert werden und der Fingerprinting-Schutz muss nicht deaktiviert werden.
Beschützte "Fingerprinting"-APIs:
<ul>
<li>canvas 2d</li>
<li>webGL</li>
<li>audio</li>
<li>history</li>
<li>window (standardmäßig deaktiviert)</li>
<li>DOMRect</li>
<li>TextMetrics</li>
<li>navigator (standardmäßig deaktiviert)</li>
<li>screen</li>
</ul>
Nähere Informationen zum Fingerprinting können Sie finden auf:
<ul>
@ -27,17 +42,4 @@ Die verschiedenen Blockiermodi sind:
<li>nur Einträge der Whitelist erlauben: Nur Seiten, die in der Whitelist gelistet sind, dürfen die beschützten APIs verwenden.</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>
</ul>
Beschützte "Fingerprinting"-APIs:
<ul>
<li>canvas 2d</li>
<li>webGL</li>
<li>audio</li>
<li>history</li>
<li>window (standardmäßig deaktiviert)</li>
<li>DOMRect</li>
<li>navigator (standardmäßig deaktiviert)</li>
</ul>
Falls Sie Fehler finden oder Verbesserungsvorschläge haben, teilen Sie mir das bitte auf https://github.com/kkapsner/CanvasBlocker/issues mit.
</ul>

View File

@ -1,7 +1,22 @@
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)
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.
Protected "fingerprinting" APIs:
<ul>
<li>canvas 2d</li>
<li>webGL</li>
<li>audio</li>
<li>history</li>
<li>window (disabled by default)</li>
<li>DOMRect</li>
<li>TextMetrics</li>
<li>navigator (disabled by default)</li>
<li>screen</li>
</ul>
More information on fingerprinting can be found at:
<ul>
@ -29,16 +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> allow everything: Ignore all lists and allow the protected APIs on all websites.</li>
</ul>
Protected "fingerprinting" APIs:
<ul>
<li>canvas 2d</li>
<li>webGL</li>
<li>audio</li>
<li>history</li>
<li>window (disabled by default)</li>
<li>DOMRect</li>
<li>navigator (disabled by default)</li>
</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.

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
!/.tools

View File

@ -1,40 +1,93 @@
{
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"webextensions": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "script"
},
"extends": "eslint:recommended",
"globals": {
"exportFunction": false
},
"rules": {
"brace-style": ["error", "stroustrup", {"allowSingleLine": true}],
"comma-spacing": ["error", { "before": false, "after": true }],
"constructor-super": "warn",
"eqeqeq": "error",
"max-len": ["warn", {"code": 120, "tabWidth": 4}],
"max-lines": ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}],
"max-params": ["warn", 4],
"no-const-assign": "warn",
"no-this-before-super": "warn",
"no-undef": "error",
"no-unreachable": "warn",
"no-unused-vars": "off",
"no-trailing-spaces": ["error", {"skipBlankLines": true}],
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"indent": ["error", "tab", {"SwitchCase": 1}],
"space-in-parens": ["error", "never"],
"valid-typeof": "warn",
"quotes": ["error", "double"],
"semi": ["error", "always"],
"strict": ["error", "function"]
}
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"webextensions": true
},
"parserOptions": {
"ecmaVersion": 8,
"ecmaFeatures": {
"jsx": true
},
"sourceType": "script"
},
"plugins": [
"promise",
"eslint-comments",
"html"
],
"extends": [
"eslint:recommended",
"plugin:promise/recommended",
"plugin:eslint-comments/recommended"
],
"globals": {
"exportFunction": false
},
"rules": {
"brace-style": ["error", "stroustrup", {"allowSingleLine": true}],
"comma-spacing": ["error", { "before": false, "after": true }],
"complexity": ["warn", 20],
"consistent-return": "error",
"constructor-super": "warn",
"eqeqeq": "error",
"eslint-comments/no-use": ["error", {"allow": ["eslint-disable-next-line", "globals"]}],
"indent": ["error", "tab", {"SwitchCase": 1}],
"max-depth": ["warn", 4],
"max-len": ["warn", {"code": 120, "tabWidth": 4}],
"max-lines-per-function": ["warn", {"max": 80,"skipBlankLines": true, "skipComments": true}],
"max-lines": ["warn", {"max": 500, "skipBlankLines": true, "skipComments": true}],
"max-params": ["warn", 4],
"no-console": "error",
"no-const-assign": "error",
"no-inner-declarations": "warn",
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"no-prototype-builtins": "off",
"no-this-before-super": "warn",
"no-trailing-spaces": ["error", {"skipBlankLines": true}],
"no-undef": "error",
"no-unreachable": "warn",
"no-unused-vars": "off",
"no-use-before-define": ["error", {"functions": false}],
"no-useless-rename": "warn",
"no-useless-return": "warn",
"no-var": "error",
"quotes": ["error", "double"],
"require-atomic-updates": "off",
"semi": ["error", "always"],
"space-in-parens": ["error", "never"],
"strict": ["error", "function"],
"valid-typeof": "warn"
},
"overrides": [
{
"files": ["detectionTest.js", "modifiedCanvasAPI.js", "options.js", "settingsDisplay.js"],
"rules": {
"max-lines": "off"
}
},
{
"files": ["test/*"],
"rules": {
"no-console": "off"
}
},
{
"files": [".tools/*.js"],
"env": {
"node": true
},
"rules": {
"no-console": "off"
}
},
{
"files": ["*.html", "*.php"],
"rules": {
"no-useless-escape": "off",
"no-undef": "off"
}
}
]
}

View File

@ -2,13 +2,13 @@
## Description
<!--- 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 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 suggesting a change/improvement, explain the difference from current behavior. -->
@ -18,10 +18,10 @@
## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. -->
1.
2.
3.
4.
1. create a fresh Firefox profile
2.
3.
4.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
@ -36,6 +36,8 @@
* Installed addons:
## Your Settings
~~~ json
<!--- Copy your CanvasBlocker settings here. -->
<!-- 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". -->
~~~

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
printed.png
web-ext-artifacts/
versions/*.xpi
versions/*.xpi
node_modules/

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);

View File

@ -7,6 +7,8 @@ const la = require("../_locales/" + language + "/messages.json");
const laKeys = Object.keys(la);
enKeys.forEach(function(key){
"use strict";
if (en[key].message){
if (!la[key] || !la[key].message){
console.log(key, "missing");

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

@ -5,9 +5,11 @@ const util = require("util");
function getMessagesInContent(content){
"use strict";
const foundMessages = [];
[
/\b(?:_|browser.i18n.getMessage|notify|extension)\(["']([^"']+)["']\s*(?:\)|,)/g,
/\b(?:_|browser.i18n.getMessage|extension.getTranslation|notify|extension)\(["']([^"']+)["']\s*(?:\)|,)/g,
/\b(?:messageId|name|getTranslation)\s*:\s*["']([^"']+)["']/g,
].forEach(function(re){
let match;
@ -19,58 +21,57 @@ function getMessagesInContent(content){
}
async function getMessagesInFile(path){
return await util.promisify(fs.exists)(path)
.then(function(exists){
if (exists){
return util.promisify(fs.readFile)(path, {encoding: "UTF-8"})
.then(function(content){
return getMessagesInContent(content);
});
}
else {
console.log("file does not exist:", path);
return [];
}
});
"use strict";
const exists = await util.promisify(fs.exists)(path);
if (exists){
const content = await util.promisify(fs.readFile)(path, {encoding: "UTF-8"});
return getMessagesInContent(content);
}
else {
console.log("file does not exist:", path);
return [];
}
}
async function getMessagesInFolder(folder){
return await util.promisify(fs.readdir)(folder, {encoding: "UTF-8"})
.then(function(files){
return Promise.all(
files.filter(function(file){
return !file.startsWith(".");
}).map(function(file){
return path.join(folder, file);
}).map(function(path){
return util.promisify(fs.stat)(path).then(function(stat){
if (stat.isDirectory()){
return getMessagesInFolder(path);
}
else {
if (path.endsWith(".js")){
return getMessagesInFile(path);
}
else {
return [];
}
}
});
})
).then(function(messages){
const flat = [];
messages.forEach(function(messages){
messages.forEach(function(message){
flat.push(message);
});
});
return flat;
"use strict";
const files = await util.promisify(fs.readdir)(folder, {encoding: "UTF-8"});
const messages = await Promise.all(
files.filter(function(file){
return !file.startsWith(".");
}).map(function(file){
return path.join(folder, file);
}).map(async function(path){
const stat = await util.promisify(fs.stat)(path);
if (stat.isDirectory()){
return getMessagesInFolder(path);
}
else {
if (path.endsWith(".js")){
return getMessagesInFile(path);
}
else {
return [];
}
}
})
);
const flat = [];
messages.forEach(function(messages){
messages.forEach(function(message){
flat.push(message);
});
})
});
return flat;
}
async function getSettingMessages(){
async function getSettingMessages(){
"use strict";
const settingStrings = require("../lib/settingStrings");
const settingDefinitions = require("../lib/settingDefinitions");
function getDefinition(name){
@ -81,66 +82,93 @@ async function getSettingMessages(){
const settingsDisplay = require("../options/settingsDisplay");
const foundMessages = [];
settingsDisplay.forEach(function(display){
if ((typeof display) === "string"){
foundMessages.push("section_" + display.toLowerCase());
settingsDisplay.forEach(function(groupDefinition){
if (groupDefinition.name){
foundMessages.push("group_" + groupDefinition.name.toLowerCase());
}
else {
let settingDefinition = getDefinition(display.name);
if (!settingDefinition){
settingDefinition = display;
display.action = true;
groupDefinition.sections.forEach(function(sectionDefinition){
if (sectionDefinition.name){
foundMessages.push("section_" + sectionDefinition.name.toLowerCase());
}
if (settingDefinition){
if (display.inputs){
settingDefinition.inputs = display.inputs.map(function(input){
return getDefinition(input);
sectionDefinition.settings.forEach(function(display){
let settingDefinition = getDefinition(display.name);
if (!settingDefinition){
settingDefinition = display;
display.action = true;
}
if (settingDefinition){
if (display.inputs){
settingDefinition.inputs = display.inputs.map(function(input){
return getDefinition(input);
});
}
else if (display.actions){
settingDefinition.actions = display.actions.map(function(action){
return {name: action};
});
}
settingStrings.getMessages(settingDefinition).forEach(function(message){
foundMessages.push(message.toLowerCase());
});
}
else if (display.actions){
settingDefinition.actions = display.actions.map(function(action){
return {name: action};
});
}
settingStrings.getMessages(settingDefinition).forEach(function(message){
foundMessages.push(message.toLowerCase());
});
}
}
});
});
});
const presets = require("../options/presets.json");
Object.keys(presets).forEach(function(preset){
foundMessages.push("preset_" + preset + "_title");
foundMessages.push("preset_" + preset + "_description");
});
return foundMessages.map(function(message){return message.toLowerCase();});
}
async function getKnownMessages(){
"use strict";
return [
"addon_title",
"addon_description",
"urlsettings_title",
"urlSettings_title",
"installnotice",
"presets_installnotice",
"updatenotice",
"disablenotifications",
"disableNotifications",
"showoptions",
"displayhiddensettings_title",
"displayhiddensettings_description",
"displayHiddenSettings_title",
"displayHiddenSettings_description",
"browseraction_settings",
"browseraction_test",
"browseraction_review",
"browseraction_reportissue",
];
"browseraction_reportIssue",
].map(function(message){
return message.toLowerCase();
});
}
const en = require("../_locales/en/messages.json");
const declaredMessages = Object.keys(en)
// .filter(function(key){return en[key].message;})
.map(function(key){return key.toLowerCase();});
Promise.all([getSettingMessages(), getMessagesInFolder(path.join(__dirname, "..")), getKnownMessages()]).then(function([settingMessages, fileMessages, knownMessages]){
async function main(){
"use strict";
const en = require("../_locales/en/messages.json");
const declaredMessages = Object.keys(en)
// .filter(function(key){return en[key].message;})
.map(function(key){
return key.toLowerCase();
});
const [settingMessages, fileMessages, knownMessages] = await Promise.all([
getSettingMessages(),
getMessagesInFolder(path.join(__dirname, "..")),
getKnownMessages()]
);
declaredMessages.forEach(function(message){
if (
fileMessages.indexOf(message) === -1 &&
settingMessages.indexOf(message) === -1 &&
knownMessages.indexOf(message) === -1
){
console.log(`${message} not used`);
console.log(`usage of ${message} not found`);
}
});
});
}
main();

View File

@ -8,35 +8,39 @@ const language = process.argv[2];
function getTranslationPath(language){
"use strict";
return path.join(__dirname, "../_locales/" + language + "/messages.json");
}
async function loadTranslation(language){
"use strict";
const path = getTranslationPath(language);
return await util.promisify(fs.exists)(path)
.then(function(exists){
if (exists){
console.log("language exists -> load data");
return util.promisify(fs.readFile)(path, {encoding: "UTF-8"})
.then(function(data){
return JSON.parse(data);
});
}
else {
console.log("language does not exist -> create it");
return {};
}
});
const exists = await util.promisify(fs.exists)(path);
if (exists){
console.log("language exists -> load data");
const data = await util.promisify(fs.readFile)(path, {encoding: "UTF-8"});
return JSON.parse(data);
}
else {
console.log("language does not exist -> create it");
return {};
}
}
async function saveTranslation(language, data){
"use strict";
const path = getTranslationPath(language);
return await util.promisify(fs.writeFile)(path, JSON.stringify(data, null, "\t"));
}
async function getInput(prompt){
return new Promise(function(resolve, reject){
"use strict";
return new Promise(function(resolve){
process.stdout.write(prompt);
process.stdin.setEncoding('utf8');
process.stdin.setEncoding("utf8");
process.stdin.resume();
process.stdin.on("data", function onData(data){
process.stdin.removeListener("data", onData);
@ -47,18 +51,22 @@ async function getInput(prompt){
}
async function askForTranslation(key){
"use strict";
const enData = en[key];
console.log("English translation for", key, ":", enData.message);
if (enData.description){
console.log("\nDescription:", enData.description);
}
return await getInput("Please enter translation: ");
return getInput("Please enter translation: ");
}
async function translate(language){
"use strict";
const originalData = await loadTranslation(language);
const data = {};
for (var i = 0; i < enKeys.length; i += 1){
for (let i = 0; i < enKeys.length; i += 1){
const key = enKeys[i];
const oldData = originalData[key];
const enData = en[key];
@ -75,6 +83,10 @@ async function translate(language){
return data;
}
translate(language).then(function(data){
return saveTranslation(language, data);
});
(async function(){
"use strict";
const data = await translate(language);
saveTranslation(language, data);
}());

74
.vscode/settings.json vendored
View File

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

43
.vscode/tasks.json vendored
View File

@ -16,7 +16,9 @@
"command": "eslint"
},
"args": [
"./"
"./",
"--ext",
".js,.html,.php"
],
"presentation": {
"echo": true,
@ -43,7 +45,7 @@
"-f",
"nightly",
"--url",
"http://canvasblocker.local/test/"
"http://canvasblocker.localhost/test/"
],
"presentation": {
"echo": true,
@ -68,7 +70,7 @@
"args": [
"run",
"--url",
"http://canvasblocker.local/test/"
"http://canvasblocker.localhost/test/"
],
"presentation": {
"echo": true,
@ -95,7 +97,7 @@
"-f",
"firefox-esr",
"--url",
"http://canvasblocker.local/test/"
"http://canvasblocker.localhost/test/"
],
"presentation": {
"echo": true,
@ -122,7 +124,7 @@
"-f",
"firefox-beta",
"--url",
"http://canvasblocker.local/test/"
"http://canvasblocker.localhost/test/"
],
"presentation": {
"echo": true,
@ -150,7 +152,36 @@
"--ignore-files",
"test",
"--ignore-files",
"versions"
"versions",
"--ignore-files",
"crowdin.yml",
"--ignore-files",
"package*"
],
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
},
"problemMatcher": []
},
{
"label": "build alpha",
"type": "shell",
"windows": {
"command": "node"
},
"linux": {
"command": "node"
},
"osx": {
"command": "node"
},
"args": [
".tools/build.js",
"--type",
"alpha"
],
"presentation": {
"echo": true,

View File

@ -1,10 +1,27 @@
CanvasBlocker [![codebeat badge](https://codebeat.co/badges/0edd6c9f-250a-4f1e-9c64-0958741522af)](https://codebeat.co/projects/github-com-kkapsner-canvasblocker-master)
=====
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)
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.
-----
Protected "fingerprinting" APIs:
* canvas 2d
* webGL
* audio
* history
* window (disabled by default)
* DOMRect
* SVG
* TextMetrics
* navigator (disabled by default)
* screen
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:
* https://audiofingerprint.openwpm.com/ (very poorly written = slow)
* https://webtransparency.cs.princeton.edu/webcensus/#audio-fp
@ -13,6 +30,12 @@ More information on fingerprinting can be found at:
* https://browserleaks.com/rects
* https://github.com/ghacksuserjs/ghacks-user.js/wiki/Appendix-A---Test-Sites
-----
Beta versions can be found at https://canvasblocker.kkapsner.de/versions/.
-----
The different block modes are:
* fake: Canvas Blocker's default setting, and my favorite! All websites not on the white list or black list can use the protected APIs. But values obtained by the APIs are altered so that a consistent fingerprinting is not possible
* ask for permission: If a website is not listed on the white list or black list, the user will be asked if the website should be allowed to use the protected APIs each time they are called.
@ -21,27 +44,22 @@ The different block modes are:
* block only black list: Block the protected APIs only for websites on the black list.
* allow everything: Ignore all lists and allow the protected APIs on all websites.
Protected "fingerprinting" APIs:
* canvas 2d
* webGL
* audio
* history
* window (disabled by default)
* DOMRect
* navigator (disabled by default)
-----
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:
* spodermenpls for finding all the typos
* Thorin-Oakenpants for the icon idea
* anthologist and unbranched for the Italian translation
* Maleficient for the French translation
* yfdyh000 for the Chinese translation
* yfdyh000 and KrasnayaPloshchad for the Chinese translation
* micrococo for the Spanish translation
* STim99 for the Russian translation
Beta versions can be found at https://canvasblocker.kkapsner.de/versions/.
-----
If you want to support this addon you can donate to the following addresses:
* bitcoin: 159Y9BLcfHyrp6wj6f3syEuk92xkRVTiie
* bitcoin cash:qrchnszkdwv9knhg9wjucrqy43rpl4klkq7jhkc8dz
* monero: 482QYZaagALWtPmwbptwBaexDYmcVsJrhJp2VVjTgjYA3Kk1YyMdSg9Wz2qz1Gh31E843PFVCDWS4hR4Bjf6ipWuB9iz2cs
* monero: 482QYZaagALWtPmwbptwBaexDYmcVsJrhJp2VVjTgjYA3Kk1YyMdSg9Wz2qz1Gh31E843PFVCDWS4hR4Bjf6ipWuB9iz2cs

1670
_locales/cs/messages.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
"description": ""
},
"addon_description": {
"message": "Verändert einige JS-APIs um Fingerprinting zu verhindern.",
"message": "Verändert einige JS-APIs, um Fingerprinting zu verhindern.",
"description": ""
},
"browserAction_title_default": {
@ -20,7 +20,15 @@
"description": ""
},
"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": ""
},
"more": {
@ -43,6 +51,10 @@
"message": "OK",
"description": ""
},
"apply": {
"message": "Anwenden",
"description": ""
},
"cancel": {
"message": "Abbrechen",
"description": ""
@ -60,7 +72,7 @@
"description": ""
},
"options_title": {
"message": "CanvasBlocker Einstellungen",
"message": "CanvasBlocker-Einstellungen",
"description": ""
},
"optionsIntroduction": {
@ -79,10 +91,34 @@
"message": "Bei Aktualisierung nicht wieder anzeigen.",
"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": {
"message": "In separatem Tab öffnen",
"description": ""
},
"labelForDefaultOption": {
"message": " (Standard)",
"description": ""
},
"group_general": {
"message": "Allgemein",
"description": ""
},
"group_APIs": {
"message": "APIs",
"description": ""
},
"group_misc": {
"message": "Sonstiges",
"description": ""
},
"section_asking": {
"message": "Nachfragen",
"description": ""
@ -100,7 +136,7 @@
"description": ""
},
"section_misc": {
"message": "Gemischtes",
"message": "Sonstiges",
"description": ""
},
"section_settings": {
@ -127,10 +163,22 @@
"message": "DOMRect-API",
"description": ""
},
"section_SVG-api": {
"message": "SVG-API",
"description": ""
},
"section_TextMetrics-api": {
"message": "TextMetrics-API",
"description": ""
},
"section_Navigator-api": {
"message": "Navigator-API",
"description": ""
},
"section_Screen-api": {
"message": "Screen-API",
"description": ""
},
"displayAdvancedSettings_title": {
"message": "Expertenmodus",
"description": ""
@ -147,6 +195,26 @@
"message": "Zeigt die Beschreibungen der Einstellungen an.",
"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": {
"message": "Hier klicken, um diese Einstellung zu verbergen.",
"description": ""
@ -243,6 +311,30 @@
"message": "Wollen Sie das Auslesen über die DOMRect-API erlauben?",
"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": {
"message": "Wollen Sie die Navigator-API erlauben?",
"description": ""
@ -255,6 +347,18 @@
"message": "Wollen Sie das Auslesen über die Navigator-API erlauben?",
"description": ""
},
"askForScreenPermission": {
"message": "Wollen Sie die Screen-API erlauben?",
"description": ""
},
"askForScreenInputPermission": {
"message": "Wollen Sie das Schreiben über die Screen-API erlauben?",
"description": ""
},
"askForScreenReadoutPermission": {
"message": "Wollen Sie das Auslesen über die Screen-API erlauben?",
"description": ""
},
"askOnlyOnce_title": {
"message": "Nur einmal nachfragen",
"description": ""
@ -404,7 +508,7 @@
"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": ""
},
"rng_options.persistent": {
@ -416,7 +520,7 @@
"description": ""
},
"rng_options.nonPersistent": {
"message": "nicht persistent",
"message": "nichtpersistent",
"description": ""
},
"rng_options.white": {
@ -535,6 +639,38 @@
"message": "Aktiviert das Vortäuschen des Alpha-Kanals (Transparenz).",
"description": ""
},
"webGLVendor_title": {
"message": "Verwendeter webGL \"vendor\"",
"description": ""
},
"webGLVendor_description": {
"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": ""
},
"webGLRenderer_title": {
"message": "Verwendeter webGL \"renderer\"",
"description": ""
},
"webGLRenderer_description": {
"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": ""
},
"webGLUnmaskedVendor_title": {
"message": "Verwendeter webGL \"unmasked vendor\"",
"description": ""
},
"webGLUnmaskedVendor_description": {
"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": ""
},
"webGLUnmaskedRenderer_title": {
"message": "Verwendeter webGL \"unmasked renderer\"",
"description": ""
},
"webGLUnmaskedRenderer_description": {
"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": ""
},
"useCanvasCache_title": {
"message": "Zwischenspeicher für Canvas verwenden",
"description": ""
@ -603,10 +739,22 @@
"message": "DOMRect-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedSVGReadout": {
"message": "SVG-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedTextMetricsReadout": {
"message": "TextMetrics-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedNavigatorReadout": {
"message": "Navigator-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedScreenReadout": {
"message": "Screen-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedInput": {
"message": "Bei Ausgabe vorgetäuscht auf {url}",
"description": ""
@ -627,6 +775,10 @@
"message": "Für die ausgewählten APIs werden keinerlei Benachrichtigungen angezeigt.",
"description": ""
},
"localFile": {
"message": "lokale Datei",
"description": ""
},
"ignorelistDomain": {
"message": "verschweige Domain",
"description": ""
@ -687,6 +839,10 @@
"message": "Was soll erlaubt werden?",
"description": ""
},
"selectWhitelistType": {
"message": "Wie soll erlaubt werden?",
"description": ""
},
"whitelistOnlyAPI": {
"message": "Erlaube nur die {api}",
"description": ""
@ -996,7 +1152,19 @@
"description": ""
},
"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": ""
},
"protectDOMRect_title": {
@ -1019,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.",
"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": {
"message": "Navigator-API beschützen",
"description": ""
@ -1055,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.",
"description": ""
},
"navigatorSettings_contextualIdentities": {
"message": "Es werden die Einstellungen der Tab-Umgebung {select} gezeigt.",
"description": ""
},
"navigatorSettings_presetSection.os": {
"message": "Betriebssystemvoreinstellungen",
"description": ""
@ -1071,6 +1267,42 @@
"message": "Zurücksetzen",
"description": ""
},
"protectScreen_title": {
"message": "Screen-API beschützen",
"description": ""
},
"protectScreen_description": {
"message": "Dies schützt vor Fingerprinting, das die Bildschirmgröße einbezieht.",
"description": ""
},
"protectScreen_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": ""
},
"screenSize_title": {
"message": "Bildschirmgröße",
"description": ""
},
"screenSize_description": {
"message": "Wenn dies auf einen Wert \"...x...\" gesetzt wird, werden diese Größen als Bildschirmgröße verwendet.",
"description": ""
},
"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ünschte Domain oder URL mit einem Klick auf \"+\" hinzu und geben sie den gewünschten Wert ein.",
"description": ""
},
"fakeMinimalScreenSize_title": {
"message": "Minimale Bildschirmgröße vortäuschen",
"description": ""
},
"fakeMinimalScreenSize_description": {
"message": "Verwende die minimale Bildschirmgröße aus der folgenden Liste, die zur inneren Browserfenstergröße passt. Bildschirmgrößen: 1366x768, 1440x900, 1600x900, 1920x1080, 4096x2160, 8192x4320",
"description": ""
},
"fakeMinimalScreenSize_urlSpecific": {
"message": "Um bestimmte Seiten von dem Vortäuschen 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": ""
},
"theme_title": {
"message": "Theme",
"description": ""
@ -1167,15 +1399,31 @@
"message": "",
"description": ""
},
"openSettingSanitation_title": {
"settingControlling_title": {
"message": "Einstellungsüberprüfung",
"description": ""
},
"openSettingSanitation_description": {
"settingControlling_description": {
"message": "",
"description": ""
},
"openSettingSanitation_label": {
"message": "Einstellungsüberprüfung",
"description": ""
},
"inspectWhitelist_label": {
"message": "Erlaubnisse ansehen",
"description": ""
},
"openSettingPresets_title": {
"message": "Voreinstellungen",
"description": ""
},
"openSettingPresets_description": {
"message": "Dies öffnet die Voreinstellungsseite, die bei der Installation angezeigt wurde. Die Voreinstellungen sind Sammlungen von Einstellungswerten, die häufigen Nutzungsprofilen von CanvasBlocker entsprechen.",
"description": ""
},
"openSettingPresets_label": {
"message": "Öffnen",
"description": ""
},
@ -1191,10 +1439,6 @@
"message": "Laden",
"description": ""
},
"inspectWhitelist_label": {
"message": "Erlaubnisse ansehen",
"description": ""
},
"resetSettings_title": {
"message": "Einstellungen zurücksetzen",
"description": ""
@ -1215,6 +1459,10 @@
"message": "Einstellungen",
"description": ""
},
"browserAction_faq": {
"message": "FAQ",
"description": ""
},
"browserAction_test": {
"message": "Testen",
"description": ""
@ -1275,6 +1523,10 @@
"message": "Alle Funktionen der {api} sind deaktiviert, aber der Schutz ist eingeschaltet.",
"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": {
"message": "Hauptschalter deaktivieren",
"description": ""
@ -1308,7 +1560,7 @@
"description": ""
},
"sanitation_resolution.switchToNonPersistentRng": {
"message": "wechsle zu \"nicht persistent\"",
"message": "wechsle zu \"nichtpersistent\"",
"description": ""
},
"sanitation_error.fakeEverythingInCanvas": {
@ -1343,12 +1595,76 @@
"message": "Teilen Sie die persistenten Zufallszahlen nicht zwischen Domains, da dies den Browser 100% eindeutig identifizierbar macht.",
"description": ""
},
"sanitation_error.customScreenSize": {
"message": "Verwenden Sie keine benutzerdefinierte Bildschirmgröße, da sie den Browser verfolgbarer macht.",
"description": ""
},
"whitelist_inspection_title": {
"message": "CanvasBlocker Erlaubnisse ansehen",
"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": {
"message": "Alle APIs",
"description": ""
},
"presets": {
"message": "Voreinstellungen",
"description": ""
},
"presets_title": {
"message": "CanvasBlocker Voreinstellungen",
"description": ""
},
"presets_installNotice": {
"message": "{image:../icons/icon.svg}Danke, dass Sie CanvasBlocker installiert haben. Um ihn an Ihre Bedürfnisse anzupassen können Sie ein paar Voreinstellungen anwenden. In den {link:Optionen:options.html} können Sie CanvasBlocker noch weiter anpassen. Dort können Sie auch diese Voreinstellungsseite später wieder öffnen.",
"description": ""
},
"presets_introduction": {
"message": "Dies sind ein paar Voreinstellungen für verschiedene Bedürfnisse. Die Werte in Klammern sind die aktuellen Einstellungen. Sie können mehrere Voreinstellungen hintereinander anwenden, aber ein paar Einstellungen können dabei von der später angewandten Voreinstellung überschrieben werden.",
"description": ""
},
"preset_default_title": {
"message": "Standard-Einstellungen",
"description": ""
},
"preset_default_description": {
"message": "Keine speziellen Einstellungen werden gesetzt.",
"description": ""
},
"preset_convenience_title": {
"message": "Praktische Einstellungen",
"description": ""
},
"preset_convenience_description": {
"message": "Setzt einige Einstellungen so, dass CanvasBlocker die Browsererfahrung so wenig wie möglich beeinträchtigt. Dies beinhaltet, dass die Benutzung mancher APIs für ein paar Seiten erlaubt wird.",
"description": ""
},
"preset_stealth_title": {
"message": "Schwer zu detektieren",
"description": ""
},
"preset_stealth_description": {
"message": "Stellt CanvasBlocker so ein, dass er schwer zu detektieren ist. Dies kann zu hoher CPU-Auslastung führen und den Browser verlangsamen.",
"description": ""
},
"preset_max_protection_title": {
"message": "Maximaler Schutz",
"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. 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": ""
}
}
}

View File

@ -24,6 +24,14 @@
"message": "\n \u00B7 {api}",
"description": ""
},
"browserAction_status_on": {
"message": "CanvasBlocker on",
"description": ""
},
"browserAction_status_off": {
"message": "CanvasBlocker off",
"description": ""
},
"more": {
"message": "more",
@ -45,6 +53,10 @@
"message": "OK",
"description": ""
},
"apply": {
"message": "Apply",
"description": ""
},
"cancel": {
"message": "Cancel",
"description": ""
@ -80,15 +92,41 @@
"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": ""
},
"dontShowOptionsOnUpdate":{
"dontShowOptionsOnUpdate": {
"message": "Don't show up again after update.",
"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": {
"message": "Open in separate tab",
"description": ""
},
"labelForDefaultOption": {
"message": " (default)",
"description": ""
},
"group_general": {
"message": "General",
"description": ""
},
"group_APIs": {
"message": "APIs",
"description": ""
},
"group_misc": {
"message": "Misc",
"description": ""
},
"section_asking": {
"message": "Asking",
"description": ""
@ -113,30 +151,42 @@
"message": "Settings",
"description": ""
},
"section_canvas-api":{
"section_canvas-api": {
"message": "Canvas API",
"description": ""
},
"section_audio-api":{
"section_audio-api": {
"message": "Audio API",
"description": ""
},
"section_history-api":{
"section_history-api": {
"message": "History API",
"description": ""
},
"section_window-api":{
"section_window-api": {
"message": "Window API",
"description": ""
},
"section_DOMRect-api":{
"section_DOMRect-api": {
"message": "DOMRect API",
"description": ""
},
"section_Navigator-api":{
"section_SVG-api": {
"message": "SVG API",
"description": ""
},
"section_TextMetrics-api": {
"message": "TextMetrics API",
"description": ""
},
"section_Navigator-api": {
"message": "Navigator API",
"description": ""
},
"section_Screen-api": {
"message": "Screen API",
"description": ""
},
"displayAdvancedSettings_title": {
"message": "Expert mode",
@ -156,6 +206,28 @@
"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": {
"message": "Click here to hide this setting.",
"description": ""
@ -254,6 +326,30 @@
"message": "Do you want to allow DOMRect API readout?",
"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": {
"message": "Do you want to allow the navigator API?",
"description": ""
@ -266,12 +362,24 @@
"message": "Do you want to allow navigator API readout?",
"description": ""
},
"askForScreenPermission": {
"message": "Do you want to allow the screen API?",
"description": ""
},
"askForScreenInputPermission": {
"message": "Do you want to allow screen API input?",
"description": ""
},
"askForScreenReadoutPermission": {
"message": "Do you want to allow screen API readout?",
"description": ""
},
"askOnlyOnce_title": {
"message": "Ask only once",
"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": ""
},
"askOnlyOnce_options.no": {
@ -286,32 +394,32 @@
"message": "combined",
"description": ""
},
"askDenyMode_title":{
"askDenyMode_title": {
"message": "Ask deny mode",
"description": ""
},
"askDenyMode_description":{
"askDenyMode_description": {
"message": "Which mode shall be used when the permission is denied.",
"description": ""
},
"askDenyMode_options.block":{
"askDenyMode_options.block": {
"message": "block",
"description": ""
},
"askDenyMode_options.fake":{
"askDenyMode_options.fake": {
"message": "fake",
"description": ""
},
"showCanvasWhileAsking_title":{
"showCanvasWhileAsking_title": {
"message": "Show canvas content",
"description": ""
},
"showCanvasWhileAsking_description":{
"showCanvasWhileAsking_description": {
"message": "Shows the content of the canvas for which the permission is asked for, if possible.",
"description": ""
},
"showCanvasWhileAsking_message":{
"showCanvasWhileAsking_message": {
"message": "The webpage wants to read the content of the following canvas:",
"description": ""
},
@ -392,7 +500,7 @@
},
"urlSettings_title": {
"message": "Site specific values",
"message": "Site-specific values",
"description": ""
},
"urlSettings_description": {
@ -424,7 +532,7 @@
},
"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": ""
},
"rng_options.persistent": {
@ -436,7 +544,7 @@
"description": ""
},
"rng_options.nonPersistent": {
"message": "non persistent",
"message": "nonpersistent",
"description": ""
},
"rng_options.white": {
@ -564,6 +672,39 @@
"description": ""
},
"webGLVendor_title": {
"message": "Reported webGL vendor",
"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": "Reported webGL renderer",
"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": "Reported webGL unmasked vendor",
"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": "Reported webGL unmasked renderer",
"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": {
"message": "Use canvas cache",
"description": ""
@ -635,10 +776,22 @@
"message": "Faked DOMRect readout on {url}",
"description": ""
},
"fakedSVGReadout": {
"message": "Faked SVG readout on {url}",
"description": ""
},
"fakedTextMetricsReadout": {
"message": "Faked TextMetrics readout on {url}",
"description": ""
},
"fakedNavigatorReadout": {
"message": "Faked navigator readout on {url}",
"description": ""
},
"fakedScreenReadout": {
"message": "Faked screen readout on {url}",
"description": ""
},
"fakedInput": {
"message": "Faked at input on {url}",
"description": ""
@ -661,6 +814,10 @@
"message": "No notifications will be displayed for the selected APIs.",
"description": ""
},
"localFile": {
"message": "local file",
"description": ""
},
"ignorelistDomain": {
"message": "silence domain",
"description": ""
@ -721,6 +878,10 @@
"message": "What is the scope of the whitelisting?",
"description": ""
},
"selectWhitelistType": {
"message": "What is the type of the whitelisting?",
"description": ""
},
"whitelistOnlyAPI": {
"message": "Whitelist only the {api}",
"description": ""
@ -1037,7 +1198,20 @@
"description": ""
},
"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": ""
},
@ -1063,6 +1237,32 @@
"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": {
"message": "Protect navigator API",
"description": ""
@ -1101,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.",
"description": ""
},
"navigatorSettings_contextualIdentities": {
"message": "Settings for the container {select} are shown.",
"description": ""
},
"navigatorSettings_presetSection.os": {
"message": "Operating system presets",
"description": ""
@ -1118,6 +1322,43 @@
"description": ""
},
"protectScreen_title": {
"message": "Protect screen API",
"description": ""
},
"protectScreen_description": {
"message": "This protects against fingerprinting attempts including the screen size.",
"description": ""
},
"protectScreen_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": ""
},
"screenSize_title": {
"message": "Screen size",
"description": ""
},
"screenSize_description": {
"message": "If this is set with a value \"...x...\" the specified dimensions will be reported as the screen size.",
"description": ""
},
"screenSize_urlSpecific": {
"message": "To provide specific sizes for certain websites, click on the black arrow to open the menu, add the domain or URL by clicking on \"+\" and enter the desired value.",
"description": ""
},
"fakeMinimalScreenSize_title": {
"message": "Fake minimal screen size",
"description": ""
},
"fakeMinimalScreenSize_description": {
"message": "Use a minimal screen size from the following set that can fit the inner window dimensions. Screen sizes: 1366x768, 1440x900, 1600x900, 1920x1080, 4096x2160, 8192x4320",
"description": ""
},
"fakeMinimalScreenSize_urlSpecific": {
"message": "To exclude specific websites from the faking, click on the black arrow to open the menu, add the domain or URL by clicking on \"+\" and remove its checkmark.",
"description": ""
},
"theme_title": {
"message": "Theme",
"description": ""
@ -1219,15 +1460,32 @@
"description": ""
},
"openSettingSanitation_title": {
"message": "Setting sanitation",
"settingControlling_title": {
"message": "Setting controlling",
"description": ""
},
"openSettingSanitation_description": {
"settingControlling_description": {
"message": "",
"description": ""
},
"openSettingSanitation_label": {
"message": "Setting sanitation",
"description": ""
},
"inspectWhitelist_label": {
"message": "Inspect whitelist",
"description": ""
},
"openSettingPresets_title": {
"message": "Presets",
"description": ""
},
"openSettingPresets_description": {
"message": "This opens the preset page that was shown upon installation. The presets are collections of setting values for some common use cases of CanvasBlocker.",
"description": ""
},
"openSettingPresets_label": {
"message": "Open",
"description": ""
},
@ -1244,10 +1502,6 @@
"message": "Load",
"description": ""
},
"inspectWhitelist_label": {
"message": "Inspect whitelist",
"description": ""
},
"resetSettings_title": {
"message": "Reset settings",
@ -1270,6 +1524,10 @@
"message": "Settings",
"description": ""
},
"browserAction_faq": {
"message": "FAQ",
"description": ""
},
"browserAction_test": {
"message": "Test",
"description": ""
@ -1331,6 +1589,10 @@
"message": "All features of {api} are disabled but the protection is enabled.",
"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": {
"message": "disable main flag",
"description": ""
@ -1364,7 +1626,7 @@
"description": ""
},
"sanitation_resolution.switchToNonPersistentRng": {
"message": "switch to \"non persistent\" rng",
"message": "switch to \"nonpersistent\" rng",
"description": ""
},
"sanitation_error.fakeEverythingInCanvas": {
@ -1399,13 +1661,79 @@
"message": "Do not share persistent randomness between domains because this makes the browser 100% trackable.",
"description": ""
},
"sanitation_error.customScreenSize": {
"message": "Do not use a custom screen size as it makes the browser more trackable.",
"description": ""
},
"whitelist_inspection_title": {
"message": "CanvasBlocker whitelist inspection",
"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": {
"message": "All APIs",
"description": ""
},
"presets": {
"message": "Presets",
"description": ""
},
"presets_title": {
"message": "CanvasBlocker presets",
"description": ""
},
"presets_installNotice": {
"message": "{image:../icons/icon.svg}Thanks for installing CanvasBlocker. To adjust it to your needs you can apply some presets. For further customization you can open the {link:options:options.html} page. There you can also open this preset page later.",
"description": ""
},
"presets_introduction": {
"message": "These are some setting presets to fit different people's needs. The values in brackets are the current state of the setting. You can apply multiple presets after each other but some settings might be overwritten by the later applied one.",
"description": ""
},
"preset_default_title": {
"message": "Default settings",
"description": ""
},
"preset_default_description": {
"message": "No special settings are applied.",
"description": ""
},
"preset_convenience_title": {
"message": "Convenient settings",
"description": ""
},
"preset_convenience_description": {
"message": "Apply some settings to make the browsing experience as convenient as possible. This includes whitelisting some sites.",
"description": ""
},
"preset_stealth_title": {
"message": "Stealth settings",
"description": ""
},
"preset_stealth_description": {
"message": "Configures CanvasBlocker to be hard to detect. This might lead to high CPU usage and might slow down the browser.",
"description": ""
},
"preset_max_protection_title": {
"message": "Maximum protection",
"description": ""
},
"preset_max_protection_description": {
"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": ""
}
}

File diff suppressed because it is too large Load Diff

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

@ -19,4 +19,53 @@ div {
.action.search {
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

@ -4,16 +4,23 @@
<title>CanvasBlocker browser action</title>
<link href="browserAction.css" rel="stylesheet" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="headerActions">
<button id="addonStatus" class="undefined"></button>
<div id="reload" class="hidden"></div>
</div>
<div id="actions" class="stackedInputs"></div>
<script src="../lib/require.js"></script>
<script src="../lib/extension.js"></script>
<div id="version" class="versionDisplay"></div>
<script src="../lib/require.js"></script>
<script src="../lib/logging.js"></script>
<script src="../lib/settingDefinitions.js"></script>
<script src="../lib/settingContainers.js"></script>
<script src="../lib/settings.js"></script>
<script src="../lib/theme.js"></script>
<script src="../lib/extension.js"></script>
<script src="../lib/settingDefinitions.js"></script>
<script src="../lib/settingContainers.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>
</body>
</html>

View File

@ -7,72 +7,147 @@
const extension = require("../lib/extension");
const logging = require("../lib/logging");
const settings = require("../lib/settings");
const settingContainers = require("../lib/settingContainers");
const lists = require("../lib/lists");
require("../lib/theme").init();
logging.message("Opened browser action");
settings.onloaded(function(){
var actions = document.getElementById("actions");
browser.tabs.query({active: true, currentWindow: true}).then(async function([currentTab]){
function isWhitelisted(url){
if (!(url instanceof URL)){
url = new URL(url);
}
return lists.get("white").match(url) ||
settings.get("blockMode", url).startsWith("allow");
}
[
{
label: "settings",
icon: browser.extension.getURL("icons/pageAction-showOptions.svg"),
action: function(){
if (browser.runtime && browser.runtime.openOptionsPage){
browser.runtime.openOptionsPage();
}
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);
}
},
{
label: "test",
advanced: true,
icon: browser.extension.getURL("icons/browserAction-test.svg"),
action: function(){
window.open("https://canvasblocker.kkapsner.de/test", "_blank");
if (settings.get("blockDataURLs")){
settingContainers.resetUrlValue("blockDataURLs", currentURL);
}
},
{
label: "review",
icon: browser.extension.getURL("icons/browserAction-review.svg"),
action: function(){
window.open("https://addons.mozilla.org/firefox/addon/canvasblocker/reviews/", "_blank");
const entries = lists.get("white").filter(e => e.match(currentURL)).map(e => e.value);
await Promise.all([
lists.removeFrom("white", entries),
lists.removeFrom("sessionWhite", entries)
]);
}
else {
settings.set("blockMode", "allowEverything", currentURL.hostname);
if (settings.get("blockDataURLs")){
settings.set("blockDataURLs", false, currentURL.hostname);
}
},
{
label: "reportIssue",
icon: browser.extension.getURL("icons/browserAction-reportIssue.svg"),
action: function(){
window.open("https://github.com/kkapsner/CanvasBlocker/issues", "_blank");
}
update();
});
function update(){
if (isWhitelisted(currentURL)){
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();
}
},
].forEach(function(action){
else {
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);
if (action.advanced && !settings.displayAdvancedSettings){
logging.verbose("Hiding advanced action");
return;
}
var actionButton = document.createElement("button");
const actionButton = document.createElement("button");
actionButton.className = "action";
var icon = document.createElement("span");
const icon = document.createElement("span");
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(
document.createTextNode(
extension.getTranslation("browserAction_" + action.label) || action.label
)
);
actionButton.addEventListener("click", action.action);
const textNode = document.createTextNode("");
function setLabel(label){
textNode.nodeValue = extension.getTranslation("browserAction_" + label) || label;
}
setLabel(action.label);
actionButton.appendChild(textNode);
actionButton.addEventListener("click", function(){
action.action.call(this, {setIcon, setLabel});
});
actions.appendChild(actionButton);
});
var search = document.createElement("input");
const search = document.createElement("input");
search.placeholder = extension.getTranslation("search");
search.className = "search action";
actions.appendChild(search);
@ -80,13 +155,17 @@
search.addEventListener("keypress", function(event){
if ([10, 13].indexOf(event.keyCode) !== -1){
window.open(browser.extension.getURL(
browser.tabs.create({url: extension.getURL(
"options/options.html" +
"?search=" +
encodeURIComponent(this.value)
));
)});
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"
id="path2985"
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>
</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"
id="layer1"
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
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"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -52,18 +52,6 @@
inkscape:groupmode="layer"
id="layer1"
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
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"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -52,17 +52,6 @@
inkscape:groupmode="layer"
id="layer1"
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
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"

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -52,17 +52,6 @@
inkscape:groupmode="layer"
id="layer1"
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
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"

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -52,18 +52,6 @@
inkscape:groupmode="layer"
id="layer1"
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
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"

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -52,17 +52,6 @@
inkscape:groupmode="layer"
id="layer1"
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
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"

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -15,15 +15,15 @@
// Check canvas appearance
function canvasAppearance(window, api, context){
var oldBorder = false;
var canvas = false;
var inDOM = null;
let oldBorder = false;
let canvas = false;
let inDOM = null;
if (api === "canvas" && context){
var nodeName;
let nodeName;
try {
nodeName = context.nodeName;
}
catch (e){
catch (error){
nodeName = "";
}
if (nodeName === "CANVAS"){
@ -45,17 +45,17 @@
canvas: canvas,
askCategory: canvas? (inDOM? "visible": "invisible"): (api === "canvas"? "nocanvas": api),
get text(){
var text = canvas? (this.visible? "visible": "invisible"): (api === "canvas"? "nocanvas": api);
const text = canvas? (this.visible? "visible": "invisible"): (api === "canvas"? "nocanvas": api);
Object.defineProperty(this, "text", {value: text});
return text;
},
inDom: inDOM,
get visible(){
var visible = inDOM;
let visible = inDOM;
if (inDOM){
canvas.scrollIntoView();
var rect = canvas.getBoundingClientRect();
var foundEl = window.document.elementFromPoint(
const rect = canvas.getBoundingClientRect();
const foundEl = window.document.elementFromPoint(
rect.left + rect.width / 2,
rect.top + rect.height / 2
);
@ -72,9 +72,9 @@
};
}
var modes = new WeakMap();
const modes = new WeakMap();
function getAskMode(window, type, _){
var mode = modes.get(window);
let mode = modes.get(window);
if (mode){
return mode[type];
}
@ -88,7 +88,8 @@
audio: _("askForAudioPermission"),
history: _("askForHistoryPermission"),
window: _("askForWindowPermission"),
domRect: _("askForDOMRectPermission")
domRect: _("askForDOMRectPermission"),
svg: _("askForSVGPermission"),
},
askStatus: {
alreadyAsked: {},
@ -103,7 +104,8 @@
audio: _("askForAudioInputPermission"),
history: _("askForHistoryInputPermission"),
window: _("askForWindowInputPermission"),
domRect: _("askForDOMRectInputPermission")
domRect: _("askForDOMRectInputPermission"),
svg: _("askForSVGInputPermission"),
},
askStatus: {
alreadyAsked: {},
@ -118,7 +120,8 @@
audio: _("askForAudioReadoutPermission"),
history: _("askForHistoryReadoutPermission"),
window: _("askForWindowReadoutPermission"),
domRect: _("askForDOMRectReadoutPermission")
domRect: _("askForDOMRectReadoutPermission"),
svg: _("askForSVGReadoutPermission"),
},
askStatus: {
alreadyAsked: {},
@ -132,11 +135,11 @@
}
scope.ask = function({window, type, api, canvas, errorStack}, {_, prefs}){
var answer;
var askMode = getAskMode(window, type, _);
var askStatus = askMode.askStatus;
var appearance = canvasAppearance(window, api, canvas);
var category = appearance.askCategory;
let answer;
const askMode = getAskMode(window, type, _);
const askStatus = askMode.askStatus;
const appearance = canvasAppearance(window, api, canvas);
let category = appearance.askCategory;
if (prefs("askOnlyOnce") !== "no" && askStatus.alreadyAsked[category]){
// already asked
appearance.reset();
@ -146,7 +149,6 @@
let imgContainer = null;
if (type === "readout" && prefs("showCanvasWhileAsking") && canvas){
try {
let content = canvas.toDataURL();
let document = window.top.document;
imgContainer = document.createElement("div");
imgContainer.style.cssText = `
@ -177,12 +179,12 @@
imgContainer.appendChild(img);
document.body.appendChild(imgContainer);
}
catch (e){
catch (error){
// unable to read the canvas
}
}
// asking
var msg = askMode.askText[appearance.text];
let msg = askMode.askText[appearance.text];
// visible vs invisible is only calculated here correctly
category = appearance.text;
@ -196,8 +198,8 @@
if (prefs("askOnlyOnce") === "combined"){
["context", "readout", "input"].forEach(function(type){
var askMode = getAskMode(window, type, _);
var askStatus = askMode.askStatus;
const askMode = getAskMode(window, type, _);
const askStatus = askMode.askStatus;
askStatus.alreadyAsked[category] = true;
askStatus.answer[category] = answer;
});

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -16,12 +16,12 @@
const extension = require("./extension");
// Translation
var _ = function(name, replace, translateAPI){
const _ = function(name, replace, translateAPI){
if (!translateAPI){
translateAPI = extension.getTranslation;
}
var str = translateAPI(name) || name;
let str = translateAPI(name) || name;
if (replace){
// replace generic content in the translation by given parameter
Object.keys(replace).forEach(function(name){
@ -33,7 +33,7 @@
// Stack parsing
function parseStackEntry(entry){
var m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"];
const m = /@(.*):(\d*):(\d*)$/.exec(entry) || ["", entry, "--", "--"];
return {
url: m[1],
line: parseInt(m[2], 10),
@ -61,13 +61,12 @@
// parse calling stack
const extensionID = extension.extensionID;
function parseErrorStack(errorStack){
var callers = errorStack.trim().split("\n");
callers = callers.map(parseStackEntry).filter(function(caller){
const callers = errorStack.trim().split("\n").map(parseStackEntry).filter(function(caller){
return !caller.url.startsWith(extensionID);
});
return {
toString: function(translateAPI){
var msg = "";
let msg = "";
msg += "\n\n" + _("sourceOutput", undefined, translateAPI) + ": ";
if (settings.showCompleteCallingStack){
msg += callers.reduce(function(stack, c){
@ -82,7 +81,7 @@
},
match: function(stackRule){
if (typeof stackRule.stackPosition !== "undefined"){
var pos = stackRule.stackPosition;
let pos = stackRule.stackPosition;
if (pos < 0){
pos += callers.length;
}

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -19,7 +19,7 @@
scope.check = function check({url, errorStack}){
url = new URL(url || "about:blank");
var match = checkBoth(errorStack, url, settings.get("blockMode", url)).match(
const match = checkBoth(errorStack, url, settings.get("blockMode", url)).match(
/^(block|allow|fake|ask)(|Everything|Internal)$/
);
if (match){
@ -52,7 +52,7 @@
logging.message("check url %s for block mode %s", url, blockMode);
switch (url.protocol){
case "about:":
if (url.href === "about:blank"){
if (url.pathname === "blank"){
logging.message("use regular mode on about:blank");
break;
}
@ -63,7 +63,7 @@
return "allowInternal";
}
var mode = "block";
let mode = "block";
switch (blockMode){
case "blockEverything":
mode = "block";

View File

@ -5,7 +5,7 @@
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -23,8 +23,8 @@
this.minBoundary.nextColor = this.maxBoundary;
}
addColor(r, g, b, a){
var index = String.fromCharCode(r, g, b, a);
var color = this.colors[index];
const index = String.fromCharCode(r, g, b, a);
let color = this.colors[index];
if (!color){
color = {
index,
@ -42,10 +42,10 @@
if (color.count > color.nextColor.count){
// swap colors to remain in right order
// a_ -> b_ -> c -> d becomes a_ -> c -> b_ -> d
var a_ = color.previousColor;
var b_ = color;
var c = color.nextColor;
var d = color.nextColor.nextColor;
const a_ = color.previousColor;
const b_ = color;
const c = color.nextColor;
const d = color.nextColor.nextColor;
a_.nextColor = c;
c.previousColor = a_;
@ -59,8 +59,8 @@
}
getMaxColors(n){
n = Math.min(n, this.numberOfColors);
var colors = Object.create(null);
var current = this.maxBoundary;
const colors = Object.create(null);
let current = this.maxBoundary;
for (;n && current;n -= 1){
current = current.previousColor;
colors[current.index] = current;
@ -70,8 +70,8 @@
}
scope.compute = function computeColorStatistics(rawData){
var statistic = new Statistic();
for (var i = 0, l = rawData.length; i < l; i += 4){
const statistic = new Statistic();
for (let i = 0, l = rawData.length; i < l; i += 4){
statistic.addColor(
rawData[i + 0],
rawData[i + 1],
@ -86,10 +86,10 @@
return statistic.numberOfColors > threshold;
}
else {
var colors = Object.create(null);
var count = 0;
for (var i = 0, l = rawData.length; i < l; i += 4){
var index = String.fromCharCode(
const colors = Object.create(null);
let count = 0;
for (let i = 0, l = rawData.length; i < l; i += 4){
const index = String.fromCharCode(
rawData[i + 0],
rawData[i + 1],
rawData[i + 2],

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -21,6 +21,10 @@
const mainVersion = parseInt(info.version.replace(/\..+/, ""), 10);
canMergeHeader = mainVersion > 59;
blockBlob = mainVersion < 60;
return canMergeHeader;
}).catch(function(){
canMergeHeader = false;
blockBlob = true;
});
function setHeader(headers, header){
if (canMergeHeader){
@ -43,7 +47,10 @@
scope.init = function(){
function listener(details){
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: *";
logging.verbose("Adding CSP header to", details);
setHeader(headers, {

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -13,6 +13,7 @@
}
const browserAvailable = typeof browser !== "undefined";
const logging = require("./logging");
scope.inBackgroundScript = !!(
browserAvailable &&
@ -26,7 +27,56 @@
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;
@ -44,5 +94,237 @@
};
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){
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);
}());

View File

@ -9,18 +9,16 @@
const {ask} = require("./askForPermission");
const {sha256String: hashing} = require("./hash");
const {check: originalCheck, checkStack: originalCheckStack} = require("./check");
const {getWrapped} = require("./modifiedAPIFunctions");
const extension = require("./extension");
const iframeProtection = require("./iframeProtection");
const logging = require("./logging");
const {error, warning, message, notice, verbose, setPrefix: setLogPrefix} = logging;
setLogPrefix("frame script");
logging.setPrefix("frame script");
// Variable to "unload" the script
var enabled = true;
let enabled = true;
message("starting", location.href);
logging.message("starting", location.href);
function check(message){
if (enabled){
@ -72,38 +70,40 @@
}
computeExtensionSecret();
message("open port to background script");
var port = browser.runtime.connect();
logging.message("open port to background script");
const port = browser.runtime.connect();
if (window === window.top){
message("Is top level window -> tab had navigation -> clear page action");
logging.message("Is top level window -> tab had navigation -> clear page action");
port.postMessage({"canvasBlocker-clear-page-action": true});
}
var tabId;
let tabId;
port.onMessage.addListener(function(data){
message("Got data from port", data);
logging.message("Got data from port", data);
if (data.hasOwnProperty("tabId")){
notice("my tab id is", data.tabId);
logging.notice("my tab id is", data.tabId);
tabId = data.tabId;
}
if (data.hasOwnProperty("cookieStoreId")){
notice("my tab cookie store id is", data.cookieStoreId);
logging.notice("my tab cookie store id is", data.cookieStoreId);
const {persistent: persistentRnd} = require("./randomSupplies");
persistentRnd.setCookieStoreId(data.cookieStoreId);
const modifiedNavigatorAPI = require("./modifiedNavigatorAPI");
modifiedNavigatorAPI.setCookieStoreId(data.cookieStoreId);
}
const persistentRndName = "persistent" + (extension.inIncognitoContext? "Incognito": "") + "Rnd";
if (data.hasOwnProperty(persistentRndName)){
const persistentRndValue = data[persistentRndName];
notice("got persistent random data", persistentRndValue);
logging.notice("got persistent random data", persistentRndValue);
const {persistent: persistentRnd} = require("./randomSupplies");
Object.keys(persistentRndValue).forEach(function(domain){
verbose("random data for", domain, persistentRndValue[domain]);
logging.verbose("random data for", domain, persistentRndValue[domain]);
persistentRnd.setDomainRnd(domain, persistentRndValue[domain]);
});
}
});
var notifications = [];
var notificationCounter = {};
var sentAPIs = {};
const notifications = [];
const notificationCounter = {};
const sentAPIs = {};
function notify(data){
if (!settings.ignoredAPIs[data.api]){
if (settings.storeNotificationData){
@ -127,17 +127,17 @@
return settings.get(...args);
}
var interceptedWindows = new WeakMap();
const interceptedWindows = new WeakMap();
function interceptWindow(window){
let wrappedTry;
try {
var href = window.location.href;
var wrappedTry = getWrapped(window);
const href = window.location.href;
wrappedTry = extension.getWrapped(window);
}
catch (e){
catch (error){
// we are unable to read the location due to SOP
// therefore we also can not intercept anything.
warning("NOT intercepting window due to SOP", window);
logging.notice("NOT intercepting window due to SOP", window);
return false;
}
const wrappedWindow = wrappedTry;
@ -145,17 +145,23 @@
if (!enabled || interceptedWindows.get(wrappedWindow)){
return false;
}
if (wrappedWindow.matchMedia(extensionSecret[0]) === extensionSecret[1]){
interceptedWindows.set(wrappedWindow, true);
return false;
const canvasBlockerData = wrappedWindow.matchMedia(extensionSecret[0]);
if (canvasBlockerData.secret === extensionSecret[1]){
if (wrappedWindow.top === wrappedWindow){
canvasBlockerData.undoIntercept(extension.extensionID);
}
else {
interceptedWindows.set(wrappedWindow, true);
return false;
}
}
message("intercepting window", window);
logging.message("intercepting window", window);
intercept(
{subject: window},
{check, checkStack, ask: askWrapper, notify, prefs}
);
message("prepare to intercept (i)frames.");
logging.message("prepare to intercept (i)frames.");
function interceptAllFrames(){
const currentLength = window.length;
@ -169,39 +175,54 @@
const matchMediaDescriptor = Object.getOwnPropertyDescriptor(wrappedWindow, "matchMedia");
const originalMatchMedia = matchMediaDescriptor.value;
matchMediaDescriptor.value = exportFunction(function matchMedia(query){
if (query === extensionSecret[0]){
return extensionSecret[1];
}
else {
return arguments.length > 1?
originalMatchMedia.apply(this, wrappedWindow.Array.from(arguments)):
originalMatchMedia.call(this, query);
}
}, window);
Object.defineProperty(wrappedWindow, "matchMedia", matchMediaDescriptor);
extension.changeProperty(window, "matchMedia", {
object: wrappedWindow,
name: "matchMedia",
type: "value",
changed: extension.exportFunctionWithName(function matchMedia(query){
if (query === extensionSecret[0]){
return {
secret: extensionSecret[1],
undoIntercept: function(token){
if (token === extension.extensionID){
extension.revertProperties(window);
}
}
};
}
else {
return arguments.length > 1?
originalMatchMedia.call(this, ...arguments):
originalMatchMedia.call(this, query);
}
}, window, originalMatchMedia.name)
});
interceptedWindows.set(wrappedWindow, true);
return true;
}
message("register listener for messages from background script");
logging.message("register listener for messages from background script");
extension.message.on(function(data){
if (data["canvasBlocker-unload"]){
extension.revertProperties(window);
for (let frameIndex = 0; frameIndex < window.length; frameIndex += 1){
extension.revertProperties(window[frameIndex]);
}
enabled = false;
}
if (
data.hasOwnProperty("canvasBlocker-sendNotifications") &&
data["canvasBlocker-sendNotifications"] === tabId
){
notice("sending notifications:", notifications);
logging.notice("sending notifications:", notifications);
extension.message.send({
sender: tabId,
url: window.location.href,
"canvasBlocker-notificationCounter": notificationCounter,
"canvasBlocker-notifications": notifications
});
notice("notifications sent");
logging.notice("notifications sent");
}
});

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -20,11 +20,11 @@
};
scope.sumXor = function(inputByteArray){
var hash = new Uint32Array(4);
var sum = new Float64Array(hash.buffer, 8, 1);
var intView = new Uint32Array(inputByteArray.buffer);
var floatView = new Float32Array(inputByteArray.buffer);
var length = intView.length;
const hash = new Uint32Array(4);
// const sum = new Float64Array(hash.buffer, 8, 1);
const intView = new Uint32Array(inputByteArray.buffer);
// const floatView = new Float32Array(inputByteArray.buffer);
const length = intView.length;
for (let i = 0; i < length; i += 1){
// sum[0] += floatView[i];
hash[0] ^= intView[i];
@ -34,11 +34,11 @@
};
scope.hashCode = function(inputByteArray){
var hash = new Uint32Array(1);
var intView = new Uint32Array(inputByteArray.buffer);
var length = intView.length;
const hash = new Uint32Array(1);
const intView = new Uint32Array(inputByteArray.buffer);
const length = intView.length;
for (let i = 0; i < length; i += 1){
var v = hash[0];
const v = hash[0];
hash[0] = ((v << 5) - v) + intView[i];
}
return hash;
@ -81,21 +81,21 @@
return function md5(inputByteArray){
h.set(hInitial);
var length = inputByteArray.buffer.byteLength;
var messageBitLength = length * 8;
const length = inputByteArray.buffer.byteLength;
const messageBitLength = length * 8;
// create byte array with length dividable by 64 (512 bit)
var neededLength = Math.ceil((length + 1 + 8) / 64) * 64;
var messageByteArray = new Uint8Array(neededLength);
const neededLength = Math.ceil((length + 1 + 8) / 64) * 64;
const messageByteArray = new Uint8Array(neededLength);
messageByteArray.set(new Uint8Array(inputByteArray.buffer));
var view = new DataView(messageByteArray.buffer);
const view = new DataView(messageByteArray.buffer);
// append 10...000000
messageByteArray[length] = 0x80;
// append size in 64 bit big endian
view.setUint32(neededLength - 8, messageBitLength, true);
for (var i = 0; i < neededLength; i += 64){
for (let i = 0; i < neededLength; i += 64){
for (let j = 0; j < 64; j += 4){
w[j / 4] = view.getUint32(i + j, true);
}
@ -118,7 +118,7 @@
temp[4] = (temp[2] ^ (temp[1] | (~ temp[3])));
temp[5] = (7*j) % 16;
}
var temp_ = temp[3];
const temp_ = temp[3];
temp[3] = temp[2];
temp[2] = temp[1];
temp[1] = (leftRotate(temp[0] + temp[4] + k[j] + w[temp[5]], r[j]) + temp[1]);
@ -165,14 +165,14 @@
h.set(hInitial);
var length = inputByteArray.buffer.byteLength;
var messageBitLength = length * 8;
const length = inputByteArray.buffer.byteLength;
const messageBitLength = length * 8;
// create byte array with length dividable by 64 (512 bit)
var neededLength = Math.ceil((length + 1 + 8) / 64) * 64;
var messageByteArray = new Uint8Array(neededLength);
const neededLength = Math.ceil((length + 1 + 8) / 64) * 64;
const messageByteArray = new Uint8Array(neededLength);
messageByteArray.set(new Uint8Array(inputByteArray.buffer));
var view = new DataView(messageByteArray.buffer);
const view = new DataView(messageByteArray.buffer);
// append 10...000000
messageByteArray[length] = 0x80;

View File

@ -12,8 +12,42 @@
scope = require.register("./iframeProtection", {});
}
scope.protect = function protect(window, wrappedWindow, singleCallback, allCallback){
const settings = require("./settings");
const extension = require("./extension");
const lists = require("./lists");
function isWhitelisted(url){
return lists.get("white").match(url) || settings.get("blockMode", url).startsWith("allow");
}
function createChangeProperty(window){
function changeProperty(object, name, type, changed){
const descriptor = Object.getOwnPropertyDescriptor(object, name);
const original = descriptor[type];
if ((typeof changed) === "function"){
changed = extension.createProxyFunction(window, original, changed);
}
extension.changeProperty(window, "iframeProtection", {object, name, type, changed});
}
if (settings.isStillDefault){
settings.onloaded(function(){
if (isWhitelisted(window.location)){
extension.revertProperties(window, "iframeProtection");
}
});
}
else {
if (isWhitelisted(window.location)){
return false;
}
}
window.addEventListener("unload", function(){
extension.revertProperties(window, "iframeProtection");
});
return changeProperty;
}
function protectFrameProperties({window, wrappedWindow, changeProperty, singleCallback}){
["HTMLIFrameElement", "HTMLFrameElement"].forEach(function(constructorName){
const constructor = window[constructorName];
const wrappedConstructor = wrappedWindow[constructorName];
@ -25,18 +59,16 @@
const originalContentWindowGetter = contentWindowDescriptor.get;
const contentWindowTemp = {
get contentWindow(){
var window = originalContentWindowGetter.call(this);
const window = originalContentWindowGetter.call(this);
if (window){
singleCallback(window);
}
return window;
}
};
contentWindowDescriptor.get = exportFunction(
Object.getOwnPropertyDescriptor(contentWindowTemp, "contentWindow").get,
window
changeProperty(wrappedConstructor.prototype, "contentWindow", "get",
Object.getOwnPropertyDescriptor(contentWindowTemp, "contentWindow").get
);
Object.defineProperty(wrappedConstructor.prototype, "contentWindow", contentWindowDescriptor);
const contentDocumentDescriptor = Object.getOwnPropertyDescriptor(
constructor.prototype,
@ -45,19 +77,20 @@
const originalContentDocumentGetter = contentDocumentDescriptor.get;
const contentDocumentTemp = {
get contentDocument(){
var document = originalContentDocumentGetter.call(this);
const document = originalContentDocumentGetter.call(this);
if (document){
singleCallback(document.defaultView);
}
return document;
}
};
contentDocumentDescriptor.get = exportFunction(
Object.getOwnPropertyDescriptor(contentDocumentTemp, "contentDocument").get,
window
changeProperty(wrappedConstructor.prototype, "contentDocument", "get",
Object.getOwnPropertyDescriptor(contentDocumentTemp, "contentDocument").get
);
Object.defineProperty(wrappedConstructor.prototype, "contentDocument", contentDocumentDescriptor);
});
}
function protectDOMModifications({window, wrappedWindow, changeProperty, allCallback}){
[
// useless as length could be obtained before the iframe is created and window.frames === window
// {
@ -90,120 +123,180 @@
protectionDefinition.methods.forEach(function(method){
const descriptor = Object.getOwnPropertyDescriptor(object, method);
const original = descriptor.value;
descriptor.value = exportFunction(eval(`(function ${method}(){
const value = arguments.length?
original.apply(this, window.Array.from(arguments)):
original.call(this);
allCallback();
return value;
})`), window);
Object.defineProperty(object, method, descriptor);
changeProperty(object, method, "value", class {
[method](){
const value = arguments.length?
original.call(this, ...arguments):
original.call(this);
allCallback();
return value;
}
}.prototype[method]);
});
protectionDefinition.getters.forEach(function(property){
const descriptor = Object.getOwnPropertyDescriptor(object, property);
const temp = eval(`({
get ${property}(){
const ret = this.${property};
const temp = {
get [property](){
const ret = this[property];
allCallback();
return ret;
}
})`);
descriptor.get = exportFunction(Object.getOwnPropertyDescriptor(temp, property).get, window);
Object.defineProperty(object, property, descriptor);
};
changeProperty(object, property, "get",
Object.getOwnPropertyDescriptor(temp, property).get
);
});
protectionDefinition.setters.forEach(function(property){
const descriptor = Object.getOwnPropertyDescriptor(object, property);
const setter = descriptor.set;
const temp = eval(`({
set ${property}(value){
const temp = {
set [property](value){
const ret = setter.call(this, value);
// const ret = this.${property} = value;
allCallback();
return ret;
}
})`);
descriptor.set = exportFunction(Object.getOwnPropertyDescriptor(temp, property).set, window);
Object.defineProperty(object, property, descriptor);
};
changeProperty(object, property, "set",
Object.getOwnPropertyDescriptor(temp, property).set
);
});
});
// MutationObserver to intercept iFrames while generating the DOM.
const observe = function(){
var observer = new MutationObserver(allCallback);
var observing = false;
function observe(){
if (
!observing &&
window.document
){
observer.observe(window.document, {subtree: true, childList: true});
observing = true;
}
}
function enableMutationObserver({window, allCallback}){
const observer = new MutationObserver(allCallback);
let observing = false;
function observe(){
if (
!observing &&
window.document
){
observer.observe(window.document, {subtree: true, childList: true});
observing = true;
}
observe();
window.document.addEventListener("DOMContentLoaded", function(){
if (observing){
observer.disconnect();
observing = false;
}
});
return observe;
}();
// MutationObserver does not trigger fast enough when document.write is used
const documentWriteDescriptor = Object.getOwnPropertyDescriptor(
}
observe();
window.document.addEventListener("DOMContentLoaded", function(){
if (observing){
observer.disconnect();
observing = false;
}
});
return observe;
}
function protectDocumentWrite({window, wrappedWindow, changeProperty, observe, allCallback}){
const documentWriteDescriptorOnHTMLDocument = Object.getOwnPropertyDescriptor(
wrappedWindow.HTMLDocument.prototype,
"write"
) || Object.getOwnPropertyDescriptor(
);
const documentWriteDescriptor = documentWriteDescriptorOnHTMLDocument || Object.getOwnPropertyDescriptor(
wrappedWindow.Document.prototype,
"write"
);
const documentWrite = documentWriteDescriptor.value;
documentWriteDescriptor.value = exportFunction(function write(markup){
for (let i = 0, l = arguments.length; i < l; i += 1){
const str = "" + arguments[i];
// weird problem with waterfox and google docs
const parts = (
str.match(/^\s*<!doctype/i) &&
!str.match(/frame/i)
)? [str]: str.split(/(?=<)/);
const length = parts.length;
const scripts = window.document.getElementsByTagName("script");
for (let i = 0; i < length; i += 1){
documentWrite.call(this, parts[i]);
allCallback();
if (scripts.length && scripts[scripts.length - 1].src){
observe();
changeProperty(
documentWriteDescriptorOnHTMLDocument?
wrappedWindow.HTMLDocument.prototype:
wrappedWindow.Document.prototype,
"write", "value", function write(markup){
for (let i = 0, l = arguments.length; i < l; i += 1){
const str = "" + arguments[i];
// weird problem with waterfox and google docs
const parts = (
str.match(/^\s*<!doctype/i) &&
!str.match(/frame/i)
)? [str]: str.split(/(?=<)/);
const length = parts.length;
const scripts = window.document.getElementsByTagName("script");
for (let i = 0; i < length; i += 1){
documentWrite.call(this, parts[i]);
allCallback();
if (scripts.length && scripts[scripts.length - 1].src){
observe();
}
}
}
}
}, window);
Object.defineProperty(wrappedWindow.HTMLDocument.prototype, "write", documentWriteDescriptor);
);
const documentWritelnDescriptor = Object.getOwnPropertyDescriptor(
const documentWritelnDescriptorOnHTMLDocument = Object.getOwnPropertyDescriptor(
wrappedWindow.HTMLDocument.prototype,
"writeln"
) || Object.getOwnPropertyDescriptor(
);
const documentWritelnDescriptor = documentWritelnDescriptorOnHTMLDocument || Object.getOwnPropertyDescriptor(
wrappedWindow.Document.prototype,
"writeln"
);
const documentWriteln = documentWritelnDescriptor.value;
documentWritelnDescriptor.value = exportFunction(function writeln(markup){
for (let i = 0, l = arguments.length; i < l; i += 1){
const str = "" + arguments[i];
const parts = str.split(/(?=<)/);
const length = parts.length;
const scripts = window.document.getElementsByTagName("script");
for (let i = 0; i < length; i += 1){
documentWrite.call(this, parts[i]);
allCallback();
if (scripts.length && scripts[scripts.length - 1].src){
observe();
changeProperty(
documentWritelnDescriptorOnHTMLDocument?
wrappedWindow.HTMLDocument.prototype:
wrappedWindow.Document.prototype,
"writeln", "value", function writeln(markup){
for (let i = 0, l = arguments.length; i < l; i += 1){
const str = "" + arguments[i];
const parts = str.split(/(?=<)/);
const length = parts.length;
const scripts = window.document.getElementsByTagName("script");
for (let i = 0; i < length; i += 1){
documentWrite.call(this, parts[i]);
allCallback();
if (scripts.length && scripts[scripts.length - 1].src){
observe();
}
}
}
documentWriteln.call(this, "");
}
documentWriteln.call(this, "");
}, window);
Object.defineProperty(wrappedWindow.HTMLDocument.prototype, "writeln", documentWritelnDescriptor);
);
}
function protectWindowOpen({window, wrappedWindow, changeProperty, singleCallback}){
const windowOpenDescriptor = Object.getOwnPropertyDescriptor(
wrappedWindow,
"open"
);
const windowOpen = windowOpenDescriptor.value;
const getDocument = Object.getOwnPropertyDescriptor(
window,
"document"
).get;
changeProperty(
wrappedWindow,
"open", "value", function open(){
const newWindow = arguments.length?
windowOpen.call(this, ...arguments):
windowOpen.call(this);
if (newWindow){
// if we use windowOpen from the normal window we see some SOP errors
// BUT we need the unwrapped window...
singleCallback(getDocument.call(newWindow).defaultView);
}
return newWindow;
}
);
}
scope.protect = function protect(window, wrappedWindow, singleCallback, allCallback){
const changeProperty = createChangeProperty(window);
if (!changeProperty){
return;
}
const api = {window, wrappedWindow, changeProperty, singleCallback, allCallback};
protectFrameProperties(api);
protectDOMModifications(api);
// MutationObserver to intercept iFrames while generating the DOM.
api.observe = enableMutationObserver(api);
// MutationObserver does not trigger fast enough when document.write is used
protectDocumentWrite(api);
protectWindowOpen(api);
};
}());

View File

@ -14,7 +14,6 @@
const {changedFunctions, changedGetters, setRandomSupply} = require("./modifiedAPI");
const randomSupplies = require("./randomSupplies");
const {getWrapped} = require("./modifiedAPIFunctions");
const logging = require("./logging");
const settings = require("./settings");
const extension = require("./extension");
@ -40,322 +39,429 @@
settings.on("rng", function(){
setRandomSupplyByType(settings.rng);
});
if (!settings.isStillDefault){
setRandomSupplyByType(settings.rng);
}
function getURL(window){
function getURL(windowToProcess){
let href;
try {
href = window.location.href;
href = windowToProcess.location.href;
}
catch (e){
catch (error){
// unable to read location due to SOP
// since we are not able to do anything in that case we can allow everything
return "about:SOP";
}
if (!href || href === "about:blank"){
if (window !== window.parent){
return getURL(window.parent);
if (windowToProcess !== windowToProcess.parent){
return getURL(windowToProcess.parent);
}
else if (window.opener){
return getURL(window.opener);
else if (windowToProcess.opener){
return getURL(windowToProcess.opener);
}
}
return href;
}
const getAllFunctionObjects = function(windowToProcess, changedFunction){
return (
Array.isArray(changedFunction.object)?
changedFunction.object:
[changedFunction.object]
).map(function(name){
if (name){
const constructor = extension.getWrapped(windowToProcess)[name];
if (constructor){
return constructor.prototype;
}
}
return false;
}).concat(
changedFunction.objectGetters?
changedFunction.objectGetters.map(function(objectGetter){
return objectGetter(extension.getWrapped(windowToProcess));
}):
[]
);
};
const forEachFunction = function(windowToProcess, callback){
apiNames.forEach(function(name){
const changedFunction = changedFunctions[name];
if (changedFunction.name){
name = changedFunction.name;
}
getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){
if (object){
callback({name, object: object, changedFunction});
}
});
});
};
const forEachGetter = function(windowToProcess, callback){
changedGetters.forEach(function(changedGetter){
const name = changedGetter.name;
changedGetter.objectGetters.forEach(function(changedGetter){
const object = changedGetter(extension.getWrapped(windowToProcess));
if (object){
callback({name, object, objectGetter: changedGetter});
}
});
});
};
scope.preIntercept = function preIntercept({subject: window}, apis){
if (!settings.isStillDefault){
logging.message("settings already loaded -> no need to pre intercept");
scope.intercept({subject: window}, apis);
const forEach = function(windowToProcess, callback){
forEachFunction(windowToProcess, callback);
forEachGetter(windowToProcess, callback);
};
const doRealIntercept = function(windowToProcess, apis, state){
if (!state.intercepted){
scope.intercept({subject: windowToProcess}, apis);
state.intercepted = true;
}
else {
logging.message("settings not loaded -> need to pre intercept");
let forceLoad = true;
let preIntercepted = false;
let intercepted = false;
const forEachFunction = function(callback){
apiNames.forEach(function(name){
const changedFunction = changedFunctions[name];
(
Array.isArray(changedFunction.object)?
changedFunction.object:
[changedFunction.object]
).forEach(function(object){
const constructor = getWrapped(window)[object];
if (constructor){
callback({name, object: constructor.prototype});
}
});
});
changedGetters.forEach(function(changedGetter){
const name = changedGetter.name;
changedGetter.objectGetters.forEach(function(objectGetter){
const object = objectGetter(getWrapped(window));
if (object){
callback({name, object});
}
});
});
};
let originalPropertyDescriptors = {};
const doPreIntercept = function(){
if (!preIntercepted){
forEachFunction(function({name, object}){
const map = originalPropertyDescriptors[name] || new WeakMap();
originalPropertyDescriptors[name] = map;
const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name);
if (!originalPropertyDescriptor){
return;
}
map.set(object, originalPropertyDescriptor);
};
const doPreIntercept = function(windowToProcess, apis, state){
const forceLoad = true;
const originalPropertyDescriptors = {};
const undoPreIntercept = function(){
if (state.preIntercepted){
state.preIntercepted = false;
forEach(windowToProcess, function({name, object}){
const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object);
if (originalPropertyDescriptor){
Object.defineProperty(
object,
name,
{
enumerable: true,
configurable: true,
get: exportFunction(function(){
if (forceLoad){
logging.warning("force load the settings. Calling stack:", (new Error()).stack);
undoPreIntercept();
settings.forceLoad();
doRealIntercept();
const descriptor = Object.getOwnPropertyDescriptor(object, name);
return descriptor.value || descriptor.get.call(this);
}
else {
logging.notice("API blocked (%s)", name);
const url = getURL(window);
if (!url){
return undef;
}
const error = new Error();
apis.notify({
url,
errorStack: error.stack,
messageId: "preBlock",
timestamp: new Date(),
functionName: name,
dataURL: false
});
return;
}
}, window),
set: exportFunction(function(){}, window)
}
originalPropertyDescriptor
);
});
preIntercepted = true;
}
});
}
};
if (!state.preIntercepted){
forEach(windowToProcess, function({name, object}){
const map = originalPropertyDescriptors[name] || new WeakMap();
originalPropertyDescriptors[name] = map;
const originalPropertyDescriptor = Object.getOwnPropertyDescriptor(object, name);
if (!originalPropertyDescriptor){
return;
}
};
const undoPreIntercept = function(){
if (preIntercepted){
preIntercepted = false;
forEachFunction(function({name, object}){
const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object);
if (originalPropertyDescriptor){
Object.defineProperty(
object,
name,
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);
}
});
}
};
const doRealIntercept = function(){
if (!intercepted){
scope.intercept({subject: window}, apis);
intercepted = true;
}
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,
name,
{
enumerable: true,
configurable: true,
get: extension.exportFunctionWithName(temp[`get ${name}`], windowToProcess, `get ${name}`),
set: extension.exportFunctionWithName(temp[`set ${name}`], windowToProcess, `set ${name}`)
}
);
});
state.preIntercepted = true;
}
return undoPreIntercept;
};
scope.preIntercept = function preIntercept({subject: windowToProcess}, apis){
if (!settings.isStillDefault){
logging.message("settings already loaded -> no need to pre intercept");
scope.intercept({subject: windowToProcess}, apis);
}
else {
logging.message("settings not loaded -> need to pre intercept");
const state = {
preIntercepted: false,
intercepted: false
};
doPreIntercept();
const undoPreIntercept = doPreIntercept(windowToProcess, apis, state);
settings.onloaded(function(){
undoPreIntercept();
doRealIntercept();
doRealIntercept(windowToProcess, apis, state);
});
}
};
function getDataURL(object, prefs){
return (
object &&
prefs("storeImageForInspection") &&
prefs("showNotifications")?
(
object instanceof HTMLCanvasElement?
object.toDataURL():
(
object.canvas instanceof HTMLCanvasElement?
object.canvas.toDataURL():
false
)
):
false
);
}
let extensionID = extension.extensionID;
scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){
function getDataURL(object, prefs){
return (
object &&
prefs("storeImageForInspection") &&
prefs("showNotifications")?
(
object instanceof HTMLCanvasElement?
object.toDataURL():
(
object.canvas instanceof HTMLCanvasElement?
object.canvas.toDataURL():
false
)
):
false
);
}
function generateChecker(name, changedFunction, siteStatus, original){
return function checker(callingDepth = 2){
const error = new Error();
const errorStack = error.stack;
try {
// return original if the extension itself requested the function
if (
errorStack
.split("\n", callingDepth + 2)[callingDepth + 1]
.split("@", callingDepth + 1)[1]
.startsWith(extensionID)
){
return {allow: true, original, window};
}
function generateChecker({
name, changedFunction, siteStatus, original,
window: windowToProcess, prefs, notify, checkStack, ask
}){
return function checker(callingDepth = 3){
const errorStack = (new Error()).stack;
try {
// return original if the extension itself requested the function
if (
errorStack
.split("\n", callingDepth + 2)[callingDepth + 1]
.split("@", callingDepth + 1)[1]
.startsWith(extensionID)
){
return {allow: true, original, window: windowToProcess};
}
catch (e) {
// stack had an unknown form
}
if (checkStack(errorStack)){
return {allow: true, original, window};
}
const funcStatus = changedFunction.getStatus(this, siteStatus, prefs);
const This = this;
function notifyCallback(messageId){
notify({
url: getURL(window),
errorStack,
messageId,
timestamp: new Date(),
functionName: name,
}
catch (error) {
// stack had an unknown form
}
if (checkStack(errorStack)){
return {allow: true, original, window: windowToProcess};
}
const funcStatus = changedFunction.getStatus(this, siteStatus, prefs);
const This = this;
function notifyCallback(messageId){
notify({
url: getURL(windowToProcess),
errorStack,
messageId,
timestamp: new Date(),
functionName: name,
api: changedFunction.api,
dataURL: getDataURL(This, prefs)
});
}
const protectedAPIFeatures = prefs("protectedAPIFeatures");
if (
funcStatus.active &&
(
!protectedAPIFeatures.hasOwnProperty(name + " @ " + changedFunction.api) ||
protectedAPIFeatures[name + " @ " + changedFunction.api]
)
){
if (funcStatus.mode === "ask"){
funcStatus.mode = ask({
window: windowToProcess,
type: changedFunction.type,
api: changedFunction.api,
dataURL: getDataURL(This, prefs)
canvas: this instanceof HTMLCanvasElement?
this:
(
this &&
(this.canvas instanceof HTMLCanvasElement)?
this.canvas:
false
),
errorStack
});
}
const protectedAPIFeatures = prefs("protectedAPIFeatures");
if (
funcStatus.active &&
(
!protectedAPIFeatures.hasOwnProperty(name) ||
protectedAPIFeatures[name]
)
){
if (funcStatus.mode === "ask"){
funcStatus.mode = ask({
window: window,
type: changedFunction.type,
api: changedFunction.api,
canvas: this instanceof HTMLCanvasElement?
this:
(
this &&
(this.canvas instanceof HTMLCanvasElement)?
this.canvas:
false
),
errorStack
});
switch (funcStatus.mode){
case "allow":
return {allow: true, original, window: windowToProcess};
case "fake":
return {
allow: "fake",
prefs,
notify: notifyCallback,
window: windowToProcess,
original
};
//case "block":
default:
return {allow: false, notify: notifyCallback};
}
}
else {
return {allow: true, original, window: windowToProcess};
}
};
}
function interceptFunctions(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){
apiNames.forEach(function(name){
const changedFunction = changedFunctions[name];
if (changedFunction.name){
name = changedFunction.name;
}
const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs);
logging.verbose("status for", name, ":", functionStatus);
if (!functionStatus.active) return;
getAllFunctionObjects(windowToProcess, changedFunction).forEach(function(object){
if (!object) return;
const original = object[name];
const checker = generateChecker({
name, changedFunction, siteStatus, original,
window: windowToProcess, prefs, checkStack, ask, notify
});
const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (!descriptor) return;
const type = descriptor.hasOwnProperty("value")? "value": "get";
let changed;
if (type ==="value"){
if (changedFunction.fakeGenerator){
if ((changedFunction.exportOptions || {}).allowCallbacks){
changed = extension.exportFunctionWithName(
changedFunction.fakeGenerator(checker, original, windowToProcess),
windowToProcess,
original.name
);
}
else {
const generated = changedFunction.fakeGenerator(checker, original, windowToProcess);
changed = extension.createProxyFunction(windowToProcess, original, generated);
}
}
switch (funcStatus.mode){
case "allow":
return {allow: true, original, window};
case "fake":
return {
allow: "fake",
prefs,
notify: notifyCallback,
window,
original
};
//case "block":
default:
return {
allow: false,
notify: notifyCallback
};
else {
changed = null;
}
}
else {
return {allow: true, original, window};
changed = extension.createProxyFunction(windowToProcess, original, extension.exportFunctionWithName(
changedFunction.fakeGenerator(checker),
windowToProcess,
original.name
));
}
};
}
const siteStatus = check({url: getURL(window)});
logging.verbose("status for page", window, siteStatus);
extension.changeProperty(windowToProcess, changedFunction.api, {
object, name, type, changed
});
});
});
}
function interceptGetters(windowToProcess, siteStatus, {checkStack, ask, notify, prefs}){
changedGetters.forEach(function(changedGetter){
const name = changedGetter.name;
const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs);
logging.verbose("status for", changedGetter, ":", functionStatus);
if (!functionStatus.active) return;
changedGetter.objectGetters.forEach(function(objectGetter){
const object = objectGetter(extension.getWrapped(windowToProcess));
if (!object) return;
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)
}
);
if (descriptor.hasOwnProperty("set") && descriptor.set && changedGetter.setterGenerator){
const original = descriptor.set;
const setter = changedGetter.setterGenerator(
windowToProcess,
original,
prefs
);
extension.changeProperty(windowToProcess, changedGetter.api,
{
object, name, type: "set",
changed: extension.createProxyFunction(windowToProcess, original, setter)
}
);
}
}
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){
const siteStatus = apis.check({url: getURL(windowToProcess)});
logging.verbose("status for page", windowToProcess, siteStatus);
if (siteStatus.mode !== "allow"){
apiNames.forEach(function(name){
const changedFunction = changedFunctions[name];
const functionStatus = changedFunction.getStatus(undefined, siteStatus, prefs);
logging.verbose("status for", name, ":", functionStatus);
if (functionStatus.active){
(
Array.isArray(changedFunction.object)?
changedFunction.object:
[changedFunction.object]
).forEach(function(object){
const constructor = getWrapped(window)[object];
if (constructor){
const original = constructor.prototype[name];
const checker = generateChecker(name, changedFunction, siteStatus, original);
const descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name);
if (descriptor){
if (descriptor.hasOwnProperty("value")){
if (changedFunction.fakeGenerator){
descriptor.value = exportFunction(
changedFunction.fakeGenerator(checker, original, window),
window
);
}
else {
descriptor.value = null;
}
}
else {
descriptor.get = exportFunction(function(){
return exportFunction(
changedFunction.fakeGenerator(checker),
window
);
}, window);
}
Object.defineProperty(constructor.prototype, name, descriptor);
}
}
});
}
});
changedGetters.forEach(function(changedGetter){
const name = changedGetter.name;
const functionStatus = changedGetter.getStatus(undefined, siteStatus, prefs);
logging.verbose("status for", changedGetter, ":", functionStatus);
if (functionStatus.active){
changedGetter.objectGetters.forEach(function(objectGetter){
const object = objectGetter(getWrapped(window));
if (object){
const descriptor = Object.getOwnPropertyDescriptor(object, name);
if (descriptor && descriptor.hasOwnProperty("get")){
const original = descriptor.get;
const checker = generateChecker(name, changedGetter, siteStatus, original);
const getter = changedGetter.getterGenerator(checker, original, window);
descriptor.get = exportFunction(getter, window);
if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){
const setter = changedGetter.setterGenerator(window, descriptor.set, prefs);
descriptor.set = exportFunction(setter, window);
}
Object.defineProperty(object, name, descriptor);
}
else {
logging.error("Try to fake non getter property:", changedGetter);
}
}
});
}
});
interceptFunctions(windowToProcess, siteStatus, apis);
interceptGetters(windowToProcess, siteStatus, apis);
}
};
}());

View File

@ -6,7 +6,7 @@
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -14,11 +14,12 @@
scope = require.register("./lists", {});
}
var settings = require("./settings");
const settings = require("./settings");
const logging = require("./logging");
function getDomainRegExpList(domainList){
var list = domainList
const list = domainList
.split(",")
.map(function(entry){
return entry.replace(/^\s+|\s+$/g, "");
@ -27,15 +28,28 @@
return !!entry.length;
})
.map(function(entry){
var regExp;
var domain = !!entry.match(/^[A-Za-z0-9_.-]+$/);
let regExp;
const domain = !!entry.match(/^[A-Za-z0-9_.*-]+$/);
if (domain){
regExp = new RegExp("(?:^|\\.)" + entry.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$", "i");
regExp = new RegExp(
"(?:^|\\.)" + entry.replace(/([\\+?[^\]$(){}=!|.])/g, "\\$1").replace(/\*/g, ".+") + "\\.?$",
"i"
);
}
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 {
value: entry,
match: function(url){
if (domain){
return (url.hostname || "").match(regExp);
@ -59,7 +73,7 @@
return list;
}
var lists = {
const lists = {
white: [],
sessionWhite: [],
"ignore": [],
@ -81,9 +95,9 @@
});
function updateStackList(value){
var list;
let list;
try {
var data = JSON.parse(value);
let data = JSON.parse(value);
if (!Array.isArray(data)){
data = [data];
}
@ -91,7 +105,7 @@
return typeof entry === "object" && typeof entry.url === "string";
});
}
catch(e){
catch(error){
list = [];
}
list.match = function(stack){
@ -109,20 +123,33 @@
scope.get = function getList(type){
if (type === "white"){
var combined = lists.white.slice().concat(lists.sessionWhite);
const combined = lists.white.slice().concat(lists.sessionWhite);
return addMatchToList(combined);
}
return lists[type];
};
scope.appendTo = function appendToList(type, entry){
var oldValue = settings[type + "List"];
return settings.set(type + "List", oldValue + (oldValue? ",": "") + entry).then(function(){
return updateList(type);
});
scope.appendTo = async function appendToList(type, entry){
const oldValue = settings[type + "List"];
await settings.set(type + "List", oldValue + (oldValue? ",": "") + entry);
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.updateAll = function updateAllLists(){
updateList("white");
updateList("sessionWhite");
updateList("ignore");
updateList("black");
updateStackList(settings.stackList);

View File

@ -1,11 +1,10 @@
/* eslint no-console: off */
/* 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";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -14,24 +13,26 @@
}
let settings = false;
scope.setSettings = function(realSettings){
scope.setSettings = async function(realSettings){
if (!settings){
settings = realSettings;
settings.loaded.then(scope.clearQueue);
await settings.loaded;
return scope.clearQueue();
}
else {
warning("logging: Settings can only be set once.");
return settings.loaded;
}
};
var prefix = "";
let prefix = "";
function leftPad(str, char, pad){
str = "" + str;
return char.repeat(pad - str.length) + str;
}
var colors = {
const colors = {
1: "color: red",
25: "color: orange",
50: "",
@ -40,7 +41,7 @@
999: "background-color: lightgray"
};
var queue = [];
let queue = [];
function performLog(level, args, date){
if (!date){
date = new Date();
@ -50,7 +51,7 @@
}
else {
if (settings.logLevel >= level){
var pre = "%c[CanvasBlocker] ";
let pre = "%c[CanvasBlocker] ";
if (prefix){
pre += prefix + ": ";
}
@ -71,15 +72,16 @@
args.unshift(colors[level] || "");
args.unshift(pre);
}
// eslint-disable-next-line no-console
console.log.apply(console, args);
}
}
}
function error (...args){performLog(1, args);}
function warning(...args){performLog(25, args);}
function message(...args){performLog(50, args);}
function notice (...args){performLog(75, args);}
function error (...args){performLog(1, args);}
function warning(...args){performLog(25, args);}
function message(...args){performLog(50, args);}
function notice (...args){performLog(75, args);}
function verbose(...args){performLog(100, args);}
function metaLog(...args){performLog(999, args);}
@ -94,7 +96,7 @@
scope.clearQueue = function(){
if (queue.length){
metaLog("clear logging queue");
var tmp = queue;
const tmp = queue;
queue = [];
tmp.forEach(function(item){
performLog(item.level, item.args, item.date);

View File

@ -6,25 +6,80 @@
const settings = require("./settings");
const logging = require("./logging");
const {error, warning, message, notice, verbose, } = logging;
logging.setPrefix("main script");
const persistentRndStorage = require("./persistentRndStorage");
const notification = require("./notification");
const mobile = require("./mobile");
const extension = require("./extension");
message("start of background script");
message("waiting for settings to be loaded");
const registerSettingsContentScript = (function(){
let unregisterSettingsContentScript = function(){};
let lastRegistering;
return async function registerSettingsContentScript(){
logging.message("Register content script for the settings.");
logging.verbose("Unregister old content script, if present.");
unregisterSettingsContentScript();
const data = {};
settings.forEach(function(def){
data[def.name] = def.get();
});
lastRegistering = data;
const api = await browser.contentScripts.register({
matches: ["<all_urls>"],
matchAboutBlank: true,
allFrames: true,
runAt: "document_start",
js: [{
code: `(function(settingsData){
if (typeof require !== "undefined"){
const settings = require("./settings");
const logging = require("./logging");
if (settings.init(settingsData)){
logging.message("Initialized settings by dynamic content script.");
}
else {
logging.warning("Dynamic content script was too late to provide settings.");
}
}
else {
if (!window.scope){
window.scope = {};
}
window.scope.settingsData = settingsData;
console.warn(
"[CanvasBlocker] invalid content script order: require not defined at",
window.location.href
);
}
}(${JSON.stringify(data)}))`
}]
});
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;
}
};
}());
logging.message("start of background script");
logging.message("waiting for settings to be loaded");
settings.onloaded(function(){
notice("everything loaded");
logging.notice("everything loaded");
message("perform startup reset");
logging.message("perform startup reset");
settings.startupReset();
persistentRndStorage.init();
message("register non port message listener");
browser.runtime.onMessage.addListener(function(data){
notice("got data without port", data);
var keys = Object.keys(data);
logging.message("register non port message listener");
browser.runtime.onMessage.addListener(async function(data){
logging.notice("got data without port", data);
const keys = Object.keys(data);
if (data["canvasBlocker-new-domain-rnd"]){
persistentRndStorage.setDomainData(
data["canvasBlocker-new-domain-rnd"].domain,
@ -36,7 +91,7 @@
}
}
if (data["canvasBlocker-clear-domain-rnd"]){
persistentRndStorage.clear();
persistentRndStorage.clear(data["canvasBlocker-clear-domain-rnd"] === "force");
if (keys.length === 1){
return;
}
@ -47,31 +102,30 @@
return;
}
}
notice("pass the message to the tabs");
browser.tabs.query({}).then(function(tabs){
tabs.forEach(function(tab){
browser.tabs.sendMessage(tab.id, data);
});
logging.notice("pass the message to the tabs");
const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){
browser.tabs.sendMessage(tab.id, data);
});
});
message("register port listener");
logging.message("register port listener");
browser.runtime.onConnect.addListener(function(port){
notice("got port", port);
logging.notice("got port", port);
if (!port.sender.tab){
notice("got port without tab = Firefox bug:", port);
logging.notice("got port without tab = Firefox bug:", port);
return;
}
verbose("send back the tab id", port.sender.tab.id);
verbose("send back the tab cookie store id", port.sender.tab.cookieStoreId);
verbose("send back the persistent random seeds", persistentRndStorage.persistentRnd);
logging.verbose("send back the tab id", port.sender.tab.id);
logging.verbose("send back the tab cookie store id", port.sender.tab.cookieStoreId);
logging.verbose("send back the persistent random seeds", persistentRndStorage.persistentRnd);
port.postMessage({
tabId: port.sender.tab.id,
cookieStoreId: port.sender.tab.cookieStoreId,
cookieStoreId: port.sender.tab.cookieStoreId || "",
persistentRnd: persistentRndStorage.persistentRnd,
persistentIncognitoRnd: persistentRndStorage.persistentIncognitoRnd
});
var url = new URL(port.sender.url);
const url = new URL(port.sender.url);
port.onMessage.addListener(function(data){
if (data.hasOwnProperty("canvasBlocker-notify")){
notification.show(port.sender.tab.id, url, data["canvasBlocker-notify"].api);
@ -79,76 +133,25 @@
if (data.hasOwnProperty("canvasBlocker-clear-page-action")){
notification.hide(port.sender.tab.id, url);
}
verbose("got data", data, "from port", port);
logging.verbose("got data", data, "from port", port);
});
});
message("register storage change event listener");
logging.message("register storage change event listener");
if (browser.contentScripts){
let unregister = function(){};
let lastRegistering;
const register = function register(){
logging.message("Register content script for the settings.");
logging.verbose("Unregister old content script, if present.");
unregister();
var data = {};
settings.forEach(function(def){
data[def.name] = def.get();
});
lastRegistering = data;
browser.contentScripts.register({
matches: ["<all_urls>"],
matchAboutBlank: true,
allFrames: true,
runAt: "document_start",
js: [{
code: `(function(settingsData){
if (typeof require !== "undefined"){
const settings = require("./settings");
const logging = require("./logging");
if (settings.init(settingsData)){
logging.message("Initialized settings by dynamic content script.");
}
else {
logging.error("Dynamic content script was too late to provide settings.");
}
}
else {
if (!window.scope){
window.scope = {};
}
window.scope.settingsData = settingsData;
console.warn(
"[CanvasBlocker] invalid content script order: require not defined at",
window.location.href
);
}
}(${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 {
unregister = api.unregister;
}
});
};
register();
settings.on("any", register);
registerSettingsContentScript();
settings.on("any", registerSettingsContentScript);
}
else {
logging.error("Old Firefox does not support browser.contentScript.register()");
}
});
message("Initialize data-URL workaround.");
logging.message("Initialize data-URL workaround.");
require("./dataUrls").init();
message("Initialize navigator HTTP header protection.");
logging.message("Initialize navigator HTTP header protection.");
require("./navigator").init();
browser.runtime.onInstalled.addListener(function(details){
@ -159,24 +162,71 @@
!browser.pageAction.openPopup
){
browser.tabs.create({
url: browser.extension.getURL("options/options.html?notice=" + reason)
url: extension.getURL("options/options.html?notice=" + reason)
});
}
}
switch (details.reason){
case "install":
message("CanvasBlocker installed");
logging.message("CanvasBlocker installed");
openOptions(details.reason);
settings.onloaded(function(){
if (settings.showPresetsOnInstallation){
browser.tabs.create({
url: extension.getURL("options/presets.html?notice=" + details.reason)
});
}
});
break;
case "update":
settings.onloaded(function(){
if (!settings.dontShowOptionsOnUpdate){
message("CanvasBlocker updated");
logging.message("CanvasBlocker updated");
openOptions(details.reason);
}
});
}
// mobile default settings
mobile.ifMobile(async function(){
const settings = await browser.storage.local.get();
mobile.applyMobileDefaults(settings);
});
});
message("end");
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");
}());

53
lib/mobile.js Normal file
View File

@ -0,0 +1,53 @@
/* 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("./mobile", {});
}
const settings = require("./settings");
const settingDefinitions = require("./settingDefinitions");
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
return !browser.pageAction ||
!browser.pageAction.show ||
!browser.pageAction.openPopup
;
};
scope.ifMobile = async function ifMobile(ifCallback, elseCallback){
const isMobile = await scope.isMobile();
if (isMobile){
return ifCallback();
}
else if (elseCallback){
return elseCallback();
}
else {
return false;
}
};
scope.applyMobileDefaults = async function applyMobileDefaults(storage = false){
await Promise.all(settingDefinitions.filter(function(definition){
return definition.hasOwnProperty("mobileDefaultValue") && (
!storage ||
!storage.hasOwnProperty(definition.name)
);
}).map(function(definition){
return settings.set(definition.name, definition.mobileDefaultValue);
}));
};
}());

View File

@ -96,8 +96,7 @@
const parentTop = getGlobalOffsetTop(parent) - getGlobalScrollTop(parent);
const parentHeight = parent.offsetHeight;
const height = dialog.offsetHeight;
const top = Math.max(
0,
const top = Math.max(0,
Math.min(
container.offsetHeight - height,
parentTop + parentHeight / 2 - height / 2
@ -142,8 +141,16 @@
});
const container = openDialog(text, [
{text: extension.getTranslation("cancel"), default: true, callback: ()=>reject(false)},
{text: extension.getTranslation("OK"), focused: true, callback: ()=>resolve(select.options[select.selectedIndex].object)}
{
text: extension.getTranslation("cancel"),
default: true,
callback: () => reject(false)
},
{
text: extension.getTranslation("OK"),
focused: true,
callback: () => resolve(select.options[select.selectedIndex].object)
}
], parent);
container.querySelector(".text").insertAdjacentElement("afterend", select);
});

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -39,5 +39,8 @@
appendModified(require("./modifiedHistoryAPI"));
appendModified(require("./modifiedWindowAPI"));
appendModified(require("./modifiedDOMRectAPI"));
appendModified(require("./modifiedSVGAPI"));
appendModified(require("./modifiedTextMetricsAPI"));
appendModified(require("./modifiedNavigatorAPI"));
appendModified(require("./modifiedScreenAPI"));
}());

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -12,16 +12,12 @@
scope = require.register("./modifiedAPIFunctions", {});
}
scope.getWrapped = function getWrapped(obj){
return obj && (obj.wrappedJSObject || obj);
};
scope.checkerWrapper = function checkerWrapper(checker, object, args, callback){
const check = checker.call(object);
if (check.allow){
if (check.allow === true){
return args.length?
check.original.apply(object, check.window.Array.from(args)):
check.original.call(object, ...args):
check.original.call(object);
}
return callback.call(object, args, check);
@ -30,9 +26,17 @@
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){
Object.keys(functions).forEach(function(key){
var func = functions[key];
const func = functions[key];
["type", "api", "getStatus"].forEach(function(property){
if (data[property] && !func[property]){
func[property] = data[property];

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -12,25 +12,24 @@
scope = require.register("./modifiedAudioAPI", {});
}
const logging = require("./logging");
const {sha256String: hashing} = require("./hash");
const {checkerWrapper} = require("./modifiedAPIFunctions");
const {checkerWrapper, setFunctionProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
var randomSupply = null;
let randomSupply = null;
const getAudioFakeRate = function(){
const audioFakeRate = {
"1": function(array){return 1;},
"10": function(array){return 10;},
"100": function(array){return 100;},
"1000": function(array){return 1000;},
"1": function(){return 1;},
"10": function(){return 10;},
"100": function(){return 100;},
"1000": function(){return 1000;},
"0.1%": function(array){return array.length / 1000;},
"1%": function(array){return array.length / 100;},
"10%": function(array){return array.length / 10;},
"100%": function(array){return array.length;},
};
return function getAudioFakeRate(array, prefs){
var func = audioFakeRate[prefs("audioFakeRate")];
const func = audioFakeRate[prefs("audioFakeRate")];
if (typeof func === "function"){
return func(array);
}
@ -66,18 +65,18 @@
}
function forEachIndex(array, prefs, callback){
var length = array.length;
var rate = getAudioFakeRate(array, prefs);
var start = 0;
const length = array.length;
const rate = getAudioFakeRate(array, prefs);
let start = 0;
forEachFixedIndex(prefs, function(index){
callback(index, start);
start += 1;
});
if (start < rate){
var delta = Math.floor(length / (rate - start));
var indexRng = randomSupply.getIndexRng(1, length - delta * (rate - start - 1), window);
var offset = indexRng(0);
for (var i = start; i < rate; i += 1){
const delta = Math.floor(length / (rate - start));
const indexRng = randomSupply.getIndexRng(1, length - delta * (rate - start - 1), window);
let offset = indexRng(0);
for (let i = start; i < rate; i += 1){
callback(offset, i);
offset += delta;
}
@ -88,7 +87,7 @@
const intCache = Object.create(null);
function arrayHasAnyNonZero(array){
for (var i = 0, l = array.length; i < l; i += 1){
for (let i = 0, l = array.length; i < l; i += 1){
if (array[i]){
return true;
}
@ -105,9 +104,9 @@
cached = floatCache[hash];
}
if (!cached){
var rate = getAudioFakeRate(array, prefs);
var noiseLevel = getAudioNoiseLevel(prefs);
var rng = randomSupply.getRng(rate, window);
const rate = getAudioFakeRate(array, prefs);
const noiseLevel = getAudioNoiseLevel(prefs);
const rng = randomSupply.getRng(rate, window);
forEachIndex(array, prefs, function(index, i){
let value;
if (array[index] !== 0){
@ -137,8 +136,8 @@
cached = intCache[hash];
}
if (!cached){
var rate = getAudioFakeRate(array, prefs);
var rng = randomSupply.getValueRng(rate, window);
const rate = getAudioFakeRate(array, prefs);
const rng = randomSupply.getValueRng(rate, window);
forEachIndex(array, prefs, function(index, i){
array[index] = rng(array[index], i);
});
@ -157,26 +156,23 @@
randomSupply = supply;
};
function getStatus(obj, status, prefs){
status = Object.create(status);
status.active = prefs("protectAudio", status.url);
return status;
}
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
scope.changedFunctions = {
getFloatFrequencyData: {
object: ["AnalyserNode"],
fakeGenerator: function(checker){
return function getFloatFrequencyData(array){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
notify("fakedAudioReadout");
var ret = original.apply(this, window.Array.from(args));
fakeFloat32Array(array, window, prefs);
return ret;
});
return checkerWrapper(checker, this, arguments,
fakeArrayCheckerCallback.bind(this, array, fakeFloat32Array)
);
};
}
},
@ -184,13 +180,9 @@
object: ["AnalyserNode"],
fakeGenerator: function(checker){
return function getByteFrequencyData(array){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
notify("fakedAudioReadout");
var ret = original.apply(this, window.Array.from(args));
fakeUint8Array(array, window, prefs);
return ret;
});
return checkerWrapper(checker, this, arguments,
fakeArrayCheckerCallback.bind(this, array, fakeUint8Array)
);
};
}
},
@ -198,13 +190,9 @@
object: ["AnalyserNode"],
fakeGenerator: function(checker){
return function getFloatTimeDomainData(array){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
notify("fakedAudioReadout");
var ret = original.apply(this, window.Array.from(args));
fakeFloat32Array(array, window, prefs);
return ret;
});
return checkerWrapper(checker, this, arguments,
fakeArrayCheckerCallback.bind(this, array, fakeFloat32Array)
);
};
}
},
@ -212,13 +200,9 @@
object: ["AnalyserNode"],
fakeGenerator: function(checker){
return function getByteTimeDomainData(array){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
notify("fakedAudioReadout");
var ret = original.apply(this, window.Array.from(args));
fakeUint8Array(array, window, prefs);
return ret;
});
return checkerWrapper(checker, this, arguments,
fakeArrayCheckerCallback.bind(this, array, fakeUint8Array)
);
};
}
},
@ -227,8 +211,8 @@
fakeGenerator: function(checker){
return function getChannelData(channel){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var ret = original.apply(this, window.Array.from(args));
const {prefs, notify, window, original} = check;
const ret = original.call(this, ...args);
if (!getChannelDataAlreadyFakedArrays.get(ret)){
notify("fakedAudioReadout");
fakeFloat32Array(ret, window, prefs);
@ -244,14 +228,14 @@
fakeGenerator: function(checker){
return function copyFromChannel(destination, channelNumber, startInChannel){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var channelData = this.getChannelData(channelNumber);
const {prefs, notify, window, original} = check;
const channelData = this.getChannelData(channelNumber);
if (!getChannelDataAlreadyFakedArrays.get(channelData)){
notify("fakedAudioReadout");
fakeFloat32Array(channelData, window, prefs);
getChannelDataAlreadyFakedArrays.set(channelData, true);
}
var ret = original.apply(this, window.Array.from(args));
const ret = original.call(this, ...args);
return ret;
});
};
@ -262,9 +246,9 @@
fakeGenerator: function(checker){
return function getFrequencyResponse(frequencyArray, magResponseOutput, phaseResponseOutput){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
const {prefs, notify, window, original} = check;
notify("fakedAudioReadout");
var ret = original.apply(this, window.Array.from(args));
const ret = original.call(this, ...args);
fakeFloat32Array(magResponseOutput, window, prefs);
fakeFloat32Array(phaseResponseOutput, window, prefs);
return ret;
@ -273,9 +257,10 @@
}
},
};
Object.keys(scope.changedFunctions).forEach(function(key){
scope.changedFunctions[key].type = "readout";
scope.changedFunctions[key].getStatus = getStatus;
scope.changedFunctions[key].api = "audio";
setFunctionProperties(scope.changedFunctions, {
type: "readout",
getStatus: getStatusByFlag("protectAudio"),
api: "audio"
});
}());

View File

@ -7,10 +7,11 @@
const scope = ((typeof exports) !== "undefined")? exports: require.register("./modifiedCanvasAPI");
const colorStatistics = require("./colorStatistics");
const logging = require("./logging");
const {copyCanvasToWebgl} = require("./webgl");
const {getWrapped, checkerWrapper} = require("./modifiedAPIFunctions");
const extension = require("./extension");
const webgl = require("./webgl");
const {checkerWrapper} = require("./modifiedAPIFunctions");
var randomSupply = null;
let randomSupply = null;
function getContext(window, canvas){
return window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d") ||
@ -20,11 +21,11 @@
window.HTMLCanvasElement.prototype.getContext.call(canvas, "experimental-webgl2");
}
function getImageData(window, context){
var imageData;
var source;
let imageData;
let source;
if ((context.canvas.width || 0) * (context.canvas.height || 0) === 0){
imageData = new (getWrapped(window).ImageData)(0, 0);
source = new (getWrapped(window).ImageData)(0, 0);
imageData = new (extension.getWrapped(window).ImageData)(0, 0);
source = new (extension.getWrapped(window).ImageData)(0, 0);
}
else if (context instanceof window.CanvasRenderingContext2D){
imageData = window.CanvasRenderingContext2D.prototype.getImageData.call(
@ -35,7 +36,7 @@
source = imageData.data;
}
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);
(
context instanceof window.WebGLRenderingContext?
@ -54,24 +55,25 @@
};
}
var canvasCache = Object.create(null);
const canvasCache = Object.create(null);
function getFakeCanvas(window, original, prefs){
try {
let originalDataURL;
if (prefs("useCanvasCache")){
var originalDataURL = original.toDataURL();
var cached = canvasCache[originalDataURL];
originalDataURL = original.toDataURL();
const cached = canvasCache[originalDataURL];
if (cached){
return cached;
}
}
// original may not be a canvas -> we must not leak an error
var context = getContext(window, original);
var {imageData, source} = getImageData(window, context);
var desc = imageData.data;
var l = desc.length;
let context = getContext(window, original);
const {imageData, source} = getImageData(window, context);
const desc = imageData.data;
const l = desc.length;
var ignoredColors = {};
var statistic;
let ignoredColors = {};
let statistic;
if (prefs("ignoreFrequentColors")){
statistic = colorStatistics.compute(source);
ignoredColors = statistic.getMaxColors(prefs("ignoreFrequentColors"));
@ -82,10 +84,10 @@
}
}
var rng = randomSupply.getPixelRng(l, window, ignoredColors);
var fakeAlphaChannel = prefs("fakeAlphaChannel");
for (var i = 0; i < l; i += 4){
var [r, g, b, a] = rng(
const rng = randomSupply.getPixelRng(l, window, ignoredColors);
const fakeAlphaChannel = prefs("fakeAlphaChannel");
for (let i = 0; i < l; i += 4){
const [r, g, b, a] = rng(
source[i + 0],
source[i + 1],
source[i + 2],
@ -97,7 +99,7 @@
desc[i + 2] = b;
desc[i + 3] = fakeAlphaChannel? a: source[i + 3];
}
var canvas = original.cloneNode(true);
const canvas = original.cloneNode(true);
context = window.HTMLCanvasElement.prototype.getContext.call(canvas, "2d");
context.putImageData(imageData, 0, 0);
if (prefs("useCanvasCache")){
@ -106,25 +108,25 @@
}
return canvas;
}
catch (e){
logging.warning("Error while faking:", e);
catch (error){
logging.warning("Error while faking:", error);
return original;
}
}
function randomMixImageData(window, imageData1, imageData2){
var data1 = imageData1.data;
var data2 = imageData2.data;
var l = data1.length;
const data1 = imageData1.data;
const data2 = imageData2.data;
const l = data1.length;
if (l === data2.length){
var rng = randomSupply.getPixelRng(l, window, {});
const rng = randomSupply.getPixelRng(l, window, {});
for (var i = 0; i < l; i += 4){
for (let i = 0; i < l; i += 4){
const signR = data1[i + 0] > data2[i + 0]? -1: 1;
const signG = data1[i + 1] > data2[i + 1]? -1: 1;
const signB = data1[i + 2] > data2[i + 2]? -1: 1;
const signA = data1[i + 3] > data2[i + 3]? -1: 1;
var [deltaR, deltaG, deltaB, deltaA] = rng(
const [deltaR, deltaG, deltaB, deltaA] = rng(
signR * (data2[i + 0] - data1[i + 0]),
signG * (data2[i + 1] - data1[i + 1]),
signB * (data2[i + 2] - data1[i + 2]),
@ -142,9 +144,9 @@
function canvasSizeShouldBeFaked(canvas, prefs){
if (canvas){
var size = canvas.height * canvas.width;
var maxSize = prefs("maxFakeSize") || Number.POSITIVE_INFINITY;
var minSize = prefs("minFakeSize") || 0;
const size = canvas.height * canvas.width;
const maxSize = prefs("maxFakeSize") || Number.POSITIVE_INFINITY;
const minSize = prefs("minFakeSize") || 0;
return size > minSize && size <= maxSize;
}
else {
@ -180,8 +182,134 @@
scope.setRandomSupply = function(supply){
randomSupply = supply;
webgl.setRandomSupply(supply);
};
var canvasContextType = new WeakMap();
const canvasContextType = new WeakMap();
function createGetStatus(protectedPart){
if (protectedPart === "readout+"){
return function(obj, status, prefs){
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;
};
}
return function getStatus(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker(protectedPart);
return status;
};
}
function useFakeCanvasCallback(args, check){
const {prefs, notify, window, original} = check;
if (canvasSizeShouldBeFaked(this, prefs)){
const fakeCanvas = getFakeCanvas(window, this, prefs);
if (fakeCanvas !== this){
notify("fakedReadout");
}
return original.call(fakeCanvas, ...args);
}
else {
return original.call(this, ...args);
}
}
function mixOnInputCallback(args, check){
const {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
notify("fakedInput");
let oldImageData;
let x, y, width, height;
const getImageData = window.CanvasRenderingContext2D.prototype.getImageData;
const border = 4;
try {
// "this" is not trustable - it may be not a context
const measurement = window.CanvasRenderingContext2D.prototype.measureText.call(this, args[0]);
const left = Math.max(0, measurement.actualBoundingBoxLeft);
const top = Math.max(0, measurement.actualBoundingBoxAscent);
width = Math.ceil(measurement.actualBoundingBoxRight + left) + 2 * border;
height = Math.ceil(measurement.actualBoundingBoxDescent + top) + 2 * border;
x = args[1] - border - left;
y = args[2] - border - top;
oldImageData = getImageData.call(this, x, y, width, height);
}
catch (error){
// nothing to do here
}
// if "this" is not a correct context the next line will throw an error
const ret = original.call(this, ...args);
const newImageData = getImageData.call(this, x, y, width, height);
this.putImageData(randomMixImageData(window, oldImageData, newImageData), x, y);
return ret;
}
else {
return original.call(this, ...args);
}
}
function offscreenToBlobCallback(args, check){
const {prefs, notify, window, original} = check;
if (canvasSizeShouldBeFaked(this, prefs)){
try {
const canvas = window.document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
const context = canvas.getContext("2d");
context.drawImage(this.transferToImageBitmap(), 0, 0);
const fakeCanvas = getFakeCanvas(window, canvas, prefs);
if (fakeCanvas !== canvas){
notify("fakedReadout");
}
const fakeContext = getContext(window, fakeCanvas);
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
scope.changedFunctions = {
getContext: {
@ -209,125 +337,63 @@
},
object: "HTMLCanvasElement",
fakeGenerator: function(checker){
return function(context, contextAttributes){
return function getContext(context, contextAttributes){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
const {original} = check;
canvasContextType.set(this, context);
return original.apply(this, window.Array.from(args));
return original.call(this, ...args);
});
};
}
},
toDataURL: {
type: "readout",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
if (!status.active && protectedPartChecker("input")){
var contextType = canvasContextType.get(obj);
status.active = contextType !== "2d";
}
return status;
},
getStatus: createGetStatus("readout+"),
object: "HTMLCanvasElement",
fakeGenerator: function(checker){
return function toDataURL(){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
if (canvasSizeShouldBeFaked(this, prefs)){
var 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));
}
});
return checkerWrapper(checker, this, arguments, useFakeCanvasCallback);
};
}
},
toBlob: {
type: "readout",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
if (!status.active && protectedPartChecker("input")){
var contextType = canvasContextType.get(obj);
status.active = contextType !== "2d";
}
return status;
},
getStatus: createGetStatus("readout+"),
object: "HTMLCanvasElement",
fakeGenerator: function(checker){
return function toBlob(callback){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
if (canvasSizeShouldBeFaked(this, prefs)){
var 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));
}
});
if (this instanceof toBlob){
throw new extension.getWrapped(window).TypeError(
"HTMLCanvasElement.prototype.toBlob is not a constructor"
);
}
return checkerWrapper(checker, this, arguments, useFakeCanvasCallback);
};
},
exportOptions: {allowCallbacks: true}
},
mozGetAsFile: {
type: "readout",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
if (!status.active && protectedPartChecker("input")){
var contextType = canvasContextType.get(obj);
status.active = contextType !== "2d";
}
return status;
},
getStatus: createGetStatus("readout+"),
object: "HTMLCanvasElement",
fakeGenerator: function(checker){
return function mozGetAsFile(callback){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
if (canvasSizeShouldBeFaked(this, prefs)){
var 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));
}
});
return checkerWrapper(checker, this, arguments, useFakeCanvasCallback);
};
}
},
exportOptions: {allowCallbacks: true}
},
getImageData: {
type: "readout",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
return status;
},
getStatus: createGetStatus("readout"),
object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){
return function getImageData(sx, sy, sw, sh){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
const {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
var fakeCanvas;
var context = this;
let fakeCanvas;
let context = this;
if (this && this.canvas) {
fakeCanvas = getFakeCanvas(window, this.canvas, prefs);
}
@ -338,10 +404,10 @@
"2d"
);
}
return original.apply(context, window.Array.from(args));
return original.call(context, ...args);
}
else {
return original.apply(this, window.Array.from(args));
return original.call(this, ...args);
}
});
};
@ -349,23 +415,21 @@
},
isPointInPath: {
type: "readout",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
return status;
},
getStatus: createGetStatus("readout"),
object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){
return function isPointInPath(x, y){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var rng = randomSupply.getValueRng(1, window);
var originalValue = original.apply(this, window.Array.from(args));
const {prefs, notify, window, original} = check;
const originalValue = original.call(this, ...args);
if ((typeof originalValue) === "boolean"){
notify("fakedReadout");
var index = x + this.width * y;
return original.call(this, rng(x, index), rng(y, index), args[2]);
return getIsPointValue({
func: (x, y) => original.call(this, x, y, args[2]),
x, y,
index: x + this.canvas.width * y,
originalValue, window, prefs
});
}
else {
return originalValue;
@ -376,32 +440,31 @@
},
isPointInStroke: {
type: "readout",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("readout");
return status;
},
getStatus: createGetStatus("readout"),
object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){
return function isPointInStroke(x, y){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
var rng = randomSupply.getValueRng(1, window);
var originalValue = original.apply(this, window.Array.from(args));
const {prefs, notify, window, original} = check;
const originalValue = original.call(this, ...args);
if ((typeof originalValue) === "boolean"){
notify("fakedReadout");
let func;
if (x instanceof window.Path2D){
let path = x;
x = y;
y = args[2];
let index = x + this.width * y;
return original.call(this, path, rng(x, index), rng(y, index));
func = (x, y) => original.call(this, path, x, y);
}
else {
let index = x + this.width * y;
return original.call(this, rng(x, index), rng(y, index));
func = (x, y) => original.call(this, x, y);
}
return getIsPointValue({
func,
x, y,
index: x + this.canvas.width * y,
originalValue, window, prefs
});
}
else {
return originalValue;
@ -412,106 +475,138 @@
},
fillText: {
type: "input",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("input");
return status;
},
getStatus: createGetStatus("input"),
object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){
return function fillText(str, x, y){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
notify("fakedInput");
var oldImageData;
try {
// "this" is not trustable - it may be not a context
oldImageData = getImageData(window, this).imageData;
}
catch (e){
// nothing to do here
}
// if "this" is not a correct context the next line will throw an error
var ret = original.apply(this, window.Array.from(args));
var newImageData = getImageData(window, this).imageData;
this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0);
return ret;
}
else {
return original.apply(this, window.Array.from(args));
}
});
return checkerWrapper(checker, this, arguments, mixOnInputCallback);
};
}
},
strokeText: {
type: "input",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker("input");
return status;
},
getStatus: createGetStatus("input"),
object: "CanvasRenderingContext2D",
fakeGenerator: function(checker){
return function strokeText(str, x, y){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
notify("fakedInput");
var oldImageData;
try {
// "this" is not trustable - it may be not a context
oldImageData = getImageData(window, this).imageData;
}
catch (e){
// nothing to do here
}
// if "this" is not a correct context the next line will throw an error
var ret = original.apply(this, window.Array.from(args));
var newImageData = getImageData(window, this).imageData;
this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0);
return ret;
}
else {
return original.apply(this, window.Array.from(args));
}
});
return checkerWrapper(checker, this, arguments, mixOnInputCallback);
};
}
},
readPixels: {
type: "readout",
getStatus: function(obj, status, prefs){
const protectedPartChecker = getProtectedPartChecker(prefs, status.url);
status = Object.create(status);
status.active = protectedPartChecker(["readout", "input"]);
return status;
},
getStatus: createGetStatus(["readout", "input"]),
object: ["WebGLRenderingContext", "WebGL2RenderingContext"],
fakeGenerator: function(checker){
return function readPixels(x, y, width, height, format, type, pixels){ // eslint-disable-line max-params
// eslint-disable-next-line max-params
return function readPixels(x, y, width, height, format, type, pixels){
return checkerWrapper(checker, this, arguments, function(args, check){
var {prefs, notify, window, original} = check;
const {prefs, notify, window, original} = check;
if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){
notify("fakedReadout");
var fakeCanvas = getFakeCanvas(window, this.canvas, prefs);
var {context} = copyCanvasToWebgl(
const fakeCanvas = getFakeCanvas(window, this.canvas, prefs);
const {context} = webgl.copyCanvasToWebgl(
window,
fakeCanvas,
this instanceof window.WebGLRenderingContext? "webgl": "webgl2"
);
return original.apply(context, window.Array.from(args));
return original.call(context, ...args);
}
else {
return original.apply(this, window.Array.from(args));
return original.call(this, ...args);
}
});
};
}
}
},
getParameter: {
type: "readout",
getStatus: createGetStatus(["readout", "input"]),
object: ["WebGLRenderingContext", "WebGL2RenderingContext"],
fakeGenerator: function(checker){
webgl.initializeParameterDefinitions();
return function getParameter(pname){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.call(this, ...args);
if (webgl.parameterChangeDefinition[pname]){
const definition = webgl.parameterChangeDefinition[pname];
const {value, faked} = definition.fake(originalValue, window, prefs);
if (faked){
notify("fakedReadout");
}
return value;
}
else {
return originalValue;
}
});
};
}
},
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){
scope.changedFunctions[key].api = "canvas";

View File

@ -12,7 +12,8 @@
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");
@ -30,7 +31,7 @@
const registeredRects = new WeakMap();
function registerDOMRect(domRect, notify, window, prefs){
registeredRects.set(getWrapped(domRect), {
registeredRects.set(extension.getWrapped(domRect), {
notify: function(){
let done = false;
return function(message){
@ -45,47 +46,104 @@
});
}
function getDOMRectRegistration(domRect){
return registeredRects.get(getWrapped(domRect));
return registeredRects.get(extension.getWrapped(domRect));
}
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){
const hash = getHash(domRect);
let cached = cache[hash];
if (!cached){
notify("fakedDOMRectReadout");
const rng = randomSupply.getRng(4, window);
const getFakeValue = function getFakeValue(value, i){
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;
}
};
cached = new domRect.constructor(
getFakeValue(domRect.x, 0),
getFakeValue(domRect.y, 1),
getFakeValue(domRect.width, 2),
getFakeValue(domRect.height, 3)
const env = {window, prefs, rng};
cached = new (domRect instanceof window.SVGRect? window.DOMRectReadOnly: domRect.constructor)(
getFakeValue(domRect.x, 0, env),
getFakeValue(domRect.y, 1, env),
getFakeValue(domRect.width, 2, env),
getFakeValue(domRect.height, 3, env)
);
cache[hash] = cached;
cache[getHash(cached)] = 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 = {
getClientRects: {
@ -94,7 +152,7 @@
return function getClientRects(){
return checkerWrapper(checker, this, arguments, function(args, 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){
registerDOMRect(ret[i], notify, window, prefs);
}
@ -107,52 +165,74 @@
object: ["Range", "Element"],
fakeGenerator: function(checker){
return function getBoundingClientRect(){
return checkerWrapper(checker, this, arguments, function(args, check){
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;
});
return checkerWrapper(checker, this, arguments, registerCallback);
};
}
},
getBounds: {
object: ["DOMQuad"],
getBoxQuads: {
object: ["Document", "Element", "Text", "CSSPseudoElement"],
fakeGenerator: function(checker){
return function getBounds(){
return function getBoxQuads(){
return checkerWrapper(checker, this, arguments, function(args, check){
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);
const ret = args.length? original.call(this, ...args): original.call(this);
for (let i = 0; i < ret.length; i += 1){
ret[i] = getFakeDOMQuad(window, ret[i], prefs, notify);
}
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: {
object: ["SVGGraphicsElement"],
fakeGenerator: function(checker){
return function getBBox(){
return checkerWrapper(checker, this, arguments, function(args, check){
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;
});
return checkerWrapper(checker, this, arguments, registerCallback);
};
}
},
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: {
object: ["SVGTextContentElement"],
fakeGenerator: function(checker){
return function getBBox(){
return checkerWrapper(checker, this, arguments, function(args, check){
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;
});
return function getExtentOfChar(){
return checkerWrapper(checker, this, arguments, registerCallback);
};
}
},
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.SVGRect && window.SVGRect.prototype;},
function(window){return window.DOMRectReadOnly && window.DOMRectReadOnly.prototype;}
],
name: property,
getterGenerator: function(){
const temp = eval(`({
get ${property}(){
const temp = {
get [property](){
const registration = getDOMRectRegistration(this);
if (registration){
return getFakeDomRect(
@ -179,24 +260,24 @@
this,
registration.prefs,
registration.notify
).${property};
)[property];
}
return this.${property};
return this[property];
}
})`);
};
return Object.getOwnPropertyDescriptor(temp, property).get;
}
};
if (!readonly){
changedGetter.setterGenerator = function(window, original, prefs){
const temp = eval(`({
set ${property}(newValue){
const temp = {
set [property](newValue){
const registration = getDOMRectRegistration(this);
if (registration){
const fakeDomRect = getFakeDomRect(window, this, prefs, registration.notify);
registeredRects.delete(getWrapped(this));
registeredRects.delete(extension.getWrapped(this));
["x", "y", "width", "height"].forEach((prop) => {
if (prop === "${property}"){
if (prop === property){
this[prop] = newValue;
}
else {
@ -205,10 +286,10 @@
});
}
else {
original.apply(this, window.Array.from(arguments));
original.call(this, ...arguments);
}
}
})`);
};
return Object.getOwnPropertyDescriptor(temp, property).set;
};
}
@ -234,14 +315,7 @@
getterGenerator: function(checker){
const temp = {
get intersectionRect(){
return checkerWrapper(checker, this, arguments, function(args, check){
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 checkerWrapper(checker, this, arguments, registerCallback);
}
};
return Object.getOwnPropertyDescriptor(temp, "intersectionRect").get;
@ -257,14 +331,7 @@
getterGenerator: function(checker){
const temp = {
get boundingClientRect(){
return checkerWrapper(checker, this, arguments, function(args, check){
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 checkerWrapper(checker, this, arguments, registerCallback);
}
};
return Object.getOwnPropertyDescriptor(temp, "boundingClientRect").get;
@ -280,14 +347,7 @@
getterGenerator: function(checker){
const temp = {
get rootBounds(){
return checkerWrapper(checker, this, arguments, function(args, check){
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 checkerWrapper(checker, this, arguments, registerCallback);
}
};
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, {
type: "readout",
getStatus: getStatus,
getStatus: getStatusByFlag("protectDOMRect"),
api: "domRect"
});
}());

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -12,7 +12,7 @@
scope = require.register("./modifiedHistoryAPI", {});
}
const {checkerWrapper} = require("./modifiedAPIFunctions");
const {checkerWrapper, setGetterProperties} = require("./modifiedAPIFunctions");
scope.changedGetters = [
{
@ -23,7 +23,7 @@
get length(){
return checkerWrapper(checker, this, arguments, function(args, 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);
if (originalLength > threshold){
notify("fakedHistoryReadout");
@ -47,9 +47,9 @@
return status;
}
scope.changedGetters.forEach(function(changedGetter){
changedGetter.type = "readout";
changedGetter.getStatus = getStatus;
changedGetter.api = "history";
setGetterProperties(scope.changedGetters, {
type: "readout",
getStatus: getStatus,
api: "history"
});
}());

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -12,41 +12,85 @@
scope = require.register("./modifiedNavigatorAPI", {});
}
const {checkerWrapper} = require("./modifiedAPIFunctions");
const {checkerWrapper, setProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const extension = require("./extension");
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){
return {
objectGetters: [function(window){return window.Navigator && window.Navigator.prototype;}],
name: property,
getterGenerator: function(checker){
const temp = eval(`({
get ${property}(){
const temp = {
get [property](){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const returnValue = navigator.getNavigatorValue("${property}");
const {notify, original} = check;
const originalValue = original.call(this, ...args);
const returnValue = navigator.getNavigatorValue(property, getCookieStoreId);
if (originalValue !== returnValue){
notify("fakedNavigatorReadout");
}
return returnValue;
});
}
})`);
};
return Object.getOwnPropertyDescriptor(temp, property).get;
}
};
});
function getStatus(obj, status, prefs){
status = Object.create(status);
status.active = prefs("protectNavigator", status.url);
return status;
}
scope.changedFunctions = {
estimate: {
objectGetters: [function(window){return window.StorageManager && window.StorageManager.prototype;}],
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;
notify("fakedNavigatorReadout");
}
resolve(originalValue);
}
catch (error){
reject(error);
}
});
});
};
}
}
};
scope.changedGetters.forEach(function(changedGetter){
changedGetter.type = "readout";
changedGetter.getStatus = getStatus;
changedGetter.api = "navigator";
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"
});
}());

291
lib/modifiedScreenAPI.js Normal file
View File

@ -0,0 +1,291 @@
/* 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("./modifiedScreenAPI", {});
}
const {checkerWrapper, setGetterProperties, getStatusByFlag} = require("./modifiedAPIFunctions");
const physical = {
width: Math.round(window.screen.width * window.devicePixelRatio),
height: Math.round(window.screen.height * window.devicePixelRatio)
};
if (!window.matchMedia(`(device-width: ${physical.width / window.devicePixelRatio}px`).matches){
let minWidth = Math.ceil((window.screen.width - 0.5) * window.devicePixelRatio);
let maxWidth = Math.floor((window.screen.width + 0.5) * window.devicePixelRatio);
for (let width = minWidth; width <= maxWidth; width += 1){
if (window.matchMedia(`(device-width: ${width / window.devicePixelRatio}px`).matches){
physical.width = width;
break;
}
}
}
if (!window.matchMedia(`(device-height: ${physical.height / window.devicePixelRatio}px`).matches){
let minHeight = Math.ceil((window.screen.height - 0.5) * window.devicePixelRatio);
let maxHeight = Math.floor((window.screen.height + 0.5) * window.devicePixelRatio);
for (let height = minHeight; height <= maxHeight; height += 1){
if (window.matchMedia(`(device-height: ${height / window.devicePixelRatio}px`).matches){
physical.height = height;
break;
}
}
}
const resolutions = {
portrait: [
{height: 1366, width: 768},
{height: 1440, width: 900},
{height: 1600, width: 900},
{height: 1920, width: 1080},
{height: 2560, width: 1440},
{height: 4096, width: 2160},
{height: 8192, width: 6144},
],
landscape: [
{width: 1366, height: 768},
{width: 1440, height: 900},
{width: 1600, height: 900},
{width: 1920, height: 1080},
{width: 2560, height: 1440},
{width: 4096, height: 2160},
{width: 8192, height: 6144},
]
};
function getScreenDimensions(prefs, window){
const screenSize = prefs("screenSize", window.location);
if (screenSize.match(/\s*\d+\s*x\s*\d+\s*$/)){
const [width, height] = screenSize.split("x").map(function(value){
return Math.round(parseFloat(value.trim()));
});
return {
width: width / window.devicePixelRatio,
height: height / window.devicePixelRatio
};
}
if (!prefs("fakeMinimalScreenSize", window.location)){
return window.screen;
}
const isLandscape = window.screen.width > window.screen.height;
// subtract 0.5 to adjust for potential rounding errors
const innerWidth = (window.innerWidth - 0.5) * window.devicePixelRatio;
const innerHeight = (window.innerHeight - 0.5) * window.devicePixelRatio;
for (let resolution of resolutions[isLandscape? "landscape": "portrait"]){
if (resolution.width >= innerWidth && resolution.height >= innerHeight){
return {
width: resolution.width / window.devicePixelRatio,
height: resolution.height / window.devicePixelRatio
};
}
}
return window.screen;
}
function getFaker(dimension){
return function fake(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.call(this, ...args);
const returnValue = (typeof dimension) === "function"?
dimension(window):
dimension?
Math.round(getScreenDimensions(prefs, window)[dimension]):
0;
if (originalValue !== returnValue){
notify("fakedScreenReadout");
}
return returnValue;
};
}
scope.changedGetters = [
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "width",
getterGenerator: function(checker){
const temp = {
get width(){
return checkerWrapper(checker, this, arguments, getFaker("width"));
}
};
return Object.getOwnPropertyDescriptor(temp, "width").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "height",
getterGenerator: function(checker){
const temp = {
get height(){
return checkerWrapper(checker, this, arguments, getFaker("height"));
}
};
return Object.getOwnPropertyDescriptor(temp, "height").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availWidth",
getterGenerator: function(checker){
const temp = {
get availWidth(){
return checkerWrapper(checker, this, arguments, getFaker("width"));
}
};
return Object.getOwnPropertyDescriptor(temp, "availWidth").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availHeight",
getterGenerator: function(checker){
const temp = {
get availHeight(){
return checkerWrapper(checker, this, arguments, getFaker("height"));
}
};
return Object.getOwnPropertyDescriptor(temp, "availHeight").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availLeft",
getterGenerator: function(checker){
const temp = {
get availLeft(){
return checkerWrapper(checker, this, arguments, getFaker(0));
}
};
return Object.getOwnPropertyDescriptor(temp, "availLeft").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availTop",
getterGenerator: function(checker){
const temp = {
get availTop(){
return checkerWrapper(checker, this, arguments, getFaker(0));
}
};
return Object.getOwnPropertyDescriptor(temp, "availTop").get;
}
},
{
objectGetters: [function(window){return window;}],
name: "outerWidth",
getterGenerator: function(checker){
const temp = {
get outerWidth(){
return checkerWrapper(checker, this, arguments, getFaker(window => window.top.innerWidth));
}
};
return Object.getOwnPropertyDescriptor(temp, "outerWidth").get;
}
},
{
objectGetters: [function(window){return window;}],
name: "outerHeight",
getterGenerator: function(checker){
const temp = {
get outerHeight(){
return checkerWrapper(checker, this, arguments, getFaker(window => window.top.innerHeight));
}
};
return Object.getOwnPropertyDescriptor(temp, "outerHeight").get;
}
},
{
objectGetters: [function(window){return window.MediaQueryList && window.MediaQueryList.prototype;}],
name: "matches",
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 = {
get matches(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.call(this, ...args);
const screenSize = prefs("screenSize", window.location);
if (
(
screenSize.match(/\s*\d+\s*x\s*\d+\s*$/) ||
prefs("fakeMinimalScreenSize", window.location)
) &&
this.media.match(/device-(width|height)/)
){
const originalMedia = this.media;
const alteredMedia = getAlteredMedia(originalMedia, prefs, window);
if (alteredMedia !== originalMedia){
const alteredQuery = window.matchMedia(alteredMedia);
const fakedValue = original.call(alteredQuery);
if (originalValue !== fakedValue){
notify("fakedScreenReadout");
}
return fakedValue;
}
}
return originalValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "matches").get;
}
},
];
setGetterProperties(scope.changedGetters, {
type: "readout",
getStatus: getStatusByFlag("protectScreen"),
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

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

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -26,6 +26,7 @@
original[property] = window.navigator[property];
});
original["real Firefox version"] = window.navigator.userAgent.replace(/^.+Firefox\//, "");
original["real Firefox version - rv"] = window.navigator.userAgent.replace(/^.+; rv:([\d.]+).*$/, "$1");
let changedValues = {};
@ -36,35 +37,63 @@
changedValues = newValue;
});
function getValue(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());
const getValue = function(){
function getChangedValues(getCookieStoreId){
if (changedValues.contextualIdentities){
const cookieStoreId = getCookieStoreId();
if (
cookieStoreId !== "" &&
cookieStoreId !== "firefox-default" &&
changedValues.contextualIdentities[cookieStoreId]
){
return changedValues.contextualIdentities[cookieStoreId];
}
else {
return original[name];
return changedValues;
}
}
else {
return changedValues;
}
}
}
function parseString(string, stack){
return string.replace(/{([a-z[\]_. -]*)}/ig, function(m, name){
return getValue(name, stack.slice());
});
}
return function getValue(name, getCookieStoreId){
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){
return getValue(name);
scope.getNavigatorValue = function getNavigatorValue(name, getCookieStoreId){
return getValue(name, getCookieStoreId);
};
function changeHTTPHeader(details){
@ -73,13 +102,15 @@
settings.get("protectNavigator", url) &&
check.check({url}).mode !== "allow" &&
(
!settings.protectedAPIFeatures.hasOwnProperty("userAgent") ||
settings.protectedAPIFeatures.userAgent
!settings.protectedAPIFeatures.hasOwnProperty("userAgent @ navigator") ||
settings.protectedAPIFeatures["userAgent @ navigator"]
)
){
for (var header of details.requestHeaders){
for (let header of details.requestHeaders){
if (header.name.toLowerCase() === "user-agent"){
header.value = getValue("userAgent");
header.value = getValue("userAgent", function(){
return details.cookieStoreId;
});
}
}
}
@ -115,5 +146,24 @@
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

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -163,13 +163,12 @@
});
};
settings.on("showNotifications", function({newValue}){
settings.on("showNotifications", async function({newValue}){
if (!newValue){
logging.message("notifications were disabled -> hide all page actions");
browser.tabs.query({}).then(function(tabs){
tabs.forEach(function(tab){
browser.pageAction.hide(tab.id);
});
const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){
browser.pageAction.hide(tab.id);
});
}
});
@ -177,16 +176,15 @@
browser.tabs.onRemoved.addListener(function(tabId){
tabsData.delete(tabId);
});
settings.on("displayBadge", function({newValue}){
settings.on("displayBadge", async function({newValue}){
if (!newValue){
logging.message("badge was disabled -> hide all badges");
if (browser.browserAction.setBadgeText){
browser.tabs.query({}).then(function(tabs){
tabs.forEach(function(tab){
browser.browserAction.setBadgeText({
tabId: tab.id,
text: ""
});
const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){
browser.browserAction.setBadgeText({
tabId: tab.id,
text: ""
});
});
}

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -18,6 +18,7 @@
scope.persistentRnd = Object.create(null);
scope.persistentIncognitoRnd = Object.create(null);
let clearTimeout;
scope.init = function init(){
logging.message("initializing persistent rng storage");
@ -26,8 +27,8 @@
if (settings.storePersistentRnd){
try {
let storedData = JSON.parse(settings.persistentRndStorage);
for (var domain in storedData){
var value = storedData[domain];
for (let domain in storedData){
const value = storedData[domain];
if (
Array.isArray(value) &&
value.length === 128 &&
@ -39,7 +40,7 @@
}
}
}
catch (e){
catch (error){
// JSON is not valid -> ignore it
}
}
@ -59,15 +60,22 @@
settings.persistentRndStorage = newValue? JSON.stringify(scope.persistentRnd): "";
});
logging.message("register contextual identities removal");
browser.contextualIdentities.onRemoved.addListener(function(details){
logging.message("Contextual identity", details.contextualIdentity.cookieStoreId, "removed.");
clearContainerData(details.contextualIdentity.cookieStoreId);
});
if (browser.contextualIdentities && browser.contextualIdentities.onRemoved){
logging.message("register contextual identities removal");
browser.contextualIdentities.onRemoved.addListener(function(details){
logging.message("Contextual identity", details.contextualIdentity.cookieStoreId, "removed.");
clearContainerData(details.contextualIdentity.cookieStoreId);
});
}
else {
logging.error(
"Old Firefox does not support browser.contextualIdentities.onRemoved"
);
}
};
const getInterval = function(){
var units = {
const units = {
seconds: 1000,
minutes: 60 * 1000,
hours: 60 * 60 * 1000,
@ -81,25 +89,14 @@
};
}();
browser.windows.onRemoved.addListener(function(){
browser.windows.getAll().then(function(windows){
if (windows.every(function(window){
return !window.incognito;
})){
clearIncognito();
}
});
});
let clearTimeout;
function registerTimeout(){
var interval = getInterval();
const interval = getInterval();
if (interval > 0){
var 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");
if (timeout > 1073741824){
// window.setTimeout can only handle delays up to 32 bit.
// Therefore we repeat the registering afert 2^30 = 1073741824 seconds
// Therefore we repeat the registering after 2^30 = 1073741824 seconds
clearTimeout = window.setTimeout(registerTimeout, 1073741824);
}
else {
@ -107,25 +104,24 @@
}
}
}
function broadcast(data){
browser.tabs.query({}).then(function(tabs){
tabs.forEach(function(tab){
browser.tabs.sendMessage(tab.id, data);
});
async function broadcast(data){
const tabs = await browser.tabs.query({});
tabs.forEach(function(tab){
browser.tabs.sendMessage(tab.id, data);
});
}
function clearIncognito(){
scope.persistentIncognitoRnd = Object.create(null);
settings.persistentIncognitoRndStorage = JSON.stringify(scope.persistentIncognitoRnd);
}
function clear(){
function clear(force = false){
logging.verbose("domain rnd cleared");
scope.persistentRnd = Object.create(null);
settings.persistentRndStorage = JSON.stringify(scope.persistentRnd);
settings.lastPersistentRndClearing = Date.now();
clearIncognito();
registerTimeout();
broadcast({"canvasBlocker-clear-domain-rnd": true});
broadcast({"canvasBlocker-clear-domain-rnd": force? "force": true});
}
function setDomainData(domain, incognito, rnd){
logging.verbose("got new domain rnd for ", domain, " (incognito:", incognito, "):", rnd);
@ -168,4 +164,18 @@
scope.setDomainData = setDomainData;
scope.clearDomainData = clearDomainData;
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

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -15,7 +15,6 @@
const rngTemplate = {
getBitRng: function(length, window){
const rng = this.getRng(Math.ceil(length / 32), window);
let bitIndex = 32;
let rnd = 0;
let mask = 0xffffffff * 2;
return function(value, i){
@ -38,20 +37,21 @@
getValueRng: function(length, window){
const rng = this.getBitRng(length, window);
return function(value, i){
var rnd = rng(value, i);
const rnd = rng(value, i);
// XOR the last bit to alter it... or not
return value ^ (rnd & 0x01);
};
},
getPixelRng: function(length, window, ignoredColors){
var rng = this.getValueRng(length, window);
return function(r, g, b, a, i){ // eslint-disable-line max-params
var index = String.fromCharCode(r, g, b, a);
const rng = this.getValueRng(length, window);
// eslint-disable-next-line max-params
return function(r, g, b, a, i){
const index = String.fromCharCode(r, g, b, a);
if (ignoredColors[index]){
return [r, g, b, a];
}
var baseIndex = i * 4;
const baseIndex = i * 4;
return [
rng(r, baseIndex + 0),
rng(g, baseIndex + 1),
@ -63,7 +63,6 @@
};
const settings = require("./settings");
const logging = require("./logging");
const extension = require("./extension");
function getDomain(window){
@ -81,8 +80,14 @@
return window.location.host;
}
var persistentRnd = Object.create(null);
let persistentRnd = Object.create(null);
let cookieStoreId = false;
function getCookieStoreId(){
while (cookieStoreId === false){
extension.waitSync("to wait for cookie store id");
}
return cookieStoreId;
}
settings.onloaded(function(){
try {
let storedData = JSON.parse(
@ -90,8 +95,8 @@
settings.persistentIncognitoRndStorage:
settings.persistentRndStorage
);
for (var domain in storedData){
var value = storedData[domain];
for (let domain in storedData){
const value = storedData[domain];
if (
Array.isArray(value) &&
value.length === 128 &&
@ -103,7 +108,7 @@
}
}
}
catch (e){
catch (error){
// JSON is not valid -> ignore it
}
});
@ -111,30 +116,30 @@
extension.message.on(function(data){
if (data["canvasBlocker-set-domain-rnd"]){
var {domain, incognito, rnd} = data["canvasBlocker-set-domain-rnd"];
const {domain, incognito, rnd} = data["canvasBlocker-set-domain-rnd"];
if (incognito === extension.inIncognitoContext){
persistentRnd[domain] = new Uint8Array(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);
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){
while (cookieStoreId === false){
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 (e){
logging.verbose("Error in XHR:", e);
}
}
var domain = cookieStoreId + getDomain(window);
const domain = getCookieStoreId() + getDomain(window);
if (!persistentRnd[domain]){
// create the (sub-)domains random numbers if not existing
persistentRnd[domain] = new Uint8Array(128);
@ -165,26 +170,26 @@
}
};
scope.persistent.getRng = function(length, window){
var bitSet = new Uint32Array(getPersistentRnd(window).buffer);
var bitSetLength = bitSet.length;
const bitSet = new Uint32Array(getPersistentRnd(window).buffer);
const bitSetLength = bitSet.length;
return function(i){
return bitSet[i % bitSetLength];
};
};
scope.persistent.getBitRng = function(length, window){
var bitSet = getPersistentRnd(window);
const bitSet = getPersistentRnd(window);
return function(value, i){
// use the last 7 bits from the value for the index of the
// random number
var index = value & 0x7F;
const index = value & 0x7F;
// use the last 3 bits from the position and the first bit from
// from the value to get bit to use from the random number
var bitIndex = ((i & 0x03) << 1) | (value >>> 7);
const bitIndex = ((i & 0x03) << 1) | (value >>> 7);
// extract the bit
var bit = (bitSet[index] >>> bitIndex) & 0x01;
const bit = (bitSet[index] >>> bitIndex) & 0x01;
return bit;
};
@ -196,16 +201,17 @@
return scope.nonPersistent.getRng(length, window);
};
scope.constant.getPixelRng = (function(){
var colors = Object.create(null);
const colors = Object.create(null);
return function getConstantPixelRng(length, window, ignoredColors){
var rng = scope.nonPersistent.getValueRng(1024, window);
const rng = scope.nonPersistent.getValueRng(1024, window);
return function(r, g, b, a, i){ // eslint-disable-line max-params
var index = String.fromCharCode(r, g, b, a);
// eslint-disable-next-line max-params
return function(r, g, b, a, i){
const index = String.fromCharCode(r, g, b, a);
if (ignoredColors[index]){
return [r, g, b, a];
}
var color = colors[index];
let color = colors[index];
if (!color){
color = [
rng(r, 0),
@ -224,8 +230,8 @@
scope.nonPersistent.name = "nonPersistent";
scope.nonPersistent.getRng = function(length, window){
const maxLength = 0x4000;
var randomI = maxLength;
var randomNumbers = new Uint32Array(Math.min(maxLength, length));
let randomI = maxLength;
let randomNumbers = new Uint32Array(Math.min(maxLength, length));
return function(i){
if (randomI >= randomNumbers.length){
// refill the random number bucket if empty
@ -235,7 +241,7 @@
}
window.crypto.getRandomValues(randomNumbers);
}
var rnd = randomNumbers[randomI];
const rnd = randomNumbers[randomI];
randomI += 1;
return rnd;

View File

@ -10,14 +10,14 @@ const require = function(){
const scope = window.scope;
function getScopeName(module){
var scopeName = module.replace(/^\..*\//, "").replace(/\..+/, "");
const scopeName = module.replace(/^\..*\//, "").replace(/\..+/, "");
// console.log(scopeName);
return scopeName;
}
function require(module){
if (module.startsWith(".")){
var scopeName = getScopeName(module);
const scopeName = getScopeName(module);
return scope[scopeName];
}
throw new ReferenceError("Unable to get non relative module " + module + "!");

View File

@ -4,7 +4,7 @@
(function(){
"use strict";
var scope;
let scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
@ -19,23 +19,32 @@
texts.push({text: text.toLowerCase(), content});
};
scope.search = function(search){
const resultSets = search.toLowerCase().split(/\s+/).filter(function(term){
const resultSets = search.split(/\s+/).filter(function(term){
return term.trim();
}).map(function(term){
return new RegExp(term);
}).map(function(term){
const matching = new Set();
texts.forEach(function(text){
if (term.test(text.text)){
matching.add(text.content);
}
});
if (term.match(/^:[a-z]+$/i)){
const tag = term.substring(1);
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;
});
if (resultSets.length){
return Array.from(
resultSets.reduce(function(previousSet, set){
var andSet = new Set();
const andSet = new Set();
set.forEach(function(entry){
if (previousSet.has(entry)){
andSet.add(entry);

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