Compare commits
639 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
9a57758425 | 5 years ago |
|
|
64a911333f | 5 years ago |
|
|
300c482314 | 5 years ago |
|
|
ad456600ed | 5 years ago |
|
|
a5a9104e77 | 5 years ago |
|
|
9140e3392d | 5 years ago |
|
|
06b6ca01f1 | 5 years ago |
|
|
1c45aa35a3 | 5 years ago |
|
|
c3fd99a692 | 5 years ago |
|
|
122e5ca082 | 5 years ago |
|
|
088eea918b | 5 years ago |
|
|
e4fd3a0e93 | 5 years ago |
|
|
af770ebeb8 | 5 years ago |
|
|
1cc436b779 | 5 years ago |
|
|
b2f09f89b5 | 5 years ago |
|
|
e9274d6e30 | 5 years ago |
|
|
eca5ca3f32 | 5 years ago |
|
|
bda7383bc8 | 5 years ago |
|
|
7562a3d697 | 5 years ago |
|
|
954d827934 | 5 years ago |
|
|
e7899435fe | 5 years ago |
|
|
93edb4aca0 | 5 years ago |
|
|
c9ddc9d5d0 | 5 years ago |
|
|
0bcb7669e7 | 6 years ago |
|
|
89b7e026c3 | 6 years ago |
|
|
ba0dfe5d9b | 6 years ago |
|
|
57eb9fc150 | 6 years ago |
|
|
e0800f89df | 6 years ago |
|
|
b26b3bb960 | 6 years ago |
|
|
2d8e32c61b | 6 years ago |
|
|
41133dffd2 | 6 years ago |
|
|
a1ec20423c | 6 years ago |
|
|
e3ebfdfba4 | 6 years ago |
|
|
43cd137fff | 6 years ago |
|
|
a697ce0be9 | 6 years ago |
|
|
b75500eff4 | 6 years ago |
|
|
3f4425d9db | 6 years ago |
|
|
f8c82add03 | 6 years ago |
|
|
a78a6465b0 | 6 years ago |
|
|
ae7d450827 | 6 years ago |
|
|
c908ae5210 | 6 years ago |
|
|
ea688f3942 | 6 years ago |
|
|
8a39b0e171 | 6 years ago |
|
|
5c06fed370 | 6 years ago |
|
|
035f3f53a2 | 6 years ago |
|
|
17430ded93 | 6 years ago |
|
|
b8e62f0c55 | 6 years ago |
|
|
e800756163 | 6 years ago |
|
|
0190fdaf3d | 6 years ago |
|
|
0db579da97 | 6 years ago |
|
|
e5cf0d4684 | 6 years ago |
|
|
a2f213492e | 6 years ago |
|
|
a9f32a475f | 6 years ago |
|
|
cb9934f67a | 6 years ago |
|
|
e7140262f2 | 6 years ago |
|
|
a9bcc70a2b | 6 years ago |
|
|
2936d73911 | 6 years ago |
|
|
df4a403473 | 6 years ago |
|
|
d993b8442a | 6 years ago |
|
|
5aab1fb00b | 6 years ago |
|
|
9b292304d3 | 6 years ago |
|
|
1d936742a7 | 6 years ago |
|
|
5156e23a09 | 6 years ago |
|
|
93b28cdd84 | 6 years ago |
|
|
3da449b8a4 | 7 years ago |
|
|
eb2a4699d4 | 7 years ago |
|
|
e01ebef2cf | 7 years ago |
|
|
c3919da2c2 | 7 years ago |
|
|
d5fe3edb15 | 7 years ago |
|
|
f332cd9f68 | 7 years ago |
|
|
d6471b3556 | 7 years ago |
|
|
0da9645d91 | 7 years ago |
|
|
6e9f23c940 | 7 years ago |
|
|
04a9d00a5e | 7 years ago |
|
|
1ddb822509 | 7 years ago |
|
|
0807d71704 | 7 years ago |
|
|
d5f75e84d8 | 7 years ago |
|
|
901c325eab | 7 years ago |
|
|
dbe6218926 | 7 years ago |
|
|
732d0e84d8 | 7 years ago |
|
|
ab303d3c17 | 7 years ago |
|
|
1f1429c47a | 7 years ago |
|
|
5c3661b69c | 7 years ago |
|
|
3f2916ed05 | 7 years ago |
|
|
fa795dd446 | 7 years ago |
|
|
26aedcc5f9 | 7 years ago |
|
|
054935d025 | 7 years ago |
|
|
62fc318e89 | 7 years ago |
|
|
6bba6c4f85 | 7 years ago |
|
|
fb94c85df1 | 7 years ago |
|
|
3cc56afe63 | 7 years ago |
|
|
9789182b2c | 7 years ago |
|
|
4d7109bf99 | 7 years ago |
|
|
130db1f351 | 7 years ago |
|
|
2045397084 | 7 years ago |
|
|
9494da7195 | 7 years ago |
|
|
94fdd3b587 | 7 years ago |
|
|
d7a96081f5 | 7 years ago |
|
|
521430115a | 7 years ago |
|
|
acfb1a6310 | 7 years ago |
|
|
de56cfe351 | 7 years ago |
|
|
5ed6a9703c | 7 years ago |
|
|
f94f0010b6 | 7 years ago |
|
|
9a6b1c2c9c | 7 years ago |
|
|
0f7c82a2d9 | 7 years ago |
|
|
b71eed544f | 7 years ago |
|
|
68e8d96b57 | 7 years ago |
|
|
073500ca81 | 7 years ago |
|
|
494cd9e95c | 7 years ago |
|
|
ae894fb571 | 7 years ago |
|
|
60da12b0ef | 7 years ago |
|
|
5e86922100 | 7 years ago |
|
|
6dcc90c639 | 7 years ago |
|
|
45be8d04e3 | 7 years ago |
|
|
9bbfed8d37 | 7 years ago |
|
|
5b1287d2de | 7 years ago |
|
|
886664abd4 | 7 years ago |
|
|
495dddc389 | 7 years ago |
|
|
b822ae72d9 | 7 years ago |
|
|
742fba96f0 | 7 years ago |
|
|
8eb17934b0 | 7 years ago |
|
|
605368a6e2 | 7 years ago |
|
|
52246a5250 | 7 years ago |
|
|
3ce484688a | 7 years ago |
|
|
ec7accc32c | 7 years ago |
|
|
495c2bc916 | 7 years ago |
|
|
62a7d8f30f | 7 years ago |
|
|
c6e29be792 | 7 years ago |
|
|
fc0bb7a35c | 7 years ago |
|
|
77a7b8febe | 7 years ago |
|
|
7c63d2b03d | 7 years ago |
|
|
136c434b4d | 7 years ago |
|
|
efa0085a67 | 7 years ago |
|
|
9432e3b6eb | 7 years ago |
|
|
7c73664fc8 | 7 years ago |
|
|
228e326111 | 7 years ago |
|
|
6b8be2ca43 | 7 years ago |
|
|
2256552899 | 7 years ago |
|
|
cb2ab59297 | 7 years ago |
|
|
934d497ef5 | 7 years ago |
|
|
e02360f096 | 7 years ago |
|
|
d42abee957 | 7 years ago |
|
|
956b79816b | 7 years ago |
|
|
7328a46086 | 7 years ago |
|
|
8ed8666e77 | 7 years ago |
|
|
db7feed9c5 | 7 years ago |
|
|
6699e5c56f | 7 years ago |
|
|
017c167ea9 | 7 years ago |
|
|
f075f96c58 | 7 years ago |
|
|
7f3122c5fd | 7 years ago |
|
|
be19816f33 | 7 years ago |
|
|
1221431f44 | 7 years ago |
|
|
12a471c3c5 | 7 years ago |
|
|
cc0bed69bf | 7 years ago |
|
|
0f5a155e4f | 7 years ago |
|
|
66ec464b66 | 7 years ago |
|
|
943d1084d4 | 7 years ago |
|
|
bb29ad2c34 | 7 years ago |
|
|
b724a6356a | 7 years ago |
|
|
0ae91ed00f | 7 years ago |
|
|
a7753fd356 | 7 years ago |
|
|
a0c72fc302 | 7 years ago |
|
|
28d50f2347 | 7 years ago |
|
|
3795b99fc5 | 7 years ago |
|
|
4b4db46663 | 7 years ago |
|
|
637c0e42f1 | 7 years ago |
|
|
73891f217d | 7 years ago |
|
|
e6b1648f53 | 7 years ago |
|
|
b53d1be672 | 7 years ago |
|
|
d22981dfbe | 8 years ago |
|
|
5c3e8c6863 | 8 years ago |
|
|
a68f7a7899 | 8 years ago |
|
|
322d689f8b | 8 years ago |
|
|
9693ea9e96 | 8 years ago |
|
|
fbf3f1f263 | 8 years ago |
|
|
3f8794dd89 | 8 years ago |
|
|
4284a57b4e | 8 years ago |
|
|
e1173fc0fd | 8 years ago |
|
|
18490b9bf8 | 8 years ago |
|
|
2714193b73 | 8 years ago |
|
|
1930844e68 | 8 years ago |
|
|
297c2a3b35 | 8 years ago |
|
|
46669115dc | 8 years ago |
|
|
18ecb90bd0 | 8 years ago |
|
|
c1bbf7b199 | 8 years ago |
|
|
925b3bdcc9 | 8 years ago |
|
|
94e4bdd9d6 | 8 years ago |
|
|
8da0dcd84f | 8 years ago |
|
|
d1afcd2e9e | 8 years ago |
|
|
cd4fc2596e | 8 years ago |
|
|
00e62b48be | 8 years ago |
|
|
bd7e864950 | 8 years ago |
|
|
abfe7a8037 | 8 years ago |
|
|
ffcd211dcc | 8 years ago |
|
|
2d97fafbe8 | 8 years ago |
|
|
aba697f2f0 | 8 years ago |
|
|
4564ee3b6d | 8 years ago |
|
|
e768b78b73 | 8 years ago |
|
|
b1d51861e5 | 8 years ago |
|
|
14bcb5ed56 | 8 years ago |
|
|
9fa2777589 | 8 years ago |
|
|
41bec3b53e | 8 years ago |
|
|
6958c4ed26 | 8 years ago |
|
|
b793312143 | 8 years ago |
|
|
b986ff5565 | 8 years ago |
|
|
97bb075cf1 | 8 years ago |
|
|
18aa29860c | 8 years ago |
|
|
fc80b2c581 | 8 years ago |
|
|
4f1c7dd671 | 8 years ago |
|
|
72b89e83e6 | 8 years ago |
|
|
89aed08b4f | 8 years ago |
|
|
a3812d52be | 8 years ago |
|
|
a7eeb40e13 | 8 years ago |
|
|
fe995d2bc8 | 8 years ago |
|
|
762de2aecd | 8 years ago |
|
|
98e012599c | 8 years ago |
|
|
06436c61fc | 8 years ago |
|
|
b627b62f6c | 8 years ago |
|
|
7a1cb53c04 | 8 years ago |
|
|
f45361fa56 | 8 years ago |
|
|
31cc15ca6c | 8 years ago |
|
|
dd5a3a807c | 8 years ago |
|
|
455155c635 | 8 years ago |
|
|
872975656c | 8 years ago |
|
|
62d06792f1 | 8 years ago |
|
|
fde72e4f2a | 8 years ago |
|
|
d4d97d018c | 8 years ago |
|
|
eb3a43a71c | 8 years ago |
|
|
1225405d8e | 8 years ago |
|
|
5b86e2901e | 8 years ago |
|
|
2c2f0f8f03 | 8 years ago |
|
|
5dd6564b6a | 8 years ago |
|
|
fbfce62582 | 8 years ago |
|
|
8741f7ee8e | 8 years ago |
|
|
13451a3a4f | 8 years ago |
|
|
9fd6bdfddd | 8 years ago |
|
|
7c0fd0c9b3 | 8 years ago |
|
|
591483d07e | 8 years ago |
|
|
9d95bbe9b6 | 8 years ago |
|
|
9b9d7e586f | 8 years ago |
|
|
3935b8417b | 8 years ago |
|
|
d9251ce00b | 8 years ago |
|
|
d2a0de861b | 8 years ago |
|
|
24289831c2 | 8 years ago |
|
|
707fd92307 | 8 years ago |
|
|
c8c8430993 | 8 years ago |
|
|
8fa12d8d7e | 8 years ago |
|
|
a976bd0d22 | 8 years ago |
|
|
d825b5562e | 8 years ago |
|
|
747748c9ba | 8 years ago |
|
|
4b368c407b | 8 years ago |
|
|
b6d47f9e86 | 8 years ago |
|
|
a52ea23121 | 8 years ago |
|
|
6e119fbaaf | 8 years ago |
|
|
8e48dd18ba | 8 years ago |
|
|
b4ce23f15e | 8 years ago |
|
|
1470cf12bc | 8 years ago |
|
|
702fbba914 | 8 years ago |
|
|
08a0cdc218 | 8 years ago |
|
|
45ed6a7e25 | 8 years ago |
|
|
f35b230c37 | 8 years ago |
|
|
fb4e07f107 | 8 years ago |
|
|
429af12aa2 | 8 years ago |
|
|
4947019dbb | 8 years ago |
|
|
2f0f038668 | 8 years ago |
|
|
51a68683f8 | 8 years ago |
|
|
4e00e6e3b0 | 8 years ago |
|
|
860e9185cd | 8 years ago |
|
|
21c9480965 | 8 years ago |
|
|
858ee6df69 | 8 years ago |
|
|
335709bc50 | 8 years ago |
|
|
55b9f1c645 | 8 years ago |
|
|
a88a301d81 | 8 years ago |
|
|
91cbd94ba7 | 8 years ago |
|
|
749e8d5cc4 | 8 years ago |
|
|
1a5d7e400d | 8 years ago |
|
|
b5c377ec73 | 8 years ago |
|
|
931a44ee9d | 8 years ago |
|
|
01216b9520 | 8 years ago |
|
|
82ed807bd2 | 8 years ago |
|
|
ebfe08e0a2 | 8 years ago |
|
|
b963ccb778 | 8 years ago |
|
|
c2a46c5da4 | 8 years ago |
|
|
d0a41e09d0 | 8 years ago |
|
|
c90ce95f59 | 8 years ago |
|
|
00df01f633 | 8 years ago |
|
|
081dbf8541 | 8 years ago |
|
|
e9e5aa295c | 8 years ago |
|
|
b8c4eadc7a | 8 years ago |
|
|
7b88cef36d | 8 years ago |
|
|
4ea5334275 | 8 years ago |
|
|
3e4e1d9def | 8 years ago |
|
|
2cf4a57bf4 | 8 years ago |
|
|
bf4f3e71d3 | 8 years ago |
|
|
1867635e0d | 8 years ago |
|
|
6207e35a1e | 8 years ago |
|
|
44a1f6da0d | 8 years ago |
|
|
6abc89dfd5 | 8 years ago |
|
|
b8c0089a1a | 8 years ago |
|
|
1be2f40eae | 8 years ago |
|
|
a9799b70bc | 8 years ago |
|
|
785b430263 | 8 years ago |
|
|
aafb85c6c6 | 8 years ago |
|
|
eeb890000b | 8 years ago |
|
|
6485c2e035 | 8 years ago |
|
|
c89a4cfe5e | 8 years ago |
|
|
08275aa527 | 8 years ago |
|
|
328f9695d8 | 8 years ago |
|
|
7dac50008a | 8 years ago |
|
|
f680fc004c | 8 years ago |
|
|
c3957d3979 | 8 years ago |
|
|
a8ac21ced0 | 8 years ago |
|
|
3435f4f444 | 8 years ago |
|
|
1228ee55d6 | 8 years ago |
|
|
93d827c63a | 8 years ago |
|
|
2aeb7696c3 | 8 years ago |
|
|
e8772404c7 | 8 years ago |
|
|
f1d0e7a3b1 | 8 years ago |
|
|
d766209ab5 | 8 years ago |
|
|
b2e760f40d | 8 years ago |
|
|
ed8556a9fe | 8 years ago |
|
|
533d24b4be | 8 years ago |
|
|
6e273545a4 | 8 years ago |
|
|
c5f90a9cae | 8 years ago |
|
|
32819b5cca | 8 years ago |
|
|
cfaed1e490 | 8 years ago |
|
|
171552370a | 8 years ago |
|
|
2a2685ff84 | 8 years ago |
|
|
6df5e5212c | 8 years ago |
|
|
35ddcc80eb | 8 years ago |
|
|
60cd27c347 | 8 years ago |
|
|
62f48a15ff | 8 years ago |
|
|
821e45751a | 8 years ago |
|
|
b870757e94 | 8 years ago |
|
|
6ecea191b4 | 8 years ago |
|
|
2d14874d69 | 8 years ago |
|
|
c96fbb4d5c | 8 years ago |
|
|
1b1d3ad728 | 8 years ago |
|
|
deeb211c5d | 8 years ago |
|
|
52139a9137 | 8 years ago |
|
|
6d85d53868 | 8 years ago |
|
|
aef92a21ef | 8 years ago |
|
|
204aaae213 | 8 years ago |
|
|
2b02690b93 | 8 years ago |
|
|
c1494d719a | 8 years ago |
|
|
af94a80c59 | 8 years ago |
|
|
19c32608f3 | 8 years ago |
|
|
dba034fd90 | 8 years ago |
|
|
2c262d1198 | 8 years ago |
|
|
9d1b89a0c1 | 8 years ago |
|
|
36e7c874cd | 8 years ago |
|
|
748891a094 | 8 years ago |
|
|
3a6f550223 | 8 years ago |
|
|
12630e8a8a | 8 years ago |
|
|
f459f67e72 | 8 years ago |
|
|
6581071b01 | 8 years ago |
|
|
eebf2c7d32 | 8 years ago |
|
|
157045924b | 8 years ago |
|
|
c1325d06b7 | 8 years ago |
|
|
fc24d902ad | 8 years ago |
|
|
dd14e13db9 | 8 years ago |
|
|
298dac337c | 8 years ago |
|
|
9724cb0013 | 8 years ago |
|
|
c82fab635c | 8 years ago |
|
|
b35aa272d5 | 8 years ago |
|
|
23cefde335 | 8 years ago |
|
|
111d44b7cb | 8 years ago |
|
|
43630426ff | 8 years ago |
|
|
613bab0a9d | 8 years ago |
|
|
3b77c0732e | 8 years ago |
|
|
4b5cd19396 | 8 years ago |
|
|
8e073579f6 | 8 years ago |
|
|
05a312f6d0 | 8 years ago |
|
|
2016e28ee5 | 8 years ago |
|
|
dfb4127583 | 8 years ago |
|
|
8608b29ded | 8 years ago |
|
|
72a9fbf267 | 8 years ago |
|
|
e30f189a19 | 8 years ago |
|
|
c4c0d450fd | 8 years ago |
|
|
ff5789d4ac | 8 years ago |
|
|
1e9f728a3b | 8 years ago |
|
|
b7127541a3 | 8 years ago |
|
|
038b047116 | 8 years ago |
|
|
73c8da05b7 | 8 years ago |
|
|
8615f3b804 | 8 years ago |
|
|
fa2658cd2b | 8 years ago |
|
|
16eceedbf5 | 8 years ago |
|
|
776fda15a0 | 8 years ago |
|
|
a5a69c2fdf | 8 years ago |
|
|
da20f49c59 | 8 years ago |
|
|
cadd9c751d | 8 years ago |
|
|
c983d47c8e | 8 years ago |
|
|
91db7e4a84 | 8 years ago |
|
|
e278a7d4e3 | 8 years ago |
|
|
a90e5cc7b4 | 8 years ago |
|
|
5c340e85e8 | 8 years ago |
|
|
3ff2926871 | 8 years ago |
|
|
8660d35dcf | 8 years ago |
|
|
25128992b1 | 8 years ago |
|
|
3059d15c17 | 8 years ago |
|
|
f80c8b8141 | 8 years ago |
|
|
23047bc23c | 8 years ago |
|
|
3292b030ce | 8 years ago |
|
|
9e2dd8108c | 8 years ago |
|
|
6e15dba281 | 8 years ago |
|
|
4b32e8b4d3 | 8 years ago |
|
|
6a10411f6a | 8 years ago |
|
|
a38a8d9c59 | 8 years ago |
|
|
ebac131247 | 8 years ago |
|
|
6f8a30c0e3 | 8 years ago |
|
|
67e7c8a5bf | 8 years ago |
|
|
c05f9fe9c9 | 8 years ago |
|
|
3f8168ce0b | 8 years ago |
|
|
773fe2da59 | 8 years ago |
|
|
a4ad3a4ea9 | 8 years ago |
|
|
39b2f1ddac | 8 years ago |
|
|
d048ba15cb | 8 years ago |
|
|
0207c91312 | 8 years ago |
|
|
6aa8c856cf | 8 years ago |
|
|
03120f8dd9 | 8 years ago |
|
|
fa95f964de | 8 years ago |
|
|
29a59808ab | 8 years ago |
|
|
43df802cff | 8 years ago |
|
|
db30e572db | 8 years ago |
|
|
9d43f929bc | 8 years ago |
|
|
b5d94c708d | 8 years ago |
|
|
357b44c265 | 8 years ago |
|
|
6f7d793abd | 8 years ago |
|
|
ad9d591b16 | 8 years ago |
|
|
ec5faf04f8 | 8 years ago |
|
|
c095b722fc | 8 years ago |
|
|
040812d813 | 8 years ago |
|
|
5b8bcb1a9a | 8 years ago |
|
|
28d2f0b66e | 8 years ago |
|
|
65d62ec379 | 8 years ago |
|
|
e9968713e4 | 8 years ago |
|
|
c090fa175e | 8 years ago |
|
|
537f8ef79a | 8 years ago |
|
|
07eb60abc6 | 8 years ago |
|
|
f2bb21f129 | 8 years ago |
|
|
9d6d27ae21 | 8 years ago |
|
|
b4112d5641 | 8 years ago |
|
|
2ddde65a57 | 8 years ago |
|
|
706fa4fca4 | 8 years ago |
|
|
a4d2ca6935 | 8 years ago |
|
|
d9cd957f4d | 8 years ago |
|
|
ac7870280a | 8 years ago |
|
|
6e729c517d | 8 years ago |
|
|
0cd3073134 | 8 years ago |
|
|
284b26abf4 | 8 years ago |
|
|
113cd638f5 | 8 years ago |
|
|
3755655239 | 8 years ago |
|
|
040b2a3d7f | 8 years ago |
|
|
2f2fea617b | 8 years ago |
|
|
1825b65d4c | 8 years ago |
|
|
0a43c6fedd | 8 years ago |
|
|
170a665d4a | 8 years ago |
|
|
11a47c189c | 8 years ago |
|
|
d3e789f5da | 8 years ago |
|
|
068ffe6c9a | 8 years ago |
|
|
a36042b5ce | 8 years ago |
|
|
d9bfbb5525 | 8 years ago |
|
|
cacdb07a6e | 8 years ago |
|
|
396f4fc78e | 8 years ago |
|
|
9adc29318e | 8 years ago |
|
|
d4c0c1747b | 8 years ago |
|
|
e6a0379e54 | 8 years ago |
|
|
5a070388ae | 8 years ago |
|
|
53c786e73c | 8 years ago |
|
|
6ca45c66b8 | 8 years ago |
|
|
06d7612bf5 | 8 years ago |
|
|
ca9229e40c | 8 years ago |
|
|
f2a20688c6 | 8 years ago |
|
|
3f07dd4094 | 8 years ago |
|
|
f8a4630095 | 8 years ago |
|
|
ef7b831a82 | 8 years ago |
|
|
083cf4bf46 | 8 years ago |
|
|
61ae0d2bee | 8 years ago |
|
|
8deb398675 | 8 years ago |
|
|
878d4fa6d5 | 8 years ago |
|
|
7c5a4b6e71 | 8 years ago |
|
|
054d3a5b2b | 8 years ago |
|
|
d0a91d5f50 | 8 years ago |
|
|
364d2985db | 8 years ago |
|
|
d7cb45c237 | 8 years ago |
|
|
a699567a61 | 8 years ago |
|
|
dd1cb33626 | 8 years ago |
|
|
b1f53a6d09 | 8 years ago |
|
|
c4718246ea | 8 years ago |
|
|
0b8291c329 | 8 years ago |
|
|
cdffe16b98 | 8 years ago |
|
|
c2f9d10f71 | 8 years ago |
|
|
e8136cb667 | 8 years ago |
|
|
9fd92d2245 | 8 years ago |
|
|
6460e5893c | 8 years ago |
|
|
48eb207fa5 | 8 years ago |
|
|
76639f528a | 8 years ago |
|
|
88912170fc | 8 years ago |
|
|
507c33dc0e | 8 years ago |
|
|
404d740305 | 8 years ago |
|
|
a15f1fa92e | 8 years ago |
|
|
ef09f42af8 | 8 years ago |
|
|
bc1ac63af0 | 8 years ago |
|
|
5ddffa6c00 | 8 years ago |
|
|
e23276b83a | 8 years ago |
|
|
2692e866dd | 8 years ago |
|
|
70b4eb0877 | 8 years ago |
|
|
97957b7bc3 | 8 years ago |
|
|
9c634ebd45 | 8 years ago |
|
|
ffc44db085 | 8 years ago |
|
|
00d9f0c003 | 8 years ago |
|
|
540436fe7f | 8 years ago |
|
|
b8121aae79 | 8 years ago |
|
|
f2d7d32d1e | 8 years ago |
|
|
6de0f2b9b9 | 8 years ago |
|
|
9672465292 | 8 years ago |
|
|
893b885b5a | 8 years ago |
|
|
acf318d83c | 8 years ago |
|
|
84aa1fd0e6 | 8 years ago |
|
|
71fd4427cc | 8 years ago |
|
|
08f93ae95a | 8 years ago |
|
|
c374ce60c3 | 8 years ago |
|
|
9eda7bf59c | 8 years ago |
|
|
f36735a6b4 | 8 years ago |
|
|
bb84ffaf87 | 8 years ago |
|
|
2fa62e12a1 | 8 years ago |
|
|
b92e03eff6 | 8 years ago |
|
|
406f6473b6 | 8 years ago |
|
|
5ea59ab7c4 | 8 years ago |
|
|
f8a54005bf | 8 years ago |
|
|
b58d09cf02 | 8 years ago |
|
|
2388af05e1 | 8 years ago |
|
|
c3f4f18115 | 8 years ago |
|
|
37ce825dc1 | 8 years ago |
|
|
10972a55df | 9 years ago |
|
|
be2ab1bdca | 9 years ago |
|
|
dc6a80a77d | 9 years ago |
|
|
d7b3841f93 | 9 years ago |
|
|
08ecc1a241 | 9 years ago |
|
|
fa7b0777f3 | 9 years ago |
|
|
68b8baef34 | 9 years ago |
|
|
275899fd29 | 9 years ago |
|
|
df548f1db9 | 9 years ago |
|
|
6da5e5e5b2 | 9 years ago |
|
|
ea16b28e65 | 9 years ago |
|
|
839c185f5e | 9 years ago |
|
|
c74174a3c4 | 9 years ago |
|
|
3df27ffbdf | 9 years ago |
|
|
f2bb7df1db | 9 years ago |
|
|
4135a490ff | 9 years ago |
|
|
3bff9d2aae | 9 years ago |
|
|
cb482d3651 | 9 years ago |
|
|
7730994a79 | 9 years ago |
|
|
930b6dd820 | 9 years ago |
|
|
4765db4d86 | 9 years ago |
|
|
27dea59284 | 9 years ago |
|
|
da8cc90ab4 | 9 years ago |
|
|
754e8213cf | 9 years ago |
|
|
69a9cd66d1 | 9 years ago |
|
|
6781a98fd6 | 9 years ago |
|
|
5922c3fcde | 9 years ago |
|
|
840bf9564d | 9 years ago |
|
|
27f9925fd1 | 9 years ago |
|
|
3b48d64ec3 | 9 years ago |
|
|
57022a410f | 9 years ago |
|
|
7b0cb1b729 | 9 years ago |
|
|
b07972c526 | 9 years ago |
|
|
35e9417e8f | 9 years ago |
|
|
ef172bcf81 | 9 years ago |
|
|
40cc80371b | 9 years ago |
|
|
29f0171291 | 9 years ago |
|
|
12a6978748 | 9 years ago |
|
|
e8bcc4ca9f | 9 years ago |
|
|
5f77d89db6 | 9 years ago |
|
|
09abdc1b9f | 9 years ago |
|
|
6e8ebd8253 | 9 years ago |
|
|
276a1dfa1b | 9 years ago |
|
|
d361ee33fc | 9 years ago |
|
|
96012c6956 | 9 years ago |
|
|
3df7ee9cb1 | 9 years ago |
|
|
89c5f02d76 | 9 years ago |
|
|
3e5afa025a | 9 years ago |
|
|
8835aceb35 | 9 years ago |
|
|
ce97268895 | 9 years ago |
|
|
85255cf195 | 9 years ago |
|
|
9a99a06cd4 | 9 years ago |
|
|
ad4b9b7b00 | 9 years ago |
|
|
b1618632f0 | 9 years ago |
|
|
e0251f55cd | 9 years ago |
|
|
97509f6d03 | 9 years ago |
|
|
585802510a | 9 years ago |
|
|
c2d7096500 | 9 years ago |
|
|
a1e84f4978 | 9 years ago |
|
|
357753dc48 | 9 years ago |
|
|
da992312f3 | 9 years ago |
|
|
076382caf3 | 9 years ago |
|
|
4b8345bb94 | 9 years ago |
|
|
48d9a0fd26 | 9 years ago |
|
|
1c27c01ac5 | 9 years ago |
|
|
33f2dc27d3 | 9 years ago |
|
|
cc7be90de4 | 9 years ago |
|
|
8828c4d5cb | 9 years ago |
|
|
bf7acf678a | 9 years ago |
|
|
a4fb878b08 | 9 years ago |
|
|
09004e0d1e | 9 years ago |
|
|
4ccb9bd0d4 | 9 years ago |
|
|
e68d9bd76e | 9 years ago |
|
|
e451e70fe0 | 9 years ago |
|
|
b3941f8a33 | 9 years ago |
|
|
3b5b572f36 | 9 years ago |
|
|
70910fd2aa | 9 years ago |
|
|
af5afd12f2 | 9 years ago |
|
|
fb3a259e41 | 9 years ago |
|
|
978c145180 | 9 years ago |
|
|
3c539f0ec8 | 10 years ago |
|
|
fdc9c38f1a | 10 years ago |
|
|
e21f87a5f6 | 10 years ago |
|
|
52ae23c598 | 10 years ago |
|
|
e6bebd4775 | 10 years ago |
|
|
83b342d4e4 | 10 years ago |
|
|
6df6794f7a | 10 years ago |
|
|
d586c125a6 | 10 years ago |
|
|
8464e3d594 | 10 years ago |
|
|
9a26274b11 | 10 years ago |
|
|
2ef2505507 | 10 years ago |
|
|
5bb96c1b74 | 10 years ago |
|
|
cf4bb059f9 | 10 years ago |
|
|
12ebb136ba | 10 years ago |
|
|
941d710247 | 10 years ago |
|
|
a429b71665 | 10 years ago |
|
|
7f632afe29 | 10 years ago |
|
|
c978b2c5e9 | 10 years ago |
|
|
144a557ddb | 10 years ago |
|
|
09cf683e1a | 10 years ago |
|
|
73646ed7ce | 10 years ago |
|
|
5fe51be814 | 10 years ago |
|
|
4336c5c7c4 | 10 years ago |
|
|
93f963bc6c | 10 years ago |
|
|
8f7f184837 | 10 years ago |
@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Browser (please complete the following information):**
|
||||||
|
- OS: [e.g. MacOS, Linux]
|
||||||
|
- Browser [e.g. Firefox]
|
||||||
|
- Version [e.g. 80.0.1]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@ -1 +1 @@
|
|||||||
dist/
|
dist/build/
|
||||||
|
|||||||
@ -1,12 +1,20 @@
|
|||||||
{
|
{
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"esnext": true,
|
"devel": true,
|
||||||
"globalstrict": true,
|
"eqeqeq": true,
|
||||||
|
"esversion": 8,
|
||||||
"globals": {
|
"globals": {
|
||||||
|
"browser": false,
|
||||||
"self": false,
|
"self": false,
|
||||||
"vAPI": false,
|
|
||||||
"chrome": false,
|
"chrome": false,
|
||||||
"safari": false,
|
"log": false,
|
||||||
"Components": false // global variable in Firefox
|
"webext": false,
|
||||||
}
|
"vAPI": false,
|
||||||
|
"µMatrix": false
|
||||||
|
},
|
||||||
|
"laxbreak": true,
|
||||||
|
"strict": "global",
|
||||||
|
"undef": true,
|
||||||
|
"unused": true,
|
||||||
|
"validthis": true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
9f9b2c35acd233d92b67c61ddd4848a5 assets/umatrix/hosts-files.json
|
|
||||||
188ce926323d816ae9d7d5ebbb567862 assets/umatrix/blacklist.txt
|
|
||||||
3f870be0cde75b2560f945c0d5a22908 assets/thirdparties/mirror1.malwaredomains.com/files/immortal_domains.txt
|
|
||||||
7f0443f3dcc9abfd47cfbc95ce824ddb assets/thirdparties/mirror1.malwaredomains.com/files/README.md
|
|
||||||
b6d676582288285c639336fd73b64da8 assets/thirdparties/mirror1.malwaredomains.com/files/justdomains
|
|
||||||
8d2e70bb096f8c9f9eced97c41c5a201 assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat
|
|
||||||
0a9b9fc36673e38ce3d13006c6e8f22a assets/thirdparties/someonewhocares.org/hosts/hosts
|
|
||||||
9ed51ad85086c002ac9fb61b04a3aff3 assets/thirdparties/winhelp2002.mvps.org/hosts.txt
|
|
||||||
042419405031f0fcfac3735bf4f05e21 assets/thirdparties/www.malwaredomainlist.com/hostslist/README.md
|
|
||||||
a6e04103353f982fb0bf16b362518239 assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt
|
|
||||||
1105acc610ae213f425996d7bd41b49d assets/thirdparties/hosts-file.net/ad-servers
|
|
||||||
6b0d134e221bf4bd4ac67e8b1c8d54d0 assets/thirdparties/pgl.yoyo.org/as/serverlist
|
|
||||||
5b8e13b618c68293430913029118781a assets/thirdparties/pgl.yoyo.org/as/README.md
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,3 +0,0 @@
|
|||||||
<http://www.malwaredomains.com/?page_id=1508>:
|
|
||||||
|
|
||||||
"This malware block lists provided here are for free for noncommercial use as part of the fight against malware."
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
|||||||
<http://pgl.yoyo.org/as/index.php>:
|
|
||||||
|
|
||||||
Site does encourage use of the list, and nowhere could I find terms and
|
|
||||||
conditions to use the list. Assuming it can be used freely.
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,3 +0,0 @@
|
|||||||
<http://www.malwaredomainlist.com/>:
|
|
||||||
|
|
||||||
"Our list can be used for free by anyone. Feel free to use it."
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,61 +0,0 @@
|
|||||||
# Blacklist maintained by HTTP Switchboard
|
|
||||||
# For those domain names which are not found in other blacklists
|
|
||||||
|
|
||||||
2mdn.net # "2mdn.net is a domain used by Doubleclick which is an advertising company..."
|
|
||||||
aad73c550c.se # Related to adrotator.se which is itself blacklisted
|
|
||||||
acxiom-online.com # Wikipedia: "Acxiom Corporation is a marketing technology and services company".
|
|
||||||
adextent.com # "We are an advertising technology company - we build technologies that improve ads performance"
|
|
||||||
adgear.com # "AdGear is an online advertising technologies company based in Montreal, Canada"
|
|
||||||
adnxs.com # "Adnxs.com is run by AppNexus, a company that provides technology, data and analytics to help companies buy and sell online display advertising" (Ref.: http://www.theguardian.com/technology/2012/apr/23/adnxs-tracking-trackers-cookies-web-monitoring)
|
|
||||||
adobetag.com # "Adobe Announces Adobe Tag Manager for the Online Marketing Suite"
|
|
||||||
aimatch.com # "Ad Server, SAS® Intelligent Advertising for Publishers"
|
|
||||||
analytics.edgesuite.net
|
|
||||||
atedra.com # "Atedra est un réseau de publicité Internet francophone au Canada"
|
|
||||||
axf8.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
|
|
||||||
betrad.com # "Evidon: Home | Online Marketing Intelligence, Web Analytics, Privacy" (which also publishes "Ghostery" add-on..)
|
|
||||||
bizographics.com # "Business Audience Marketing"
|
|
||||||
bkrtx.com
|
|
||||||
|
|
||||||
# "Imagine Having The Power To Turn Abandoning Visitors Into Customers"
|
|
||||||
# "The BounceX software is tracking all the cursor movements of every visitor in real-time" (yikes!)
|
|
||||||
bounceexchange.com
|
|
||||||
|
|
||||||
clicktale.com # "See absolutely everything your visitors do on your webpage ... See their every mouse move, click and keystroke"
|
|
||||||
clicktale.net # Redirect to `clicktale.com`
|
|
||||||
crosspixel.net # (cookies, localStorage) "leading provider of high performance audience data and information for the real-time advertising industry"
|
|
||||||
crsspxl.com # Related to crosspixel.net
|
|
||||||
datarating.com # see https://github.com/gorhill/httpswitchboard/issues/343
|
|
||||||
displaymarketplace.com
|
|
||||||
erovinmo.com # No info whatsoever from site itself can be found = naughty corner. Ironically spotted at "http://www.technologyreview.com/news/519336/bruce-schneier-nsa-spying-is-making-us-less-safe/" (also: http://www.mywot.com/en/scorecard/erovinmo.com)
|
|
||||||
exelator.com # "domain used by eXelate Media which is an advertising company that is part of a network of sites, cookies, and other technologies used to track you" (Ref.: http://www.donottrackplus.com/trackers/exelator.com.php)
|
|
||||||
everestjs.net # related to `everesttech.net`
|
|
||||||
everesttech.net # "search engine marketing (SEM) solutions", pixel image on the page, looks like tracking to me. Spotted @ `http://www.homedepot.ca/` (search worked fine when blocking this hostname)
|
|
||||||
eyereturn.com # "eyeReturn Marketing is the only end-to-end digital advertising platform in the market"
|
|
||||||
gigya.com # "The tools you need to connect with consumers, harness rich data, and make marketing relevant"
|
|
||||||
inmuiads.info #
|
|
||||||
janrainbackplane.com # "Easily visualize, segment and update customer profiles to enable true personalized marketing"
|
|
||||||
krxd.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
|
|
||||||
lijit.com # "We provide online advertising services, audience analytics"
|
|
||||||
llnwd.net # http://en.wikipedia.org/wiki/Limelight_Networks
|
|
||||||
lduhtrp.net
|
|
||||||
mathtag.com # "domain used by MediaMath to place cookies, on behalf of its customers, on the computers of visitors to our selected customer's websites and who may view our customer's display advertisements"
|
|
||||||
mxpnl.com #
|
|
||||||
moatads.com # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
|
|
||||||
mookie1.com # "Specializing in online digital advertising, search marketing"
|
|
||||||
msads.net # Sounds like ads, and no home web page...
|
|
||||||
omtrdc.net # Redirect to Omniture
|
|
||||||
outbrain.com # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
|
|
||||||
panoramtech.net # As seen in a screenshot at http://arstechnica.com/security/2014/01/malware-vendors-buy-chrome-extensions-to-send-adware-filled-updates/
|
|
||||||
parsely.com # http://en.wikipedia.org/wiki/Parse.ly
|
|
||||||
peer39.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
|
|
||||||
pub2srv.com # "This url is used by ad network Propeller Ads Media for ad serving"
|
|
||||||
servebom.com # no home page, seen as 'tracking.servebom.com': good enough for this list
|
|
||||||
|
|
||||||
# These have "tracking" in domain name...
|
|
||||||
tracking.tomsguide.com
|
|
||||||
tracking.tomshardware.com
|
|
||||||
tracking.tomshardware.co.uk
|
|
||||||
|
|
||||||
wunderloop.net # https://www.eff.org/deeplinks/2013/06/third-party-resources-nsa-leaks
|
|
||||||
yceml.net
|
|
||||||
|
|
||||||
@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import jwt
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
from distutils.version import StrictVersion
|
||||||
|
from string import Template
|
||||||
|
|
||||||
|
# - Download target (raw) uMatrix.chromium.zip from GitHub
|
||||||
|
# - This is referred to as "raw" package
|
||||||
|
# - This will fail if not a dev build
|
||||||
|
# - Upload uMatrix.chromium.zip to Chrome store
|
||||||
|
# - Publish uMatrix.chromium.zip to Chrome store
|
||||||
|
|
||||||
|
# Find path to project root
|
||||||
|
projdir = os.path.split(os.path.abspath(__file__))[0]
|
||||||
|
while not os.path.isdir(os.path.join(projdir, '.git')):
|
||||||
|
projdir = os.path.normpath(os.path.join(projdir, '..'))
|
||||||
|
|
||||||
|
# We need a version string to work with
|
||||||
|
if len(sys.argv) >= 2 and sys.argv[1]:
|
||||||
|
version = sys.argv[1]
|
||||||
|
else:
|
||||||
|
version = input('Github release version: ')
|
||||||
|
version.strip()
|
||||||
|
if not re.search('^\d+\.\d+\.\d+(b|rc)\d+$', version):
|
||||||
|
print('Error: Invalid version string.')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
cs_extension_id = 'eckgcipdkhcfghnmincccnhpdmnbefki'
|
||||||
|
tmpdir = tempfile.TemporaryDirectory()
|
||||||
|
raw_zip_filename = 'uMatrix_'+ version + '.chromium.zip'
|
||||||
|
raw_zip_filepath = os.path.join(tmpdir.name, raw_zip_filename)
|
||||||
|
github_owner = 'gorhill'
|
||||||
|
github_repo = 'uMatrix'
|
||||||
|
|
||||||
|
# Load/save auth secrets
|
||||||
|
# The build directory is excluded from git
|
||||||
|
ubo_secrets = dict()
|
||||||
|
ubo_secrets_filename = os.path.join(projdir, 'dist', 'build', 'ubo_secrets')
|
||||||
|
if os.path.isfile(ubo_secrets_filename):
|
||||||
|
with open(ubo_secrets_filename) as f:
|
||||||
|
ubo_secrets = json.load(f)
|
||||||
|
|
||||||
|
def input_secret(prompt, token):
|
||||||
|
if token in ubo_secrets:
|
||||||
|
prompt += ' ✔'
|
||||||
|
prompt += ': '
|
||||||
|
value = input(prompt).strip()
|
||||||
|
if len(value) == 0:
|
||||||
|
if token not in ubo_secrets:
|
||||||
|
print('Token error:', token)
|
||||||
|
exit(1)
|
||||||
|
value = ubo_secrets[token]
|
||||||
|
elif token not in ubo_secrets or value != ubo_secrets[token]:
|
||||||
|
ubo_secrets[token] = value
|
||||||
|
exists = os.path.isfile(ubo_secrets_filename)
|
||||||
|
with open(ubo_secrets_filename, 'w') as f:
|
||||||
|
json.dump(ubo_secrets, f, indent=2)
|
||||||
|
if not exists:
|
||||||
|
os.chmod(ubo_secrets_filename, 0o600)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
# GitHub API token
|
||||||
|
github_token = input_secret('Github token', 'github_token')
|
||||||
|
github_auth = 'token ' + github_token
|
||||||
|
|
||||||
|
#
|
||||||
|
# Get metadata from GitHub about the release
|
||||||
|
#
|
||||||
|
|
||||||
|
# https://developer.github.com/v3/repos/releases/#get-a-single-release
|
||||||
|
print('Downloading release info from GitHub...')
|
||||||
|
release_info_url = 'https://api.github.com/repos/{0}/{1}/releases/tags/{2}'.format(github_owner, github_repo, version)
|
||||||
|
headers = { 'Authorization': github_auth, }
|
||||||
|
response = requests.get(release_info_url, headers=headers)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print('Error: Release not found: {0}'.format(response.status_code))
|
||||||
|
exit(1)
|
||||||
|
release_info = response.json()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Extract URL to raw package from metadata
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find url for uMatrix.chromium.zip
|
||||||
|
raw_zip_url = ''
|
||||||
|
for asset in release_info['assets']:
|
||||||
|
if asset['name'] == raw_zip_filename:
|
||||||
|
raw_zip_url = asset['url']
|
||||||
|
if len(raw_zip_url) == 0:
|
||||||
|
print('Error: Release asset URL not found')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Download raw package from GitHub
|
||||||
|
#
|
||||||
|
|
||||||
|
# https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
|
||||||
|
print('Downloading raw zip package from GitHub...')
|
||||||
|
headers = {
|
||||||
|
'Authorization': github_auth,
|
||||||
|
'Accept': 'application/octet-stream',
|
||||||
|
}
|
||||||
|
response = requests.get(raw_zip_url, headers=headers)
|
||||||
|
# Redirections are transparently handled:
|
||||||
|
# http://docs.python-requests.org/en/master/user/quickstart/#redirection-and-history
|
||||||
|
if response.status_code != 200:
|
||||||
|
print('Error: Downloading raw package failed -- server error {0}'.format(response.status_code))
|
||||||
|
exit(1)
|
||||||
|
with open(raw_zip_filepath, 'wb') as f:
|
||||||
|
f.write(response.content)
|
||||||
|
print('Downloaded raw package saved as {0}'.format(raw_zip_filepath))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Upload to Chrome store
|
||||||
|
#
|
||||||
|
|
||||||
|
# Auth tokens
|
||||||
|
cs_id = input_secret('Chrome store id', 'cs_id')
|
||||||
|
cs_secret = input_secret('Chrome store secret', 'cs_secret')
|
||||||
|
cs_refresh = input_secret('Chrome store refresh token', 'cs_refresh')
|
||||||
|
|
||||||
|
print('Uploading to Chrome store...')
|
||||||
|
with open(raw_zip_filepath, 'rb') as f:
|
||||||
|
print('Generating access token...')
|
||||||
|
auth_url = 'https://accounts.google.com/o/oauth2/token'
|
||||||
|
auth_payload = {
|
||||||
|
'client_id': cs_id,
|
||||||
|
'client_secret': cs_secret,
|
||||||
|
'grant_type': 'refresh_token',
|
||||||
|
'refresh_token': cs_refresh,
|
||||||
|
}
|
||||||
|
auth_response = requests.post(auth_url, data=auth_payload)
|
||||||
|
if auth_response.status_code != 200:
|
||||||
|
print('Error: Auth failed -- server error {0}'.format(auth_response.status_code))
|
||||||
|
print(auth_response.text)
|
||||||
|
exit(1)
|
||||||
|
response_dict = auth_response.json()
|
||||||
|
if 'access_token' not in response_dict:
|
||||||
|
print('Error: Auth failed -- no access token')
|
||||||
|
exit(1)
|
||||||
|
# Prepare access token
|
||||||
|
cs_auth = 'Bearer ' + response_dict['access_token']
|
||||||
|
headers = {
|
||||||
|
'Authorization': cs_auth,
|
||||||
|
'x-goog-api-version': '2',
|
||||||
|
}
|
||||||
|
# Upload
|
||||||
|
print('Uploading package...')
|
||||||
|
upload_url = 'https://www.googleapis.com/upload/chromewebstore/v1.1/items/{0}'.format(cs_extension_id)
|
||||||
|
upload_response = requests.put(upload_url, headers=headers, data=f)
|
||||||
|
f.close()
|
||||||
|
if upload_response.status_code != 200:
|
||||||
|
print('Upload failed -- server error {0}'.format(upload_response.status_code))
|
||||||
|
print(upload_response.text)
|
||||||
|
exit(1)
|
||||||
|
response_dict = upload_response.json();
|
||||||
|
if 'uploadState' not in response_dict or response_dict['uploadState'] != 'SUCCESS':
|
||||||
|
print('Upload failed -- server error {0}'.format(response_dict['uploadState']))
|
||||||
|
exit(1)
|
||||||
|
print('Upload succeeded.')
|
||||||
|
# Publish
|
||||||
|
print('Publishing package...')
|
||||||
|
publish_url = 'https://www.googleapis.com/chromewebstore/v1.1/items/{0}/publish'.format(cs_extension_id)
|
||||||
|
headers = {
|
||||||
|
'Authorization': cs_auth,
|
||||||
|
'x-goog-api-version': '2',
|
||||||
|
'Content-Length': '0',
|
||||||
|
}
|
||||||
|
publish_response = requests.post(publish_url, headers=headers)
|
||||||
|
if publish_response.status_code != 200:
|
||||||
|
print('Error: Chrome store publishing failed -- server error {0}'.format(publish_response.status_code))
|
||||||
|
exit(1)
|
||||||
|
response_dict = publish_response.json();
|
||||||
|
if 'status' not in response_dict or response_dict['status'][0] != 'OK':
|
||||||
|
print('Publishing failed -- server error {0}'.format(response_dict['status']))
|
||||||
|
exit(1)
|
||||||
|
print('Publishing succeeded.')
|
||||||
|
|
||||||
|
print('All done.')
|
||||||
@ -0,0 +1,321 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import jwt
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import requests
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
from string import Template
|
||||||
|
|
||||||
|
# - Download target (raw) nuTensor.firefox.xpi from GitHub
|
||||||
|
# - This is referred to as "raw" package
|
||||||
|
# - This will fail if not a dev build
|
||||||
|
# - Modify raw package to make it self-hosted
|
||||||
|
# - This is referred to as "unsigned" package
|
||||||
|
# - Ask AMO to sign nuTensor.firefox.xpi
|
||||||
|
# - Generate JWT to be used for communication with server
|
||||||
|
# - Upload unsigned package to AMO
|
||||||
|
# - Wait for a valid download URL for signed package
|
||||||
|
# - Download signed package as nuTensor.firefox.signed.xpi
|
||||||
|
# - This is referred to as "signed" package
|
||||||
|
# - Upload nuTensor.firefox.signed.xpi to GitHub
|
||||||
|
# - Remove nuTensor.firefox.xpi from GitHub
|
||||||
|
# - Modify updates.json to point to new version
|
||||||
|
# - Commit changes to repo
|
||||||
|
|
||||||
|
# Find path to project root
|
||||||
|
projdir = os.path.split(os.path.abspath(__file__))[0]
|
||||||
|
while not os.path.isdir(os.path.join(projdir, '.git')):
|
||||||
|
projdir = os.path.normpath(os.path.join(projdir, '..'))
|
||||||
|
# Check that found project root is valid
|
||||||
|
version_filepath = os.path.join(projdir, 'dist', 'version')
|
||||||
|
if not os.path.isfile(version_filepath):
|
||||||
|
print('Version file not found.')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# We need a version string to work with
|
||||||
|
if len(sys.argv) >= 2 and sys.argv[1]:
|
||||||
|
tag_version = sys.argv[1]
|
||||||
|
else:
|
||||||
|
tag_version = input('Github release version: ')
|
||||||
|
tag_version.strip()
|
||||||
|
match = re.search('^(\d+\.\d+\.\d+)(?:(b|rc)(\d+))?$', tag_version)
|
||||||
|
if not match:
|
||||||
|
print('Error: Invalid version string.')
|
||||||
|
exit(1)
|
||||||
|
ext_version = match.group(1);
|
||||||
|
if match.group(2):
|
||||||
|
revision = int(match.group(3))
|
||||||
|
if match.group(2) == 'rc':
|
||||||
|
revision += 100;
|
||||||
|
ext_version += '.' + str(revision)
|
||||||
|
|
||||||
|
extension_id = 'nuTensor@geekprojects.com'
|
||||||
|
tmpdir = tempfile.TemporaryDirectory()
|
||||||
|
raw_xpi_filename = 'nuTensor_' + tag_version + '.firefox.xpi'
|
||||||
|
raw_xpi_filepath = os.path.join(tmpdir.name, raw_xpi_filename)
|
||||||
|
unsigned_xpi_filepath = os.path.join(tmpdir.name, 'nuTensor.firefox.unsigned.xpi')
|
||||||
|
signed_xpi_filename = 'nuTensor_' + tag_version + '.firefox.signed.xpi'
|
||||||
|
signed_xpi_filepath = os.path.join(tmpdir.name, signed_xpi_filename)
|
||||||
|
github_owner = 'geekprojects'
|
||||||
|
github_repo = 'nuTensor'
|
||||||
|
|
||||||
|
# Load/save auth secrets
|
||||||
|
# The build directory is excluded from git
|
||||||
|
ubo_secrets = dict()
|
||||||
|
ubo_secrets_filename = os.path.join(projdir, 'dist', 'build', 'ubo_secrets')
|
||||||
|
if os.path.isfile(ubo_secrets_filename):
|
||||||
|
with open(ubo_secrets_filename) as f:
|
||||||
|
ubo_secrets = json.load(f)
|
||||||
|
|
||||||
|
def input_secret(prompt, token):
|
||||||
|
if token in ubo_secrets:
|
||||||
|
prompt += ' ✔'
|
||||||
|
prompt += ': '
|
||||||
|
value = input(prompt).strip()
|
||||||
|
if len(value) == 0:
|
||||||
|
if token not in ubo_secrets:
|
||||||
|
print('Token error:', token)
|
||||||
|
exit(1)
|
||||||
|
value = ubo_secrets[token]
|
||||||
|
elif token not in ubo_secrets or value != ubo_secrets[token]:
|
||||||
|
ubo_secrets[token] = value
|
||||||
|
exists = os.path.isfile(ubo_secrets_filename)
|
||||||
|
with open(ubo_secrets_filename, 'w') as f:
|
||||||
|
json.dump(ubo_secrets, f, indent=2)
|
||||||
|
if not exists:
|
||||||
|
os.chmod(ubo_secrets_filename, 0o600)
|
||||||
|
return value
|
||||||
|
|
||||||
|
# GitHub API token
|
||||||
|
github_token = input_secret('Github token', 'github_token')
|
||||||
|
github_auth = 'token ' + github_token
|
||||||
|
|
||||||
|
#
|
||||||
|
# Get metadata from GitHub about the release
|
||||||
|
#
|
||||||
|
|
||||||
|
# https://developer.github.com/v3/repos/releases/#get-a-single-release
|
||||||
|
print('Downloading release info from GitHub...')
|
||||||
|
release_info_url = 'https://api.github.com/repos/{0}/{1}/releases/tags/{2}'.format(github_owner, github_repo, tag_version)
|
||||||
|
headers = { 'Authorization': github_auth, }
|
||||||
|
response = requests.get(release_info_url, headers=headers)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print('Error: Release not found: {0}'.format(response.status_code))
|
||||||
|
exit(1)
|
||||||
|
release_info = response.json()
|
||||||
|
|
||||||
|
#
|
||||||
|
# Extract URL to raw package from metadata
|
||||||
|
#
|
||||||
|
|
||||||
|
# Find url for nuTensor.firefox.xpi
|
||||||
|
raw_xpi_url = ''
|
||||||
|
for asset in release_info['assets']:
|
||||||
|
if asset['name'] == signed_xpi_filename:
|
||||||
|
print('Error: Found existing signed self-hosted package.')
|
||||||
|
exit(1)
|
||||||
|
if asset['name'] == raw_xpi_filename:
|
||||||
|
raw_xpi_url = asset['url']
|
||||||
|
if len(raw_xpi_url) == 0:
|
||||||
|
print('Error: Release asset URL not found')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Download raw package from GitHub
|
||||||
|
#
|
||||||
|
|
||||||
|
# https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
|
||||||
|
print('Downloading raw xpi package from GitHub...')
|
||||||
|
headers = {
|
||||||
|
'Authorization': github_auth,
|
||||||
|
'Accept': 'application/octet-stream',
|
||||||
|
}
|
||||||
|
response = requests.get(raw_xpi_url, headers=headers)
|
||||||
|
# Redirections are transparently handled:
|
||||||
|
# http://docs.python-requests.org/en/master/user/quickstart/#redirection-and-history
|
||||||
|
if response.status_code != 200:
|
||||||
|
print('Error: Downloading raw package failed -- server error {0}'.format(response.status_code))
|
||||||
|
exit(1)
|
||||||
|
with open(raw_xpi_filepath, 'wb') as f:
|
||||||
|
f.write(response.content)
|
||||||
|
print('Downloaded raw package saved as {0}'.format(raw_xpi_filepath))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Convert the package to a self-hosted one: add `update_url` to the manifest
|
||||||
|
#
|
||||||
|
|
||||||
|
print('Converting raw xpi package into self-hosted xpi package...')
|
||||||
|
with zipfile.ZipFile(raw_xpi_filepath, 'r') as zipin:
|
||||||
|
with zipfile.ZipFile(unsigned_xpi_filepath, 'w') as zipout:
|
||||||
|
for item in zipin.infolist():
|
||||||
|
data = zipin.read(item.filename)
|
||||||
|
if item.filename == 'manifest.json':
|
||||||
|
manifest = json.loads(bytes.decode(data))
|
||||||
|
manifest['browser_specific_settings']['gecko']['update_url'] = 'https://raw.githubusercontent.com/{0}/{1}/master/dist/firefox/updates.json'.format(github_owner, github_repo)
|
||||||
|
data = json.dumps(manifest, indent=2, separators=(',', ': '), sort_keys=True).encode()
|
||||||
|
zipout.writestr(item, data)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ask AMO to sign the self-hosted package
|
||||||
|
# - https://developer.mozilla.org/en-US/Add-ons/Distribution#Distributing_your_add-on
|
||||||
|
# - https://pyjwt.readthedocs.io/en/latest/usage.html
|
||||||
|
# - https://addons-server.readthedocs.io/en/latest/topics/api/auth.html
|
||||||
|
# - https://addons-server.readthedocs.io/en/latest/topics/api/signing.html
|
||||||
|
#
|
||||||
|
|
||||||
|
amo_api_key = ''
|
||||||
|
amo_secret = ''
|
||||||
|
|
||||||
|
def get_jwt_auth():
|
||||||
|
global amo_api_key
|
||||||
|
if amo_api_key == '':
|
||||||
|
amo_api_key = input_secret('AMO API key', 'amo_api_key')
|
||||||
|
global amo_secret
|
||||||
|
if amo_secret == '':
|
||||||
|
amo_secret = input_secret('AMO API secret', 'amo_secret')
|
||||||
|
amo_nonce = os.urandom(8).hex()
|
||||||
|
jwt_payload = {
|
||||||
|
'iss': amo_api_key,
|
||||||
|
'jti': amo_nonce,
|
||||||
|
'iat': datetime.datetime.utcnow(),
|
||||||
|
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=15),
|
||||||
|
}
|
||||||
|
return 'JWT ' + jwt.encode(jwt_payload, amo_secret).decode()
|
||||||
|
|
||||||
|
print('Ask AMO to sign self-hosted xpi package...')
|
||||||
|
with open(unsigned_xpi_filepath, 'rb') as f:
|
||||||
|
# https://blog.mozilla.org/addons/2019/11/11/security-improvements-in-amo-upload-tools/
|
||||||
|
# "We recommend allowing up to 15 minutes."
|
||||||
|
interval = 60 # check every 60 seconds
|
||||||
|
countdown = 15 * 60 / interval # for at most 15 minutes
|
||||||
|
headers = { 'Authorization': get_jwt_auth(), }
|
||||||
|
data = { 'channel': 'unlisted' }
|
||||||
|
files = { 'upload': f, }
|
||||||
|
signing_url = 'https://addons.mozilla.org/api/v3/addons/{0}/versions/{1}/'.format(extension_id, ext_version)
|
||||||
|
print('Submitting package to be signed...')
|
||||||
|
response = requests.put(signing_url, headers=headers, data=data, files=files)
|
||||||
|
if response.status_code != 202:
|
||||||
|
print('Error: Creating new version failed -- server error {0}'.format(response.status_code))
|
||||||
|
print(response.text)
|
||||||
|
exit(1)
|
||||||
|
print('Request for signing self-hosted xpi package succeeded.')
|
||||||
|
signing_request_response = response.json();
|
||||||
|
f.close()
|
||||||
|
print('Waiting for AMO to process the request to sign the self-hosted xpi package...')
|
||||||
|
# Wait for signed package to be ready
|
||||||
|
signing_check_url = signing_request_response['url']
|
||||||
|
while True:
|
||||||
|
sys.stdout.write('.')
|
||||||
|
sys.stdout.flush()
|
||||||
|
time.sleep(interval)
|
||||||
|
countdown -= 1
|
||||||
|
if countdown <= 0:
|
||||||
|
print('Error: AMO signing timed out')
|
||||||
|
exit(1)
|
||||||
|
headers = { 'Authorization': get_jwt_auth(), }
|
||||||
|
response = requests.get(signing_check_url, headers=headers)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print('Error: AMO signing failed -- server error {0}'.format(response.status_code))
|
||||||
|
print(response.text)
|
||||||
|
exit(1)
|
||||||
|
signing_check_response = response.json()
|
||||||
|
if not signing_check_response['processed']:
|
||||||
|
continue
|
||||||
|
if not signing_check_response['valid']:
|
||||||
|
print('Error: AMO validation failed')
|
||||||
|
print(response.text)
|
||||||
|
exit(1)
|
||||||
|
if not signing_check_response['files'] or len(signing_check_response['files']) == 0:
|
||||||
|
continue
|
||||||
|
if not signing_check_response['files'][0]['signed']:
|
||||||
|
continue
|
||||||
|
if not signing_check_response['files'][0]['download_url']:
|
||||||
|
print('Error: AMO signing failed')
|
||||||
|
print(response.text)
|
||||||
|
exit(1)
|
||||||
|
print('\r')
|
||||||
|
print('Self-hosted xpi package successfully signed.')
|
||||||
|
download_url = signing_check_response['files'][0]['download_url']
|
||||||
|
print('Downloading signed self-hosted xpi package from {0}...'.format(download_url))
|
||||||
|
response = requests.get(download_url, headers=headers)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print('Error: Download signed package failed -- server error {0}'.format(response.status_code))
|
||||||
|
print(response.text)
|
||||||
|
exit(1)
|
||||||
|
with open(signed_xpi_filepath, 'wb') as f:
|
||||||
|
f.write(response.content)
|
||||||
|
f.close()
|
||||||
|
print('Signed self-hosted xpi package downloaded.')
|
||||||
|
break
|
||||||
|
|
||||||
|
#
|
||||||
|
# Upload signed package to GitHub
|
||||||
|
#
|
||||||
|
|
||||||
|
# https://developer.github.com/v3/repos/releases/#upload-a-release-asset
|
||||||
|
print('Uploading signed self-hosted xpi package to GitHub...')
|
||||||
|
with open(signed_xpi_filepath, 'rb') as f:
|
||||||
|
url = release_info['upload_url'].replace('{?name,label}', '?name=' + signed_xpi_filename)
|
||||||
|
headers = {
|
||||||
|
'Authorization': github_auth,
|
||||||
|
'Content-Type': 'application/zip',
|
||||||
|
}
|
||||||
|
response = requests.post(url, headers=headers, data=f.read())
|
||||||
|
if response.status_code != 201:
|
||||||
|
print('Error: Upload signed package failed -- server error: {0}'.format(response.status_code))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Remove raw package from GitHub
|
||||||
|
#
|
||||||
|
|
||||||
|
# https://developer.github.com/v3/repos/releases/#delete-a-release-asset
|
||||||
|
print('Remove raw xpi package from GitHub...')
|
||||||
|
headers = { 'Authorization': github_auth, }
|
||||||
|
response = requests.delete(raw_xpi_url, headers=headers)
|
||||||
|
if response.status_code != 204:
|
||||||
|
print('Error: Deletion of raw package failed -- server error: {0}'.format(response.status_code))
|
||||||
|
|
||||||
|
#
|
||||||
|
# Update updates.json to point to new package -- but only if just-signed
|
||||||
|
# package is higher version than current one.
|
||||||
|
#
|
||||||
|
|
||||||
|
print('Update GitHub to point to newly signed self-hosted xpi package...')
|
||||||
|
updates_json_filepath = os.path.join(projdir, 'dist', 'firefox', 'updates.json')
|
||||||
|
with open(updates_json_filepath) as f:
|
||||||
|
updates_json = json.load(f)
|
||||||
|
f.close()
|
||||||
|
previous_version = updates_json['addons'][extension_id]['updates'][0]['version']
|
||||||
|
if LooseVersion(ext_version) > LooseVersion(previous_version):
|
||||||
|
with open(os.path.join(projdir, 'dist', 'firefox', 'updates.template.json')) as f:
|
||||||
|
template_json = Template(f.read())
|
||||||
|
f.close()
|
||||||
|
updates_json = template_json.substitute(ext_version=ext_version, tag_version=tag_version)
|
||||||
|
with open(updates_json_filepath, 'w') as f:
|
||||||
|
f.write(updates_json)
|
||||||
|
f.close()
|
||||||
|
# Automatically git add/commit if needed.
|
||||||
|
# - Stage the changed file
|
||||||
|
r = subprocess.run(['git', 'status', '-s', updates_json_filepath], stdout=subprocess.PIPE)
|
||||||
|
rout = bytes.decode(r.stdout).strip()
|
||||||
|
if len(rout) >= 2 and rout[1] == 'M':
|
||||||
|
subprocess.run(['git', 'add', updates_json_filepath])
|
||||||
|
# - Commit the staged file
|
||||||
|
r = subprocess.run(['git', 'status', '-s', updates_json_filepath], stdout=subprocess.PIPE)
|
||||||
|
rout = bytes.decode(r.stdout).strip()
|
||||||
|
if len(rout) >= 2 and rout[0] == 'M':
|
||||||
|
subprocess.run(['git', 'commit', '-m', 'Make Firefox dev build auto-update', updates_json_filepath])
|
||||||
|
subprocess.run(['git', 'push', 'origin', 'HEAD'])
|
||||||
|
|
||||||
|
print('All done.')
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"addons": {
|
||||||
|
"nuTensor@geekprojects.com": {
|
||||||
|
"updates": [
|
||||||
|
{
|
||||||
|
"version": "1.4.1.6",
|
||||||
|
"browser_specific_settings": { "gecko": { "strict_min_version": "56" } },
|
||||||
|
"update_info_url": "https://github.com/geekprojects/nuTensor/releases/tag/1.4.1b6",
|
||||||
|
"update_link": "https://github.com/geekprojects/nuTensor/releases/download/1.4.1b6/nuTensor_1.4.1b6.firefox.signed.xpi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"addons": {
|
||||||
|
"nuTensor@geekprojects.com": {
|
||||||
|
"updates": [
|
||||||
|
{
|
||||||
|
"version": "$ext_version",
|
||||||
|
"browser_specific_settings": { "gecko": { "strict_min_version": "56" } },
|
||||||
|
"update_info_url": "https://github.com/geekprojects/nuTensor/releases/tag/$tag_version",
|
||||||
|
"update_link": "https://github.com/geekprojects/nuTensor/releases/download/$tag_version/nuTensor_$tag_version.firefox.signed.xpi"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1 @@
|
|||||||
|
1.5.0.0
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
## Building nuTensor
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
|
||||||
|
Tools:
|
||||||
|
* bash
|
||||||
|
* python 3
|
||||||
|
|
||||||
|
You will need both this nuTensor and the nuAssets repositories. These should both be placed in the same directory:
|
||||||
|
```
|
||||||
|
git clone https://github.com/geekprojects/nuAssets.git
|
||||||
|
git clone https://github.com/geekprojects/nuTensor.git
|
||||||
|
cd nuTensor
|
||||||
|
```
|
||||||
|
|
||||||
|
### Packaging
|
||||||
|
You can now run the scripts that package everything up.
|
||||||
|
These are bash scripts. They have only been tested on Linux and MacOS.
|
||||||
|
|
||||||
|
#### For Firefox
|
||||||
|
```
|
||||||
|
tools/make-firefox.sh all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### For Chrome/Chromium (Not yet tested)
|
||||||
|
```
|
||||||
|
tools/make-chromium.sh all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### For Opera (Not yet tested)
|
||||||
|
```
|
||||||
|
tools/make-opera.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The installation package should now be found in dist/build/
|
||||||
|
|
||||||
|
### Installing
|
||||||
|
|
||||||
|
Follow the instructions in [README.md](README.md) to install it.
|
||||||
@ -1,8 +1,8 @@
|
|||||||
### This is µMatrix's manifesto
|
### This is uMatrix's manifesto
|
||||||
|
|
||||||
1. The **user decides** what web content is acceptable or not in their browser.
|
1. The **user decides** what web content is acceptable or not in their browser.
|
||||||
|
|
||||||
That is all.
|
That is all.
|
||||||
|
|
||||||
The purpose of _µMatrix_ is to give the user the means for informed
|
The purpose of _uMatrix_ is to give the user the means for informed
|
||||||
consent and informed dissent.
|
consent and informed dissent.
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<head>
|
|
||||||
<script src="js/vapi-client.js"></script>
|
|
||||||
<script src="js/options_ui.js"></script>
|
|
||||||
<title></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,308 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2019-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For non-background page
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Direct messaging connection ability
|
||||||
|
|
||||||
|
(( ) => {
|
||||||
|
// >>>>>>>> start of private namespace
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof vAPI !== 'object' ||
|
||||||
|
vAPI.messaging instanceof Object === false ||
|
||||||
|
vAPI.MessagingConnection instanceof Function
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listeners = new Set();
|
||||||
|
const connections = new Map();
|
||||||
|
|
||||||
|
vAPI.MessagingConnection = class {
|
||||||
|
constructor(handler, details) {
|
||||||
|
this.messaging = vAPI.messaging;
|
||||||
|
this.handler = handler;
|
||||||
|
this.id = details.id;
|
||||||
|
this.to = details.to;
|
||||||
|
this.toToken = details.toToken;
|
||||||
|
this.from = details.from;
|
||||||
|
this.fromToken = details.fromToken;
|
||||||
|
this.checkTimer = undefined;
|
||||||
|
// On Firefox it appears ports are not automatically disconnected
|
||||||
|
// when navigating to another page.
|
||||||
|
const ctor = vAPI.MessagingConnection;
|
||||||
|
if ( ctor.pagehide !== undefined ) { return; }
|
||||||
|
ctor.pagehide = ( ) => {
|
||||||
|
for ( const connection of connections.values() ) {
|
||||||
|
connection.disconnect();
|
||||||
|
connection.handler(
|
||||||
|
connection.toDetails('connectionBroken')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('pagehide', ctor.pagehide);
|
||||||
|
}
|
||||||
|
toDetails(what, payload) {
|
||||||
|
return {
|
||||||
|
what: what,
|
||||||
|
id: this.id,
|
||||||
|
from: this.from,
|
||||||
|
fromToken: this.fromToken,
|
||||||
|
to: this.to,
|
||||||
|
toToken: this.toToken,
|
||||||
|
payload: payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
disconnect() {
|
||||||
|
if ( this.checkTimer !== undefined ) {
|
||||||
|
clearTimeout(this.checkTimer);
|
||||||
|
this.checkTimer = undefined;
|
||||||
|
}
|
||||||
|
connections.delete(this.id);
|
||||||
|
const port = this.messaging.getPort();
|
||||||
|
if ( port === null ) { return; }
|
||||||
|
port.postMessage({
|
||||||
|
channel: 'vapi',
|
||||||
|
msg: this.toDetails('connectionBroken'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
checkAsync() {
|
||||||
|
if ( this.checkTimer !== undefined ) {
|
||||||
|
clearTimeout(this.checkTimer);
|
||||||
|
}
|
||||||
|
this.checkTimer = vAPI.setTimeout(
|
||||||
|
( ) => { this.check(); },
|
||||||
|
499
|
||||||
|
);
|
||||||
|
}
|
||||||
|
check() {
|
||||||
|
this.checkTimer = undefined;
|
||||||
|
if ( connections.has(this.id) === false ) { return; }
|
||||||
|
const port = this.messaging.getPort();
|
||||||
|
if ( port === null ) { return; }
|
||||||
|
port.postMessage({
|
||||||
|
channel: 'vapi',
|
||||||
|
msg: this.toDetails('connectionCheck'),
|
||||||
|
});
|
||||||
|
this.checkAsync();
|
||||||
|
}
|
||||||
|
receive(details) {
|
||||||
|
switch ( details.what ) {
|
||||||
|
case 'connectionAccepted':
|
||||||
|
this.toToken = details.toToken;
|
||||||
|
this.handler(details);
|
||||||
|
this.checkAsync();
|
||||||
|
break;
|
||||||
|
case 'connectionBroken':
|
||||||
|
connections.delete(this.id);
|
||||||
|
this.handler(details);
|
||||||
|
break;
|
||||||
|
case 'connectionMessage':
|
||||||
|
this.handler(details);
|
||||||
|
this.checkAsync();
|
||||||
|
break;
|
||||||
|
case 'connectionCheck':
|
||||||
|
const port = this.messaging.getPort();
|
||||||
|
if ( port === null ) { return; }
|
||||||
|
if ( connections.has(this.id) ) {
|
||||||
|
this.checkAsync();
|
||||||
|
} else {
|
||||||
|
details.what = 'connectionBroken';
|
||||||
|
port.postMessage({ channel: 'vapi', msg: details });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'connectionRefused':
|
||||||
|
connections.delete(this.id);
|
||||||
|
this.handler(details);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
send(payload) {
|
||||||
|
const port = this.messaging.getPort();
|
||||||
|
if ( port === null ) { return; }
|
||||||
|
port.postMessage({
|
||||||
|
channel: 'vapi',
|
||||||
|
msg: this.toDetails('connectionMessage', payload),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static addListener(listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
static async connectTo(from, to, handler) {
|
||||||
|
const port = vAPI.messaging.getPort();
|
||||||
|
if ( port === null ) { return; }
|
||||||
|
const connection = new vAPI.MessagingConnection(handler, {
|
||||||
|
id: `${from}-${to}-${vAPI.sessionId}`,
|
||||||
|
to: to,
|
||||||
|
from: from,
|
||||||
|
fromToken: port.name
|
||||||
|
});
|
||||||
|
connections.set(connection.id, connection);
|
||||||
|
port.postMessage({
|
||||||
|
channel: 'vapi',
|
||||||
|
msg: {
|
||||||
|
what: 'connectionRequested',
|
||||||
|
id: connection.id,
|
||||||
|
from: from,
|
||||||
|
fromToken: port.name,
|
||||||
|
to: to,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return connection.id;
|
||||||
|
}
|
||||||
|
static disconnectFrom(connectionId) {
|
||||||
|
const connection = connections.get(connectionId);
|
||||||
|
if ( connection === undefined ) { return; }
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
static sendTo(connectionId, payload) {
|
||||||
|
const connection = connections.get(connectionId);
|
||||||
|
if ( connection === undefined ) { return; }
|
||||||
|
connection.send(payload);
|
||||||
|
}
|
||||||
|
static canDestroyPort() {
|
||||||
|
return listeners.length === 0 && connections.size === 0;
|
||||||
|
}
|
||||||
|
static mustDestroyPort() {
|
||||||
|
if ( connections.size === 0 ) { return; }
|
||||||
|
for ( const connection of connections.values() ) {
|
||||||
|
connection.receive({ what: 'connectionBroken' });
|
||||||
|
}
|
||||||
|
connections.clear();
|
||||||
|
}
|
||||||
|
static canProcessMessage(details) {
|
||||||
|
if ( details.channel !== 'vapi' ) { return; }
|
||||||
|
switch ( details.msg.what ) {
|
||||||
|
case 'connectionAccepted':
|
||||||
|
case 'connectionBroken':
|
||||||
|
case 'connectionCheck':
|
||||||
|
case 'connectionMessage':
|
||||||
|
case 'connectionRefused': {
|
||||||
|
const connection = connections.get(details.msg.id);
|
||||||
|
if ( connection === undefined ) { break; }
|
||||||
|
connection.receive(details.msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 'connectionRequested':
|
||||||
|
if ( listeners.length === 0 ) { return; }
|
||||||
|
const port = vAPI.messaging.getPort();
|
||||||
|
if ( port === null ) { break; }
|
||||||
|
let listener, result;
|
||||||
|
for ( listener of listeners ) {
|
||||||
|
result = listener(details.msg);
|
||||||
|
if ( result !== undefined ) { break; }
|
||||||
|
}
|
||||||
|
if ( result === undefined ) { break; }
|
||||||
|
if ( result === true ) {
|
||||||
|
details.msg.what = 'connectionAccepted';
|
||||||
|
details.msg.toToken = port.name;
|
||||||
|
const connection = new vAPI.MessagingConnection(
|
||||||
|
listener,
|
||||||
|
details.msg
|
||||||
|
);
|
||||||
|
connections.set(connection.id, connection);
|
||||||
|
} else {
|
||||||
|
details.msg.what = 'connectionRefused';
|
||||||
|
}
|
||||||
|
port.postMessage(details);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
vAPI.messaging.extensions.push(vAPI.MessagingConnection);
|
||||||
|
|
||||||
|
// <<<<<<<< end of private namespace
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Broadcast listening ability
|
||||||
|
|
||||||
|
(( ) => {
|
||||||
|
// >>>>>>>> start of private namespace
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof vAPI !== 'object' ||
|
||||||
|
vAPI.messaging instanceof Object === false ||
|
||||||
|
vAPI.broadcastListener instanceof Object
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listeners = new Set();
|
||||||
|
|
||||||
|
vAPI.broadcastListener = {
|
||||||
|
add: function(listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
vAPI.messaging.getPort();
|
||||||
|
},
|
||||||
|
remove: function(listener) {
|
||||||
|
listeners.delete(listener);
|
||||||
|
},
|
||||||
|
canDestroyPort() {
|
||||||
|
return listeners.size === 0;
|
||||||
|
},
|
||||||
|
mustDestroyPort() {
|
||||||
|
listeners.clear();
|
||||||
|
},
|
||||||
|
canProcessMessage(details) {
|
||||||
|
if ( details.broadcast === false ) { return; }
|
||||||
|
for ( const listener of listeners ) {
|
||||||
|
listener(details.msg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
vAPI.messaging.extensions.push(vAPI.broadcastListener);
|
||||||
|
|
||||||
|
// <<<<<<<< end of private namespace
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
DO NOT:
|
||||||
|
- Remove the following code
|
||||||
|
- Add code beyond the following code
|
||||||
|
Reason:
|
||||||
|
- https://github.com/gorhill/uBlock/pull/3721
|
||||||
|
- uBO never uses the return value from injected content scripts
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
void 0;
|
||||||
@ -0,0 +1,178 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uMatrix - a browser extension to block requests.
|
||||||
|
Copyright (C) 2017-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uMatrix
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For background page
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(( ) => {
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/407
|
||||||
|
if ( vAPI.webextFlavor.soup.has('chromium') === false ) { return; }
|
||||||
|
|
||||||
|
const extToTypeMap = new Map([
|
||||||
|
['eot','font'],['otf','font'],['svg','font'],['ttf','font'],['woff','font'],['woff2','font'],
|
||||||
|
['mp3','media'],['mp4','media'],['webm','media'],
|
||||||
|
['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image']
|
||||||
|
]);
|
||||||
|
|
||||||
|
const headerValue = (headers, name) => {
|
||||||
|
let i = headers.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
if ( headers[i].name.toLowerCase() === name ) {
|
||||||
|
return headers[i].value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const parsedURL = new URL('https://www.example.org/');
|
||||||
|
|
||||||
|
// Extend base class to normalize as per platform.
|
||||||
|
|
||||||
|
vAPI.Net = class extends vAPI.Net {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.suspendedTabIds = new Set();
|
||||||
|
}
|
||||||
|
normalizeDetails(details) {
|
||||||
|
// Chromium 63+ supports the `initiator` property, which contains
|
||||||
|
// the URL of the origin from which the network request was made.
|
||||||
|
if (
|
||||||
|
typeof details.initiator === 'string' &&
|
||||||
|
details.initiator !== 'null'
|
||||||
|
) {
|
||||||
|
details.documentUrl = details.initiator;
|
||||||
|
}
|
||||||
|
|
||||||
|
let type = details.type;
|
||||||
|
|
||||||
|
if ( type === 'imageset' ) {
|
||||||
|
details.type = 'image';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest of the function code is to normalize type
|
||||||
|
if ( type !== 'other' ) { return; }
|
||||||
|
|
||||||
|
// Try to map known "extension" part of URL to request type.
|
||||||
|
parsedURL.href = details.url;
|
||||||
|
const path = parsedURL.pathname,
|
||||||
|
pos = path.indexOf('.', path.length - 6);
|
||||||
|
if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) {
|
||||||
|
details.type = type;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to extract type from response headers if present.
|
||||||
|
if ( details.responseHeaders ) {
|
||||||
|
type = headerValue(details.responseHeaders, 'content-type');
|
||||||
|
if ( type.startsWith('font/') ) {
|
||||||
|
details.type = 'font';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( type.startsWith('image/') ) {
|
||||||
|
details.type = 'image';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( type.startsWith('audio/') || type.startsWith('video/') ) {
|
||||||
|
details.type = 'media';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// https://www.reddit.com/r/uBlockOrigin/comments/9vcrk3/
|
||||||
|
// Some types can be mapped from 'other', thus include 'other' if and
|
||||||
|
// only if the caller is interested in at least one of those types.
|
||||||
|
denormalizeTypes(types) {
|
||||||
|
if ( types.length === 0 ) {
|
||||||
|
return Array.from(this.validTypes);
|
||||||
|
}
|
||||||
|
const out = new Set();
|
||||||
|
for ( const type of types ) {
|
||||||
|
if ( this.validTypes.has(type) ) {
|
||||||
|
out.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( out.has('other') === false ) {
|
||||||
|
for ( const type of extToTypeMap.values() ) {
|
||||||
|
if ( out.has(type) ) {
|
||||||
|
out.add('other');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(out);
|
||||||
|
}
|
||||||
|
suspendOneRequest(details) {
|
||||||
|
this.suspendedTabIds.add(details.tabId);
|
||||||
|
return { cancel: true };
|
||||||
|
}
|
||||||
|
unsuspendAllRequests() {
|
||||||
|
for ( const tabId of this.suspendedTabIds ) {
|
||||||
|
vAPI.tabs.reload(tabId);
|
||||||
|
}
|
||||||
|
this.suspendedTabIds.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/548
|
||||||
|
// Use `X-DNS-Prefetch-Control` to workaround Chromium's disregard of the
|
||||||
|
// setting "Predict network actions to improve page load performance".
|
||||||
|
|
||||||
|
vAPI.prefetching = (( ) => {
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/407
|
||||||
|
if ( vAPI.webextFlavor.soup.has('chromium') === false ) { return; }
|
||||||
|
|
||||||
|
let listening = false;
|
||||||
|
|
||||||
|
const onHeadersReceived = function(details) {
|
||||||
|
details.responseHeaders.push({
|
||||||
|
name: 'X-DNS-Prefetch-Control',
|
||||||
|
value: 'off'
|
||||||
|
});
|
||||||
|
return { responseHeaders: details.responseHeaders };
|
||||||
|
};
|
||||||
|
|
||||||
|
return state => {
|
||||||
|
const wr = chrome.webRequest;
|
||||||
|
if ( state && listening ) {
|
||||||
|
wr.onHeadersReceived.removeListener(onHeadersReceived);
|
||||||
|
listening = false;
|
||||||
|
} else if ( !state && !listening ) {
|
||||||
|
wr.onHeadersReceived.addListener(
|
||||||
|
onHeadersReceived,
|
||||||
|
{
|
||||||
|
urls: [ 'http://*/*', 'https://*/*' ],
|
||||||
|
types: [ 'main_frame', 'sub_frame' ]
|
||||||
|
},
|
||||||
|
[ 'blocking', 'responseHeaders' ]
|
||||||
|
);
|
||||||
|
listening = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uMatrix - a browser extension to block requests.
|
||||||
|
Copyright (C) 2017-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* global HTMLDocument, XMLDocument */
|
||||||
|
|
||||||
|
// For background page, auxiliary pages, and content scripts.
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if ( self.browser instanceof Object ) {
|
||||||
|
self.chrome = self.browser;
|
||||||
|
} else {
|
||||||
|
self.browser = self.chrome;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1408996#c9
|
||||||
|
var vAPI = self.vAPI; // jshint ignore:line
|
||||||
|
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/464
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||||
|
// A XMLDocument can be a valid HTML document.
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/1124
|
||||||
|
// Looks like `contentType` is on track to be standardized:
|
||||||
|
// https://dom.spec.whatwg.org/#concept-document-content-type
|
||||||
|
|
||||||
|
// https://forums.lanik.us/viewtopic.php?f=64&t=31522
|
||||||
|
// Skip text/plain documents.
|
||||||
|
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
document instanceof HTMLDocument ||
|
||||||
|
document instanceof XMLDocument &&
|
||||||
|
document.createElement('div') instanceof HTMLDivElement
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
/^image\/|^text\/plain/.test(document.contentType || '') === false
|
||||||
|
) &&
|
||||||
|
(
|
||||||
|
self.vAPI instanceof Object === false || vAPI.nuTensor !== true
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
vAPI = self.vAPI = { nuTensor: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
DO NOT:
|
||||||
|
- Remove the following code
|
||||||
|
- Add code beyond the following code
|
||||||
|
Reason:
|
||||||
|
- https://github.com/gorhill/uBlock/pull/3721
|
||||||
|
- uMatrix never uses the return value from injected content scripts
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
void 0;
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2019-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// `webext` is a promisified api of `chrome`. Entries are added as
|
||||||
|
// the promisification of uBO progress.
|
||||||
|
|
||||||
|
const webext = (( ) => { // jshint ignore:line
|
||||||
|
// >>>>> start of private scope
|
||||||
|
|
||||||
|
const noopFunc = ( ) => { };
|
||||||
|
|
||||||
|
const promisifyNoFail = function(thisArg, fnName, outFn = r => r) {
|
||||||
|
const fn = thisArg[fnName];
|
||||||
|
return function() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
fn.call(thisArg, ...arguments, function() {
|
||||||
|
if ( chrome.runtime.lastError instanceof Object ) {
|
||||||
|
void chrome.runtime.lastError.message;
|
||||||
|
}
|
||||||
|
resolve(outFn(...arguments));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const promisify = function(thisArg, fnName) {
|
||||||
|
const fn = thisArg[fnName];
|
||||||
|
return function() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fn.call(thisArg, ...arguments, function() {
|
||||||
|
const lastError = chrome.runtime.lastError;
|
||||||
|
if ( lastError instanceof Object ) {
|
||||||
|
return reject(lastError.message);
|
||||||
|
}
|
||||||
|
resolve(...arguments);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const webext = {
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/browserAction
|
||||||
|
browserAction: {
|
||||||
|
setBadgeBackgroundColor: promisifyNoFail(chrome.browserAction, 'setBadgeBackgroundColor'),
|
||||||
|
setBadgeText: promisifyNoFail(chrome.browserAction, 'setBadgeText'),
|
||||||
|
setBadgeTextColor: noopFunc,
|
||||||
|
setIcon: promisifyNoFail(chrome.browserAction, 'setIcon'),
|
||||||
|
setTitle: promisifyNoFail(chrome.browserAction, 'setTitle'),
|
||||||
|
},
|
||||||
|
cookies: {
|
||||||
|
getAll: promisifyNoFail(chrome.cookies, 'getAll'),
|
||||||
|
remove: promisifyNoFail(chrome.cookies, 'remove'),
|
||||||
|
},
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/menus
|
||||||
|
/*
|
||||||
|
menus: {
|
||||||
|
create: function() {
|
||||||
|
return chrome.contextMenus.create(...arguments, ( ) => {
|
||||||
|
void chrome.runtime.lastError;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onClicked: chrome.contextMenus.onClicked,
|
||||||
|
remove: promisifyNoFail(chrome.contextMenus, 'remove'),
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy
|
||||||
|
privacy: {
|
||||||
|
},
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage
|
||||||
|
storage: {
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/local
|
||||||
|
local: {
|
||||||
|
clear: promisify(chrome.storage.local, 'clear'),
|
||||||
|
get: promisify(chrome.storage.local, 'get'),
|
||||||
|
getBytesInUse: promisify(chrome.storage.local, 'getBytesInUse'),
|
||||||
|
remove: promisify(chrome.storage.local, 'remove'),
|
||||||
|
set: promisify(chrome.storage.local, 'set'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs
|
||||||
|
tabs: {
|
||||||
|
get: promisifyNoFail(chrome.tabs, 'get', tab => tab instanceof Object ? tab : null),
|
||||||
|
executeScript: promisifyNoFail(chrome.tabs, 'executeScript'),
|
||||||
|
insertCSS: promisifyNoFail(chrome.tabs, 'insertCSS'),
|
||||||
|
query: promisifyNoFail(chrome.tabs, 'query', tabs => Array.isArray(tabs) ? tabs : []),
|
||||||
|
reload: promisifyNoFail(chrome.tabs, 'reload'),
|
||||||
|
remove: promisifyNoFail(chrome.tabs, 'remove'),
|
||||||
|
update: promisifyNoFail(chrome.tabs, 'update', tab => tab instanceof Object ? tab : null),
|
||||||
|
},
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webNavigation
|
||||||
|
webNavigation: {
|
||||||
|
getFrame: promisify(chrome.webNavigation, 'getFrame'),
|
||||||
|
},
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/windows
|
||||||
|
windows: {
|
||||||
|
get: promisifyNoFail(chrome.windows, 'get', win => win instanceof Object ? win : null),
|
||||||
|
create: promisifyNoFail(chrome.windows, 'create', win => win instanceof Object ? win : null),
|
||||||
|
update: promisifyNoFail(chrome.windows, 'update', win => win instanceof Object ? win : null),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// browser.privacy entries
|
||||||
|
{
|
||||||
|
const settings = [
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/network
|
||||||
|
[ 'network', 'networkPredictionEnabled' ],
|
||||||
|
[ 'network', 'webRTCIPHandlingPolicy' ],
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/websites
|
||||||
|
[ 'websites', 'hyperlinkAuditingEnabled' ],
|
||||||
|
];
|
||||||
|
for ( const [ category, setting ] of settings ) {
|
||||||
|
let categoryEntry = webext.privacy[category];
|
||||||
|
if ( categoryEntry instanceof Object === false ) {
|
||||||
|
categoryEntry = webext.privacy[category] = {};
|
||||||
|
}
|
||||||
|
const settingEntry = categoryEntry[setting] = {};
|
||||||
|
const thisArg = chrome.privacy[category][setting];
|
||||||
|
settingEntry.clear = promisifyNoFail(thisArg, 'clear');
|
||||||
|
settingEntry.get = promisifyNoFail(thisArg, 'get');
|
||||||
|
settingEntry.set = promisifyNoFail(thisArg, 'set');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/managed
|
||||||
|
if ( chrome.storage.managed instanceof Object ) {
|
||||||
|
webext.storage.managed = {
|
||||||
|
get: promisify(chrome.storage.managed, 'get'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage/sync
|
||||||
|
if ( chrome.storage.sync instanceof Object ) {
|
||||||
|
webext.storage.sync = {
|
||||||
|
QUOTA_BYTES: chrome.storage.sync.QUOTA_BYTES,
|
||||||
|
QUOTA_BYTES_PER_ITEM: chrome.storage.sync.QUOTA_BYTES_PER_ITEM,
|
||||||
|
MAX_ITEMS: chrome.storage.sync.MAX_ITEMS,
|
||||||
|
MAX_WRITE_OPERATIONS_PER_HOUR: chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_HOUR,
|
||||||
|
MAX_WRITE_OPERATIONS_PER_MINUTE: chrome.storage.sync.MAX_WRITE_OPERATIONS_PER_MINUTE,
|
||||||
|
|
||||||
|
clear: promisify(chrome.storage.sync, 'clear'),
|
||||||
|
get: promisify(chrome.storage.sync, 'get'),
|
||||||
|
getBytesInUse: promisify(chrome.storage.sync, 'getBytesInUse'),
|
||||||
|
remove: promisify(chrome.storage.sync, 'remove'),
|
||||||
|
set: promisify(chrome.storage.sync, 'set'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=608854
|
||||||
|
if ( chrome.tabs.removeCSS instanceof Function ) {
|
||||||
|
webext.tabs.removeCSS = promisifyNoFail(chrome.tabs, 'removeCSS');
|
||||||
|
}
|
||||||
|
|
||||||
|
return webext;
|
||||||
|
|
||||||
|
// <<<<< end of private scope
|
||||||
|
})();
|
||||||
@ -1,162 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
µBlock - a browser extension to block requests.
|
|
||||||
Copyright (C) 2014 The µBlock authors
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/gorhill/uMatrix
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global ADDON_UNINSTALL, APP_SHUTDOWN */
|
|
||||||
/* exported startup, shutdown, install, uninstall */
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// Accessing the context of the background page:
|
|
||||||
// var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=umatrix]').contentWindow;
|
|
||||||
|
|
||||||
let bgProcess;
|
|
||||||
let version;
|
|
||||||
const hostName = 'umatrix';
|
|
||||||
const restartListener = {
|
|
||||||
get messageManager() {
|
|
||||||
return Components.classes['@mozilla.org/parentprocessmessagemanager;1']
|
|
||||||
.getService(Components.interfaces.nsIMessageListenerManager);
|
|
||||||
},
|
|
||||||
|
|
||||||
receiveMessage: function() {
|
|
||||||
shutdown();
|
|
||||||
startup();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
function startup(data/*, reason*/) {
|
|
||||||
if ( data !== undefined ) {
|
|
||||||
version = data.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
let appShell = Components.classes['@mozilla.org/appshell/appShellService;1']
|
|
||||||
.getService(Components.interfaces.nsIAppShellService);
|
|
||||||
|
|
||||||
let onReady = function(e) {
|
|
||||||
if ( e ) {
|
|
||||||
this.removeEventListener(e.type, onReady);
|
|
||||||
}
|
|
||||||
|
|
||||||
let hiddenDoc = appShell.hiddenDOMWindow.document;
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/10
|
|
||||||
// Fixed by github.com/AlexVallat:
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1149
|
|
||||||
// https://github.com/AlexVallat/uBlock/commit/e762a29d308caa46578cdc34a9be92c4ad5ecdd0
|
|
||||||
if ( hiddenDoc.readyState === 'loading' ) {
|
|
||||||
hiddenDoc.addEventListener('DOMContentLoaded', onReady);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bgProcess = hiddenDoc.documentElement.appendChild(
|
|
||||||
hiddenDoc.createElementNS('http://www.w3.org/1999/xhtml', 'iframe')
|
|
||||||
);
|
|
||||||
bgProcess.setAttribute(
|
|
||||||
'src',
|
|
||||||
'chrome://' + hostName + '/content/background.html#' + version
|
|
||||||
);
|
|
||||||
|
|
||||||
restartListener.messageManager.addMessageListener(
|
|
||||||
hostName + '-restart',
|
|
||||||
restartListener
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
var ready = false;
|
|
||||||
try {
|
|
||||||
ready = appShell.hiddenDOMWindow &&
|
|
||||||
appShell.hiddenDOMWindow.document;
|
|
||||||
} catch (ex) {
|
|
||||||
}
|
|
||||||
if ( ready ) {
|
|
||||||
onReady();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ww = Components.classes['@mozilla.org/embedcomp/window-watcher;1']
|
|
||||||
.getService(Components.interfaces.nsIWindowWatcher);
|
|
||||||
|
|
||||||
ww.registerNotification({
|
|
||||||
observe: function(win, topic) {
|
|
||||||
if ( topic !== 'domwindowopened' ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
void appShell.hiddenDOMWindow;
|
|
||||||
} catch (ex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ww.unregisterNotification(this);
|
|
||||||
win.addEventListener('DOMContentLoaded', onReady);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
function shutdown(data, reason) {
|
|
||||||
if ( reason === APP_SHUTDOWN ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bgProcess.parentNode.removeChild(bgProcess);
|
|
||||||
|
|
||||||
if ( data === undefined ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the restartObserver only when the extension is being disabled
|
|
||||||
restartListener.messageManager.removeMessageListener(
|
|
||||||
hostName + '-restart',
|
|
||||||
restartListener
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
function install() {
|
|
||||||
// https://bugzil.la/719376
|
|
||||||
Components.classes['@mozilla.org/intl/stringbundle;1']
|
|
||||||
.getService(Components.interfaces.nsIStringBundleService)
|
|
||||||
.flushBundles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
function uninstall(data, aReason) {
|
|
||||||
if ( aReason !== ADDON_UNINSTALL ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// To cleanup vAPI.localStorage in vapi-common.js, aka
|
|
||||||
// "extensions.umatrix.*" in `about:config`.
|
|
||||||
Components.utils.import('resource://gre/modules/Services.jsm', null)
|
|
||||||
.Services.prefs
|
|
||||||
.getBranch('extensions.' + hostName + '.')
|
|
||||||
.deleteBranch('');
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
@ -1 +0,0 @@
|
|||||||
content umatrix ./
|
|
||||||
@ -1,348 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
µBlock - a browser extension to block requests.
|
|
||||||
Copyright (C) 2014 The µBlock authors
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/gorhill/uMatrix
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global Components */
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/800#issuecomment-146580443
|
|
||||||
this.EXPORTED_SYMBOLS = ['contentObserver', 'LocationChangeListener'];
|
|
||||||
|
|
||||||
const {interfaces: Ci, utils: Cu} = Components;
|
|
||||||
const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
|
|
||||||
const {XPCOMUtils} = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
|
|
||||||
|
|
||||||
const hostName = Services.io.newURI(Components.stack.filename, null, null).host;
|
|
||||||
|
|
||||||
// Cu.import('resource://gre/modules/Console.jsm');
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
const getMessageManager = function(win) {
|
|
||||||
let iface = win
|
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsIDocShell)
|
|
||||||
.sameTypeRootTreeItem
|
|
||||||
.QueryInterface(Ci.nsIDocShell)
|
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return iface.getInterface(Ci.nsIContentFrameMessageManager);
|
|
||||||
} catch (ex) {
|
|
||||||
// This can throw. It appears `shouldLoad` can be called *after* a
|
|
||||||
// tab has been closed. For example, a case where this happens all
|
|
||||||
// the time (FF38):
|
|
||||||
// - Open twitter.com (assuming you have an account and are logged in)
|
|
||||||
// - Close twitter.com
|
|
||||||
// There will be an exception raised when `shouldLoad` is called
|
|
||||||
// to process a XMLHttpRequest with URL `https://twitter.com/i/jot`
|
|
||||||
// fired from `https://twitter.com/`, *after* the tab is closed.
|
|
||||||
// In such case, `win` is `about:blank`.
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var contentObserver = {
|
|
||||||
classDescription: 'content-policy for ' + hostName,
|
|
||||||
classID: Components.ID('{c84283d4-9975-41b7-b1a4-f106af56b51d}'),
|
|
||||||
contractID: '@' + hostName + '/content-policy;1',
|
|
||||||
ACCEPT: Ci.nsIContentPolicy.ACCEPT,
|
|
||||||
MAIN_FRAME: Ci.nsIContentPolicy.TYPE_DOCUMENT,
|
|
||||||
contentBaseURI: 'chrome://' + hostName + '/content/js/',
|
|
||||||
cpMessageName: hostName + ':shouldLoad',
|
|
||||||
uniqueSandboxId: 1,
|
|
||||||
modernFirefox: Services.appinfo.ID === '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}' &&
|
|
||||||
Services.vc.compare(Services.appinfo.platformVersion, '44') > 0,
|
|
||||||
|
|
||||||
get componentRegistrar() {
|
|
||||||
return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
|
||||||
},
|
|
||||||
|
|
||||||
get categoryManager() {
|
|
||||||
return Components.classes['@mozilla.org/categorymanager;1']
|
|
||||||
.getService(Ci.nsICategoryManager);
|
|
||||||
},
|
|
||||||
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([
|
|
||||||
Ci.nsIFactory,
|
|
||||||
Ci.nsIObserver,
|
|
||||||
Ci.nsIContentPolicy,
|
|
||||||
Ci.nsISupportsWeakReference
|
|
||||||
]),
|
|
||||||
|
|
||||||
createInstance: function(outer, iid) {
|
|
||||||
if ( outer ) {
|
|
||||||
throw Components.results.NS_ERROR_NO_AGGREGATION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.QueryInterface(iid);
|
|
||||||
},
|
|
||||||
|
|
||||||
register: function() {
|
|
||||||
Services.obs.addObserver(this, 'document-element-inserted', true);
|
|
||||||
|
|
||||||
if ( !this.modernFirefox ) {
|
|
||||||
this.componentRegistrar.registerFactory(
|
|
||||||
this.classID,
|
|
||||||
this.classDescription,
|
|
||||||
this.contractID,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
this.categoryManager.addCategoryEntry(
|
|
||||||
'content-policy',
|
|
||||||
this.contractID,
|
|
||||||
this.contractID,
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
unregister: function() {
|
|
||||||
Services.obs.removeObserver(this, 'document-element-inserted');
|
|
||||||
|
|
||||||
if ( !this.modernFirefox ) {
|
|
||||||
this.componentRegistrar.unregisterFactory(
|
|
||||||
this.classID,
|
|
||||||
this
|
|
||||||
);
|
|
||||||
this.categoryManager.deleteCategoryEntry(
|
|
||||||
'content-policy',
|
|
||||||
this.contractID,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIContentPolicy
|
|
||||||
// https://bugzil.la/612921
|
|
||||||
shouldLoad: function(type, location, origin, context) {
|
|
||||||
if ( Services === undefined || !context ) {
|
|
||||||
return this.ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !location.schemeIs('http') && !location.schemeIs('https') ) {
|
|
||||||
return this.ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
var contextWindow;
|
|
||||||
if ( type === this.MAIN_FRAME ) {
|
|
||||||
contextWindow = context.contentWindow || context;
|
|
||||||
} else if ( type === this.SUB_FRAME ) {
|
|
||||||
contextWindow = context.contentWindow;
|
|
||||||
} else {
|
|
||||||
contextWindow = (context.ownerDocument || context).defaultView;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The context for the toolbar popup is an iframe element here,
|
|
||||||
// so check context.top instead of context
|
|
||||||
if ( !contextWindow.top || !contextWindow.location ) {
|
|
||||||
return this.ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
let messageManager = getMessageManager(contextWindow);
|
|
||||||
if ( messageManager === null ) {
|
|
||||||
return this.ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
let details = {
|
|
||||||
rawType: type,
|
|
||||||
url: location.asciiSpec
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( typeof messageManager.sendRpcMessage === 'function' ) {
|
|
||||||
// https://bugzil.la/1092216
|
|
||||||
messageManager.sendRpcMessage(this.cpMessageName, details);
|
|
||||||
} else {
|
|
||||||
// Compatibility for older versions
|
|
||||||
messageManager.sendSyncMessage(this.cpMessageName, details);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.ACCEPT;
|
|
||||||
},
|
|
||||||
|
|
||||||
initContentScripts: function(win, sandbox) {
|
|
||||||
let messager = getMessageManager(win);
|
|
||||||
let sandboxId = hostName + ':sb:' + this.uniqueSandboxId++;
|
|
||||||
|
|
||||||
if ( sandbox ) {
|
|
||||||
let sandboxName = [
|
|
||||||
win.location.href.slice(0, 100),
|
|
||||||
win.document.title.slice(0, 100)
|
|
||||||
].join(' | ');
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uMatrix/issues/325
|
|
||||||
// "Pass sameZoneAs to sandbox constructor to make GCs cheaper"
|
|
||||||
sandbox = Cu.Sandbox([win], {
|
|
||||||
sameZoneAs: win.top,
|
|
||||||
sandboxName: sandboxId + '[' + sandboxName + ']',
|
|
||||||
sandboxPrototype: win,
|
|
||||||
wantComponents: false,
|
|
||||||
wantXHRConstructor: false
|
|
||||||
});
|
|
||||||
|
|
||||||
sandbox.injectScript = function(script) {
|
|
||||||
Services.scriptloader.loadSubScript(script, sandbox);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sandbox = win;
|
|
||||||
}
|
|
||||||
|
|
||||||
sandbox._sandboxId_ = sandboxId;
|
|
||||||
sandbox.sendAsyncMessage = messager.sendAsyncMessage;
|
|
||||||
|
|
||||||
sandbox.addMessageListener = function(callback) {
|
|
||||||
if ( sandbox._messageListener_ ) {
|
|
||||||
sandbox.removeMessageListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
sandbox._messageListener_ = function(message) {
|
|
||||||
callback(message.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
messager.addMessageListener(
|
|
||||||
sandbox._sandboxId_,
|
|
||||||
sandbox._messageListener_
|
|
||||||
);
|
|
||||||
messager.addMessageListener(
|
|
||||||
hostName + ':broadcast',
|
|
||||||
sandbox._messageListener_
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
sandbox.removeMessageListener = function() {
|
|
||||||
try {
|
|
||||||
messager.removeMessageListener(
|
|
||||||
sandbox._sandboxId_,
|
|
||||||
sandbox._messageListener_
|
|
||||||
);
|
|
||||||
messager.removeMessageListener(
|
|
||||||
hostName + ':broadcast',
|
|
||||||
sandbox._messageListener_
|
|
||||||
);
|
|
||||||
} catch (ex) {
|
|
||||||
// It throws sometimes, mostly when the popup closes
|
|
||||||
}
|
|
||||||
|
|
||||||
sandbox._messageListener_ = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return sandbox;
|
|
||||||
},
|
|
||||||
|
|
||||||
observe: function(doc) {
|
|
||||||
let win = doc.defaultView;
|
|
||||||
if ( !win ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let loc = win.location;
|
|
||||||
if ( !loc ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/260
|
|
||||||
// TODO: We may have to skip more types, for now let's be
|
|
||||||
// conservative, i.e. let's not test against `text/html`.
|
|
||||||
if ( doc.contentType.lastIndexOf('image/', 0) === 0 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) {
|
|
||||||
if ( loc.protocol === 'chrome:' && loc.host === hostName ) {
|
|
||||||
this.initContentScripts(win);
|
|
||||||
}
|
|
||||||
|
|
||||||
// What about data: and about:blank?
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let lss = Services.scriptloader.loadSubScript;
|
|
||||||
let sandbox = this.initContentScripts(win, true);
|
|
||||||
|
|
||||||
// Can throw with attempts at injecting into non-HTML document.
|
|
||||||
// Example: https://a.pomf.se/avonjf.webm
|
|
||||||
try {
|
|
||||||
lss(this.contentBaseURI + 'vapi-client.js', sandbox);
|
|
||||||
lss(this.contentBaseURI + 'contentscript-start.js', sandbox);
|
|
||||||
} catch (ex) {
|
|
||||||
return; // don't further try to inject anything
|
|
||||||
}
|
|
||||||
|
|
||||||
let docReady = (e) => {
|
|
||||||
let doc = e.target;
|
|
||||||
doc.removeEventListener(e.type, docReady, true);
|
|
||||||
lss(this.contentBaseURI + 'contentscript-end.js', sandbox);
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( doc.readyState === 'loading') {
|
|
||||||
doc.addEventListener('DOMContentLoaded', docReady, true);
|
|
||||||
} else {
|
|
||||||
docReady({ target: doc, type: 'DOMContentLoaded' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
const locationChangedMessageName = hostName + ':locationChanged';
|
|
||||||
|
|
||||||
var LocationChangeListener = function(docShell) {
|
|
||||||
if ( !docShell ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var requestor = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
|
|
||||||
var ds = requestor.getInterface(Ci.nsIWebProgress);
|
|
||||||
var mm = requestor.getInterface(Ci.nsIContentFrameMessageManager);
|
|
||||||
|
|
||||||
if ( ds && mm && typeof mm.sendAsyncMessage === 'function' ) {
|
|
||||||
this.docShell = ds;
|
|
||||||
this.messageManager = mm;
|
|
||||||
ds.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
LocationChangeListener.prototype.QueryInterface = XPCOMUtils.generateQI([
|
|
||||||
'nsIWebProgressListener',
|
|
||||||
'nsISupportsWeakReference'
|
|
||||||
]);
|
|
||||||
|
|
||||||
LocationChangeListener.prototype.onLocationChange = function(webProgress, request, location, flags) {
|
|
||||||
if ( !webProgress.isTopLevel ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.messageManager.sendAsyncMessage(locationChangedMessageName, {
|
|
||||||
url: location.asciiSpec,
|
|
||||||
flags: flags,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
contentObserver.register();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
µBlock - a browser extension to block requests.
|
|
||||||
Copyright (C) 2014 The µBlock authors
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/gorhill/uMatrix
|
|
||||||
*/
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var locationChangeListener; // Keep alive while frameScript is alive
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
let {contentObserver, LocationChangeListener} = Components.utils.import(
|
|
||||||
Components.stack.filename.replace('Script', 'Module'),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
let injectContentScripts = function(win) {
|
|
||||||
if ( !win || !win.document ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
contentObserver.observe(win.document);
|
|
||||||
|
|
||||||
if ( win.frames && win.frames.length ) {
|
|
||||||
let i = win.frames.length;
|
|
||||||
while ( i-- ) {
|
|
||||||
injectContentScripts(win.frames[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let onLoadCompleted = function() {
|
|
||||||
removeMessageListener('umatrix-load-completed', onLoadCompleted);
|
|
||||||
injectContentScripts(content);
|
|
||||||
};
|
|
||||||
|
|
||||||
addMessageListener('umatrix-load-completed', onLoadCompleted);
|
|
||||||
|
|
||||||
if ( docShell ) {
|
|
||||||
let Ci = Components.interfaces;
|
|
||||||
let wp = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebProgress);
|
|
||||||
let dw = wp.DOMWindow;
|
|
||||||
if ( dw === dw.top ) {
|
|
||||||
locationChangeListener = new LocationChangeListener(docShell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 750 B |
Binary file not shown.
|
Before Width: | Height: | Size: 777 B |
@ -1,45 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<r:RDF xmlns:r="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.mozilla.org/2004/em-rdf#">
|
|
||||||
<r:Description about="urn:mozilla:install-manifest">
|
|
||||||
<id>uMatrix@raymondhill.net</id>
|
|
||||||
<version>{version}</version>
|
|
||||||
<name>{name}</name>
|
|
||||||
<description>{description}</description>
|
|
||||||
<homepageURL>{homepage}</homepageURL>
|
|
||||||
<creator>{author}</creator>
|
|
||||||
<developer>Deathamns</developer>
|
|
||||||
<developer>Alex Vallat</developer>
|
|
||||||
<type>2</type>
|
|
||||||
<bootstrap>true</bootstrap>
|
|
||||||
<multiprocessCompatible>true</multiprocessCompatible>
|
|
||||||
<optionsType>2</optionsType>
|
|
||||||
{localized}
|
|
||||||
|
|
||||||
<!-- Firefox -->
|
|
||||||
<targetApplication>
|
|
||||||
<r:Description>
|
|
||||||
<id>{{ec8030f7-c20a-464f-9b0e-13a3a9e97384}}</id>
|
|
||||||
<minVersion>24.0</minVersion>
|
|
||||||
<maxVersion>46.0</maxVersion>
|
|
||||||
</r:Description>
|
|
||||||
</targetApplication>
|
|
||||||
|
|
||||||
<!-- SeaMonkey -->
|
|
||||||
<targetApplication>
|
|
||||||
<r:Description>
|
|
||||||
<id>{{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}}</id>
|
|
||||||
<minVersion>2.21</minVersion>
|
|
||||||
<maxVersion>2.39.*</maxVersion>
|
|
||||||
</r:Description>
|
|
||||||
</targetApplication>
|
|
||||||
|
|
||||||
<!-- Pale Moon -->
|
|
||||||
<targetApplication>
|
|
||||||
<r:Description>
|
|
||||||
<id>{{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}}</id>
|
|
||||||
<minVersion>25.0</minVersion>
|
|
||||||
<maxVersion>26.*</maxVersion>
|
|
||||||
</r:Description>
|
|
||||||
</targetApplication>
|
|
||||||
</r:Description>
|
|
||||||
</r:RDF>
|
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "nuTensor@geekprojects.com",
|
||||||
|
"strict_min_version": "60.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"author": "Raymond Hill",
|
||||||
|
"background": {
|
||||||
|
"page": "background.html"
|
||||||
|
},
|
||||||
|
"browser_action": {
|
||||||
|
"browser_style": false,
|
||||||
|
"default_icon": {
|
||||||
|
"19": "img/browsericons/icon19-off.png"
|
||||||
|
},
|
||||||
|
"default_title": "nuTensor",
|
||||||
|
"default_popup": "popup.html"
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["http://*/*", "https://*/*"],
|
||||||
|
"js": ["/js/vapi.js", "/js/vapi-client.js", "/js/contentscript-start.js"],
|
||||||
|
"run_at": "document_start",
|
||||||
|
"all_frames": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matches": ["http://*/*", "https://*/*"],
|
||||||
|
"js": ["/js/contentscript.js"],
|
||||||
|
"run_at": "document_end",
|
||||||
|
"all_frames": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_locale": "en",
|
||||||
|
"description": "__MSG_extShortDesc__",
|
||||||
|
"icons": {
|
||||||
|
"16": "img/icon_16.png",
|
||||||
|
"128": "img/icon_128.png"
|
||||||
|
},
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "nuTensor",
|
||||||
|
"options_ui": {
|
||||||
|
"page":"dashboard.html",
|
||||||
|
"open_in_tab": true
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
"browsingData",
|
||||||
|
"cookies",
|
||||||
|
"dns",
|
||||||
|
"privacy",
|
||||||
|
"storage",
|
||||||
|
"tabs",
|
||||||
|
"webNavigation",
|
||||||
|
"webRequest",
|
||||||
|
"webRequestBlocking",
|
||||||
|
"<all_urls>"
|
||||||
|
],
|
||||||
|
"short_name": "nuTensor",
|
||||||
|
"sidebar_action": {
|
||||||
|
"default_title": "__MSG_loggerPageName__",
|
||||||
|
"default_panel": "logger-ui.html",
|
||||||
|
"default_icon": {
|
||||||
|
"16": "img/icon_16.png",
|
||||||
|
"128": "img/icon_128.png"
|
||||||
|
},
|
||||||
|
"open_at_install": false
|
||||||
|
},
|
||||||
|
"version": "0.9.9"
|
||||||
|
}
|
||||||
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" ?>
|
|
||||||
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
||||||
<setting type="control">
|
|
||||||
<vbox>
|
|
||||||
<button id="showDashboardButton"/>
|
|
||||||
<button id="showLoggerButton"/>
|
|
||||||
</vbox>
|
|
||||||
</setting>
|
|
||||||
</vbox>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,211 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
µBlock - a browser extension to block requests.
|
|
||||||
Copyright (C) 2014 The µBlock authors
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/gorhill/uMatrix
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* jshint esnext: true */
|
|
||||||
/* global addMessageListener, removeMessageListener, sendAsyncMessage */
|
|
||||||
|
|
||||||
// For non background pages
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
(function(self) {
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var vAPI = self.vAPI = self.vAPI || {};
|
|
||||||
vAPI.firefox = true;
|
|
||||||
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
|
|
||||||
Math.random().toString(36).slice(2);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay) {
|
|
||||||
return setTimeout(function() { callback(); }, delay);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.shutdown = (function() {
|
|
||||||
var jobs = [];
|
|
||||||
|
|
||||||
var add = function(job) {
|
|
||||||
jobs.push(job);
|
|
||||||
};
|
|
||||||
|
|
||||||
var exec = function() {
|
|
||||||
//console.debug('Shutting down...');
|
|
||||||
var job;
|
|
||||||
while ( (job = jobs.pop()) ) {
|
|
||||||
job();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
add: add,
|
|
||||||
exec: exec
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var messagingConnector = function(response) {
|
|
||||||
if ( !response ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var channels = vAPI.messaging.channels;
|
|
||||||
var channel, listener;
|
|
||||||
|
|
||||||
if ( response.broadcast && !response.channelName ) {
|
|
||||||
for ( channel in channels ) {
|
|
||||||
if ( channels.hasOwnProperty(channel) === false ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
listener = channels[channel].listener;
|
|
||||||
if ( typeof listener === 'function' ) {
|
|
||||||
listener(response.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( response.requestId ) {
|
|
||||||
listener = vAPI.messaging.listeners[response.requestId];
|
|
||||||
delete vAPI.messaging.listeners[response.requestId];
|
|
||||||
delete response.requestId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !listener ) {
|
|
||||||
channel = channels[response.channelName];
|
|
||||||
listener = channel && channel.listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( typeof listener === 'function' ) {
|
|
||||||
listener(response.msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.messaging = {
|
|
||||||
channels: {},
|
|
||||||
listeners: {},
|
|
||||||
requestId: 1,
|
|
||||||
|
|
||||||
setup: function() {
|
|
||||||
this.connector = function(msg) {
|
|
||||||
messagingConnector(JSON.parse(msg));
|
|
||||||
};
|
|
||||||
|
|
||||||
addMessageListener(this.connector);
|
|
||||||
|
|
||||||
this.channels.vAPI = {
|
|
||||||
listener: function(msg) {
|
|
||||||
if ( typeof msg.cmd === 'string' && msg.cmd === 'injectScript' ) {
|
|
||||||
var details = msg.details;
|
|
||||||
if ( !details.allFrames && window !== window.top ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.injectScript(details.file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
close: function() {
|
|
||||||
if ( !this.connector ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
removeMessageListener();
|
|
||||||
this.connector = null;
|
|
||||||
this.channels = {};
|
|
||||||
this.listeners = {};
|
|
||||||
},
|
|
||||||
|
|
||||||
channel: function(channelName, callback) {
|
|
||||||
if ( !channelName ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.channels[channelName] = {
|
|
||||||
channelName: channelName,
|
|
||||||
listener: typeof callback === 'function' ? callback : null,
|
|
||||||
send: function(message, callback) {
|
|
||||||
if ( !vAPI.messaging.connector ) {
|
|
||||||
vAPI.messaging.setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
message = {
|
|
||||||
channelName: self._sandboxId_ + '|' + this.channelName,
|
|
||||||
msg: message
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( callback ) {
|
|
||||||
message.requestId = vAPI.messaging.requestId++;
|
|
||||||
vAPI.messaging.listeners[message.requestId] = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendAsyncMessage('umatrix:background', message);
|
|
||||||
},
|
|
||||||
close: function() {
|
|
||||||
delete vAPI.messaging.channels[this.channelName];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.channels[channelName];
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleListener: function({type, persisted}) {
|
|
||||||
if ( !vAPI.messaging.connector ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( type === 'pagehide' ) {
|
|
||||||
removeMessageListener();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( persisted ) {
|
|
||||||
addMessageListener(vAPI.messaging.connector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('pagehide', vAPI.messaging.toggleListener, true);
|
|
||||||
window.addEventListener('pageshow', vAPI.messaging.toggleListener, true);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// No need to have vAPI client linger around after shutdown if
|
|
||||||
// we are not a top window (because element picker can still
|
|
||||||
// be injected in top window).
|
|
||||||
if ( window !== window.top ) {
|
|
||||||
// Can anything be done?
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
})(this);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
@ -1,186 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
µBlock - a browser extension to block requests.
|
|
||||||
Copyright (C) 2014 The µBlock authors
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/gorhill/uMatrix
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global sendAsyncMessage */
|
|
||||||
|
|
||||||
// For background page or non-background pages
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
const {Services} = Components.utils.import(
|
|
||||||
'resource://gre/modules/Services.jsm',
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
var vAPI = self.vAPI = self.vAPI || {};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, extra) {
|
|
||||||
return setTimeout(function(a) { callback(a); }, delay, extra);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// http://www.w3.org/International/questions/qa-scripts#directions
|
|
||||||
|
|
||||||
var setScriptDirection = function(language) {
|
|
||||||
document.body.setAttribute(
|
|
||||||
'dir',
|
|
||||||
['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1 ? 'rtl' : 'ltr'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.download = function(details) {
|
|
||||||
if ( !details.url ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = document.createElement('a');
|
|
||||||
a.href = details.url;
|
|
||||||
a.setAttribute('download', details.filename || '');
|
|
||||||
a.dispatchEvent(new MouseEvent('click'));
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.insertHTML = (function() {
|
|
||||||
const parser = Components.classes['@mozilla.org/parserutils;1']
|
|
||||||
.getService(Components.interfaces.nsIParserUtils);
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/845
|
|
||||||
// Apparently dashboard pages execute with `about:blank` principal.
|
|
||||||
|
|
||||||
return function(node, html) {
|
|
||||||
while ( node.firstChild ) {
|
|
||||||
node.removeChild(node.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
node.appendChild(parser.parseFragment(
|
|
||||||
html,
|
|
||||||
parser.SanitizerAllowStyle,
|
|
||||||
false,
|
|
||||||
Services.io.newURI('about:blank', null, null),
|
|
||||||
document.documentElement
|
|
||||||
));
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.getURL = function(path) {
|
|
||||||
return 'chrome://' + location.host + '/content/' + path.replace(/^\/+/, '');
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.i18n = (function() {
|
|
||||||
var stringBundle = Services.strings.createBundle(
|
|
||||||
'chrome://' + location.host + '/locale/messages.properties'
|
|
||||||
);
|
|
||||||
|
|
||||||
return function(s) {
|
|
||||||
try {
|
|
||||||
return stringBundle.GetStringFromName(s);
|
|
||||||
} catch (ex) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
setScriptDirection(navigator.language);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.closePopup = function() {
|
|
||||||
sendAsyncMessage(location.host + ':closePopup');
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// A localStorage-like object which should be accessible from the
|
|
||||||
// background page or auxiliary pages.
|
|
||||||
// This storage is optional, but it is nice to have, for a more polished user
|
|
||||||
// experience.
|
|
||||||
|
|
||||||
vAPI.localStorage = {
|
|
||||||
pbName: '',
|
|
||||||
pb: null,
|
|
||||||
str: Components.classes['@mozilla.org/supports-string;1']
|
|
||||||
.createInstance(Components.interfaces.nsISupportsString),
|
|
||||||
init: function(pbName) {
|
|
||||||
this.pbName = pbName;
|
|
||||||
this.pb = Services.prefs.getBranch(pbName);
|
|
||||||
},
|
|
||||||
getItem: function(key) {
|
|
||||||
try {
|
|
||||||
return this.pb.getComplexValue(
|
|
||||||
key,
|
|
||||||
Components.interfaces.nsISupportsString
|
|
||||||
).data;
|
|
||||||
} catch (ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setItem: function(key, value) {
|
|
||||||
this.str.data = value;
|
|
||||||
this.pb.setComplexValue(
|
|
||||||
key,
|
|
||||||
Components.interfaces.nsISupportsString,
|
|
||||||
this.str
|
|
||||||
);
|
|
||||||
},
|
|
||||||
getBool: function(key) {
|
|
||||||
try {
|
|
||||||
return this.pb.getBoolPref(key);
|
|
||||||
} catch (ex) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setBool: function(key, value) {
|
|
||||||
this.pb.setBoolPref(key, value);
|
|
||||||
},
|
|
||||||
setDefaultBool: function(key, defaultValue) {
|
|
||||||
Services.prefs.getDefaultBranch(this.pbName).setBoolPref(key, defaultValue);
|
|
||||||
},
|
|
||||||
removeItem: function(key) {
|
|
||||||
this.pb.clearUserPref(key);
|
|
||||||
},
|
|
||||||
clear: function() {
|
|
||||||
this.pb.deleteBranch('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
vAPI.localStorage.init('extensions.' + location.host + '.');
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
@ -1 +0,0 @@
|
|||||||
/* Firefox: no platform-specific code */
|
|
||||||
@ -0,0 +1,263 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uMatrix - a browser extension to block requests.
|
||||||
|
Copyright (C) 2017-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uMatrix
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For background page
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(( ) => {
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/407
|
||||||
|
if ( vAPI.webextFlavor.soup.has('firefox') === false ) { return; }
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/2950
|
||||||
|
// Firefox 56 does not normalize URLs to ASCII, uBO must do this itself.
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=945240
|
||||||
|
const evalMustPunycode = ( ) => {
|
||||||
|
return vAPI.webextFlavor.soup.has('firefox') &&
|
||||||
|
vAPI.webextFlavor.major < 57;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mustPunycode = evalMustPunycode();
|
||||||
|
|
||||||
|
// The real actual webextFlavor value may not be set in stone, so listen
|
||||||
|
// for possible future changes.
|
||||||
|
window.addEventListener('webextFlavor', ( ) => {
|
||||||
|
mustPunycode = evalMustPunycode();
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
const punycode = self.punycode;
|
||||||
|
const reAsciiHostname = /^https?:\/\/[0-9a-z_.:@-]+[/?#]/;
|
||||||
|
const parsedURL = new URL('about:blank');
|
||||||
|
|
||||||
|
// Related issues:
|
||||||
|
// - https://github.com/gorhill/uBlock/issues/1327
|
||||||
|
// - https://github.com/uBlockOrigin/uBlock-issues/issues/128
|
||||||
|
// - https://bugzilla.mozilla.org/show_bug.cgi?id=1503721
|
||||||
|
|
||||||
|
// Extend base class to normalize as per platform.
|
||||||
|
|
||||||
|
vAPI.Net = class extends vAPI.Net {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.pendingRequests = [];
|
||||||
|
this.cnames = new Map([ [ '', '' ] ]);
|
||||||
|
this.cnameIgnoreList = null;
|
||||||
|
this.cnameIgnore1stParty = true;
|
||||||
|
this.cnameIgnoreExceptions = true;
|
||||||
|
this.cnameIgnoreRootDocument = true;
|
||||||
|
this.cnameMaxTTL = 60;
|
||||||
|
this.cnameReplayFullURL = false;
|
||||||
|
this.cnameTimer = undefined;
|
||||||
|
this.canRevealCNAME = browser.dns instanceof Object;
|
||||||
|
}
|
||||||
|
setOptions(options) {
|
||||||
|
super.setOptions(options);
|
||||||
|
this.cnameIgnoreList = this.regexFromStrList(options.cnameIgnoreList);
|
||||||
|
this.cnameIgnore1stParty = options.cnameIgnore1stParty !== false;
|
||||||
|
this.cnameIgnoreExceptions = options.cnameIgnoreExceptions !== false;
|
||||||
|
this.cnameIgnoreRootDocument = options.cnameIgnoreRootDocument !== false;
|
||||||
|
this.cnameMaxTTL = options.cnameMaxTTL || 120;
|
||||||
|
this.cnameReplayFullURL = options.cnameReplayFullURL === true;
|
||||||
|
this.cnames.clear(); this.cnames.set('', '');
|
||||||
|
}
|
||||||
|
normalizeDetails(details) {
|
||||||
|
if ( mustPunycode && !reAsciiHostname.test(details.url) ) {
|
||||||
|
parsedURL.href = details.url;
|
||||||
|
details.url = details.url.replace(
|
||||||
|
parsedURL.hostname,
|
||||||
|
punycode.toASCII(parsedURL.hostname)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = details.type;
|
||||||
|
|
||||||
|
if ( type === 'imageset' ) {
|
||||||
|
details.type = 'image';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/345
|
||||||
|
// Re-categorize an embedded object as a `sub_frame` if its
|
||||||
|
// content type is that of a HTML document.
|
||||||
|
if ( type === 'object' && Array.isArray(details.responseHeaders) ) {
|
||||||
|
for ( const header of details.responseHeaders ) {
|
||||||
|
if ( header.name.toLowerCase() === 'content-type' ) {
|
||||||
|
if ( header.value.startsWith('text/html') ) {
|
||||||
|
details.type = 'sub_frame';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
denormalizeTypes(types) {
|
||||||
|
if ( types.length === 0 ) {
|
||||||
|
return Array.from(this.validTypes);
|
||||||
|
}
|
||||||
|
const out = new Set();
|
||||||
|
for ( const type of types ) {
|
||||||
|
if ( this.validTypes.has(type) ) {
|
||||||
|
out.add(type);
|
||||||
|
}
|
||||||
|
if ( type === 'image' && this.validTypes.has('imageset') ) {
|
||||||
|
out.add('imageset');
|
||||||
|
}
|
||||||
|
if ( type === 'sub_frame' ) {
|
||||||
|
out.add('object');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Array.from(out);
|
||||||
|
}
|
||||||
|
processCanonicalName(hn, cn, details) {
|
||||||
|
const hnBeg = details.url.indexOf(hn);
|
||||||
|
if ( hnBeg === -1 ) { return; }
|
||||||
|
const oldURL = details.url;
|
||||||
|
let newURL = oldURL.slice(0, hnBeg) + cn;
|
||||||
|
const hnEnd = hnBeg + hn.length;
|
||||||
|
if ( this.cnameReplayFullURL ) {
|
||||||
|
newURL += oldURL.slice(hnEnd);
|
||||||
|
} else {
|
||||||
|
const pathBeg = oldURL.indexOf('/', hnEnd);
|
||||||
|
if ( pathBeg !== -1 ) {
|
||||||
|
newURL += oldURL.slice(hnEnd, pathBeg + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
details.url = newURL;
|
||||||
|
details.aliasURL = oldURL;
|
||||||
|
return super.onBeforeSuspendableRequest(details);
|
||||||
|
}
|
||||||
|
recordCanonicalName(hn, record) {
|
||||||
|
let cname =
|
||||||
|
typeof record.canonicalName === 'string' &&
|
||||||
|
record.canonicalName !== hn
|
||||||
|
? record.canonicalName
|
||||||
|
: '';
|
||||||
|
if (
|
||||||
|
cname !== '' &&
|
||||||
|
this.cnameIgnore1stParty &&
|
||||||
|
vAPI.domainFromHostname(cname) === vAPI.domainFromHostname(hn)
|
||||||
|
) {
|
||||||
|
cname = '';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
cname !== '' &&
|
||||||
|
this.cnameIgnoreList !== null &&
|
||||||
|
this.cnameIgnoreList.test(cname)
|
||||||
|
) {
|
||||||
|
cname = '';
|
||||||
|
}
|
||||||
|
this.cnames.set(hn, cname);
|
||||||
|
if ( this.cnameTimer === undefined ) {
|
||||||
|
this.cnameTimer = self.setTimeout(
|
||||||
|
( ) => {
|
||||||
|
this.cnameTimer = undefined;
|
||||||
|
this.cnames.clear(); this.cnames.set('', '');
|
||||||
|
},
|
||||||
|
this.cnameMaxTTL * 60000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return cname;
|
||||||
|
}
|
||||||
|
regexFromStrList(list) {
|
||||||
|
if (
|
||||||
|
typeof list !== 'string' ||
|
||||||
|
list.length === 0 ||
|
||||||
|
list === 'unset' ||
|
||||||
|
browser.dns instanceof Object === false
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ( list === '*' ) {
|
||||||
|
return /^./;
|
||||||
|
}
|
||||||
|
return new RegExp(
|
||||||
|
'(?:^|\.)(?:' +
|
||||||
|
list.trim()
|
||||||
|
.split(/\s+/)
|
||||||
|
.map(a => a.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
||||||
|
.join('|') +
|
||||||
|
')$'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onBeforeSuspendableRequest(details) {
|
||||||
|
const r = super.onBeforeSuspendableRequest(details);
|
||||||
|
if ( this.canRevealCNAME === false ) { return r; }
|
||||||
|
if ( r !== undefined ) {
|
||||||
|
if ( r.cancel === false ) { return; }
|
||||||
|
if (
|
||||||
|
r.cancel === true ||
|
||||||
|
r.redirectUrl !== undefined ||
|
||||||
|
this.cnameIgnoreExceptions
|
||||||
|
) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
details.type === 'main_frame' &&
|
||||||
|
this.cnameIgnoreRootDocument
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hn = vAPI.hostnameFromNetworkURL(details.url);
|
||||||
|
const cname = this.cnames.get(hn);
|
||||||
|
if ( cname === '' ) { return; }
|
||||||
|
if ( cname !== undefined ) {
|
||||||
|
return this.processCanonicalName(hn, cname, details);
|
||||||
|
}
|
||||||
|
return browser.dns.resolve(hn, [ 'canonical_name' ]).then(
|
||||||
|
rec => {
|
||||||
|
const cname = this.recordCanonicalName(hn, rec);
|
||||||
|
if ( cname === '' ) { return; }
|
||||||
|
return this.processCanonicalName(hn, cname, details);
|
||||||
|
},
|
||||||
|
( ) => {
|
||||||
|
this.cnames.set(hn, '');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
suspendOneRequest(details) {
|
||||||
|
const pending = {
|
||||||
|
details: Object.assign({}, details),
|
||||||
|
resolve: undefined,
|
||||||
|
promise: undefined
|
||||||
|
};
|
||||||
|
pending.promise = new Promise(resolve => {
|
||||||
|
pending.resolve = resolve;
|
||||||
|
});
|
||||||
|
this.pendingRequests.push(pending);
|
||||||
|
return pending.promise;
|
||||||
|
}
|
||||||
|
unsuspendAllRequests() {
|
||||||
|
const pendingRequests = this.pendingRequests;
|
||||||
|
this.pendingRequests = [];
|
||||||
|
for ( const entry of pendingRequests ) {
|
||||||
|
entry.resolve(this.onBeforeSuspendableRequest(entry.details));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canSuspend() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2019-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const webext = browser; // jshint ignore:line
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"sidebar_action": {
|
||||||
|
"default_title": "__MSG_loggerPageName__",
|
||||||
|
"default_panel": "logger-ui.html",
|
||||||
|
"default_icon": {
|
||||||
|
"16": "img/icon_16.png",
|
||||||
|
"128": "img/icon_128.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,866 @@
|
|||||||
|
{
|
||||||
|
"extName": {
|
||||||
|
"message": "nuTensor",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"extShortDesc": {
|
||||||
|
"message": "Point & click to forbid/allow any class of requests made by your browser. Use it to block scripts, iframes, ads, facebook, etc.",
|
||||||
|
"description": "this will be used as short description in web stores: MUST BE 132 characters OR LESS"
|
||||||
|
},
|
||||||
|
"dashboardPageName": {
|
||||||
|
"message": "nuTensor — Panelo",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"loggerPageName": {
|
||||||
|
"message": "nuTensor — Logger",
|
||||||
|
"description": "Title for the logger window"
|
||||||
|
},
|
||||||
|
"settingsPageName": {
|
||||||
|
"message": "Agordoj",
|
||||||
|
"description": "a tab in dashboard"
|
||||||
|
},
|
||||||
|
"privacyPageName": {
|
||||||
|
"message": "Privateco",
|
||||||
|
"description": "a tab in dashboard"
|
||||||
|
},
|
||||||
|
"statsPageName": {
|
||||||
|
"message": "Statistics",
|
||||||
|
"description": "a tab in dashboard"
|
||||||
|
},
|
||||||
|
"userRulesPageName": {
|
||||||
|
"message": "Miaj reguloj",
|
||||||
|
"description": "a tab in dashboard"
|
||||||
|
},
|
||||||
|
"ubiquitousRulesPageName": {
|
||||||
|
"message": "Assets",
|
||||||
|
"description": "a tab in dashboard"
|
||||||
|
},
|
||||||
|
"rawSettingsPageName": {
|
||||||
|
"message": "More",
|
||||||
|
"description": "a tab in dashboard"
|
||||||
|
},
|
||||||
|
"aboutPageName": {
|
||||||
|
"message": "Pri",
|
||||||
|
"description": "a tab in dashboard"
|
||||||
|
},
|
||||||
|
"allPrettyName": {
|
||||||
|
"message": "all",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"cookiePrettyName": {
|
||||||
|
"message": "kuketo",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"cssPrettyName": {
|
||||||
|
"message": "css",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"imagePrettyName": {
|
||||||
|
"message": "bildo",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"mediaPrettyName": {
|
||||||
|
"message": "media",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"pluginPrettyName": {
|
||||||
|
"message": "kromaĵo",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"scriptPrettyName": {
|
||||||
|
"message": "skripto",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"fetchPrettyName": {
|
||||||
|
"message": "fetch",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"framePrettyName": {
|
||||||
|
"message": "kadro",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"otherPrettyName": {
|
||||||
|
"message": "alia",
|
||||||
|
"description": "HAS TO FIT IN MATRIX HEADER!"
|
||||||
|
},
|
||||||
|
"matrixNoNetTrafficPrompt": {
|
||||||
|
"message": "No net traffic seen for this tab so far.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"matrixLocalScopeTip": {
|
||||||
|
"message": "Select a local scope to see/create rules which apply only in that scope",
|
||||||
|
"description": "Tool tip for the local scope button"
|
||||||
|
},
|
||||||
|
"matrixGlobalScopeTip": {
|
||||||
|
"message": "Select the global scope to see/create rules which apply everywhere",
|
||||||
|
"description": "Tool tip for the global scope button"
|
||||||
|
},
|
||||||
|
"matrixMtxButtonTip": {
|
||||||
|
"message": "Disable/enable matrix filtering for this scope",
|
||||||
|
"description": "Tool tip for matrix button"
|
||||||
|
},
|
||||||
|
"matrixPersistButtonTip": {
|
||||||
|
"message": "Save all temporary changes for this scope",
|
||||||
|
"description": "Tool tip for the persist button"
|
||||||
|
},
|
||||||
|
"matrixRevertButtonTip": {
|
||||||
|
"message": "Revert temporary changes for this scope",
|
||||||
|
"description": "Tool tip for the revert local permission button"
|
||||||
|
},
|
||||||
|
"matrixReloadButton": {
|
||||||
|
"message": "Reŝargi la paĝon.",
|
||||||
|
"description": "Tool tip for the reload button"
|
||||||
|
},
|
||||||
|
"matrix1stPartyLabel": {
|
||||||
|
"message": "1st-party",
|
||||||
|
"description": "1st-party"
|
||||||
|
},
|
||||||
|
"matrixBlacklistedHostnames": {
|
||||||
|
"message": "{{count}} blacklisted hostname(s)",
|
||||||
|
"description": "Appears in the metadata row of bottom-most group: **mind the limited width**"
|
||||||
|
},
|
||||||
|
"matrixSwitchNoMixedContent": {
|
||||||
|
"message": "Forbid mixed content",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixSwitchNoWorker": {
|
||||||
|
"message": "Forbid web workers",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixSwitchReferrerSpoof": {
|
||||||
|
"message": "Spoof <code>Referer</code> header",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixSwitchNoscriptSpoof": {
|
||||||
|
"message": "Spoof <code><noscript></code> tags",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixSwitchRevealCname": {
|
||||||
|
"message": "Reveal canonical names",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixRevertAllEntry": {
|
||||||
|
"message": "Revert all temporary changes",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixLoggerMenuEntry": {
|
||||||
|
"message": "Iri al protokolilo",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixDashboardMenuEntry": {
|
||||||
|
"message": "Iri al panelo",
|
||||||
|
"description": "A menu entry in the matrix popup"
|
||||||
|
},
|
||||||
|
"matrixNoTabFound": {
|
||||||
|
"message": "No web page found",
|
||||||
|
"description": "Displays in place of matrix when no data is found for the current page"
|
||||||
|
},
|
||||||
|
"matrixRecipeImportTip": {
|
||||||
|
"message": "Import rules",
|
||||||
|
"description": "Used as a tooltip for the recipe import button"
|
||||||
|
},
|
||||||
|
"matrixRecipeSaveTip": {
|
||||||
|
"message": "Save rules",
|
||||||
|
"description": "Used as a tooltip for the recipe padlock button"
|
||||||
|
},
|
||||||
|
"statsPageTitle": {
|
||||||
|
"message": "nuTensor – Statistics",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageGenericStats": {
|
||||||
|
"message": "Generic statistics",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageCookieHeadersFoiled": {
|
||||||
|
"message": "<a href='https://en.wikipedia.org/wiki/HTTP_cookie'>HTTP cookie</a> headers foiled: {{count}}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageRefererHeadersFoiled": {
|
||||||
|
"message": "<a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referer</a> headers foiled: {{count}}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageHyperlinkAuditingFoiled": {
|
||||||
|
"message": "<a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>Hyperlink auditing</a> attempts foiled: {{count}}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageCookiesRemoved": {
|
||||||
|
"message": "Lokaj kuketoj forigitaj: {{count}}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageLocalStoragesCleared": {
|
||||||
|
"message": "<a href='http://diveintohtml5.info/storage.html'>Lokaj konservadoj</a> malplenigitaj: {{count}}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageBrowserCacheCleared": {
|
||||||
|
"message": "Browser caches cleared: {{count}}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageDetailedStats": {
|
||||||
|
"message": "Detailed statistics",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageDetailedAllPages": {
|
||||||
|
"message": "All",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageDetailedBehindTheScenePage": {
|
||||||
|
"message": "Behind the scene",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageOverview": {
|
||||||
|
"message": "Superrigardo",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageRequests": {
|
||||||
|
"message": "Petoj",
|
||||||
|
"description": "header for the stat type column"
|
||||||
|
},
|
||||||
|
"statsPageAllowed": {
|
||||||
|
"message": "Allowed",
|
||||||
|
"description": "header for the allowed requests column"
|
||||||
|
},
|
||||||
|
"statsPageBlocked": {
|
||||||
|
"message": "Blocked",
|
||||||
|
"description": "header for the blocked requests column"
|
||||||
|
},
|
||||||
|
"statsPageAll": {
|
||||||
|
"message": "All",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPagePages": {
|
||||||
|
"message": "Paĝoj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageCookies": {
|
||||||
|
"message": "Kuketoj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageCSS": {
|
||||||
|
"message": "CSS",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageImages": {
|
||||||
|
"message": "Bildoj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPagePlugins": {
|
||||||
|
"message": "Kromaĵoj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageScripts": {
|
||||||
|
"message": "Skriptoj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageXHRs": {
|
||||||
|
"message": "XHR-oj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageFrames": {
|
||||||
|
"message": "Kadroj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageOthers": {
|
||||||
|
"message": "Aliaj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageDetailed": {
|
||||||
|
"message": "Protokolilo",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"statsPageLogSizePrompt1": {
|
||||||
|
"message": "Remember the last",
|
||||||
|
"description": "First part of Remember the last [n] HTTP requests per page"
|
||||||
|
},
|
||||||
|
"statsPageLogSizePrompt2": {
|
||||||
|
"message": "HTTP requests <b>per page</b>",
|
||||||
|
"description": "Second part of Remember the last [n] HTTP requests per page"
|
||||||
|
},
|
||||||
|
"statsPageLogSizeHelp": {
|
||||||
|
"message": "<p>You can inspect details of the most recent raw HTTP requests which have been made by a web page (see below).</p><p>This is mostly useful to advanced users who want to investigate exactly what a web page has been doing. But logging these HTTP requests requires memory, and if you don't care about this technical information, then memory is being wasted.</p><p>Hence this field which lets you adjust the maximum number of the most recent HTTP requests which are to be logged for further inspection.</p><p>Enter “<code>0</code>” to turn off detailed logging (and consequently reduce the memory footprint of <i>nuTensor</i>).</p>",
|
||||||
|
"description": "To help user understand the purpose of the log size value"
|
||||||
|
},
|
||||||
|
"statsPageRefresh": {
|
||||||
|
"message": "Aktualigi",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"logAll": {
|
||||||
|
"message": "All",
|
||||||
|
"description": "Appears in the logger's tab selector"
|
||||||
|
},
|
||||||
|
"logBehindTheScene": {
|
||||||
|
"message": "Tabless",
|
||||||
|
"description": "Pretty name for behind-the-scene network requests"
|
||||||
|
},
|
||||||
|
"loggerCurrentTab": {
|
||||||
|
"message": "Current tab",
|
||||||
|
"description": "Appears in the logger's tab selector"
|
||||||
|
},
|
||||||
|
"loggerReloadTip": {
|
||||||
|
"message": "Reload the tab content",
|
||||||
|
"description": "Tooltip for the reload button in the logger page"
|
||||||
|
},
|
||||||
|
"loggerFilterInputPlaceholder": {
|
||||||
|
"message": "filter expression(s)",
|
||||||
|
"description": "Appears in the input filed where filter expressions are entered"
|
||||||
|
},
|
||||||
|
"loggerEntryCookieDeleted": {
|
||||||
|
"message": "kuketo forigita: {{value}}",
|
||||||
|
"description": "An entry for when a cookie is deleted"
|
||||||
|
},
|
||||||
|
"loggerEntryDeleteCookieError": {
|
||||||
|
"message": "failed to delete cookie: {{value}}",
|
||||||
|
"description": "An entry for when the browser cache is cleared"
|
||||||
|
},
|
||||||
|
"loggerEntryBrowserCacheCleared": {
|
||||||
|
"message": "browser cache cleared",
|
||||||
|
"description": "An entry for when a cookie can't be deleted"
|
||||||
|
},
|
||||||
|
"loggerEntryAssetUpdated": {
|
||||||
|
"message": "asset updated: {{value}}",
|
||||||
|
"description": "An entry for when an asset was updated"
|
||||||
|
},
|
||||||
|
"loggerRowFiltererButtonTip": {
|
||||||
|
"message": "Toggle logger filtering",
|
||||||
|
"description": "Tooltip for the row filterer button in the logger page"
|
||||||
|
},
|
||||||
|
"logFilterPrompt": {
|
||||||
|
"message": "filter logger content",
|
||||||
|
"description": "Placeholder string for logger output filtering input field"
|
||||||
|
},
|
||||||
|
"loggerPopupPanelTip": {
|
||||||
|
"message": "Toggle the popup panel",
|
||||||
|
"description": "Tooltip for the popup panel button in the logger page"
|
||||||
|
},
|
||||||
|
"loggerInfoTip": {
|
||||||
|
"message": "uBlock Origin wiki: The logger",
|
||||||
|
"description": "Tooltip for the top-right info label in the logger page"
|
||||||
|
},
|
||||||
|
"loggerClearTip": {
|
||||||
|
"message": "Clear logger",
|
||||||
|
"description": "Tooltip for the eraser in the logger page; used to blank the content of the logger"
|
||||||
|
},
|
||||||
|
"loggerPauseTip": {
|
||||||
|
"message": "Pause logger (discard all incoming data)",
|
||||||
|
"description": "Tooltip for the pause button in the logger page"
|
||||||
|
},
|
||||||
|
"loggerUnpauseTip": {
|
||||||
|
"message": "Unpause logger",
|
||||||
|
"description": "Tooltip for the play button in the logger page"
|
||||||
|
},
|
||||||
|
"loggerRowFiltererBuiltinTip": {
|
||||||
|
"message": "Logger filtering options",
|
||||||
|
"description": "Tooltip for the button to bring up logger output filtering options"
|
||||||
|
},
|
||||||
|
"loggerRowFiltererBuiltinNot": {
|
||||||
|
"message": "Not",
|
||||||
|
"description": "A keyword in the built-in row filtering expression"
|
||||||
|
},
|
||||||
|
"loggerRowFiltererBuiltinBlocked": {
|
||||||
|
"message": "blocked",
|
||||||
|
"description": "A keyword in the built-in row filtering expression"
|
||||||
|
},
|
||||||
|
"loggerRowFiltererBuiltinInfo": {
|
||||||
|
"message": "info",
|
||||||
|
"description": "A keyword in the built-in row filtering expression"
|
||||||
|
},
|
||||||
|
"loggerRowFiltererBuiltin1p": {
|
||||||
|
"message": "1st-party",
|
||||||
|
"description": "A keyword in the built-in row filtering expression"
|
||||||
|
},
|
||||||
|
"loggerRowFiltererBuiltin3p": {
|
||||||
|
"message": "3rd-party",
|
||||||
|
"description": "A keyword in the built-in row filtering expression"
|
||||||
|
},
|
||||||
|
"loggerEntryDetailsHeader": {
|
||||||
|
"message": "Details",
|
||||||
|
"description": "Small header to identify the 'Details' pane for a specific logger entry"
|
||||||
|
},
|
||||||
|
"loggerEntryDetailsContext": {
|
||||||
|
"message": "Context",
|
||||||
|
"description": "Label to identify a context field (typically a hostname)"
|
||||||
|
},
|
||||||
|
"loggerEntryDetailsPartyness": {
|
||||||
|
"message": "Partyness",
|
||||||
|
"description": "Label to identify a field providing partyness information"
|
||||||
|
},
|
||||||
|
"loggerEntryDetailsType": {
|
||||||
|
"message": "Type",
|
||||||
|
"description": "Label to identify the type of an entry"
|
||||||
|
},
|
||||||
|
"loggerEntryDetailsURL": {
|
||||||
|
"message": "URL",
|
||||||
|
"description": "Label to identify the URL of an entry"
|
||||||
|
},
|
||||||
|
"loggerEntryRuleHeader": {
|
||||||
|
"message": "Rule",
|
||||||
|
"description": "Small header to identify the 'Rule' pane for a specific logger entry"
|
||||||
|
},
|
||||||
|
"loggerSettingDiscardPrompt": {
|
||||||
|
"message": "Logger entries which do not fulfill all three conditions below will be automatically discarded:",
|
||||||
|
"description": "Logger setting: A sentence to describe the purpose of the settings below"
|
||||||
|
},
|
||||||
|
"loggerSettingPerEntryMaxAge": {
|
||||||
|
"message": "Preserve entries from the last {{input}} minutes",
|
||||||
|
"description": "A logger setting"
|
||||||
|
},
|
||||||
|
"loggerSettingPerTabMaxLoads": {
|
||||||
|
"message": "Preserve at most {{input}} page loads per tab",
|
||||||
|
"description": "A logger setting"
|
||||||
|
},
|
||||||
|
"loggerSettingPerTabMaxEntries": {
|
||||||
|
"message": "Preserve at most {{input}} entries per tab",
|
||||||
|
"description": "A logger setting"
|
||||||
|
},
|
||||||
|
"loggerSettingPerEntryLineCount": {
|
||||||
|
"message": "Use {{input}} lines per entry in vertically expanded mode",
|
||||||
|
"description": "A logger setting"
|
||||||
|
},
|
||||||
|
"loggerExportFormatList": {
|
||||||
|
"message": "List",
|
||||||
|
"description": "Label for radio-button to pick export format"
|
||||||
|
},
|
||||||
|
"loggerExportFormatTable": {
|
||||||
|
"message": "Table",
|
||||||
|
"description": "Label for radio-button to pick export format"
|
||||||
|
},
|
||||||
|
"loggerExportEncodePlain": {
|
||||||
|
"message": "Plain",
|
||||||
|
"description": "Label for radio-button to pick export text format"
|
||||||
|
},
|
||||||
|
"loggerExportEncodeMarkdown": {
|
||||||
|
"message": "Markdown",
|
||||||
|
"description": "Label for radio-button to pick export text format"
|
||||||
|
},
|
||||||
|
"settingsPageTitle": {
|
||||||
|
"message": "nuTensor – Agordoj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"settingsMatrixDisplayHeader": {
|
||||||
|
"message": "Apero",
|
||||||
|
"description": "header for matrix settings used in Settings page"
|
||||||
|
},
|
||||||
|
"settingsMatrixDisplayTextSizePrompt": {
|
||||||
|
"message": "Tekstogrando:",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"settingsIconBadgeEnabled": {
|
||||||
|
"message": "Show the number of blocked resources on the icon",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"settingsMatrixDisplayColorBlind": {
|
||||||
|
"message": "Afabla por kolorblinduloj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"settingsMatrixConvenienceHeader": {
|
||||||
|
"message": "Komforto",
|
||||||
|
"description": "English: Convenience"
|
||||||
|
},
|
||||||
|
"settingsDefaultScopeLevel": {
|
||||||
|
"message": "Default scope level:",
|
||||||
|
"description": "Label for default scope level selector in Settings pane"
|
||||||
|
},
|
||||||
|
"settingsDefaultScopeLevel0": {
|
||||||
|
"message": "Global",
|
||||||
|
"description": "Scope will be global"
|
||||||
|
},
|
||||||
|
"settingsDefaultScopeLevel1": {
|
||||||
|
"message": "Domain",
|
||||||
|
"description": "Scope will be base domain"
|
||||||
|
},
|
||||||
|
"settingsDefaultScopeLevel2": {
|
||||||
|
"message": "Site",
|
||||||
|
"description": "Scope will be full hostname of site"
|
||||||
|
},
|
||||||
|
"settingsCollapseBlocked": {
|
||||||
|
"message": "Hide placeholder of blocked elements",
|
||||||
|
"description": "A setting in the dashboard's Settings pane"
|
||||||
|
},
|
||||||
|
"settingsCollapseBlacklisted": {
|
||||||
|
"message": "Hide placeholder of blacklisted elements",
|
||||||
|
"description": "A setting in the dashboard's Settings pane: 'blacklisted' means 'for which there is a specific block rule', 'specific' means 'a rule for which the destination hostname is not `*`'"
|
||||||
|
},
|
||||||
|
"settingsNoscriptTagsSpoofed": {
|
||||||
|
"message": "Spoof <code><noscript></code> tags when 1st-party scripts are blocked",
|
||||||
|
"description": "This appears in the Settings pane in the dashboard"
|
||||||
|
},
|
||||||
|
"settingsCloudStorageEnabled": {
|
||||||
|
"message": "Ŝalti subtenon por nuba konservado",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"settingsMatrixNoTooltips": {
|
||||||
|
"message": "Disable tooltips",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyPageTitle": {
|
||||||
|
"message": "nuTensor – Privateco",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyDeleteBlockedCookiesPrompt": {
|
||||||
|
"message": "Delete blocked cookies",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyDeleteBlockedCookiesHelp": {
|
||||||
|
"message": "<p>Blacklisted cookies are not prevented by <i>nuTensor</i> from entering your browser. However they are prevented from leaving your browser, which is what really matters. Not blocking cookies before they enter your browser gives you the opportunity to be informed that a site tried to use cookies, and furthermore to inspect their contents if you wish.</p><p>Once these blacklisted cookies have been accounted for by <i>nuTensor</i>, they can be removed from your browser if you wish so.</p><p><b>Important note:</b> Extensions can make web requests during the course of their normal operation. These requests can result in cookies being created in the browser. If the hostname from where a cookie originate is not whitelisted, the cookie will be removed from the browser by <i>nuTensor</i> if this option is checked. So be sure that the hostname(s) with which an extension communicate is whitelisted.</p>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyDeleteNonBlockedSessionCookiesPrompt1": {
|
||||||
|
"message": "Delete non-blocked session cookies ",
|
||||||
|
"description": "First part of 'Delete non-blocked session cookies [n] minutes after the last time they have been used'"
|
||||||
|
},
|
||||||
|
"privacyDeleteNonBlockedSessionCookiesPrompt2": {
|
||||||
|
"message": " minutes after the last time they have been used",
|
||||||
|
"description": "Second part of 'Delete non-blocked session cookies [n] minutes after the last time they have been used'"
|
||||||
|
},
|
||||||
|
"privacyDeleteNonBlockedSessionCookiesHelp": {
|
||||||
|
"message": "<p><a href='http://www.w3.org/2001/tag/2010/09/ClientSideStorage.html'>W3C</a>: “A session cookie ... is erased when you end the browser session. The session cookie is stored in temporary memory and is not retained after the browser is closed.”</p><p>Except that this <a href='https://code.google.com/p/chromium/issues/detail?id=128513'>might not be happening</a> in some browsers. Also, to some, having to close the browser in order for the session cookies to clear might not be early enough.</p>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyDeleteBlockedLocalStoragePrompt": {
|
||||||
|
"message": "Delete <a href='https://en.wikipedia.org/wiki/Web_storage'>local storage</a> content set by blocked hostnames",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyDeleteBlockedLocalStorageHelp": {
|
||||||
|
"message": "TODO",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyClearCachePrompt1": {
|
||||||
|
"message": "Clear browser cache every",
|
||||||
|
"description": "First part of 'Clear browser cache every [n] minutes'"
|
||||||
|
},
|
||||||
|
"privacyClearCachePrompt2": {
|
||||||
|
"message": "minutoj.",
|
||||||
|
"description": "Second part of 'Clear browser cache every [n] minutes'"
|
||||||
|
},
|
||||||
|
"privacyClearCacheHelp": {
|
||||||
|
"message": "<p>Some web sites are really bent on tracking you, so much that they will use not-so-nice tricks to work around whatever measures you take in order to not be tracked.</p><p>A few of these tricks rely<sup>[1, 2]</sup> on the <a href='https://en.wikipedia.org/wiki/Web_cache'>browser cache</a>, which content is often long lasting since rarely will users take the time to regularly clear their browser cache.</p><p>There is little inconvenience to clear the browser cache regularly (likelihood is that you won't notice when it happens), and the benefit is to prevent these obnoxious trackers from invading your privacy.</p><p>Check this option to have <i>nuTensor</i> do it for you, at the interval you wish.</p><p>[1] <a href='https://grepular.com/Preventing_Web_Tracking_via_the_Browser_Cache'>“Preventing Web Tracking via the Browser Cache”</a>\n[2] <a href='http://lucb1e.com/rp/cookielesscookies/'>“Cookieless cookies”</a></p>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyProcessRefererPrompt": {
|
||||||
|
"message": "Spoof <a href='https://en.wikipedia.org/wiki/HTTP_referer'>HTTP referrer</a> string of third-party requests",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyProcessRefererHelp": {
|
||||||
|
"message": "From Wikipedia:<blockquote>HTTP referer is an HTTP header field that identifies the address of the webpage that linked to the resource being requested. ... <b>Because referer information can violate privacy, some web browsers allow the user to disable the sending of referer information.</b></blockquote>If this setting is checked, <i>nuTensor</i> will spoof the HTTP referrer information if the domain name of the HTTP referrer is third-party to the domain name of net request.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyNoMixedContentPrompt": {
|
||||||
|
"message": "Strict HTTPS: forbid mixed content",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyNoMixedContentHelp": {
|
||||||
|
"message": "<p>From <a href='https://developer.mozilla.org/en-US/docs/Security/MixedContent'>Mozilla Developer Network</a>:</p><blockquote>If [a] HTTPS page includes content retrieved through regular, cleartext HTTP, then the connection is only partially encrypted: the unencrypted content is accessible to sniffers and can be modified by man-in-the-middle attackers, and therefore the connection is not safeguarded anymore. When a webpage exhibits this behavior, it is called a mixed content page.</blockquote>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyProcessHyperlinkAuditingPrompt": {
|
||||||
|
"message": "Block all <a href='https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing'>hyperlink auditing</a> attempts",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"privacyProcessHyperlinkAuditingHelp": {
|
||||||
|
"message": "<p>Hyperlink auditing is a mechanism which allow a party, <b>any party</b>, to be informed about which link a user clicked on a particular web page. It is essentially a tracking feature: it allows a web site, or any third-party to that web site, to be informed about which link you clicked on which one of its web pages. The sole purpose is to track your browsing activity.</p>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"userRulesPermanentHeader": {
|
||||||
|
"message": "Daŭraj reguloj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"userRulesTemporaryHeader": {
|
||||||
|
"message": "Nedaŭraj reguloj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"userRulesRevert": {
|
||||||
|
"message": "Malfari",
|
||||||
|
"description": "Will remove all temporary rules"
|
||||||
|
},
|
||||||
|
"userRulesCommit": {
|
||||||
|
"message": "Konservi",
|
||||||
|
"description": "Will save all temporary rules"
|
||||||
|
},
|
||||||
|
"userRulesEdit": {
|
||||||
|
"message": "Redakti",
|
||||||
|
"description": "Will enable manual-edit mode (textarea)"
|
||||||
|
},
|
||||||
|
"userRulesEditSave": {
|
||||||
|
"message": "Konservi",
|
||||||
|
"description": "Will save manually-edited content and exit manual-edit mode"
|
||||||
|
},
|
||||||
|
"userRulesEditDicard": {
|
||||||
|
"message": "Ignori",
|
||||||
|
"description": "Will discard manually-edited content and exit manual-edit mode"
|
||||||
|
},
|
||||||
|
"userRulesImport": {
|
||||||
|
"message": "Importi el dosiero...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"userRulesExport": {
|
||||||
|
"message": "Eksporti al dosiero...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"userRulesFormatHint": {
|
||||||
|
"message": "See this page for rule syntax.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"userRulesDefaultFileName": {
|
||||||
|
"message": "miaj-umatrix-reguloj.txt",
|
||||||
|
"description": "default file name to use"
|
||||||
|
},
|
||||||
|
"assetsHostsSection": {
|
||||||
|
"message": "Hosts files",
|
||||||
|
"description": "header to identify the hosts files section"
|
||||||
|
},
|
||||||
|
"hostsFilesPrompt": {
|
||||||
|
"message": "All hostnames in a hosts file are loaded as blacklisted hostnames in the global scope.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesStats": {
|
||||||
|
"message": "{{blockedHostnameCount}} distinct blocked hostnames from:",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesPerFileStats": {
|
||||||
|
"message": "{{used}} used out of {{total}}",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesLastUpdate": {
|
||||||
|
"message": "Lasta ĝisdatigo: {{ago}}",
|
||||||
|
"description": "English: Last update: {{ago}}, where 'ago' will be replaced with something like '2 days ago'"
|
||||||
|
},
|
||||||
|
"hostsFilesApplyChanges": {
|
||||||
|
"message": "Apliki ŝanĝojn",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesAutoUpdatePrompt": {
|
||||||
|
"message": "Auto-update assets",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesUpdateNow": {
|
||||||
|
"message": "Ĝisdatigi nun",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesPurgeAll": {
|
||||||
|
"message": "Malplenigi ĉiujn kaŝmemorojn",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesExternalListsHint": {
|
||||||
|
"message": "Import external assets here: \nOne URL per line; invalid URLs will be silently ignored.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesExternalListsParse": {
|
||||||
|
"message": "Analizi",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesExternalListPurge": {
|
||||||
|
"message": "malplenigi kaŝmemoron",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesExternalListNew": {
|
||||||
|
"message": "nova versio disponebla",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"hostsFilesExternalListObsolete": {
|
||||||
|
"message": "neĝisdata",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"assetsRecipesSection": {
|
||||||
|
"message": "Ruleset recipes",
|
||||||
|
"description": "header to identify the ruleset files section"
|
||||||
|
},
|
||||||
|
"assetsRecipesSummary": {
|
||||||
|
"message": "Ruleset recipes are imported from the popup panel <em>on demand</em>, i.e. <b>only</b> through user interaction.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"assetsImportLabel": {
|
||||||
|
"message": "Import...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"assetsInlineHostsLabel": {
|
||||||
|
"message": "My hosts",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"assetsInlineRecipesLabel": {
|
||||||
|
"message": "My recipes",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"rawSettingsWarning": {
|
||||||
|
"message": "Warning! Change these raw configuration settings at your own risk.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutChangelog": {
|
||||||
|
"message": "<a href='https://github.com/geekprojects/nuTensor/releases'>Ŝanĝoprotokolo</a>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutStorageUsed": {
|
||||||
|
"message": "Storage used: {{storageUsed}} bytes",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutDoc": {
|
||||||
|
"message": "<a href='https://github.com/geekprojects/nuTensor/wiki'>Documentation</a>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutPermissions": {
|
||||||
|
"message": "<a href='https://github.com/geekprojects/nuTensor/wiki/Permissions'>Permissions</a>",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutCode": {
|
||||||
|
"message": "Fontkodo (GPLv3)",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutIssues": {
|
||||||
|
"message": "Cimoj kaj problemoj",
|
||||||
|
"description": "Text for a link to official issue tracker"
|
||||||
|
},
|
||||||
|
"aboutContributors": {
|
||||||
|
"message": "Kontribuantoj",
|
||||||
|
"description": "English: Contributors"
|
||||||
|
},
|
||||||
|
"aboutCodeContributors": {
|
||||||
|
"message": "Kode:",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutIssueContributors": {
|
||||||
|
"message": "Problemoj:",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutTranslationContributors": {
|
||||||
|
"message": "Tradukoj:",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutUserDataHeader": {
|
||||||
|
"message": "Viaj datumoj",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutBackupButton": {
|
||||||
|
"message": "Savkopii al dosiero...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutBackupFilename": {
|
||||||
|
"message": "mia-umatrix-savkopio.txt",
|
||||||
|
"description": "default filename to use"
|
||||||
|
},
|
||||||
|
"aboutRestoreButton": {
|
||||||
|
"message": "Restore from file...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutRestoreConfirm": {
|
||||||
|
"message": "All your settings will be overwritten using data backed up on {{time}}, and nuTensor will restart.\n\nOverwrite all existing settings using backed up data?",
|
||||||
|
"description": "Message asking user to confirm restore"
|
||||||
|
},
|
||||||
|
"aboutRestoreError": {
|
||||||
|
"message": "The data could not be read or is invalid",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"aboutOr": {
|
||||||
|
"message": "... aŭ ...",
|
||||||
|
"description": "English: ... or ..."
|
||||||
|
},
|
||||||
|
"aboutResetButton": {
|
||||||
|
"message": "Reset to default settings",
|
||||||
|
"description": "English: Reset to default settings"
|
||||||
|
},
|
||||||
|
"aboutResetConfirm": {
|
||||||
|
"message": "Caution! this will remove all your custom settings. Are you sure you want to proceed?",
|
||||||
|
"description": "Message asking user to confirm reset"
|
||||||
|
},
|
||||||
|
"mainBlockedPrompt1": {
|
||||||
|
"message": "nuTensor has prevented the following page from loading:",
|
||||||
|
"description": "English: nuTensor has prevented the following page from loading:"
|
||||||
|
},
|
||||||
|
"mainBlockedPrompt2": {
|
||||||
|
"message": "Because of the following rule",
|
||||||
|
"description": "English: Because of the following rule"
|
||||||
|
},
|
||||||
|
"mainBlockedNoParamsPrompt": {
|
||||||
|
"message": "without parameters",
|
||||||
|
"description": "label to be used for the parameter-less URL: https://cloud.githubusercontent.com/assets/585534/9832014/bfb1b8f0-593b-11e5-8a27-fba472a5529a.png"
|
||||||
|
},
|
||||||
|
"mainBlockedBack": {
|
||||||
|
"message": "Reen",
|
||||||
|
"description": "English: Go back"
|
||||||
|
},
|
||||||
|
"mainBlockedClose": {
|
||||||
|
"message": "Fermi",
|
||||||
|
"description": "English: Close"
|
||||||
|
},
|
||||||
|
"commandRevertAll": {
|
||||||
|
"message": "Revert all temporary changes",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"commandWhitelistPageDomain": {
|
||||||
|
"message": "Temporarily whitelist page domain",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"commandWhitelistAll": {
|
||||||
|
"message": "Temporarily whitelist all",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"commandOpenDashboard": {
|
||||||
|
"message": "Malfermi la panelo",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"elapsedOneMinuteAgo": {
|
||||||
|
"message": "antaŭ 1 minuto",
|
||||||
|
"description": "English: a minute ago"
|
||||||
|
},
|
||||||
|
"elapsedManyMinutesAgo": {
|
||||||
|
"message": "antaŭ {{value}} minutoj",
|
||||||
|
"description": "English: {{value}} minutes ago"
|
||||||
|
},
|
||||||
|
"elapsedOneHourAgo": {
|
||||||
|
"message": "antaŭ 1 horo",
|
||||||
|
"description": "English: an hour ago"
|
||||||
|
},
|
||||||
|
"elapsedManyHoursAgo": {
|
||||||
|
"message": "antaŭ {{value}} horoj",
|
||||||
|
"description": "English: {{value}} hours ago"
|
||||||
|
},
|
||||||
|
"elapsedOneDayAgo": {
|
||||||
|
"message": "antaŭ 1 tago",
|
||||||
|
"description": "English: a day ago"
|
||||||
|
},
|
||||||
|
"elapsedManyDaysAgo": {
|
||||||
|
"message": "antaŭ {{value}} tagoj",
|
||||||
|
"description": "English: {{value}} days ago"
|
||||||
|
},
|
||||||
|
"showDashboardButton": {
|
||||||
|
"message": "Panelo",
|
||||||
|
"description": "Appears in Firefox's add-on preferences"
|
||||||
|
},
|
||||||
|
"showLoggerButton": {
|
||||||
|
"message": "Protokolilo",
|
||||||
|
"description": "Appears in Firefox's add-on preferences"
|
||||||
|
},
|
||||||
|
"cloudPush": {
|
||||||
|
"message": "Eksporti al nuba konservado",
|
||||||
|
"description": "tooltip"
|
||||||
|
},
|
||||||
|
"cloudPull": {
|
||||||
|
"message": "Importi el nuba konservado",
|
||||||
|
"description": "tooltip"
|
||||||
|
},
|
||||||
|
"cloudNoData": {
|
||||||
|
"message": "...\n...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"cloudDeviceNamePrompt": {
|
||||||
|
"message": "Nomo de ĉi tiu aparato:",
|
||||||
|
"description": "used as a prompt for the user to provide a custom device name"
|
||||||
|
},
|
||||||
|
"genericSubmit": {
|
||||||
|
"message": "Sendi",
|
||||||
|
"description": "for generic 'submit' buttons"
|
||||||
|
},
|
||||||
|
"genericRevert": {
|
||||||
|
"message": "Malfari",
|
||||||
|
"description": "for generic 'revert' buttons"
|
||||||
|
},
|
||||||
|
"errorCantConnectTo": {
|
||||||
|
"message": "Reteraro: {{url}} ne konekteblas",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"genericApplyChanges": {
|
||||||
|
"message": "Apply changes",
|
||||||
|
"description": "for generic 'Apply changes' buttons"
|
||||||
|
},
|
||||||
|
"genericCopyToClipboard": {
|
||||||
|
"message": "Copy to clipboard",
|
||||||
|
"description": "Label for buttons used to copy something to the clipboard"
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue