Compare commits
371 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5a1a318b91 | ||
|
e549267085 | ||
|
cee0f75293 | ||
|
91dd283b9a | ||
|
af5365b4eb | ||
|
1b76df7cbc | ||
|
474e929638 | ||
|
642d518110 | ||
|
4f366ed86c | ||
|
00e60074c3 | ||
|
3eedc7b7dc | ||
|
bebcec2139 | ||
|
5181a55071 | ||
|
a8e192fa9b | ||
|
0f3141ee12 | ||
|
e9ce4668fe | ||
|
acce01bfeb | ||
|
d159769997 | ||
|
56401048d1 | ||
|
54c625cd26 | ||
|
7bb3f00b45 | ||
|
b8c6115603 | ||
|
dacc578e12 | ||
|
825fa42141 | ||
|
40a8012ab0 | ||
|
ec95cbe11b | ||
|
36b54f3ab5 | ||
|
87790c9731 | ||
|
200f6b31f3 | ||
|
1d8bf95926 | ||
|
c6cf48c489 | ||
|
9a3745b366 | ||
|
a18e3ba37d | ||
|
645d0ac550 | ||
|
809c1270c5 | ||
|
a99a0615d0 | ||
|
bc13a5e2a2 | ||
|
8176ac83dc | ||
|
4a2079bf47 | ||
|
7229133c8d | ||
|
8bdedfcb3d | ||
|
ae40caed09 | ||
|
053ae6725e | ||
|
2a6c564ed8 | ||
|
8a84dd09f3 | ||
|
df039b0f3c | ||
|
fc5cce23ea | ||
|
e9f5f710e6 | ||
|
5df98e0cf5 | ||
|
6ea89b6318 | ||
|
02dfa8bd1b | ||
|
8dcfac442f | ||
|
27d8d61da6 | ||
|
fb1311a842 | ||
|
114b109340 | ||
|
4ce2f98b10 | ||
|
2c5b00a55d | ||
|
d6916b013e | ||
|
3b92824d0f | ||
|
d3558f0bd7 | ||
|
d55921ba92 | ||
|
1ca13299ec | ||
|
53ce86a21f | ||
|
269574ae17 | ||
|
3b08fdaf9b | ||
|
e0e6926a74 | ||
|
2ac1ec277d | ||
|
8e463a6164 | ||
|
2cefed00a7 | ||
|
7cd4b2e9fa | ||
|
8d3f489d11 | ||
|
34bc1730a1 | ||
|
f900c25900 | ||
|
6b172a0a4c | ||
|
46eb608f4e | ||
|
b46341de90 | ||
|
91b7dcbb11 | ||
|
582a962d8d | ||
|
d9c14d7eed | ||
|
4bd0c0c96c | ||
|
cfb09075eb | ||
|
4443d0a117 | ||
|
bb71b10c58 | ||
|
4d4cda678c | ||
|
6c6012edf4 | ||
|
05f8f936e9 | ||
|
1a199a5049 | ||
|
e5cba569ba | ||
|
fd09e3d5cf | ||
|
6aab7f68ea | ||
|
a92373f412 | ||
|
b4cb52df15 | ||
|
d100932236 | ||
|
bfa355b58d | ||
|
dc593daba8 | ||
|
029933964f | ||
|
ffd659c7bb | ||
|
f788cd4263 | ||
|
1bd87e6953 | ||
|
fb231a070b | ||
|
740b360485 | ||
|
3621fef625 | ||
|
3d603b84d4 | ||
|
d4fc7027eb | ||
|
4c364a9a72 | ||
|
863140c1ca | ||
|
640bd36b86 | ||
|
734e76180f | ||
|
78183f9efc | ||
|
f043acf41c | ||
|
892e4d2c34 | ||
|
d97864436d | ||
|
7f154c6cc6 | ||
|
6f5cfc1080 | ||
|
cca81c4006 | ||
|
2f6ca07bba | ||
|
8e4a881288 | ||
|
709093fa4d | ||
|
b9d5eb3d27 | ||
|
e8ee1f8e9c | ||
|
4ac02003de | ||
|
cf0243d487 | ||
|
34f9dfa4fd | ||
|
bce494e744 | ||
|
87646a152a | ||
|
de14490574 | ||
|
13203a905d | ||
|
277bef1227 | ||
|
015350c385 | ||
|
cdfe72fada | ||
|
57bf81ec3a | ||
|
43ea01c178 | ||
|
3722263a6f | ||
|
020a9c5b9a | ||
|
698fc02e66 | ||
|
7ae6e14d0d | ||
|
539ddf5e46 | ||
|
70a941f5aa | ||
|
51e76bafce | ||
|
8dddff85cc | ||
|
0d581403c1 | ||
|
0930928df3 | ||
|
a7d02efd09 | ||
|
aa3f9d878d | ||
|
211d6710f6 | ||
|
42b19a4ba5 | ||
|
16bef43945 | ||
|
b614b84b5f | ||
|
bde9d3c012 | ||
|
32af464c05 | ||
|
1e1f343f28 | ||
|
7cd4ecec44 | ||
|
4f5b9b5f78 | ||
|
be55cbf983 | ||
|
872e633025 | ||
|
ee87773ce2 | ||
|
28dd7f6819 | ||
|
a5558b4144 | ||
|
8e5986817e | ||
|
e48710eee9 | ||
|
1b04da40c2 | ||
|
afd426da58 | ||
|
0d160143c1 | ||
|
b94de2b641 | ||
|
945c2716c9 | ||
|
1f1d7052a1 | ||
|
e2c5dfc06e | ||
|
b4a744660b | ||
|
fcbc622cef | ||
|
f4d09e43ac | ||
|
ae526e4710 | ||
|
822218a00c | ||
|
4e6f76ab75 | ||
|
543365cd64 | ||
|
1125d6f1a8 | ||
|
7178b409c3 | ||
|
b7e1b50166 | ||
|
184b6bb47c | ||
|
586bc97d38 | ||
|
8f13588e61 | ||
|
8c852e96c9 | ||
|
23f2bf8b23 | ||
|
83394afc90 | ||
|
0f61318c27 | ||
|
716e6aa6a9 | ||
|
701eb979ed | ||
|
d5417cf807 | ||
|
c16068ca85 | ||
|
ef69e1dcb5 | ||
|
902d31b643 | ||
|
0a290b3521 | ||
|
e65ac94dda | ||
|
71c9baec0a | ||
|
ff98b9b9af | ||
|
3eea8fe7c4 | ||
|
3ab6366994 | ||
|
cd852a0c8b | ||
|
864c2454e1 | ||
|
8d7637e65b | ||
|
393020630c | ||
|
9779d14276 | ||
|
5ab00a5584 | ||
|
6a31bb3791 | ||
|
beb3a969f3 | ||
|
5964146e22 | ||
|
679265a82f | ||
|
d761411339 | ||
|
1b3709bce5 | ||
|
00e94f9ae4 | ||
|
c6bb9766f9 | ||
|
b9c9516b14 | ||
|
cb7a98d751 | ||
|
21a950565f | ||
|
d8b5ba0744 | ||
|
ce1925d95f | ||
|
2c1756ff07 | ||
|
c6e4027a7f | ||
|
5ac5c8cc66 | ||
|
46bf7e04f4 | ||
|
10b2ef8e70 | ||
|
ac2e074fdd | ||
|
d9adcb4c3d | ||
|
5b1c4bf6cc | ||
|
8cf58ebe3b | ||
|
2e91f85d8f | ||
|
2c141277e8 | ||
|
3e3dbf2698 | ||
|
0225f94890 | ||
|
253d3f68b1 | ||
|
da5a9f4509 | ||
|
a79c4ec8c5 | ||
|
7c2a4edde0 | ||
|
51b740632a | ||
|
9a7dd3c189 | ||
|
4fa91ef3ae | ||
|
64adb49b81 | ||
|
49f3c166a2 | ||
|
308d7c4005 | ||
|
87fa607d28 | ||
|
aae0fa2f8b | ||
|
d834b7a14b | ||
|
e2d18a143d | ||
|
8ee413a951 | ||
|
1fb7f7991b | ||
|
7cfc43194a | ||
|
e7cef53bac | ||
|
a9c89ff20c | ||
|
d9d2038f0c | ||
|
f2b55fe7a2 | ||
|
dd6fbc6c1f | ||
|
736aeb371d | ||
|
ec128796e3 | ||
|
692b4616e2 | ||
|
895f1a6f66 | ||
|
73e5f5737b | ||
|
d449e5f0c3 | ||
|
9614786406 | ||
|
0f1aa1cc32 | ||
|
13b061c395 | ||
|
00f69b73ab | ||
|
1cfdcdb120 | ||
|
73694ab129 | ||
|
59d78c8e68 | ||
|
84e40b5eb4 | ||
|
ada2845213 | ||
|
9e92e4baf2 | ||
|
f65d73f125 | ||
|
f03eb4a67a | ||
|
f674ee3328 | ||
|
f358951022 | ||
|
6fbb9339a1 | ||
|
64b60c834a | ||
|
350c7b65d4 | ||
|
3d881d3c7d | ||
|
8bfaf63a99 | ||
|
cc8ca147b0 | ||
|
01b63b356c | ||
|
b48ad91dfe | ||
|
282fcb12de | ||
|
6c22788096 | ||
|
9353f71455 | ||
|
1556ee45c2 | ||
|
f3f6df229f | ||
|
e2efb727b9 | ||
|
29e61ada25 | ||
|
d547917b43 | ||
|
363940014d | ||
|
16f88a5daa | ||
|
5de24ad430 | ||
|
38e56aff31 | ||
|
b0becc0af0 | ||
|
417b234a26 | ||
|
3334fa64c1 | ||
|
accdb3cec1 | ||
|
e50e9deca4 | ||
|
4337ccbf33 | ||
|
ade99bbb8a | ||
|
e1e313cb96 | ||
|
16fb042335 | ||
|
4020ba1ebd | ||
|
6312914ba3 | ||
|
9628497277 | ||
|
db3cb3be44 | ||
|
0e8938ba6e | ||
|
d09340e84f | ||
|
b361733c73 | ||
|
85c88ad3d8 | ||
|
ccabfc2a6f | ||
|
cc3127b0a4 | ||
|
4601dd25af | ||
|
8506757c62 | ||
|
e3182b562b | ||
|
abdb95b815 | ||
|
10413a89c3 | ||
|
372ee755f7 | ||
|
3dc39e11a5 | ||
|
627ee6d21e | ||
|
09286644d8 | ||
|
26e0d21b23 | ||
|
993e72d6c8 | ||
|
ce27426ad4 | ||
|
0d0e3e30ec | ||
|
717e1d3e3a | ||
|
a9ed208505 | ||
|
ddcaf5a2a9 | ||
|
6fb7622fec | ||
|
af1dfe755c | ||
|
0d331d91a6 | ||
|
15c537cd1f | ||
|
3ab687f45b | ||
|
3668f48247 | ||
|
14a4d1cdc2 | ||
|
32f9ea7447 | ||
|
8e414becf0 | ||
|
bf757a5431 | ||
|
762367a87b | ||
|
a181780020 | ||
|
b7ba5c2050 | ||
|
5020e0b070 | ||
|
c62ddcc33f | ||
|
59ad6fc7d6 | ||
|
1aff68d802 | ||
|
635cdf8061 | ||
|
16fc49eee3 | ||
|
afd9aea767 | ||
|
17349dcb05 | ||
|
aef6bd3d59 | ||
|
b5e6d049ce | ||
|
83a8234b24 | ||
|
d4ce6c4b4b | ||
|
cded4d369a | ||
|
d930de1883 | ||
|
a555f3cd64 | ||
|
d42a4d2372 | ||
|
cc776b48de | ||
|
1430b89d55 | ||
|
320dc02941 | ||
|
5d6c2d9a47 | ||
|
73657852d3 | ||
|
ee254b3b93 | ||
|
f010c7c8fa | ||
|
aa7a4e1d06 | ||
|
64d4aa3f73 | ||
|
506f062c07 | ||
|
f3d1ca80f5 | ||
|
069165e8d6 | ||
|
5a355284f3 | ||
|
14b4bd8ac6 | ||
|
acc171041d | ||
|
a95fae3de8 | ||
|
d666d68812 |
6
.codebeatsettings
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"JAVASCRIPT": {
|
||||
"LOC": [80, 90, 100, 120],
|
||||
"BLOCK_NESTING": [4, 5, 6, 7]
|
||||
}
|
||||
}
|
@ -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>
|
||||
@ -28,16 +43,3 @@ Die verschiedenen Blockiermodi sind:
|
||||
<li>nur Einträge der Blacklist blockieren: Blockiere die beschützten APIs nur auf den Seiten der Blacklist.</li>
|
||||
<li>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.
|
@ -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
|
||||
|
12
.documentation/default.css
Normal 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;
|
||||
}
|
9
.documentation/faq/funding.md
Normal 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.
|
5
.documentation/faq/howToSupport.md
Normal 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.
|
22
.documentation/faq/index.php
Normal 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>
|
9
.documentation/faq/missingSetting.md
Normal 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.
|
3
.documentation/faq/paypal.md
Normal 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.
|
9
.documentation/faq/permissions.md
Normal 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.
|
5
.documentation/faq/reCAPTCHA.md
Normal 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.
|
5
.documentation/faq/removeUrlSetting.md
Normal 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.
|
10
.documentation/faq/uniqueFingerprint.md
Normal 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
@ -0,0 +1 @@
|
||||
!/.tools
|
@ -6,35 +6,88 @@
|
||||
"webextensions": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 8,
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"sourceType": "script"
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"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-const-assign": "warn",
|
||||
"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-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",
|
||||
"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"],
|
||||
"strict": ["error", "function"]
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
10
.github/ISSUE_TEMPLATE.md
vendored
@ -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,7 +18,7 @@
|
||||
|
||||
## Steps to Reproduce (for bugs)
|
||||
<!--- Provide a link to a live example, or an unambiguous set of steps to reproduce this bug. -->
|
||||
1.
|
||||
1. create a fresh Firefox profile
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
@ -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". -->
|
||||
~~~
|
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
printed.png
|
||||
web-ext-artifacts/
|
||||
versions/*.xpi
|
||||
node_modules/
|
125
.tools/build.js
Normal 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();
|
164
.tools/buildChromeVendors.js
Normal 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);
|
||||
|
@ -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
@ -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)"
|
||||
]
|
@ -5,6 +5,8 @@ const util = require("util");
|
||||
|
||||
|
||||
function getMessagesInContent(content){
|
||||
"use strict";
|
||||
|
||||
const foundMessages = [];
|
||||
[
|
||||
/\b(?:_|browser.i18n.getMessage|extension.getTranslation|notify|extension)\(["']([^"']+)["']\s*(?:\)|,)/g,
|
||||
@ -19,31 +21,31 @@ function getMessagesInContent(content){
|
||||
}
|
||||
|
||||
async function getMessagesInFile(path){
|
||||
return await util.promisify(fs.exists)(path)
|
||||
.then(function(exists){
|
||||
"use strict";
|
||||
|
||||
const exists = await util.promisify(fs.exists)(path);
|
||||
if (exists){
|
||||
return util.promisify(fs.readFile)(path, {encoding: "UTF-8"})
|
||||
.then(function(content){
|
||||
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(
|
||||
"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(function(path){
|
||||
return util.promisify(fs.stat)(path).then(function(stat){
|
||||
}).map(async function(path){
|
||||
const stat = await util.promisify(fs.stat)(path);
|
||||
if (stat.isDirectory()){
|
||||
return getMessagesInFolder(path);
|
||||
}
|
||||
@ -55,9 +57,8 @@ async function getMessagesInFolder(folder){
|
||||
return [];
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
).then(function(messages){
|
||||
);
|
||||
const flat = [];
|
||||
messages.forEach(function(messages){
|
||||
messages.forEach(function(message){
|
||||
@ -65,12 +66,12 @@ async function getMessagesInFolder(folder){
|
||||
});
|
||||
});
|
||||
return flat;
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
async function getSettingMessages(){
|
||||
"use strict";
|
||||
|
||||
const settingStrings = require("../lib/settingStrings");
|
||||
const settingDefinitions = require("../lib/settingDefinitions");
|
||||
function getDefinition(name){
|
||||
@ -113,33 +114,53 @@ async function getSettingMessages(){
|
||||
});
|
||||
});
|
||||
});
|
||||
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)
|
||||
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();});
|
||||
Promise.all([getSettingMessages(), getMessagesInFolder(path.join(__dirname, "..")), getKnownMessages()]).then(function([settingMessages, fileMessages, knownMessages]){
|
||||
.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 &&
|
||||
@ -148,4 +169,6 @@ Promise.all([getSettingMessages(), getMessagesInFolder(path.join(__dirname, ".."
|
||||
console.log(`usage of ${message} not found`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
@ -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){
|
||||
const exists = await util.promisify(fs.exists)(path);
|
||||
if (exists){
|
||||
console.log("language exists -> load data");
|
||||
return util.promisify(fs.readFile)(path, {encoding: "UTF-8"})
|
||||
.then(function(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);
|
||||
}());
|
73
.vscode/settings.json
vendored
@ -1,51 +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",
|
||||
"onedrive",
|
||||
"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
@ -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,
|
||||
|
40
README.md
@ -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:
|
||||
* <canvas>: http://www.browserleaks.com/canvas
|
||||
* <canvas>: 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,14 +44,9 @@ 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
|
||||
@ -39,7 +57,7 @@ Special thanks to:
|
||||
* 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
|
||||
|
1670
_locales/cs/messages.json
Normal 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": {
|
||||
@ -64,7 +72,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"options_title": {
|
||||
"message": "CanvasBlocker Einstellungen",
|
||||
"message": "CanvasBlocker-Einstellungen",
|
||||
"description": ""
|
||||
},
|
||||
"optionsIntroduction": {
|
||||
@ -83,10 +91,22 @@
|
||||
"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": ""
|
||||
@ -96,7 +116,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"group_misc": {
|
||||
"message": "Vermischtes",
|
||||
"message": "Sonstiges",
|
||||
"description": ""
|
||||
},
|
||||
"section_asking": {
|
||||
@ -116,7 +136,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"section_misc": {
|
||||
"message": "Vermischtes",
|
||||
"message": "Sonstiges",
|
||||
"description": ""
|
||||
},
|
||||
"section_settings": {
|
||||
@ -143,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": ""
|
||||
@ -163,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": ""
|
||||
@ -259,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": ""
|
||||
@ -271,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": ""
|
||||
@ -420,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": {
|
||||
@ -432,7 +520,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"rng_options.nonPersistent": {
|
||||
"message": "nicht persistent",
|
||||
"message": "nichtpersistent",
|
||||
"description": ""
|
||||
},
|
||||
"rng_options.white": {
|
||||
@ -551,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": ""
|
||||
@ -619,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": ""
|
||||
@ -707,6 +839,10 @@
|
||||
"message": "Was soll erlaubt werden?",
|
||||
"description": ""
|
||||
},
|
||||
"selectWhitelistType": {
|
||||
"message": "Wie soll erlaubt werden?",
|
||||
"description": ""
|
||||
},
|
||||
"whitelistOnlyAPI": {
|
||||
"message": "Erlaube nur die {api}",
|
||||
"description": ""
|
||||
@ -1016,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": {
|
||||
@ -1039,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": ""
|
||||
@ -1075,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": ""
|
||||
@ -1091,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": ""
|
||||
@ -1247,6 +1459,10 @@
|
||||
"message": "Einstellungen",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_faq": {
|
||||
"message": "FAQ",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_test": {
|
||||
"message": "Testen",
|
||||
"description": ""
|
||||
@ -1307,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": ""
|
||||
@ -1340,7 +1560,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_resolution.switchToNonPersistentRng": {
|
||||
"message": "wechsle zu \"nicht persistent\"",
|
||||
"message": "wechsle zu \"nichtpersistent\"",
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_error.fakeEverythingInCanvas": {
|
||||
@ -1375,10 +1595,18 @@
|
||||
"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": ""
|
||||
@ -1428,7 +1656,15 @@
|
||||
"description": ""
|
||||
},
|
||||
"preset_max_protection_description": {
|
||||
"message": "Maximiert den Schutz gegen die Fingerprint-Extraktion. Diese Einstellungen werden einige Seiten unbenutzbar machen, können den Browser etwas verlangsamen und es Seiten ermöglichen die Verwendung von CanvasBlocker zu detektieren.",
|
||||
"message": "Maximiert den Schutz gegen die Fingerprint-Extraktion. Diese Einstellungen werden einige Seiten unbenutzbar machen, können den Browser etwas verlangsamen und es Seiten ermöglichen die Verwendung von CanvasBlocker zu detektieren. Wenn Sie diese Voreinstellung angewendet haben, sollten Sie in Betracht ziehen auch die reCAPTCHA-Voreinstellung zu verwenden.",
|
||||
"description": ""
|
||||
},
|
||||
"preset_recaptcha_title": {
|
||||
"message": "reCAPTCHA-Ausnahme",
|
||||
"description": ""
|
||||
},
|
||||
"preset_recaptcha_description": {
|
||||
"message": "Der Window-API-Schutz macht reCAPTCHA unbenutzbar. Diese Voreinstellung erlaubt die Benutzung der window.name-API in eingebetteten Seiten, wodurch es wieder funktioniert.",
|
||||
"description": ""
|
||||
}
|
||||
}
|
@ -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",
|
||||
@ -84,15 +92,28 @@
|
||||
"message": "CanvasBlocker was updated. If you want to be able to access this page in the future and have not bookmarked it yet, please bookmark it.",
|
||||
"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": ""
|
||||
@ -130,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",
|
||||
@ -173,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": ""
|
||||
@ -271,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": ""
|
||||
@ -283,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": {
|
||||
@ -303,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": ""
|
||||
},
|
||||
@ -409,7 +500,7 @@
|
||||
},
|
||||
|
||||
"urlSettings_title": {
|
||||
"message": "Site specific values",
|
||||
"message": "Site-specific values",
|
||||
"description": ""
|
||||
},
|
||||
"urlSettings_description": {
|
||||
@ -441,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": {
|
||||
@ -453,7 +544,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"rng_options.nonPersistent": {
|
||||
"message": "non persistent",
|
||||
"message": "nonpersistent",
|
||||
"description": ""
|
||||
},
|
||||
"rng_options.white": {
|
||||
@ -581,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": ""
|
||||
@ -652,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": ""
|
||||
@ -742,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": ""
|
||||
@ -1058,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": ""
|
||||
},
|
||||
|
||||
@ -1084,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": ""
|
||||
@ -1122,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": ""
|
||||
@ -1139,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": ""
|
||||
@ -1304,6 +1524,10 @@
|
||||
"message": "Settings",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_faq": {
|
||||
"message": "FAQ",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_test": {
|
||||
"message": "Test",
|
||||
"description": ""
|
||||
@ -1365,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": ""
|
||||
@ -1398,7 +1626,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_resolution.switchToNonPersistentRng": {
|
||||
"message": "switch to \"non persistent\" rng",
|
||||
"message": "switch to \"nonpersistent\" rng",
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_error.fakeEverythingInCanvas": {
|
||||
@ -1433,11 +1661,19 @@
|
||||
"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": ""
|
||||
@ -1489,7 +1725,15 @@
|
||||
"description": ""
|
||||
},
|
||||
"preset_max_protection_description": {
|
||||
"message": "Maximizes the protection against fingerprint extraction. This settings will break some pages, might slow down the browser a little bit and might enable sites to detect that CanvasBlocker is used.",
|
||||
"message": "Maximizes the protection against fingerprint extraction. These settings will break some pages, might slow down the browser a little bit and might enable sites to detect that CanvasBlocker is used. After applying this preset you should consider applying the reCAPTCHA preset as well.",
|
||||
"description": ""
|
||||
},
|
||||
"preset_recaptcha_title": {
|
||||
"message": "reCAPTCHA exception",
|
||||
"description": ""
|
||||
},
|
||||
"preset_recaptcha_description": {
|
||||
"message": "Protecting the window API breaks reCAPTCHA. This preset allows the usage of the window.name API in embedded pages which will make it work again.",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@
|
||||
"message": "Altera algunas API de JS para evitar la huella digital.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"browserAction_title_default": {
|
||||
"message": "CanvasBlocker",
|
||||
"description": ""
|
||||
@ -21,10 +20,17 @@
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_title_protectedAPIs": {
|
||||
"message": "\n \u00B7 {api}",
|
||||
"message": "\n · {api}",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_status_on": {
|
||||
"message": "CanvasBlocker activado",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_status_off": {
|
||||
"message": "CanvasBlocker desactivado",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"more": {
|
||||
"message": "más",
|
||||
"description": ""
|
||||
@ -41,17 +47,26 @@
|
||||
"message": "Buscar",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"ok": {
|
||||
"message": "Aceptar",
|
||||
"description": ""
|
||||
},
|
||||
"apply": {
|
||||
"message": "Aplicar",
|
||||
"description": ""
|
||||
},
|
||||
"cancel": {
|
||||
"message": "Cancelar",
|
||||
"description": ""
|
||||
},
|
||||
"input": {
|
||||
"message": "entrada",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"readout": {
|
||||
"message": "lectura",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"options": {
|
||||
"message": "Configuración",
|
||||
"description": ""
|
||||
@ -69,18 +84,41 @@
|
||||
"description": ""
|
||||
},
|
||||
"updateNotice": {
|
||||
"message": "Se ha actualizado CanvasBlocker. Si quiere poder acceder a está página más adelante y no la tiene en los marcadores, añádala.",
|
||||
"message": "Se ha actualizado CanvasBlocker. Si quieres poder acceder a está página más adelante y no la tienes en los marcadores, añádela.",
|
||||
"description": ""
|
||||
},
|
||||
"dontShowOptionsOnUpdate":{
|
||||
"dontShowOptionsOnUpdate": {
|
||||
"message": "No mostrar de nuevo al actualizar.",
|
||||
"description": ""
|
||||
},
|
||||
"resistFingerprintingNotice": {
|
||||
"message": "Tienes habilitado privacy.resistFingerprinting. Esto cambia ligeramente el comportamiento de CanvasBlocker. Más información {link:aquí:https://github.com/kkapsner/CanvasBlocker/issues/158} y {link:aquí:https://github.com/ghacksuserjs/ghacks-user.js/issues/767}.",
|
||||
"description": ""
|
||||
},
|
||||
"settingsNotice.dom.webAudio.enabled": {
|
||||
"message": "Tiene dom.webAudio.enabled desactivado. Esto le hace más rastreable ya que muy poca gente lo hace.",
|
||||
"description": ""
|
||||
},
|
||||
"openInTab": {
|
||||
"message": "Abrir en una pestaña separada",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"labelForDefaultOption": {
|
||||
"message": " (default)",
|
||||
"description": ""
|
||||
},
|
||||
"group_general": {
|
||||
"message": "General",
|
||||
"description": ""
|
||||
},
|
||||
"group_APIs": {
|
||||
"message": "API",
|
||||
"description": ""
|
||||
},
|
||||
"group_misc": {
|
||||
"message": "Varios",
|
||||
"description": ""
|
||||
},
|
||||
"section_asking": {
|
||||
"message": "Preguntando",
|
||||
"description": ""
|
||||
@ -98,38 +136,49 @@
|
||||
"description": ""
|
||||
},
|
||||
"section_misc": {
|
||||
"message": "Miscelánea",
|
||||
"message": "Varios",
|
||||
"description": ""
|
||||
},
|
||||
"section_settings": {
|
||||
"message": "Configuración",
|
||||
"description": ""
|
||||
},
|
||||
"section_canvas-api":{
|
||||
"section_canvas-api": {
|
||||
"message": "API de canvas",
|
||||
"description": ""
|
||||
},
|
||||
"section_audio-api":{
|
||||
"section_audio-api": {
|
||||
"message": "API de audio",
|
||||
"description": ""
|
||||
},
|
||||
"section_history-api":{
|
||||
"message": "API de history",
|
||||
"section_history-api": {
|
||||
"message": "API history",
|
||||
"description": ""
|
||||
},
|
||||
"section_window-api":{
|
||||
"section_window-api": {
|
||||
"message": "API de window",
|
||||
"description": ""
|
||||
},
|
||||
"section_DOMRect-api":{
|
||||
"section_DOMRect-api": {
|
||||
"message": "API de DOMRect",
|
||||
"description": ""
|
||||
},
|
||||
"section_Navigator-api":{
|
||||
"message": "API de navigator",
|
||||
"section_SVG-api": {
|
||||
"message": "SVG API",
|
||||
"description": ""
|
||||
},
|
||||
"section_TextMetrics-api": {
|
||||
"message": "API TextMetrics",
|
||||
"description": ""
|
||||
},
|
||||
"section_Navigator-api": {
|
||||
"message": "API navigator",
|
||||
"description": ""
|
||||
},
|
||||
"section_Screen-api": {
|
||||
"message": "API pantalla",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"displayAdvancedSettings_title": {
|
||||
"message": "Modo para expertos",
|
||||
"description": ""
|
||||
@ -138,7 +187,6 @@
|
||||
"message": "Muestra opciones adicionales.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"displayDescriptions_title": {
|
||||
"message": "Mostrar las descripciones",
|
||||
"description": ""
|
||||
@ -147,12 +195,30 @@
|
||||
"message": "Muestra las descripciones de las opciones.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"disruptSessionOnUpdate_title": {
|
||||
"message": "Interrumpir sesión al actualizar",
|
||||
"description": ""
|
||||
},
|
||||
"disruptSessionOnUpdate_description": {
|
||||
"message": "Si se establece en true la extensión se actualizará tan pronto como la actualización esté disponible. Esto podría romper algunas pestañas que están actualmente abiertas.",
|
||||
"description": ""
|
||||
},
|
||||
"reloadExtension_title": {
|
||||
"message": "Recargar extensión",
|
||||
"description": ""
|
||||
},
|
||||
"reloadExtension_description": {
|
||||
"message": "Realizar una actualización pendiente.",
|
||||
"description": ""
|
||||
},
|
||||
"reloadExtension_label": {
|
||||
"message": "Recargar",
|
||||
"description": ""
|
||||
},
|
||||
"hideSetting": {
|
||||
"message": "Haga clic aquí para ocultar esta opción.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"displayHiddenSettings_title": {
|
||||
"message": "Mostrar las opciones ocultas",
|
||||
"description": ""
|
||||
@ -161,7 +227,6 @@
|
||||
"message": "Activar para mostrar las opciones ocultas.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"askForInvisiblePermission": {
|
||||
"message": "¿Quiere permitir los <canvas> invisibles?",
|
||||
"description": ""
|
||||
@ -246,6 +311,30 @@
|
||||
"message": "¿Quiere permitir la lectura para la API de DOMRect?",
|
||||
"description": ""
|
||||
},
|
||||
"askForSVGPermission": {
|
||||
"message": "¿Quiere permitir la API SVG?",
|
||||
"description": ""
|
||||
},
|
||||
"askForSVGInputPermission": {
|
||||
"message": "¿Quieres permitir la entrada de la API SVG?",
|
||||
"description": ""
|
||||
},
|
||||
"askForSVGReadoutPermission": {
|
||||
"message": "¿Quieres permitir la lectura de la API SVG?",
|
||||
"description": ""
|
||||
},
|
||||
"askForTextMetricsPermission": {
|
||||
"message": "¿Quiere permitir la API TextMetrics?",
|
||||
"description": ""
|
||||
},
|
||||
"askForTextMetricsInputPermission": {
|
||||
"message": "¿Quiere permitir la entrada de la API de TextMetrics?",
|
||||
"description": ""
|
||||
},
|
||||
"askForTextMetricsReadoutPermission": {
|
||||
"message": "¿Quiere permitir la lectura de la API de TextMetrics?",
|
||||
"description": ""
|
||||
},
|
||||
"askForNavigatorPermission": {
|
||||
"message": "¿Quiere permitir la API de navigator?",
|
||||
"description": ""
|
||||
@ -258,6 +347,18 @@
|
||||
"message": "¿Quiere permitir la lectura para la API de navigator?",
|
||||
"description": ""
|
||||
},
|
||||
"askForScreenPermission": {
|
||||
"message": "¿Quiere permitir la API de pantalla?",
|
||||
"description": ""
|
||||
},
|
||||
"askForScreenInputPermission": {
|
||||
"message": "¿Quiere permitir la entrada de la API de pantalla?",
|
||||
"description": ""
|
||||
},
|
||||
"askForScreenReadoutPermission": {
|
||||
"message": "¿Quiere permitir la lectura de la API de pantalla?",
|
||||
"description": ""
|
||||
},
|
||||
"askOnlyOnce_title": {
|
||||
"message": "Pregunar una sola vez",
|
||||
"description": ""
|
||||
@ -278,36 +379,34 @@
|
||||
"message": "combinado",
|
||||
"description": ""
|
||||
},
|
||||
"askDenyMode_title":{
|
||||
"askDenyMode_title": {
|
||||
"message": "Preguntar por el modo de denegación",
|
||||
"description": ""
|
||||
},
|
||||
"askDenyMode_description":{
|
||||
"askDenyMode_description": {
|
||||
"message": "Qué modo se debe usar cuando se deniega un permiso.",
|
||||
"description": ""
|
||||
},
|
||||
"askDenyMode_options.block":{
|
||||
"askDenyMode_options.block": {
|
||||
"message": "bloquear",
|
||||
"description": ""
|
||||
},
|
||||
"askDenyMode_options.fake":{
|
||||
"askDenyMode_options.fake": {
|
||||
"message": "falsear",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"showCanvasWhileAsking_title":{
|
||||
"showCanvasWhileAsking_title": {
|
||||
"message": "Mostrar el contenido del «canvas»",
|
||||
"description": ""
|
||||
},
|
||||
"showCanvasWhileAsking_description":{
|
||||
"showCanvasWhileAsking_description": {
|
||||
"message": "Muestra el contenido del «canvas» para el que se ha pedido permiso, si es posible.",
|
||||
"description": ""
|
||||
},
|
||||
"showCanvasWhileAsking_message":{
|
||||
"showCanvasWhileAsking_message": {
|
||||
"message": "La página web quiere leer el contenido del siguiente «canvas»:",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"blackList_description": {
|
||||
"message": "Los dominios o URL donde bloquear siempre todas las API. Para añadir varias entradas, sepárelas con comas.",
|
||||
"description": ""
|
||||
@ -316,7 +415,6 @@
|
||||
"message": "Lista negra",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"blockMode_description": {
|
||||
"message": "",
|
||||
"description": ""
|
||||
@ -353,7 +451,6 @@
|
||||
"message": "Modo de bloqueo",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"protectedCanvasPart_title": {
|
||||
"message": "Parte protegida de la API de canvas",
|
||||
"description": ""
|
||||
@ -382,7 +479,6 @@
|
||||
"message": "todo",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"urlSettings_title": {
|
||||
"message": "Valores específicos para sitios",
|
||||
"description": ""
|
||||
@ -391,12 +487,10 @@
|
||||
"message": "",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"inputURL": {
|
||||
"message": "Introduzca un dominio o «RegExp» de URL:",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"minFakeSize_description": {
|
||||
"message": "Los «canvas» con un área menor o igual que este número no se falsean. Es un parámetro para prevenir la detección.\nPRECAUCIÓN: esto reduce la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 100.",
|
||||
"description": ""
|
||||
@ -405,7 +499,6 @@
|
||||
"message": "Tamaño mínimo para falsear",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"maxFakeSize_description": {
|
||||
"message": "Los «canvas» con un área mayor que este número no se falsean (introduzca un cero para deshabilitarlo). Es un parámetro de rendimiento que puede prevenir bloqueos del navegador y debe ajustarse a la potencia de cálculo del dispositivo.\nPRECAUCIÓN: esto reduce la seguridad del complemento, por lo que es muy recomendable no darle un valor menor que 1000000.",
|
||||
"description": ""
|
||||
@ -414,9 +507,8 @@
|
||||
"message": "Tamaño máximo para falsear",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"rng_description": {
|
||||
"message": "ninguno (totalmente blanco): se devuelve una imagen totalmente blanca. La opción «Falsear el canal alfa» debe activarse junto a esta. PRECAUCIÓN: no usar con el modo «falsear a la entrada».\nno persistente: los números aleatorios se vuelven a calcular para cada acción de falseado.\nconstante: en una página web, un color se falsea siempre por el mismo color.\npersistente: el número aleatorio solo se calcula una vez por cada dominio.",
|
||||
"message": "none (completely white): a completely white image is returned. The option \"Fake the alpha channel\" should be activated with this. CAUTION: Do not use this with the \"fake at input\" mode.\n\nnonpersistent: the random numbers will be determined freshly for each faking action. Keep in mind that many API protections have caches in place to prevent detection.\n\nconstant: variation of nonpersistent. But when altering canvas data same colored pixels also share the same color afterwards.\n\npersistent: the random number will only be determined once for every domain.",
|
||||
"description": ""
|
||||
},
|
||||
"rng_options.persistent": {
|
||||
@ -439,7 +531,6 @@
|
||||
"message": "Generador de números aleatorios",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"persistentRndStorage_title": {
|
||||
"message": "Almacenamiento persistente",
|
||||
"description": ""
|
||||
@ -448,7 +539,6 @@
|
||||
"message": "Almacena los datos para el generador de números aleatorios persistente para usarlos tras reiniciar.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"storePersistentRnd_title": {
|
||||
"message": "Almacenar los datos persistentes",
|
||||
"description": ""
|
||||
@ -457,7 +547,6 @@
|
||||
"message": "Si se deben almacenar los datos para el generador de números aleatorios persistente. De lo contrario se descartan al cerrar el navegador.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"persistentRndClearInterval_title": {
|
||||
"message": "Intervalo de borrado de los datos persistentes",
|
||||
"description": ""
|
||||
@ -494,7 +583,6 @@
|
||||
"message": "años",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"clearPersistentRnd_title": {
|
||||
"message": "Limpiar el almacenamiento aleatorio persistente",
|
||||
"description": ""
|
||||
@ -507,7 +595,14 @@
|
||||
"message": "Limpiar",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"clearPersistentRndForContainer_label": {
|
||||
"message": "Limpiar contenedor",
|
||||
"description": ""
|
||||
},
|
||||
"clearPersistentRndForContainer_title": {
|
||||
"message": "Seleccionar contenedor",
|
||||
"description": ""
|
||||
},
|
||||
"sharePersistentRndBetweenDomains_title": {
|
||||
"message": "Compartir la aleatoriedad persistente entre dominios",
|
||||
"description": ""
|
||||
@ -520,7 +615,6 @@
|
||||
"message": "¿Seguro que quiere compartir la aleatoriedad persistente entre dominios?\nPRECAUCIÓN: esto hace al nevegador 100% rastreable y es, por tanto, una amenaza para su privacidad.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"ignoreFrequentColors_title": {
|
||||
"message": "Ignorar los colores más frecuentes",
|
||||
"description": ""
|
||||
@ -529,7 +623,6 @@
|
||||
"message": "Número de colores que no deben falsearse por «canvas». Es un parámetro para evitar la detección.\nPRECAUCIÓN: esto reduce el rendimiento del complemento porque las estadísticas de color deben calcularse para cada imagen. Además, puede reducir la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 3.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"minColors_title": {
|
||||
"message": "Número mínimo de colores",
|
||||
"description": ""
|
||||
@ -538,7 +631,6 @@
|
||||
"message": "Número de colores que debe falsearse en un «canvas».\nPRECAUCIÓN: esto puede reducir la seguridad del complemento, por lo que es muy recomendable no darle un valor mayor que 10.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"fakeAlphaChannel_title": {
|
||||
"message": "Falsear el canal alfa",
|
||||
"description": ""
|
||||
@ -547,7 +639,38 @@
|
||||
"message": "Activa la falsificación del canal alfa (transparencia).",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"webGLVendor_title": {
|
||||
"message": "Proveedor webGL reportado",
|
||||
"description": ""
|
||||
},
|
||||
"webGLVendor_description": {
|
||||
"message": "Value to be reported in the webGL function \"getParameter\" for the \"vendor\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
|
||||
"description": ""
|
||||
},
|
||||
"webGLRenderer_title": {
|
||||
"message": "Renderizador webGL reportado",
|
||||
"description": ""
|
||||
},
|
||||
"webGLRenderer_description": {
|
||||
"message": "Value to be reported in the webGL function \"getParameter\" for the \"renderer\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
|
||||
"description": ""
|
||||
},
|
||||
"webGLUnmaskedVendor_title": {
|
||||
"message": "Proveedor webGL desenmascarado reportado",
|
||||
"description": ""
|
||||
},
|
||||
"webGLUnmaskedVendor_description": {
|
||||
"message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked vendor\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
|
||||
"description": ""
|
||||
},
|
||||
"webGLUnmaskedRenderer_title": {
|
||||
"message": "Renderizador webGL desenmascarado reportado",
|
||||
"description": ""
|
||||
},
|
||||
"webGLUnmaskedRenderer_description": {
|
||||
"message": "Value to be reported in the webGL function \"getParameter\" for the \"unmasked renderer\". Special values:\nLeave empty to use the original value\n\n{undefined}: returns undefined (#508)\n\n{false}: returns the boolean value false (#508)\n\n{empty}: returns an empty string (#508)\n\n{disabled}: returns null (#508)\n\n{random vendor}: returns a random vendor from the built-in list (#493)\n\n{random renderer}: returns a random renderer from the built-in list (#493)\n\n<xxx|yyy|zzz>: picks one of the given options xxx, yyy or zzz at random (arbitrary number of options) (#493)",
|
||||
"description": ""
|
||||
},
|
||||
"useCanvasCache_title": {
|
||||
"message": "Usar la caché para «canvas»",
|
||||
"description": ""
|
||||
@ -556,7 +679,6 @@
|
||||
"message": "Activa la caché para «canvas». Puede prevenir la detección y aumenta el rendimiento cuando se leen varias veces «canvas» pequeños, pero lo reduce para los «canvas» grandes.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"protectedAPIFeatures_title": {
|
||||
"message": "Funciones protegidas de la API",
|
||||
"description": ""
|
||||
@ -565,7 +687,6 @@
|
||||
"message": "Lista de funciones protegidas de la API. Al desmarcar una casilla, la función de la API queda desprotegida.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"disableNotifications": {
|
||||
"message": "deshabilitar las notificaciones",
|
||||
"description": ""
|
||||
@ -590,7 +711,6 @@
|
||||
"message": "Usar la lista blanca de archivos específicos",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"preBlock": {
|
||||
"message": "API bloqueada en {url} porque la configuración de CanvasBlocker no se ha cargado a tiempo.",
|
||||
"description": ""
|
||||
@ -619,15 +739,26 @@
|
||||
"message": "Lectura de DOMRect falseada en {url}",
|
||||
"description": ""
|
||||
},
|
||||
"fakedSVGReadout": {
|
||||
"message": "Lectura SVG falseada en {url}",
|
||||
"description": ""
|
||||
},
|
||||
"fakedTextMetricsReadout": {
|
||||
"message": "Lectura de TextMetrics falseada en {url}",
|
||||
"description": ""
|
||||
},
|
||||
"fakedNavigatorReadout": {
|
||||
"message": "Lectura de navigator falseada en {url}",
|
||||
"description": ""
|
||||
},
|
||||
"fakedScreenReadout": {
|
||||
"message": "Lectura de screen falseada en {url}",
|
||||
"description": ""
|
||||
},
|
||||
"fakedInput": {
|
||||
"message": "Falseado a la entrada en {url}",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"ignoreList_description": {
|
||||
"message": "Los dominios o URL donde no mostrar notificaciones. Para añadir varias entradas, sepárelas con comas.",
|
||||
"description": ""
|
||||
@ -636,7 +767,6 @@
|
||||
"message": "Lista de ignorados",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"ignoredAPIs_title": {
|
||||
"message": "API ignoradas",
|
||||
"description": ""
|
||||
@ -645,6 +775,10 @@
|
||||
"message": "No se mostrarán notificaciones para las API seleccionadas.",
|
||||
"description": ""
|
||||
},
|
||||
"localFile": {
|
||||
"message": "archivo local",
|
||||
"description": ""
|
||||
},
|
||||
"ignorelistDomain": {
|
||||
"message": "silenciar el dominio",
|
||||
"description": ""
|
||||
@ -705,6 +839,10 @@
|
||||
"message": "¿Cuál es el alcance de la lista blanca?",
|
||||
"description": ""
|
||||
},
|
||||
"selectWhitelistType": {
|
||||
"message": "¿Cuál es el tipo de lista blanca?",
|
||||
"description": ""
|
||||
},
|
||||
"whitelistOnlyAPI": {
|
||||
"message": "Permitir solo {api}",
|
||||
"description": ""
|
||||
@ -849,7 +987,6 @@
|
||||
"message": "inspeccionar la lista blanca",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"sessionWhiteList_title": {
|
||||
"message": "Lista blanca de la sesión",
|
||||
"description": ""
|
||||
@ -858,7 +995,6 @@
|
||||
"message": "Los dominios o URL a los que se permite usar todas las API en la sesión actual. Para añadir varias entradas, sepárelas con comas.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"whitelistDomainTemporarily": {
|
||||
"message": "permitir el dominio temporalmente",
|
||||
"description": ""
|
||||
@ -867,7 +1003,6 @@
|
||||
"message": "permitir el URL temporalmente",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"storeNotificationData_title": {
|
||||
"message": "Guardar datos detallados de las notificaciones",
|
||||
"description": ""
|
||||
@ -876,7 +1011,6 @@
|
||||
"message": "",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"storeImageForInspection_title": {
|
||||
"message": "Guardar la imagen para inspeccionarla",
|
||||
"description": ""
|
||||
@ -885,9 +1019,8 @@
|
||||
"message": "Habilita el almacenamiento del contenido del «canvas» falseado.\nPRECAUCIÓN: esto puede usar mucha memoria.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"protectAudio_title": {
|
||||
"message": "Proteger la API de audio",
|
||||
"message": "Proteger la API audio",
|
||||
"description": ""
|
||||
},
|
||||
"protectAudio_description": {
|
||||
@ -923,19 +1056,19 @@
|
||||
"description": ""
|
||||
},
|
||||
"audioFakeRate_options.0.1%": {
|
||||
"message": "0.1% de los valores",
|
||||
"message": "0,1 % de los valores",
|
||||
"description": ""
|
||||
},
|
||||
"audioFakeRate_options.1%": {
|
||||
"message": "1% de los valores",
|
||||
"message": "1 % de los valores",
|
||||
"description": ""
|
||||
},
|
||||
"audioFakeRate_options.10%": {
|
||||
"message": "10% de los valores",
|
||||
"message": "10 % de los valores",
|
||||
"description": ""
|
||||
},
|
||||
"audioFakeRate_options.100%": {
|
||||
"message": "100% de los valores",
|
||||
"message": "100 % de los valores",
|
||||
"description": ""
|
||||
},
|
||||
"audioNoiseLevel_title": {
|
||||
@ -975,7 +1108,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"useAudioCache_urlSpecific": {
|
||||
"message": "Algunas páginas usan guiones para crear huellas digitales muy mal escritos que ralentizan Firefox. Para deshabilitar la caché para sitios web específicos, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casilla.",
|
||||
"message": "Algunas páginas usan scripts muy mal escritos con audio para crear huella digital que ralentizan Firefox. Para deshabilitar la caché para sitios web específicos, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
"audioUseFixedIndices_title": {
|
||||
@ -994,7 +1127,6 @@
|
||||
"message": "Los índices que siempre se falsean. Para añadir varias entradas, sepárelas con comas.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"historyLengthThreshold_title": {
|
||||
"message": "Tamaño máximo del historial",
|
||||
"description": ""
|
||||
@ -1008,7 +1140,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"protectWindow_title": {
|
||||
"message": "Proteger la API de window",
|
||||
"message": "Proteger la API window",
|
||||
"description": ""
|
||||
},
|
||||
"protectWindow_description": {
|
||||
@ -1016,27 +1148,37 @@
|
||||
"description": ""
|
||||
},
|
||||
"protectWindow_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casilla.",
|
||||
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
"protectWindow_askReCaptchaException": {
|
||||
"message": "La protección de la API de window rompe los reCAPTCHA. ¿Quiere añadir una excepción para eso?",
|
||||
"message": "La protección de la API window rompe reCAPTCHA. ¿Quieres permitir la API window.name en las páginas integradas para que vuelva a funcionar?",
|
||||
"description": ""
|
||||
},
|
||||
"allowWindowNameInFrames_title": {
|
||||
"message": "Permitir window.name en frames",
|
||||
"description": ""
|
||||
},
|
||||
"allowWindowNameInFrames_description": {
|
||||
"message": "La API window.name no es tan peligrosa en el contexto de las páginas incrustadas y se utiliza allí por razones legítimas (por ejemplo, reCAPTCHA). Esta configuración permitirá estos usos.",
|
||||
"description": ""
|
||||
},
|
||||
"allowWindowNameInFrames_urlSpecific": {
|
||||
"message": "Para permitir esto solo para sitios web específicos, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y marca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"protectDOMRect_title": {
|
||||
"message": "Proteger la API de DOMRect",
|
||||
"message": "Proteger la API DOMRect",
|
||||
"description": ""
|
||||
},
|
||||
"protectDOMRect_description": {
|
||||
"message": "Protege contra la huella digital mediante «getClientRects()» y varios métodos similares.",
|
||||
"message": "Esto protege contra la huella digital mediante «getClientRects()» y varios métodos similares.",
|
||||
"description": ""
|
||||
},
|
||||
"protectDOMRect_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casilla.",
|
||||
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"domRectIntegerFactor_title": {
|
||||
"message": "Factor de número entero de DOMRect",
|
||||
"description": ""
|
||||
@ -1045,20 +1187,42 @@
|
||||
"message": "Alguna fracción de un píxel puede ser controlada con CSS. Para prevenir la detección, los valores de un DOMRect que multiplicados por este factor sean números enteros no serán modificados.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"protectSVG_title": {
|
||||
"message": "Proteger API SVG",
|
||||
"description": ""
|
||||
},
|
||||
"protectSVG_description": {
|
||||
"message": "Esto protege contra la toma de huellas dactilares usando SVGs.",
|
||||
"description": ""
|
||||
},
|
||||
"protectSVG_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o la URL haciendo clic en \"+\" y quite su marca de verificación.",
|
||||
"description": ""
|
||||
},
|
||||
"protectTextMetrics_title": {
|
||||
"message": "Proteger la API TextMetrics",
|
||||
"description": ""
|
||||
},
|
||||
"protectTextMetrics_description": {
|
||||
"message": "Esto protege contra la huella digital mediante «measureText()» que se pueden usar para validar de forma cruzada los valores DOMRect.",
|
||||
"description": ""
|
||||
},
|
||||
"protectTextMetrics_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
"protectNavigator_title": {
|
||||
"message": "Proteger la API de navigator",
|
||||
"message": "Proteger la API navigator",
|
||||
"description": ""
|
||||
},
|
||||
"protectNavigator_description": {
|
||||
"message": "Esta página permite cambios en la API de navigator. Habilitar esta protección no cambia nada por defecto. Abra la configuración de navigator para especificar allí los cambios que desee.",
|
||||
"message": "Esta página permite cambios en la API navigator. Habilitar esta protección no cambia nada por defecto. Abre la configuración de navigator para especificar allí los cambios que desees.",
|
||||
"description": ""
|
||||
},
|
||||
"protectNavigator_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casila.",
|
||||
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"openNavigatorSettings_title": {
|
||||
"message": "Configuración de navigator",
|
||||
"description": ""
|
||||
@ -1071,19 +1235,22 @@
|
||||
"message": "Abrir",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"navigatorSettings_title": {
|
||||
"message": "Configuración de CanvasBlocker para navigator",
|
||||
"description": ""
|
||||
},
|
||||
"navigatorSettings_description": {
|
||||
"message": "En esta página puede cambiar la configuración de navigator. Si usa un preajuste, debe usar siempre uno para sistema operativo y otro para el navegador. Después de seleccionarlos, todavía puede hacer modificaciones.",
|
||||
"message": "En esta página puedes cambiar la configuración de navigator. Si usas un preajuste, siempre debes usar uno para el sistema operativo y otro para el navegador. Después de seleccionarlos, todavía puede hacer modificaciones.",
|
||||
"description": ""
|
||||
},
|
||||
"navigatorSettings_disclaimer": {
|
||||
"message": "PRECAUCIÓN: el navegador en uso no puede falsearse por completo ya que hay multitud de formas para detectarlo. Por ejemplo, la detección de características y representación específica del navegador de elementos de HTML siempre van a dejar escapar información.",
|
||||
"description": ""
|
||||
},
|
||||
"navigatorSettings_contextualIdentities": {
|
||||
"message": "Se muestra la configuración del contenedor {select}.",
|
||||
"description": ""
|
||||
},
|
||||
"navigatorSettings_presetSection.os": {
|
||||
"message": "Preajustes del sistema operativo",
|
||||
"description": ""
|
||||
@ -1097,10 +1264,45 @@
|
||||
"description": ""
|
||||
},
|
||||
"navigatorSettings_reset": {
|
||||
"message": "Reiniciar",
|
||||
"message": "Restablecer",
|
||||
"description": ""
|
||||
},
|
||||
"protectScreen_title": {
|
||||
"message": "Proteger la API screen",
|
||||
"description": ""
|
||||
},
|
||||
"protectScreen_description": {
|
||||
"message": "Esto protege contra los intentos de crear huella digital, incluido el tamaño de la pantalla.",
|
||||
"description": ""
|
||||
},
|
||||
"protectScreen_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
"screenSize_title": {
|
||||
"message": "Tamaño de pantalla",
|
||||
"description": ""
|
||||
},
|
||||
"screenSize_description": {
|
||||
"message": "Si se establece con un valor \"...x...\" las dimensiones especificadas serán reportadas como el tamaño de la pantalla.",
|
||||
"description": ""
|
||||
},
|
||||
"screenSize_urlSpecific": {
|
||||
"message": "Para proporcionar tamaños específicos para ciertos sitios web, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» e ingresa el valor deseado.",
|
||||
"description": ""
|
||||
},
|
||||
"fakeMinimalScreenSize_title": {
|
||||
"message": "Tamaño mínimo falso de la pantalla",
|
||||
"description": ""
|
||||
},
|
||||
"fakeMinimalScreenSize_description": {
|
||||
"message": "Utilice un tamaño de pantalla mínimo del siguiente conjunto que pueda ajustarse a las dimensiones de la ventana interior. Tamaños de pantalla: 1366x768, 1440x900, 1600x900, 1920x1080, 4096x2160, 8192x4320",
|
||||
"description": ""
|
||||
},
|
||||
"fakeMinimalScreenSize_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos del falseado, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"theme_title": {
|
||||
"message": "Tema",
|
||||
"description": ""
|
||||
@ -1114,7 +1316,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"theme_options.default": {
|
||||
"message": "por defecto",
|
||||
"message": "predeterminado",
|
||||
"description": ""
|
||||
},
|
||||
"theme_options.light": {
|
||||
@ -1133,7 +1335,6 @@
|
||||
"message": "ninguno",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"blockDataURLs_title": {
|
||||
"message": "Bloquear las páginas de URL de datos",
|
||||
"description": ""
|
||||
@ -1143,10 +1344,9 @@
|
||||
"description": ""
|
||||
},
|
||||
"blockDataURLs_urlSpecific": {
|
||||
"message": "Para excluir sitios web específicos de esta protección, haga clic en la flecha negra para abrir el menú, añada el dominio o URL haciendo clic en «+» y desmarque la casila.",
|
||||
"message": "Para excluir sitios web específicos de esta protección, haz clic en la flecha negra para abrir el menú, añade el dominio o URL haciendo clic en «+» y desmarca la casilla.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"showReleaseNotes_title": {
|
||||
"message": "Notas de la versión",
|
||||
"description": ""
|
||||
@ -1159,7 +1359,6 @@
|
||||
"message": "Mostrar",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"logLevel_title": {
|
||||
"message": "Nivel de registro",
|
||||
"description": ""
|
||||
@ -1192,21 +1391,19 @@
|
||||
"message": "detallado",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"exportSettings_title": {
|
||||
"message": "Exportar la configuración",
|
||||
"message": "Exportar configuración",
|
||||
"description": ""
|
||||
},
|
||||
"exportSettings_description": {
|
||||
"message": "",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"openSettingSanitation_title": {
|
||||
"message": "Saneado de la configuración",
|
||||
"settingControlling_title": {
|
||||
"message": "Control de configuración",
|
||||
"description": ""
|
||||
},
|
||||
"openSettingSanitation_description": {
|
||||
"settingControlling_description": {
|
||||
"message": "",
|
||||
"description": ""
|
||||
},
|
||||
@ -1214,7 +1411,22 @@
|
||||
"message": "Abrir",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"inspectWhitelist_label": {
|
||||
"message": "Inspeccionar la lista blanca",
|
||||
"description": ""
|
||||
},
|
||||
"openSettingPresets_title": {
|
||||
"message": "Preajustes",
|
||||
"description": ""
|
||||
},
|
||||
"openSettingPresets_description": {
|
||||
"message": "Esto abre la página de preajustes que se mostró en la instalación. Los preajustes son colecciones de valores de configuración para algunos casos de uso común de CanvasBlocker.",
|
||||
"description": ""
|
||||
},
|
||||
"openSettingPresets_label": {
|
||||
"message": "Abrir",
|
||||
"description": ""
|
||||
},
|
||||
"inspectSettings_label": {
|
||||
"message": "Inspeccionar",
|
||||
"description": ""
|
||||
@ -1227,13 +1439,8 @@
|
||||
"message": "Cargar",
|
||||
"description": ""
|
||||
},
|
||||
"inspectWhitelist_label": {
|
||||
"message": "Inspeccionar la lista blanca",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"resetSettings_title": {
|
||||
"message": "Reiniciar la configuración",
|
||||
"message": "Restablecer configuración",
|
||||
"description": ""
|
||||
},
|
||||
"resetSettings_description": {
|
||||
@ -1241,18 +1448,21 @@
|
||||
"description": ""
|
||||
},
|
||||
"resetSettings_label": {
|
||||
"message": "Reiniciar",
|
||||
"message": "Restablecer",
|
||||
"description": ""
|
||||
},
|
||||
"resetSettings_confirm": {
|
||||
"message": "¿Seguro que quiere reiniciar toda la configuración?",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"browserAction_settings": {
|
||||
"message": "Configuración",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_faq": {
|
||||
"message": "Preguntas frecuentes",
|
||||
"description": ""
|
||||
},
|
||||
"browserAction_test": {
|
||||
"message": "Probar",
|
||||
"description": ""
|
||||
@ -1265,7 +1475,6 @@
|
||||
"message": "Notificar problemas",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"sanitation_title": {
|
||||
"message": "Saneado de la configuración",
|
||||
"description": ""
|
||||
@ -1314,6 +1523,10 @@
|
||||
"message": "Todas las opciones de {api} están deshabilitadas pero la protección está habilitada.",
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_error.disabledSomeFeatures": {
|
||||
"message": "Algunas características de {api} están deshabilitadas. Esto sólo debe hacerse para probar o si realmente sabe lo que las características están haciendo.",
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_resolution.disableMainFlag": {
|
||||
"message": "deshabilitar la opción principal",
|
||||
"description": ""
|
||||
@ -1379,16 +1592,79 @@
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_error.doNotSharePersistentRndBetweenDomains": {
|
||||
"message": "No compartir la aleatoriedad persistente entre dominios porque hace al navegador 100% rastreable.",
|
||||
"message": "No compartir la aleatoriedad persistente entre dominios porque hace al navegador 100 % rastreable.",
|
||||
"description": ""
|
||||
},
|
||||
"sanitation_error.customScreenSize": {
|
||||
"message": "No utilice un tamaño de pantalla personalizado, ya que hace que el navegador sea más rastreable.",
|
||||
"description": ""
|
||||
},
|
||||
|
||||
"whitelist_inspection_title": {
|
||||
"message": "Inspección de la lista blanca de CanvasBlocker",
|
||||
"description": ""
|
||||
},
|
||||
"whitelist_inspection_description": {
|
||||
"message": "Muestra las protecciones de las API que están activas para un sitio determinado. Si eliminas una marca de verificación para una API, esta API no estará protegida para el sitio seleccionado.",
|
||||
"description": ""
|
||||
},
|
||||
"whitelist_all_apis": {
|
||||
"message": "Todas las API",
|
||||
"description": ""
|
||||
},
|
||||
"presets": {
|
||||
"message": "Preajustes",
|
||||
"description": ""
|
||||
},
|
||||
"presets_title": {
|
||||
"message": "Preajustes de CanvasBlocker",
|
||||
"description": ""
|
||||
},
|
||||
"presets_installNotice": {
|
||||
"message": "{image:../icons/icon.svg}Gracias por instalar CanvasBlocker. Para ajustarlo a tus necesidades puedes aplicar algunos preajustes. Para una mayor personalización puedes abrir la página de {link:opciones:options.html}. Allí también puedes abrir esta página de preajustes más tarde.",
|
||||
"description": ""
|
||||
},
|
||||
"presets_introduction": {
|
||||
"message": "Estos son algunos preajustes de configuración para adaptarse a las necesidades de diferentes personas. Los valores entre paréntesis son el estado actual de la configuración. Puedes aplicar múltiples preajustes de configuración uno tras otro, pero alguna configuración puede ser sobrescrita por el que se aplique más tarde.",
|
||||
"description": ""
|
||||
},
|
||||
"preset_default_title": {
|
||||
"message": "Configuración predeterminada",
|
||||
"description": ""
|
||||
},
|
||||
"preset_default_description": {
|
||||
"message": "No se han aplicado ajustes especiales.",
|
||||
"description": ""
|
||||
},
|
||||
"preset_convenience_title": {
|
||||
"message": "Configuración cómoda",
|
||||
"description": ""
|
||||
},
|
||||
"preset_convenience_description": {
|
||||
"message": "Aplica algunas configuraciones para que la experiencia de navegación sea lo más cómoda posible. Esto incluye la lista blanca de algunos sitios.",
|
||||
"description": ""
|
||||
},
|
||||
"preset_stealth_title": {
|
||||
"message": "Ajustes de sigilo",
|
||||
"description": ""
|
||||
},
|
||||
"preset_stealth_description": {
|
||||
"message": "Configura CanvasBlocker para que sea difícil de detectar. Esto puede llevar a un alto uso de la CPU y puede ralentizar el navegador.",
|
||||
"description": ""
|
||||
},
|
||||
"preset_max_protection_title": {
|
||||
"message": "Máxima protección",
|
||||
"description": ""
|
||||
},
|
||||
"preset_max_protection_description": {
|
||||
"message": "Maximiza la protección contra la extracción de huella dagital. Esta configuración romperá algunas páginas, podría ralentizar un poco el navegador y podría permitir a los sitios detectar si utilizas CanvasBlocker. Después de aplicar este preajuste, deberías considerar aplicar también el preajuste reCAPTCHA.",
|
||||
"description": ""
|
||||
},
|
||||
"preset_recaptcha_title": {
|
||||
"message": "Excepción de reCAPTCHA",
|
||||
"description": ""
|
||||
},
|
||||
"preset_recaptcha_description": {
|
||||
"message": "La protección de la API window rompe reCAPTCHA. Este preajuste permite el uso de la API window.name en páginas integradas, lo que hará que vuelva a funcionar.",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
|
1670
_locales/hi/messages.json
Normal file
1670
_locales/ja/messages.json
Normal file
1670
_locales/ko/messages.json
Normal file
1670
_locales/lt/messages.json
Normal file
1670
_locales/nb/messages.json
Normal file
1670
_locales/pl/messages.json
Normal file
1670
_locales/pt/messages.json
Normal file
1670
_locales/pt_BR/messages.json
Normal file
1670
_locales/zh_TW/messages.json
Normal file
@ -20,3 +20,52 @@ 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;
|
||||
}
|
@ -4,15 +4,22 @@
|
||||
<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>
|
||||
<div id="version" class="versionDisplay"></div>
|
||||
<script src="../lib/require.js"></script>
|
||||
<script src="../lib/extension.js"></script>
|
||||
<script src="../lib/logging.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>
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
const currentURL = new URL(currentTab.url);
|
||||
const reloadButton = document.getElementById("reload");
|
||||
reloadButton.addEventListener("click", async function(){
|
||||
await browser.tabs.reload(currentTab.id, {bypassCache: true});
|
||||
window.close();
|
||||
});
|
||||
const addonStatus = document.getElementById("addonStatus");
|
||||
addonStatus.addEventListener("click", async function(){
|
||||
reloadButton.classList.toggle("hidden");
|
||||
if (isWhitelisted(currentURL)){
|
||||
settingContainers.resetUrlValue("blockMode", currentURL);
|
||||
if (settings.get("blockMode").startsWith("allow")){
|
||||
settings.set("blockMode", "fake", currentURL.host);
|
||||
}
|
||||
if (settings.get("blockDataURLs")){
|
||||
settingContainers.resetUrlValue("blockDataURLs", currentURL);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
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: browser.extension.getURL("icons/pageAction-showOptions.svg"),
|
||||
icon: 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");
|
||||
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: browser.extension.getURL("icons/browserAction-test.svg"),
|
||||
icon: extension.getURL("icons/browserAction-test.svg"),
|
||||
action: function(){
|
||||
window.open("https://canvasblocker.kkapsner.de/test", "_blank");
|
||||
browser.tabs.create({url: "https://canvasblocker.kkapsner.de/test"});
|
||||
window.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "review",
|
||||
icon: browser.extension.getURL("icons/browserAction-review.svg"),
|
||||
icon: extension.getURL("icons/browserAction-review.svg"),
|
||||
action: function(){
|
||||
window.open("https://addons.mozilla.org/firefox/addon/canvasblocker/reviews/", "_blank");
|
||||
browser.tabs.create({url: "https://addons.mozilla.org/firefox/addon/canvasblocker/reviews/"});
|
||||
window.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "reportIssue",
|
||||
icon: browser.extension.getURL("icons/browserAction-reportIssue.svg"),
|
||||
icon: extension.getURL("icons/browserAction-reportIssue.svg"),
|
||||
action: function(){
|
||||
window.open("https://github.com/kkapsner/CanvasBlocker/issues", "_blank");
|
||||
browser.tabs.create({url: "https://github.com/kkapsner/CanvasBlocker/issues"});
|
||||
window.close();
|
||||
}
|
||||
},
|
||||
].forEach(function(action){
|
||||
];
|
||||
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
@ -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
|
110
icons/browserAction-CBoff.svg
Normal 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 |
105
icons/browserAction-CBon.svg
Normal 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
@ -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 |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
109
icons/browserAction-reload.svg
Normal 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 |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 7.9 KiB |
@ -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 |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.8 KiB |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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;
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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],
|
||||
|
@ -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, {
|
||||
|
287
lib/extension.js
@ -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,238 @@
|
||||
};
|
||||
Object.seal(scope.message);
|
||||
|
||||
scope.getWrapped = function getWrapped(obj){
|
||||
return obj && (obj.wrappedJSObject || obj);
|
||||
};
|
||||
|
||||
scope.exportFunctionWithName = function exportFunctionWithName(func, context, name){
|
||||
const targetObject = scope.getWrapped(context).Object.create(null);
|
||||
const exportedTry = exportFunction(func, targetObject, {allowCrossOriginArguments: true, defineAs: name});
|
||||
if (exportedTry.name === name){
|
||||
return exportedTry;
|
||||
}
|
||||
else {
|
||||
if (func.name === name){
|
||||
logging.message(
|
||||
"FireFox bug: Need to change name in exportFunction from",
|
||||
exportedTry.name,
|
||||
"(originally correct) to",
|
||||
name
|
||||
);
|
||||
}
|
||||
else {
|
||||
logging.error("Wrong name specified for", name, new Error());
|
||||
}
|
||||
const wrappedContext = scope.getWrapped(context);
|
||||
const options = {
|
||||
allowCrossOriginArguments: true,
|
||||
defineAs: name
|
||||
};
|
||||
const oldDescriptor = Object.getOwnPropertyDescriptor(wrappedContext, name);
|
||||
if (oldDescriptor && !oldDescriptor.configurable){
|
||||
logging.error(
|
||||
"Unable to export function with the correct name", name,
|
||||
"instead we have to use", exportedTry.name
|
||||
);
|
||||
return exportedTry;
|
||||
}
|
||||
const exported = exportFunction(func, context, options);
|
||||
if (oldDescriptor){
|
||||
Object.defineProperty(wrappedContext, name, oldDescriptor);
|
||||
}
|
||||
else {
|
||||
delete wrappedContext[name];
|
||||
}
|
||||
return exported;
|
||||
}
|
||||
};
|
||||
|
||||
const proxies = new Map();
|
||||
const changedWindowsForProxies = new WeakMap();
|
||||
function setupWindowForProxies(window){
|
||||
if (changedWindowsForProxies.get(window)){
|
||||
return;
|
||||
}
|
||||
const wrappedWindow = scope.getWrapped(window);
|
||||
|
||||
const functionPrototype = wrappedWindow.Function.prototype;
|
||||
const originalToString = functionPrototype.toString;
|
||||
changedWindowsForProxies.set(window, originalToString);
|
||||
const alteredToString = scope.createProxyFunction(
|
||||
window,
|
||||
originalToString,
|
||||
function toString(){
|
||||
if (proxies.has(this)){
|
||||
return proxies.get(this).string;
|
||||
}
|
||||
return originalToString.call(scope.getWrapped(this));
|
||||
}
|
||||
);
|
||||
scope.changeProperty(window, "toString", {
|
||||
object: functionPrototype,
|
||||
name: "toString",
|
||||
type: "value",
|
||||
changed: alteredToString
|
||||
});
|
||||
|
||||
const wrappedReflect = wrappedWindow.Reflect;
|
||||
const originalReflectSetPrototypeOf = wrappedReflect.setPrototypeOf;
|
||||
const alteredReflectSetPrototypeOf = scope.exportFunctionWithName(
|
||||
function setPrototypeOf(target, prototype){
|
||||
target = scope.getWrapped(target);
|
||||
if (proxies.has(target)){
|
||||
target = proxies.get(target).wrappedOriginal;
|
||||
}
|
||||
if (proxies.has(prototype)){
|
||||
prototype = proxies.get(prototype).wrappedOriginal;
|
||||
}
|
||||
if (prototype){
|
||||
const grandPrototype = wrappedReflect.getPrototypeOf(prototype);
|
||||
if (proxies.has(grandPrototype)){
|
||||
const testPrototype = wrappedWindow.Object.create(proxies.get(grandPrototype).wrappedOriginal);
|
||||
const value = originalReflectSetPrototypeOf.call(wrappedReflect, target, testPrototype);
|
||||
if (!value){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
const value = originalReflectSetPrototypeOf.call(wrappedReflect, target, scope.getWrapped(prototype));
|
||||
return value;
|
||||
}, window, "setPrototypeOf"
|
||||
);
|
||||
scope.changeProperty(window, "toString", {
|
||||
object: wrappedWindow.Reflect,
|
||||
name: "setPrototypeOf",
|
||||
type: "value",
|
||||
changed: alteredReflectSetPrototypeOf
|
||||
});
|
||||
}
|
||||
scope.createProxyFunction = function createProxyFunction(window, original, replacement){
|
||||
setupWindowForProxies(window);
|
||||
const wrappedObject = scope.getWrapped(window).Object;
|
||||
const handler = wrappedObject.create(null);
|
||||
handler.apply = scope.exportFunctionWithName(function(target, thisArg, args){
|
||||
args = scope.getWrapped(args);
|
||||
try {
|
||||
return args.length?
|
||||
replacement.call(thisArg, ...args):
|
||||
replacement.call(thisArg);
|
||||
}
|
||||
catch (error){
|
||||
try {
|
||||
return original.apply(thisArg, args);
|
||||
}
|
||||
catch (error){
|
||||
return scope.getWrapped(target).apply(thisArg, args);
|
||||
}
|
||||
}
|
||||
}, window, "");
|
||||
handler.setPrototypeOf = scope.exportFunctionWithName(function(target, prototype){
|
||||
target = scope.getWrapped(target);
|
||||
if (proxies.has(target)){
|
||||
target = proxies.get(target).wrappedOriginal;
|
||||
}
|
||||
if (proxies.has(prototype)){
|
||||
prototype = proxies.get(prototype).wrappedOriginal;
|
||||
}
|
||||
if (prototype){
|
||||
const grandPrototype = wrappedObject.getPrototypeOf(prototype);
|
||||
if (proxies.has(grandPrototype)){
|
||||
const testPrototype = wrappedObject.create(proxies.get(grandPrototype).wrappedOriginal);
|
||||
wrappedObject.setPrototypeOf(target, testPrototype);
|
||||
}
|
||||
}
|
||||
return wrappedObject.setPrototypeOf(target, scope.getWrapped(prototype));
|
||||
}, window, "");
|
||||
const proxy = new window.Proxy(original, handler);
|
||||
const proxyData = {
|
||||
original: original,
|
||||
wrappedOriginal: scope.getWrapped(original),
|
||||
string: changedWindowsForProxies.get(window).call(original),
|
||||
};
|
||||
proxies.set(proxy, proxyData);
|
||||
proxies.set(scope.getWrapped(proxy), proxyData);
|
||||
return scope.getWrapped(proxy);
|
||||
};
|
||||
|
||||
const changedPropertiesByWindow = new WeakMap();
|
||||
scope.changeProperty = function(window, group, {object, name, type, changed}){
|
||||
let changedProperties = changedPropertiesByWindow.get(scope.getWrapped(window));
|
||||
if (!changedProperties){
|
||||
changedProperties = [];
|
||||
changedPropertiesByWindow.set(scope.getWrapped(window), changedProperties);
|
||||
}
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
||||
const original = descriptor[type];
|
||||
descriptor[type] = changed;
|
||||
Object.defineProperty(object, name, descriptor);
|
||||
changedProperties.push({group, object, name, type, original});
|
||||
};
|
||||
scope.revertProperties = function(window, group){
|
||||
window = scope.getWrapped(window);
|
||||
let changedProperties = changedPropertiesByWindow.get(window);
|
||||
if (!changedProperties){
|
||||
return;
|
||||
}
|
||||
if (group){
|
||||
const remainingProperties = changedProperties.filter(function(changedProperty){
|
||||
return changedProperty.group !== group;
|
||||
});
|
||||
changedPropertiesByWindow.set(window, remainingProperties);
|
||||
changedProperties = changedProperties.filter(function(changedProperty){
|
||||
return changedProperty.group === group;
|
||||
});
|
||||
}
|
||||
else {
|
||||
changedPropertiesByWindow.delete(window);
|
||||
}
|
||||
|
||||
for (let i = changedProperties.length - 1; i >= 0; i -= 1){
|
||||
const {object, name, type, original} = changedProperties[i];
|
||||
logging.verbose("reverting", name, "on", object);
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
||||
descriptor[type] = original;
|
||||
Object.defineProperty(object, name, descriptor);
|
||||
}
|
||||
};
|
||||
|
||||
scope.waitSync = function waitSync(reason = "for no reason"){
|
||||
logging.message(`Starting synchronous request ${reason}.`);
|
||||
try {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "https://[::]", false);
|
||||
xhr.send();
|
||||
xhr = null;
|
||||
}
|
||||
catch (error){
|
||||
logging.verbose("Error in XHR:", error);
|
||||
}
|
||||
};
|
||||
|
||||
scope.displayVersion = async function displayVersion(node, displayRefresh = false){
|
||||
if ("string" === typeof node){
|
||||
node = document.getElementById(node);
|
||||
}
|
||||
if (!node){
|
||||
throw "display node not found";
|
||||
}
|
||||
fetch(scope.getURL("manifest.json")).then(function(response){
|
||||
return response.json();
|
||||
}).then(function(manifest){
|
||||
node.textContent = "Version " + manifest.version;
|
||||
return manifest.version;
|
||||
}).catch(function(error){
|
||||
node.textContent = "Unable to get version: " + error;
|
||||
});
|
||||
|
||||
if (displayRefresh){
|
||||
// Workaround to hide the scroll bars
|
||||
window.setTimeout(function(){
|
||||
node.style.display = "none";
|
||||
node.style.display = "";
|
||||
}, displayRefresh);
|
||||
}
|
||||
};
|
||||
|
||||
Object.seal(scope);
|
||||
}());
|
89
lib/frame.js
@ -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]){
|
||||
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){
|
||||
extension.changeProperty(window, "matchMedia", {
|
||||
object: wrappedWindow,
|
||||
name: "matchMedia",
|
||||
type: "value",
|
||||
changed: extension.exportFunctionWithName(function matchMedia(query){
|
||||
if (query === extensionSecret[0]){
|
||||
return extensionSecret[1];
|
||||
return {
|
||||
secret: extensionSecret[1],
|
||||
undoIntercept: function(token){
|
||||
if (token === extension.extensionID){
|
||||
extension.revertProperties(window);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
return arguments.length > 1?
|
||||
originalMatchMedia.apply(this, wrappedWindow.Array.from(arguments)):
|
||||
originalMatchMedia.call(this, ...arguments):
|
||||
originalMatchMedia.call(this, query);
|
||||
}
|
||||
}, window);
|
||||
Object.defineProperty(wrappedWindow, "matchMedia", matchMediaDescriptor);
|
||||
}, 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");
|
||||
}
|
||||
});
|
||||
|
||||
|
44
lib/hash.js
@ -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;
|
||||
|
@ -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,47 +123,49 @@
|
||||
protectionDefinition.methods.forEach(function(method){
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, method);
|
||||
const original = descriptor.value;
|
||||
descriptor.value = exportFunction(eval(`(function ${method}(){
|
||||
changeProperty(object, method, "value", class {
|
||||
[method](){
|
||||
const value = arguments.length?
|
||||
original.apply(this, window.Array.from(arguments)):
|
||||
original.call(this, ...arguments):
|
||||
original.call(this);
|
||||
allCallback();
|
||||
return value;
|
||||
})`), window);
|
||||
Object.defineProperty(object, method, descriptor);
|
||||
}
|
||||
}.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 enableMutationObserver({window, allCallback}){
|
||||
const observer = new MutationObserver(allCallback);
|
||||
let observing = false;
|
||||
function observe(){
|
||||
if (
|
||||
!observing &&
|
||||
@ -148,18 +183,23 @@
|
||||
}
|
||||
});
|
||||
return observe;
|
||||
}();
|
||||
}
|
||||
|
||||
// MutationObserver does not trigger fast enough when document.write is used
|
||||
const documentWriteDescriptor = Object.getOwnPropertyDescriptor(
|
||||
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){
|
||||
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
|
||||
@ -177,18 +217,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 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){
|
||||
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(/(?=<)/);
|
||||
@ -203,7 +248,55 @@
|
||||
}
|
||||
}
|
||||
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);
|
||||
};
|
||||
}());
|
374
lib/intercept.js
@ -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,66 +39,107 @@
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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];
|
||||
(
|
||||
const getAllFunctionObjects = function(windowToProcess, changedFunction){
|
||||
return (
|
||||
Array.isArray(changedFunction.object)?
|
||||
changedFunction.object:
|
||||
[changedFunction.object]
|
||||
).forEach(function(object){
|
||||
const constructor = getWrapped(window)[object];
|
||||
).map(function(name){
|
||||
if (name){
|
||||
const constructor = extension.getWrapped(windowToProcess)[name];
|
||||
if (constructor){
|
||||
callback({name, object: constructor.prototype});
|
||||
return constructor.prototype;
|
||||
}
|
||||
});
|
||||
});
|
||||
changedGetters.forEach(function(changedGetter){
|
||||
const name = changedGetter.name;
|
||||
changedGetter.objectGetters.forEach(function(objectGetter){
|
||||
const object = objectGetter(getWrapped(window));
|
||||
}
|
||||
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});
|
||||
callback({name, object: object, changedFunction});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
let originalPropertyDescriptors = {};
|
||||
const doPreIntercept = function(){
|
||||
if (!preIntercepted){
|
||||
forEachFunction(function({name, object}){
|
||||
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});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
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,
|
||||
originalPropertyDescriptor
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
if (!state.preIntercepted){
|
||||
forEach(windowToProcess, function({name, object}){
|
||||
const map = originalPropertyDescriptors[name] || new WeakMap();
|
||||
originalPropertyDescriptors[name] = map;
|
||||
|
||||
@ -109,24 +149,19 @@
|
||||
}
|
||||
|
||||
map.set(object, originalPropertyDescriptor);
|
||||
Object.defineProperty(
|
||||
object,
|
||||
name,
|
||||
{
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: exportFunction(function(){
|
||||
const temp = class {
|
||||
[`get ${name}`](){
|
||||
if (forceLoad){
|
||||
logging.warning("force load the settings. Calling stack:", (new Error()).stack);
|
||||
undoPreIntercept();
|
||||
settings.forceLoad();
|
||||
doRealIntercept();
|
||||
doRealIntercept(windowToProcess, apis, state);
|
||||
const descriptor = Object.getOwnPropertyDescriptor(object, name);
|
||||
return descriptor.value || descriptor.get.call(this);
|
||||
}
|
||||
else {
|
||||
logging.notice("API blocked (%s)", name);
|
||||
const url = getURL(window);
|
||||
const url = getURL(windowToProcess);
|
||||
if (!url){
|
||||
return undef;
|
||||
}
|
||||
@ -139,48 +174,48 @@
|
||||
functionName: name,
|
||||
dataURL: false
|
||||
});
|
||||
return;
|
||||
return undef;
|
||||
}
|
||||
}, window),
|
||||
set: exportFunction(function(){}, window)
|
||||
}
|
||||
);
|
||||
});
|
||||
preIntercepted = true;
|
||||
}
|
||||
};
|
||||
const undoPreIntercept = function(){
|
||||
if (preIntercepted){
|
||||
preIntercepted = false;
|
||||
forEachFunction(function({name, object}){
|
||||
const originalPropertyDescriptor = originalPropertyDescriptors[name].get(object);
|
||||
if (originalPropertyDescriptor){
|
||||
[`set ${name}`](newValue){}
|
||||
}.prototype;
|
||||
Object.defineProperty(
|
||||
object,
|
||||
name,
|
||||
originalPropertyDescriptor
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
const doRealIntercept = function(){
|
||||
if (!intercepted){
|
||||
scope.intercept({subject: window}, apis);
|
||||
intercepted = true;
|
||||
}
|
||||
return undoPreIntercept;
|
||||
};
|
||||
|
||||
doPreIntercept();
|
||||
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
|
||||
};
|
||||
|
||||
const undoPreIntercept = doPreIntercept(windowToProcess, apis, state);
|
||||
settings.onloaded(function(){
|
||||
undoPreIntercept();
|
||||
doRealIntercept();
|
||||
doRealIntercept(windowToProcess, apis, state);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let extensionID = extension.extensionID;
|
||||
scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){
|
||||
function getDataURL(object, prefs){
|
||||
return (
|
||||
object &&
|
||||
@ -198,10 +233,15 @@
|
||||
false
|
||||
);
|
||||
}
|
||||
function generateChecker(name, changedFunction, siteStatus, original){
|
||||
return function checker(callingDepth = 2){
|
||||
const error = new Error();
|
||||
const errorStack = error.stack;
|
||||
|
||||
let extensionID = extension.extensionID;
|
||||
|
||||
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
|
||||
@ -211,21 +251,21 @@
|
||||
.split("@", callingDepth + 1)[1]
|
||||
.startsWith(extensionID)
|
||||
){
|
||||
return {allow: true, original, window};
|
||||
return {allow: true, original, window: windowToProcess};
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
catch (error) {
|
||||
// stack had an unknown form
|
||||
}
|
||||
if (checkStack(errorStack)){
|
||||
return {allow: true, original, window};
|
||||
return {allow: true, original, window: windowToProcess};
|
||||
}
|
||||
const funcStatus = changedFunction.getStatus(this, siteStatus, prefs);
|
||||
|
||||
const This = this;
|
||||
function notifyCallback(messageId){
|
||||
notify({
|
||||
url: getURL(window),
|
||||
url: getURL(windowToProcess),
|
||||
errorStack,
|
||||
messageId,
|
||||
timestamp: new Date(),
|
||||
@ -238,13 +278,13 @@
|
||||
if (
|
||||
funcStatus.active &&
|
||||
(
|
||||
!protectedAPIFeatures.hasOwnProperty(name) ||
|
||||
protectedAPIFeatures[name]
|
||||
!protectedAPIFeatures.hasOwnProperty(name + " @ " + changedFunction.api) ||
|
||||
protectedAPIFeatures[name + " @ " + changedFunction.api]
|
||||
)
|
||||
){
|
||||
if (funcStatus.mode === "ask"){
|
||||
funcStatus.mode = ask({
|
||||
window: window,
|
||||
window: windowToProcess,
|
||||
type: changedFunction.type,
|
||||
api: changedFunction.api,
|
||||
canvas: this instanceof HTMLCanvasElement?
|
||||
@ -260,102 +300,164 @@
|
||||
}
|
||||
switch (funcStatus.mode){
|
||||
case "allow":
|
||||
return {allow: true, original, window};
|
||||
return {allow: true, original, window: windowToProcess};
|
||||
case "fake":
|
||||
return {
|
||||
allow: "fake",
|
||||
prefs,
|
||||
notify: notifyCallback,
|
||||
window,
|
||||
window: windowToProcess,
|
||||
original
|
||||
};
|
||||
//case "block":
|
||||
default:
|
||||
return {
|
||||
allow: false,
|
||||
notify: notifyCallback
|
||||
};
|
||||
return {allow: false, notify: notifyCallback};
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {allow: true, original, window};
|
||||
return {allow: true, original, window: windowToProcess};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const siteStatus = check({url: getURL(window)});
|
||||
logging.verbose("status for page", window, siteStatus);
|
||||
if (siteStatus.mode !== "allow"){
|
||||
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){
|
||||
(
|
||||
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 (!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){
|
||||
descriptor.value = exportFunction(
|
||||
changedFunction.fakeGenerator(checker, original, window),
|
||||
window
|
||||
);
|
||||
const generated = changedFunction.fakeGenerator(checker, original, windowToProcess);
|
||||
if ((changedFunction.exportOptions || {}).allowCallbacks){
|
||||
changed = extension.exportFunctionWithName(generated, windowToProcess, original.name);
|
||||
}
|
||||
else {
|
||||
descriptor.value = null;
|
||||
changed = extension.createProxyFunction(windowToProcess, original, generated);
|
||||
}
|
||||
}
|
||||
else {
|
||||
descriptor.get = exportFunction(function(){
|
||||
return exportFunction(
|
||||
changed = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
changed = extension.createProxyFunction(windowToProcess, original, extension.exportFunctionWithName(
|
||||
changedFunction.fakeGenerator(checker),
|
||||
window
|
||||
);
|
||||
}, window);
|
||||
}
|
||||
Object.defineProperty(constructor.prototype, name, descriptor);
|
||||
}
|
||||
windowToProcess,
|
||||
original.name
|
||||
));
|
||||
}
|
||||
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){
|
||||
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 (!functionStatus.active) return;
|
||||
|
||||
if (descriptor.hasOwnProperty("set") && changedGetter.setterGenerator){
|
||||
const setter = changedGetter.setterGenerator(window, descriptor.set, prefs);
|
||||
descriptor.set = exportFunction(setter, window);
|
||||
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)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Object.defineProperty(object, name, descriptor);
|
||||
}
|
||||
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"){
|
||||
interceptFunctions(windowToProcess, siteStatus, apis);
|
||||
interceptGetters(windowToProcess, siteStatus, apis);
|
||||
}
|
||||
};
|
||||
}());
|
57
lib/lists.js
@ -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 {
|
||||
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(){
|
||||
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);
|
||||
|
@ -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,6 +72,7 @@
|
||||
args.unshift(colors[level] || "");
|
||||
args.unshift(pre);
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
console.log.apply(console, 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);
|
||||
|
235
lib/main.js
@ -6,98 +6,25 @@
|
||||
|
||||
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");
|
||||
settings.onloaded(function(){
|
||||
notice("everything loaded");
|
||||
|
||||
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);
|
||||
if (data["canvasBlocker-new-domain-rnd"]){
|
||||
persistentRndStorage.setDomainData(
|
||||
data["canvasBlocker-new-domain-rnd"].domain,
|
||||
data["canvasBlocker-new-domain-rnd"].incognito,
|
||||
data["canvasBlocker-new-domain-rnd"].rnd
|
||||
);
|
||||
if (keys.length === 1){
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data["canvasBlocker-clear-domain-rnd"]){
|
||||
persistentRndStorage.clear();
|
||||
if (keys.length === 1){
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data["canvasBlocker-clear-container-rnd"]){
|
||||
persistentRndStorage.clearContainerData(data["canvasBlocker-clear-container-rnd"]);
|
||||
if (keys.length === 1){
|
||||
return;
|
||||
}
|
||||
}
|
||||
notice("pass the message to the tabs");
|
||||
browser.tabs.query({}).then(function(tabs){
|
||||
tabs.forEach(function(tab){
|
||||
browser.tabs.sendMessage(tab.id, data);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
message("register port listener");
|
||||
browser.runtime.onConnect.addListener(function(port){
|
||||
notice("got port", port);
|
||||
if (!port.sender.tab){
|
||||
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);
|
||||
port.postMessage({
|
||||
tabId: port.sender.tab.id,
|
||||
cookieStoreId: port.sender.tab.cookieStoreId,
|
||||
persistentRnd: persistentRndStorage.persistentRnd,
|
||||
persistentIncognitoRnd: persistentRndStorage.persistentIncognitoRnd
|
||||
});
|
||||
var 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);
|
||||
}
|
||||
if (data.hasOwnProperty("canvasBlocker-clear-page-action")){
|
||||
notification.hide(port.sender.tab.id, url);
|
||||
}
|
||||
verbose("got data", data, "from port", port);
|
||||
});
|
||||
});
|
||||
|
||||
message("register storage change event listener");
|
||||
|
||||
if (browser.contentScripts){
|
||||
let unregister = function(){};
|
||||
const registerSettingsContentScript = (function(){
|
||||
let unregisterSettingsContentScript = function(){};
|
||||
let lastRegistering;
|
||||
const register = function register(){
|
||||
return async function registerSettingsContentScript(){
|
||||
logging.message("Register content script for the settings.");
|
||||
logging.verbose("Unregister old content script, if present.");
|
||||
unregister();
|
||||
var data = {};
|
||||
unregisterSettingsContentScript();
|
||||
const data = {};
|
||||
settings.forEach(function(def){
|
||||
data[def.name] = def.get();
|
||||
});
|
||||
lastRegistering = data;
|
||||
browser.contentScripts.register({
|
||||
const api = await browser.contentScripts.register({
|
||||
matches: ["<all_urls>"],
|
||||
matchAboutBlank: true,
|
||||
allFrames: true,
|
||||
@ -111,7 +38,7 @@
|
||||
logging.message("Initialized settings by dynamic content script.");
|
||||
}
|
||||
else {
|
||||
logging.error("Dynamic content script was too late to provide settings.");
|
||||
logging.warning("Dynamic content script was too late to provide settings.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -126,29 +53,105 @@
|
||||
}
|
||||
}(${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;
|
||||
unregisterSettingsContentScript = api.unregister;
|
||||
}
|
||||
});
|
||||
};
|
||||
register();
|
||||
settings.on("any", register);
|
||||
}());
|
||||
|
||||
logging.message("start of background script");
|
||||
logging.message("waiting for settings to be loaded");
|
||||
settings.onloaded(function(){
|
||||
logging.notice("everything loaded");
|
||||
|
||||
logging.message("perform startup reset");
|
||||
settings.startupReset();
|
||||
|
||||
persistentRndStorage.init();
|
||||
|
||||
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,
|
||||
data["canvasBlocker-new-domain-rnd"].incognito,
|
||||
data["canvasBlocker-new-domain-rnd"].rnd
|
||||
);
|
||||
if (keys.length === 1){
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data["canvasBlocker-clear-domain-rnd"]){
|
||||
persistentRndStorage.clear(data["canvasBlocker-clear-domain-rnd"] === "force");
|
||||
if (keys.length === 1){
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (data["canvasBlocker-clear-container-rnd"]){
|
||||
persistentRndStorage.clearContainerData(data["canvasBlocker-clear-container-rnd"]);
|
||||
if (keys.length === 1){
|
||||
return;
|
||||
}
|
||||
}
|
||||
logging.notice("pass the message to the tabs");
|
||||
const tabs = await browser.tabs.query({});
|
||||
tabs.forEach(function(tab){
|
||||
browser.tabs.sendMessage(tab.id, data);
|
||||
});
|
||||
});
|
||||
|
||||
logging.message("register port listener");
|
||||
browser.runtime.onConnect.addListener(function(port){
|
||||
logging.notice("got port", port);
|
||||
if (!port.sender.tab){
|
||||
logging.notice("got port without tab = Firefox bug:", port);
|
||||
return;
|
||||
}
|
||||
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 || "",
|
||||
persistentRnd: persistentRndStorage.persistentRnd,
|
||||
persistentIncognitoRnd: persistentRndStorage.persistentIncognitoRnd
|
||||
});
|
||||
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);
|
||||
}
|
||||
if (data.hasOwnProperty("canvasBlocker-clear-page-action")){
|
||||
notification.hide(port.sender.tab.id, url);
|
||||
}
|
||||
logging.verbose("got data", data, "from port", port);
|
||||
});
|
||||
});
|
||||
|
||||
logging.message("register storage change event listener");
|
||||
|
||||
if (browser.contentScripts){
|
||||
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,27 +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: browser.extension.getURL("options/presets.html?notice=" + details.reason)
|
||||
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
@ -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);
|
||||
}));
|
||||
};
|
||||
}());
|
@ -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
|
||||
|
@ -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"));
|
||||
}());
|
@ -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];
|
||||
|
@ -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"
|
||||
});
|
||||
}());
|
@ -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");
|
||||
if (this instanceof toBlob){
|
||||
throw new extension.getWrapped(window).TypeError(
|
||||
"HTMLCanvasElement.prototype.toBlob is not a constructor"
|
||||
);
|
||||
}
|
||||
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}
|
||||
},
|
||||
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";
|
||||
|
@ -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,18 +46,22 @@
|
||||
});
|
||||
}
|
||||
function getDOMRectRegistration(domRect){
|
||||
return registeredRects.get(getWrapped(domRect));
|
||||
return registeredRects.get(extension.getWrapped(domRect));
|
||||
}
|
||||
|
||||
const cache = {};
|
||||
const valueCache = [{}, {}, {}, {}];
|
||||
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 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];
|
||||
@ -74,18 +79,71 @@
|
||||
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)
|
||||
}
|
||||
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 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"
|
||||
});
|
||||
}());
|
@ -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"
|
||||
});
|
||||
}());
|
@ -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;
|
||||
|
||||
scope.changedGetters.forEach(function(changedGetter){
|
||||
changedGetter.type = "readout";
|
||||
changedGetter.getStatus = getStatus;
|
||||
changedGetter.api = "navigator";
|
||||
notify("fakedNavigatorReadout");
|
||||
}
|
||||
resolve(originalValue);
|
||||
}
|
||||
catch (error){
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setProperties(scope.changedFunctions, scope.changedGetters, {
|
||||
type: "readout",
|
||||
getStatus: getStatusByFlag("protectNavigator"),
|
||||
api: "navigator"
|
||||
});
|
||||
}());
|
88
lib/modifiedSVGAPI.js
Normal 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
@ -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"
|
||||
});
|
||||
}());
|
96
lib/modifiedTextMetricsAPI.js
Normal 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"
|
||||
});
|
||||
}());
|
@ -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"
|
||||
});
|
||||
}());
|
@ -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,8 +37,30 @@
|
||||
changedValues = newValue;
|
||||
});
|
||||
|
||||
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 changedValues;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return changedValues;
|
||||
}
|
||||
}
|
||||
|
||||
function getValue(name, stack = []){
|
||||
return function getValue(name, getCookieStoreId){
|
||||
const changedValues = getChangedValues(getCookieStoreId);
|
||||
|
||||
function getValueInternal(name, stack = []){
|
||||
if (stack.indexOf(name) !== -1){
|
||||
return "[ERROR: loop in property definition]";
|
||||
}
|
||||
@ -58,13 +81,19 @@
|
||||
}
|
||||
}
|
||||
function parseString(string, stack){
|
||||
if (string === "{undefined}"){
|
||||
return undefined;
|
||||
}
|
||||
return string.replace(/{([a-z[\]_. -]*)}/ig, function(m, name){
|
||||
return getValue(name, stack.slice());
|
||||
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"
|
||||
);
|
||||
}
|
||||
};
|
||||
}());
|
@ -4,7 +4,7 @@
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
var scope;
|
||||
let scope;
|
||||
if ((typeof exports) !== "undefined"){
|
||||
scope = exports;
|
||||
}
|
||||
@ -163,32 +163,30 @@
|
||||
});
|
||||
};
|
||||
|
||||
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){
|
||||
const tabs = await browser.tabs.query({});
|
||||
tabs.forEach(function(tab){
|
||||
browser.pageAction.hide(tab.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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){
|
||||
const tabs = await browser.tabs.query({});
|
||||
tabs.forEach(function(tab){
|
||||
browser.browserAction.setBadgeText({
|
||||
tabId: tab.id,
|
||||
text: ""
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
@ -74,7 +75,7 @@
|
||||
};
|
||||
|
||||
const getInterval = function(){
|
||||
var units = {
|
||||
const units = {
|
||||
seconds: 1000,
|
||||
minutes: 60 * 1000,
|
||||
hours: 60 * 60 * 1000,
|
||||
@ -88,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 {
|
||||
@ -114,25 +104,24 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
function broadcast(data){
|
||||
browser.tabs.query({}).then(function(tabs){
|
||||
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);
|
||||
@ -175,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);
|
||||
}
|
||||
}());
|
@ -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;
|
||||
|
@ -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 + "!");
|
||||
|
@ -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();
|
||||
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);
|
||||
|