Compare commits
575 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
7e3df43463 | |
|
|
9ae082b29b | |
|
|
b417f2a23b | |
|
|
615cee7309 | |
|
|
65c1ca9691 | |
|
|
12bed611c5 | |
|
|
bc72687d7d | |
|
|
c32b7d4b60 | |
|
|
3f9ef123e7 | |
|
|
f2547f8a09 | |
|
|
edd5114492 | |
|
|
184a242937 | |
|
|
17c19a09b7 | |
|
|
88aa47c377 | |
|
|
0570545342 | |
|
|
f408f20ad9 | |
|
|
500a8df209 | |
|
|
13d9904b90 | |
|
|
b3e4dba731 | |
|
|
4164205663 | |
|
|
f5e2660890 | |
|
|
69f3882ce3 | |
|
|
8e42727d31 | |
|
|
31a5737c61 | |
|
|
8ae5e3d83b | |
|
|
4f40a8b46b | |
|
|
59a64af67f | |
|
|
66cbe0d9c9 | |
|
|
0ef5053a65 | |
|
|
6ddff3ae0d | |
|
|
9316d428ef | |
|
|
71ed8cdbf5 | |
|
|
62fdb15532 | |
|
|
bc191b798d | |
|
|
f8fbb0609f | |
|
|
55fb26fce1 | |
|
|
25dc019886 | |
|
|
81d7b6a74c | |
|
|
6ec5504ab3 | |
|
|
9b69c889ef | |
|
|
4e87ef303f | |
|
|
2f8234ee2e | |
|
|
a8f0b47451 | |
|
|
4a5b7bd6e4 | |
|
|
b89bf07d1e | |
|
|
70bf4689fc | |
|
|
1cbb6fccf0 | |
|
|
d3e4245d0f | |
|
|
3160048619 | |
|
|
aa93eb1614 | |
|
|
df9cef3a58 | |
|
|
214e2e9ac2 | |
|
|
68574b88cc | |
|
|
6e8e24050d | |
|
|
5fe9bf97e3 | |
|
|
b646d606d7 | |
|
|
1e4ddc2c6b | |
|
|
3fce0518bd | |
|
|
3808ebfdb3 | |
|
|
0421aacd64 | |
|
|
0c2a673e27 | |
|
|
afa456f396 | |
|
|
461077b6cf | |
|
|
e208f5e121 | |
|
|
0f320051e0 | |
|
|
ee40f13a30 | |
|
|
06c4fac54a | |
|
|
1392d55f53 | |
|
|
269d81371d | |
|
|
346a59a6f0 | |
|
|
d8079515fa | |
|
|
294534cf66 | |
|
|
da9bd515fd | |
|
|
5ac0cb97a8 | |
|
|
242cf2a22e | |
|
|
6f9cf43d6d | |
|
|
00b3bba99a | |
|
|
111178358a | |
|
|
e098609a8a | |
|
|
721ba1e036 | |
|
|
c5e7a73ab7 | |
|
|
67db09a9c9 | |
|
|
4925715dfc | |
|
|
6f8576b343 | |
|
|
7bffed47ed | |
|
|
642c5fe2fe | |
|
|
621f20adb3 | |
|
|
9d80bc2076 | |
|
|
88415c6aa0 | |
|
|
5c2076ab2d | |
|
|
7b2773814b | |
|
|
467b9a7d4a | |
|
|
816b8aaa00 | |
|
|
d38b5a423e | |
|
|
7f45b178c9 | |
|
|
26368f0b90 | |
|
|
ff76871f4e | |
|
|
9ddbc964d4 | |
|
|
2b4e5757b0 | |
|
|
ddfd17d749 | |
|
|
6f21accfae | |
|
|
6b48ae4084 | |
|
|
e300327eea | |
|
|
9d1dfcfe5c | |
|
|
897fc40f4d | |
|
|
24aab77607 | |
|
|
c37b9ebf5b | |
|
|
2ae3086244 | |
|
|
4aa807032f | |
|
|
0867a95abb | |
|
|
25ff649efe | |
|
|
f57afa39e8 | |
|
|
da8428f9ba | |
|
|
75475ee87b | |
|
|
8ebce896ad | |
|
|
43b5618c0a | |
|
|
c9498995df | |
|
|
aaba038a16 | |
|
|
92f3633662 | |
|
|
7ce0e03614 | |
|
|
d64781c31c | |
|
|
a8f9fc1396 | |
|
|
a13306a79f | |
|
|
c6b486ce81 | |
|
|
18bea32975 | |
|
|
7e770765a9 | |
|
|
1aaf621409 | |
|
|
7d8d14eb07 | |
|
|
226d167ec7 | |
|
|
a7822d1e2a | |
|
|
1f4ca40f78 | |
|
|
75239be8b6 | |
|
|
488339b3d5 | |
|
|
52917f9c31 | |
|
|
326a83309a | |
|
|
264d91eaec | |
|
|
9e280eb80f | |
|
|
c278069946 | |
|
|
5894221924 | |
|
|
af2450591c | |
|
|
65de4fde16 | |
|
|
4d6dccfed5 | |
|
|
45483d0994 | |
|
|
c3eef39050 | |
|
|
a93f2e917d | |
|
|
d8beaa1e9d | |
|
|
aa6fcab23a | |
|
|
1e243b0d06 | |
|
|
ce6be4f741 | |
|
|
195d19ec3f | |
|
|
78b55f84cc | |
|
|
7f532f69d2 | |
|
|
50080e8607 | |
|
|
17522478db | |
|
|
44dbef482e | |
|
|
c0216a1309 | |
|
|
c63a98e3e6 | |
|
|
08f5509bed | |
|
|
4180e04f24 | |
|
|
3cdacde13e | |
|
|
ada069b3e3 | |
|
|
9e99d6df34 | |
|
|
afd1515d8d | |
|
|
6d1cd0282a | |
|
|
21082a85a4 | |
|
|
745bb7f4f0 | |
|
|
ec86816be1 | |
|
|
85e1a132bb | |
|
|
d8fd61811c | |
|
|
40b3e70659 | |
|
|
0c035c41d8 | |
|
|
f2ec540a1b | |
|
|
b9aec6777a | |
|
|
6b3b83981a | |
|
|
d80d80b867 | |
|
|
c9d3b61ca2 | |
|
|
87ad9dce57 | |
|
|
961b4a212d | |
|
|
e151c2d0cf | |
|
|
c79257dc8d | |
|
|
22f1b57347 | |
|
|
2536cc2313 | |
|
|
d15052a67a | |
|
|
be87927923 | |
|
|
7315c34957 | |
|
|
8bf6a9c448 | |
|
|
9e459a004c | |
|
|
e7af54898e | |
|
|
7664fdbb34 | |
|
|
81546c97ae | |
|
|
b2f58856a8 | |
|
|
5e565f4dc7 | |
|
|
879c964c35 | |
|
|
2ec0f8fa77 | |
|
|
9d5aeefa8c | |
|
|
ed6329737f | |
|
|
04a1d11905 | |
|
|
47724f913e | |
|
|
a45d57feee | |
|
|
eca17870ae | |
|
|
959dc5a9e5 | |
|
|
7b388017c3 | |
|
|
55ca67f3e2 | |
|
|
6b86a9072f | |
|
|
96e535c7ed | |
|
|
1217f7fa45 | |
|
|
c1e7b6e8f3 | |
|
|
2e343403de | |
|
|
2ec16a947f | |
|
|
ced24549df | |
|
|
272693ef3e | |
|
|
5926932a6d | |
|
|
3f31a0005a | |
|
|
9219869cb9 | |
|
|
0b5f70f029 | |
|
|
bff2ad9086 | |
|
|
696a4490ff | |
|
|
9cf95165a9 | |
|
|
f9eb71b4c8 | |
|
|
422680046e | |
|
|
060f4a59d6 | |
|
|
291dda69e5 | |
|
|
329977b42c | |
|
|
072b03a631 | |
|
|
9ba931075b | |
|
|
0de8337969 | |
|
|
aef5355436 | |
|
|
856aeb3f47 | |
|
|
c86ddd11be | |
|
|
aab33cc596 | |
|
|
bb7bbb6ead | |
|
|
9447182809 | |
|
|
9c7a75616c | |
|
|
858606c18d | |
|
|
049cf49705 | |
|
|
4633a0704e | |
|
|
a2072db15d | |
|
|
9685c5a383 | |
|
|
c682ccb1b2 | |
|
|
81f65dbb46 | |
|
|
6cbf2847c8 | |
|
|
74c7ff2641 | |
|
|
a4b3ca48df | |
|
|
9f16a1f879 | |
|
|
ed99c82440 | |
|
|
20a9130cee | |
|
|
102f43cbde | |
|
|
c5fe31e828 | |
|
|
9aef02e96c | |
|
|
06e7950588 | |
|
|
1b7557b73b | |
|
|
5e6a878f64 | |
|
|
a3bdcca37b | |
|
|
0a9b6aab73 | |
|
|
8f8748e486 | |
|
|
a14b986326 | |
|
|
422d268fe3 | |
|
|
b3138d1be8 | |
|
|
e67aea60e8 | |
|
|
67e7677523 | |
|
|
9249c5cb0e | |
|
|
c601bd0c7a | |
|
|
f9933d2f3e | |
|
|
95f9472a6b | |
|
|
701046cd9f | |
|
|
aa41b6719f | |
|
|
05b42c57ee | |
|
|
77b5c0c1cd | |
|
|
84ec45c13f | |
|
|
ecbca59dea | |
|
|
67d8e012a3 | |
|
|
dea1de5dab | |
|
|
9aa53ea205 | |
|
|
7bc6c33bb5 | |
|
|
9e0f7ccc51 | |
|
|
1d2faf2fa2 | |
|
|
f6f2a1252b | |
|
|
86c89e00c5 | |
|
|
4f4ad4147a | |
|
|
13b87e5968 | |
|
|
7204168684 | |
|
|
3dc212a815 | |
|
|
351fef8c1e | |
|
|
4053249229 | |
|
|
8b820f015b | |
|
|
afeb1ebbaa | |
|
|
6b5ebe7187 | |
|
|
c073a30afe | |
|
|
b16551548f | |
|
|
961a9390da | |
|
|
b5a399831b | |
|
|
bbdf9faf5e | |
|
|
d9d8701696 | |
|
|
3280c8dacc | |
|
|
54b6c92713 | |
|
|
de3fa06eff | |
|
|
ef2ceccd29 | |
|
|
9a23ce6c3c | |
|
|
21b2d63f4d | |
|
|
c32f5afcc4 | |
|
|
ab3b77cb1e | |
|
|
b09a1385f5 | |
|
|
4e5eb90857 | |
|
|
72647298b7 | |
|
|
71381c0da6 | |
|
|
3aa839998b | |
|
|
65a3a3cf9a | |
|
|
163bb9fa0c | |
|
|
c165d1a0da | |
|
|
1ab93bad33 | |
|
|
69f0eae600 | |
|
|
ef397e9417 | |
|
|
c60ebe8eef | |
|
|
4dde8e1594 | |
|
|
b6d5a5cf14 | |
|
|
c4d8502872 | |
|
|
2f20e3b60b | |
|
|
873b874c5f | |
|
|
a460770414 | |
|
|
e4ab45e3b2 | |
|
|
d0407852f4 | |
|
|
59be37007d | |
|
|
b43c79989e | |
|
|
939e0fa817 | |
|
|
18db4cc937 | |
|
|
4a675115e1 | |
|
|
02b9f5eed1 | |
|
|
cc20112a67 | |
|
|
d49c96aa29 | |
|
|
b0e05ee160 | |
|
|
4c9e989c39 | |
|
|
237e90985d | |
|
|
e3db31e29e | |
|
|
f25604b819 | |
|
|
b183e6a39a | |
|
|
1f90da7694 | |
|
|
7ad4bb70fe | |
|
|
b271559f82 | |
|
|
51541add20 | |
|
|
328b5966ef | |
|
|
4cc67d3603 | |
|
|
8c2cfe8f7e | |
|
|
c35fc88211 | |
|
|
90c40152ed | |
|
|
27606f2fe3 | |
|
|
561986cad8 | |
|
|
2810021896 | |
|
|
a875f3e8be | |
|
|
7b4c163142 | |
|
|
8b2ab6b01d | |
|
|
4f985feede | |
|
|
2efaf92b7c | |
|
|
a0fd88aac8 | |
|
|
42c9973c01 | |
|
|
c9374fd80a | |
|
|
dd66300caf | |
|
|
33ba05c657 | |
|
|
990c4d4cfc | |
|
|
1a6d32cc3d | |
|
|
de8a2c04b8 | |
|
|
12e6838267 | |
|
|
fdfa9109ff | |
|
|
0eff340063 | |
|
|
eb2e39de1e | |
|
|
0b221d3e24 | |
|
|
f7c8c67818 | |
|
|
accce7fdef | |
|
|
718d66fb6d | |
|
|
60f526d81f | |
|
|
4004799f2c | |
|
|
1615e8a40e | |
|
|
bfb580ee91 | |
|
|
4a1a30bc33 | |
|
|
6b3901ccee | |
|
|
6b25fdf648 | |
|
|
9bae38ede6 | |
|
|
7bd941cda0 | |
|
|
e63781d49b | |
|
|
03224a0015 | |
|
|
322977dc0d | |
|
|
60177ab975 | |
|
|
9b59cd02cb | |
|
|
b3090870d3 | |
|
|
253a818ecc | |
|
|
0a32d6fef5 | |
|
|
c5395c23f7 | |
|
|
d272804126 | |
|
|
ed8141cb4b | |
|
|
2b8c053aee | |
|
|
7eb9bc45de | |
|
|
7d1179e0e3 | |
|
|
977c90939c | |
|
|
980105aa11 | |
|
|
3934d727e4 | |
|
|
3144264a9d | |
|
|
a52f378787 | |
|
|
1bcb536a44 | |
|
|
3f5ce51502 | |
|
|
5c899e304f | |
|
|
5a153d76ea | |
|
|
531b28771c | |
|
|
2546c0746d | |
|
|
68e52903ae | |
|
|
e643f8f881 | |
|
|
a0bbcd6259 | |
|
|
c563e2d302 | |
|
|
4a5515666e | |
|
|
b5e9cf85df | |
|
|
6c7fd52b8e | |
|
|
c462cf62e4 | |
|
|
86e2573c39 | |
|
|
f8ce3a932c | |
|
|
666b7d4df0 | |
|
|
9d1ca7cbf5 | |
|
|
9e70ffeaad | |
|
|
e7b346ecf2 | |
|
|
c0c3caead5 | |
|
|
8f274c4e18 | |
|
|
f056f49bd1 | |
|
|
d91750f5ae | |
|
|
54839c59e4 | |
|
|
728fe7625b | |
|
|
9e8ea8eee4 | |
|
|
a51b631d26 | |
|
|
45d04be1c9 | |
|
|
68eacd2036 | |
|
|
258e61f97c | |
|
|
3a956b4097 | |
|
|
bb652cc108 | |
|
|
d45de75129 | |
|
|
061c1f0f85 | |
|
|
54b220348f | |
|
|
960d9aaaa5 | |
|
|
c501ae375e | |
|
|
4918e6d8ff | |
|
|
946ec0ab25 | |
|
|
b1ad5c2e30 | |
|
|
4c7325f912 | |
|
|
48d3c7d97c | |
|
|
321898eb54 | |
|
|
db8e508530 | |
|
|
3cfefd121c | |
|
|
d7328af221 | |
|
|
4d5ed99ff5 | |
|
|
5d3df09146 | |
|
|
557cd19a1d | |
|
|
6c9fd35bfa | |
|
|
3a3c03a4df | |
|
|
3dce208237 | |
|
|
5cb14d4d4b | |
|
|
a7b566e7b3 | |
|
|
acc06cdd39 | |
|
|
e2fb1fba8b | |
|
|
aed06c5f48 | |
|
|
59fd7876a1 | |
|
|
f91dc2d52d | |
|
|
f4260a6e55 | |
|
|
a8a5068e62 | |
|
|
cec00266d0 | |
|
|
bb462c2602 | |
|
|
f524926216 | |
|
|
629a38d654 | |
|
|
e15d266309 | |
|
|
f3d280ba53 | |
|
|
e158a480f5 | |
|
|
69ac04d389 | |
|
|
df75956cf9 | |
|
|
a1d89a83b0 | |
|
|
06501886eb | |
|
|
e6a0446a92 | |
|
|
40adc6696b | |
|
|
a397aa069e | |
|
|
0144a314ad | |
|
|
ddac8a16bf | |
|
|
17792f87e8 | |
|
|
2ba07fe490 | |
|
|
3608b2b3be | |
|
|
4b7cc881ec | |
|
|
77c880544f | |
|
|
66759c7172 | |
|
|
427fb47e0c | |
|
|
1888944a69 | |
|
|
590a602e57 | |
|
|
664c554bf6 | |
|
|
6d9563035e | |
|
|
a085e3caa0 | |
|
|
4dd36aca04 | |
|
|
07bd9a691a | |
|
|
c03d3dd6ac | |
|
|
af884a02c8 | |
|
|
a04126b22b | |
|
|
f382a1a18f | |
|
|
c075f6650e | |
|
|
57465a6768 | |
|
|
49e67c4dc9 | |
|
|
b495e78b8a | |
|
|
1127c0de19 | |
|
|
7535207fd3 | |
|
|
e55624703b | |
|
|
fbd148f983 | |
|
|
643bdf7305 | |
|
|
4c3337f553 | |
|
|
f891dd3137 | |
|
|
28890f6fbb | |
|
|
f551bbbb76 | |
|
|
0192b58a6a | |
|
|
4fab7b4357 | |
|
|
5ad68024b2 | |
|
|
8064e3ea6f | |
|
|
a138ab8978 | |
|
|
d49675eeed | |
|
|
ad29c973f8 | |
|
|
dc5662b141 | |
|
|
58dec07d27 | |
|
|
85435c7574 | |
|
|
5691e04afc | |
|
|
9fc6ea0434 | |
|
|
2aacfe1a8c | |
|
|
af835c26ad | |
|
|
ffe7d00a62 | |
|
|
cc3c1e2050 | |
|
|
ff5d4d3564 | |
|
|
f175f611f8 | |
|
|
ab7d04a951 | |
|
|
2ab963cef8 | |
|
|
7083fee655 | |
|
|
cc18a624b5 | |
|
|
0fd6fc19f2 | |
|
|
088b5973eb | |
|
|
eb8121ffd5 | |
|
|
97104b2000 | |
|
|
c32b8090ca | |
|
|
8cf9cc9f1b | |
|
|
26b4ec6cef | |
|
|
463bde5c8e | |
|
|
61746e0f58 | |
|
|
c598cbbc71 | |
|
|
f895c022cb | |
|
|
80ed616230 | |
|
|
de5fbf711d | |
|
|
6bf159581e | |
|
|
b976a31ffa | |
|
|
27e446d26d | |
|
|
4d069b8fc3 | |
|
|
fe868901ab | |
|
|
2ad1a579b1 | |
|
|
71039e0d53 | |
|
|
bcff1e90ea | |
|
|
eb67b20417 | |
|
|
5337882657 | |
|
|
3288ec8598 | |
|
|
bd3774dfc8 | |
|
|
02cee732fc | |
|
|
6093306ff5 | |
|
|
0ab1e3b757 | |
|
|
dfd4fffaac | |
|
|
50bf3bdd52 | |
|
|
7475d385ac | |
|
|
4d2aa13db3 | |
|
|
775db2caef | |
|
|
cc73134ead | |
|
|
eacdca5a36 | |
|
|
94dce5f1d4 | |
|
|
7d6ea4b58b | |
|
|
5a8964dc48 | |
|
|
28480f394b | |
|
|
5057c9a2ca | |
|
|
431b2c1633 | |
|
|
52a8576d7c | |
|
|
dea648ccd8 | |
|
|
3a48111e53 | |
|
|
d24a4c5ab0 | |
|
|
fc9fe75033 | |
|
|
cff456f232 | |
|
|
faf692657c |
|
|
@ -1,19 +0,0 @@
|
||||||
[bumpversion]
|
|
||||||
current_version = 3.3.0
|
|
||||||
commit = True
|
|
||||||
message = Release v{new_version}
|
|
||||||
tag = True
|
|
||||||
sign_tags = True
|
|
||||||
tag_name = v{new_version}
|
|
||||||
|
|
||||||
[bumpversion:file:qutebrowser/__init__.py]
|
|
||||||
parse = __version__ = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
|
||||||
|
|
||||||
[bumpversion:file:misc/org.qutebrowser.qutebrowser.appdata.xml]
|
|
||||||
search = <!-- Add new releases here -->
|
|
||||||
replace = <!-- Add new releases here -->
|
|
||||||
<release version="{new_version}" date="{now:%Y-%m-%d}"/>
|
|
||||||
|
|
||||||
[bumpversion:file:doc/changelog.asciidoc]
|
|
||||||
search = (unreleased)
|
|
||||||
replace = ({now:%Y-%m-%d})
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
[tool.bumpversion]
|
||||||
|
current_version = "3.6.3"
|
||||||
|
commit = true
|
||||||
|
message = "Release v{new_version}"
|
||||||
|
tag = true
|
||||||
|
sign_tags = true
|
||||||
|
tag_name = "v{new_version}"
|
||||||
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
||||||
|
serialize = ["{major}.{minor}.{patch}"]
|
||||||
|
allow_dirty = false
|
||||||
|
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "qutebrowser/__init__.py"
|
||||||
|
search = "__version__ = \"{current_version}\""
|
||||||
|
replace = "__version__ = \"{new_version}\""
|
||||||
|
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "misc/org.qutebrowser.qutebrowser.appdata.xml"
|
||||||
|
search = "<!-- Add new releases here -->"
|
||||||
|
replace = """<!-- Add new releases here -->
|
||||||
|
<release version='{new_version}' date='{now:%Y-%m-%d}'/>"""
|
||||||
|
|
||||||
|
[[tool.bumpversion.files]]
|
||||||
|
filename = "doc/changelog.asciidoc"
|
||||||
|
search = "(unreleased)"
|
||||||
|
replace = "({now:%Y-%m-%d})"
|
||||||
|
|
@ -4,6 +4,7 @@ include =
|
||||||
tests/*
|
tests/*
|
||||||
scripts/*
|
scripts/*
|
||||||
branch = true
|
branch = true
|
||||||
|
patch = subprocess
|
||||||
omit =
|
omit =
|
||||||
qutebrowser/__main__.py
|
qutebrowser/__main__.py
|
||||||
*/__init__.py
|
*/__init__.py
|
||||||
|
|
|
||||||
5
.flake8
5
.flake8
|
|
@ -42,6 +42,7 @@ exclude = .*,__pycache__,resources.py
|
||||||
# W503: like break before binary operator
|
# W503: like break before binary operator
|
||||||
# W504: line break after binary operator
|
# W504: line break after binary operator
|
||||||
# FI18: __future__ import "annotations" missing
|
# FI18: __future__ import "annotations" missing
|
||||||
|
# FI58: __future__ import "annotations" present
|
||||||
# PT004: fixture '{name}' does not return anything, add leading underscore
|
# PT004: fixture '{name}' does not return anything, add leading underscore
|
||||||
# PT011: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception
|
# PT011: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception
|
||||||
# PT012: pytest.raises() block should contain a single simple statement
|
# PT012: pytest.raises() block should contain a single simple statement
|
||||||
|
|
@ -54,11 +55,11 @@ ignore =
|
||||||
D102,D103,D106,D107,D104,D105,D209,D211,D401,D402,D403,D412,D413,
|
D102,D103,D106,D107,D104,D105,D209,D211,D401,D402,D403,D412,D413,
|
||||||
A003,
|
A003,
|
||||||
W503, W504,
|
W503, W504,
|
||||||
FI18,
|
FI18,FI58,
|
||||||
PT004,
|
PT004,
|
||||||
PT011,
|
PT011,
|
||||||
PT012
|
PT012
|
||||||
min-version = 3.8.0
|
min-version = 3.9.0
|
||||||
max-complexity = 12
|
max-complexity = 12
|
||||||
per-file-ignores =
|
per-file-ignores =
|
||||||
qutebrowser/api/hook.py : N801
|
qutebrowser/api/hook.py : N801
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,13 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
if: "github.repository == 'qutebrowser/qutebrowser'"
|
if: "github.repository == 'qutebrowser/qutebrowser'"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- testenv: bleeding
|
- testenv: bleeding
|
||||||
image: "archlinux-webengine-unstable-qt6"
|
|
||||||
- testenv: bleeding-qt5
|
|
||||||
image: "archlinux-webengine-unstable"
|
image: "archlinux-webengine-unstable"
|
||||||
container:
|
container:
|
||||||
image: "qutebrowser/ci:${{ matrix.image }}"
|
image: "qutebrowser/ci:${{ matrix.image }}"
|
||||||
|
|
@ -33,14 +31,13 @@ jobs:
|
||||||
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
||||||
options: --privileged --tty
|
options: --privileged --tty
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up problem matchers
|
- name: Set up problem matchers
|
||||||
run: "python scripts/dev/ci/problemmatchers.py py3 ${{ runner.temp }}"
|
run: "python scripts/dev/ci/problemmatchers.py py3 ${{ runner.temp }}"
|
||||||
- name: Upgrade 3rd party assets
|
- name: Upgrade 3rd party assets
|
||||||
run: "tox exec -e ${{ matrix.testenv }} -- python scripts/dev/update_3rdparty.py --gh-token ${{ secrets.GITHUB_TOKEN }}"
|
run: "tox exec -e ${{ matrix.testenv }} -- python scripts/dev/update_3rdparty.py --gh-token ${{ secrets.GITHUB_TOKEN }} --modern-pdfjs"
|
||||||
if: "endsWith(matrix.image, '-qt6')"
|
|
||||||
- name: Run tox
|
- name: Run tox
|
||||||
run: dbus-run-session tox -e ${{ matrix.testenv }}
|
run: dbus-run-session tox -e ${{ matrix.testenv }}
|
||||||
- name: Gather info
|
- name: Gather info
|
||||||
|
|
@ -51,7 +48,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
if: failure()
|
if: failure()
|
||||||
- name: Upload screenshots
|
- name: Upload screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -61,7 +58,7 @@ jobs:
|
||||||
irc:
|
irc:
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
needs: [tests]
|
needs: [tests]
|
||||||
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ jobs:
|
||||||
linters:
|
linters:
|
||||||
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
|
@ -27,7 +27,6 @@ jobs:
|
||||||
- testenv: vulture
|
- testenv: vulture
|
||||||
- testenv: misc
|
- testenv: misc
|
||||||
- testenv: pyroma
|
- testenv: pyroma
|
||||||
- testenv: check-manifest
|
|
||||||
- testenv: eslint
|
- testenv: eslint
|
||||||
- testenv: shellcheck
|
- testenv: shellcheck
|
||||||
args: "-f gcc" # For problem matchers
|
args: "-f gcc" # For problem matchers
|
||||||
|
|
@ -35,30 +34,30 @@ jobs:
|
||||||
- testenv: actionlint
|
- testenv: actionlint
|
||||||
- testenv: package
|
- testenv: package
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
.tox
|
.tox
|
||||||
~/.cache/pip
|
~/.cache/pip
|
||||||
key: "${{ matrix.testenv }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('scripts/dev/pylint_checkers/qute_pylint/*.py') }}"
|
key: "${{ matrix.testenv }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('scripts/dev/pylint_checkers/qute_pylint/*.py') }}"
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.10'
|
python-version: '3.10'
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: '16.x'
|
node-version: '22.x'
|
||||||
if: "matrix.testenv == 'eslint'"
|
if: "matrix.testenv == 'eslint'"
|
||||||
- name: Set up problem matchers
|
- name: Set up problem matchers
|
||||||
run: "python scripts/dev/ci/problemmatchers.py ${{ matrix.testenv }} ${{ runner.temp }}"
|
run: "python scripts/dev/ci/problemmatchers.py ${{ matrix.testenv }} ${{ runner.temp }}"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
[[ ${{ matrix.testenv }} == eslint ]] && npm install -g 'eslint@<9.0.0'
|
[[ ${{ matrix.testenv }} == eslint ]] && npm install -g 'eslint@<9.0.0'
|
||||||
[[ ${{ matrix.testenv }} == docs ]] && sudo apt-get update && sudo apt-get install --no-install-recommends asciidoc libegl1-mesa
|
[[ ${{ matrix.testenv }} == docs ]] && sudo apt-get update && sudo apt-get install --no-install-recommends asciidoc libegl1
|
||||||
[[ ${{ matrix.testenv }} == vulture || ${{ matrix.testenv }} == pylint ]] && sudo apt-get update && sudo apt-get install --no-install-recommends libegl1-mesa
|
[[ ${{ matrix.testenv }} == vulture || ${{ matrix.testenv }} == pylint ]] && sudo apt-get update && sudo apt-get install --no-install-recommends libegl1
|
||||||
if [[ ${{ matrix.testenv }} == shellcheck ]]; then
|
if [[ ${{ matrix.testenv }} == shellcheck ]]; then
|
||||||
scversion="stable"
|
scversion="stable"
|
||||||
bindir="$HOME/.local/bin"
|
bindir="$HOME/.local/bin"
|
||||||
|
|
@ -86,21 +85,15 @@ jobs:
|
||||||
tests-docker:
|
tests-docker:
|
||||||
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
if: "!contains(github.event.head_commit.message, '[ci skip]')"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04 # not 24.04 because sandboxing fails by default (#8424)
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- testenv: py-qt5
|
- testenv: py
|
||||||
image: archlinux-webkit
|
|
||||||
- testenv: py-qt5
|
|
||||||
image: archlinux-webengine
|
image: archlinux-webengine
|
||||||
- testenv: py-qt5
|
- testenv: py
|
||||||
image: archlinux-webengine-unstable
|
image: archlinux-webengine-unstable
|
||||||
- testenv: py
|
|
||||||
image: archlinux-webengine-qt6
|
|
||||||
- testenv: py
|
|
||||||
image: archlinux-webengine-unstable-qt6
|
|
||||||
container:
|
container:
|
||||||
image: "qutebrowser/ci:${{ matrix.image }}"
|
image: "qutebrowser/ci:${{ matrix.image }}"
|
||||||
env:
|
env:
|
||||||
|
|
@ -113,7 +106,7 @@ jobs:
|
||||||
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
- /home/runner/work/_temp/:/home/runner/work/_temp/
|
||||||
options: --privileged --tty
|
options: --privileged --tty
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up problem matchers
|
- name: Set up problem matchers
|
||||||
|
|
@ -128,7 +121,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
if: failure()
|
if: failure()
|
||||||
- name: Upload screenshots
|
- name: Upload screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.image }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -144,10 +137,10 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
### PyQt 5.15.2 (Python 3.8)
|
### PyQt 5.15.2 (Python 3.9)
|
||||||
- testenv: py38-pyqt5152
|
- testenv: py39-pyqt5152
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
python: "3.8"
|
python: "3.9"
|
||||||
### PyQt 5.15 (Python 3.10, with coverage)
|
### PyQt 5.15 (Python 3.10, with coverage)
|
||||||
# FIXME:qt6
|
# FIXME:qt6
|
||||||
# - testenv: py310-pyqt515-cov
|
# - testenv: py310-pyqt515-cov
|
||||||
|
|
@ -155,19 +148,19 @@ jobs:
|
||||||
# python: "3.10"
|
# python: "3.10"
|
||||||
### PyQt 5.15 (Python 3.11)
|
### PyQt 5.15 (Python 3.11)
|
||||||
- testenv: py311-pyqt515
|
- testenv: py311-pyqt515
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
python: "3.11"
|
python: "3.11"
|
||||||
### PyQt 6.2 (Python 3.8)
|
### PyQt 6.2 (Python 3.9)
|
||||||
- testenv: py38-pyqt62
|
- testenv: py39-pyqt62
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
python: "3.8"
|
python: "3.9"
|
||||||
### PyQt 6.3 (Python 3.8)
|
### PyQt 6.3 (Python 3.9)
|
||||||
- testenv: py38-pyqt63
|
- testenv: py39-pyqt63
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
python: "3.8"
|
python: "3.9"
|
||||||
## PyQt 6.4 (Python 3.9)
|
## PyQt 6.4 (Python 3.9)
|
||||||
- testenv: py39-pyqt64
|
- testenv: py39-pyqt64
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
python: "3.9"
|
python: "3.9"
|
||||||
### PyQt 6.5 (Python 3.10)
|
### PyQt 6.5 (Python 3.10)
|
||||||
- testenv: py310-pyqt65
|
- testenv: py310-pyqt65
|
||||||
|
|
@ -189,31 +182,45 @@ jobs:
|
||||||
- testenv: py312-pyqt67
|
- testenv: py312-pyqt67
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
python: "3.12"
|
python: "3.12"
|
||||||
### macOS Monterey
|
### PyQt 6.8 (Python 3.13)
|
||||||
- testenv: py312-pyqt67
|
- testenv: py313-pyqt68
|
||||||
os: macos-12
|
os: ubuntu-24.04
|
||||||
python: "3.12"
|
python: "3.13"
|
||||||
args: "tests/unit" # Only run unit tests on macOS
|
### PyQt 6.8 (Python 3.13)
|
||||||
### macOS Ventura
|
- testenv: py313-pyqt68
|
||||||
- testenv: py312-pyqt67
|
os: ubuntu-24.04
|
||||||
os: macos-13
|
python: "3.13"
|
||||||
python: "3.12"
|
### PyQt 6.9 (Python 3.14)
|
||||||
args: "tests/unit" # Only run unit tests on macOS
|
- testenv: py314-pyqt69
|
||||||
|
os: ubuntu-24.04
|
||||||
|
python: "3.14"
|
||||||
|
### PyQt 6.10 (Python 3.14)
|
||||||
|
- testenv: py314-pyqt610
|
||||||
|
os: ubuntu-24.04
|
||||||
|
python: "3.14"
|
||||||
### macOS Sonoma (M1 runner)
|
### macOS Sonoma (M1 runner)
|
||||||
- testenv: py312-pyqt67
|
- testenv: py314-pyqt610
|
||||||
os: macos-14
|
os: macos-14
|
||||||
python: "3.12"
|
python: "3.14"
|
||||||
|
args: "tests/unit" # Only run unit tests on macOS
|
||||||
|
### macOS Sequoia (Intel runner)
|
||||||
|
- testenv: py314-pyqt610
|
||||||
|
os: macos-15-intel
|
||||||
|
python: "3.14"
|
||||||
args: "tests/unit" # Only run unit tests on macOS
|
args: "tests/unit" # Only run unit tests on macOS
|
||||||
### Windows
|
### Windows
|
||||||
- testenv: py312-pyqt67
|
- testenv: py314-pyqt610
|
||||||
os: windows-2019
|
os: windows-2022
|
||||||
python: "3.12"
|
python: "3.14"
|
||||||
|
- testenv: py314-pyqt610
|
||||||
|
os: windows-2025
|
||||||
|
python: "3.14"
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
.mypy_cache
|
.mypy_cache
|
||||||
|
|
@ -221,7 +228,7 @@ jobs:
|
||||||
~/.cache/pip
|
~/.cache/pip
|
||||||
key: "${{ matrix.testenv }}-${{ matrix.os }}-${{ matrix.python }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}"
|
key: "${{ matrix.testenv }}-${{ matrix.os }}-${{ matrix.python }}-${{ hashFiles('misc/requirements/requirements-*.txt') }}-${{ hashFiles('requirements.txt') }}"
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: "${{ matrix.python }}"
|
python-version: "${{ matrix.python }}"
|
||||||
- name: Set up problem matchers
|
- name: Set up problem matchers
|
||||||
|
|
@ -229,7 +236,7 @@ jobs:
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends libyaml-dev libegl1-mesa libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0
|
sudo apt-get install --no-install-recommends libyaml-dev libegl1 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 libjpeg-dev
|
||||||
if: "startsWith(matrix.os, 'ubuntu-')"
|
if: "startsWith(matrix.os, 'ubuntu-')"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -251,7 +258,7 @@ jobs:
|
||||||
if: "failure()"
|
if: "failure()"
|
||||||
- name: Upload coverage
|
- name: Upload coverage
|
||||||
if: "endsWith(matrix.testenv, '-cov')"
|
if: "endsWith(matrix.testenv, '-cov')"
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
name: "${{ matrix.testenv }}"
|
name: "${{ matrix.testenv }}"
|
||||||
- name: Gather info
|
- name: Gather info
|
||||||
|
|
@ -262,7 +269,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
if: failure()
|
if: failure()
|
||||||
- name: Upload screenshots
|
- name: Upload screenshots
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.testenv }}-${{ matrix.os }}"
|
name: "end2end-screenshots-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.testenv }}-${{ matrix.os }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -275,24 +282,24 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
security-events: write
|
security-events: write
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v3
|
uses: github/codeql-action/init@v4
|
||||||
with:
|
with:
|
||||||
languages: javascript, python
|
languages: javascript, python
|
||||||
queries: +security-extended
|
queries: +security-extended
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v4
|
||||||
|
|
||||||
irc:
|
irc:
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
needs: [linters, tests, tests-docker, codeql]
|
needs: [linters, tests, tests-docker, codeql]
|
||||||
if: "always() && github.repository_owner == 'qutebrowser'"
|
if: "always() && github.repository_owner == 'qutebrowser'"
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,16 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
docker:
|
docker:
|
||||||
if: "github.repository == 'qutebrowser/qutebrowser'"
|
if: "github.repository == 'qutebrowser/qutebrowser'"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image:
|
image:
|
||||||
- archlinux-webkit
|
|
||||||
- archlinux-webengine
|
- archlinux-webengine
|
||||||
- archlinux-webengine-unstable
|
- archlinux-webengine-unstable
|
||||||
- archlinux-webengine-unstable-qt6
|
|
||||||
- archlinux-webengine-qt6
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- run: pip install jinja2
|
- run: pip install jinja2
|
||||||
|
|
@ -42,7 +39,7 @@ jobs:
|
||||||
irc:
|
irc:
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
needs: [docker]
|
needs: [docker]
|
||||||
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -14,50 +14,45 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: macos-12
|
- os: macos-15-intel
|
||||||
toxenv: build-release-qt5
|
|
||||||
name: qt5-macos
|
|
||||||
- os: windows-2019
|
|
||||||
toxenv: build-release-qt5
|
|
||||||
name: qt5-windows
|
|
||||||
- os: macos-12
|
|
||||||
args: --debug
|
|
||||||
toxenv: build-release-qt5
|
|
||||||
name: qt5-macos-debug
|
|
||||||
- os: windows-2019
|
|
||||||
args: --debug
|
|
||||||
toxenv: build-release-qt5
|
|
||||||
name: qt5-windows-debug
|
|
||||||
- os: macos-12
|
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos-intel
|
name: macos-intel
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos-apple-silicon
|
name: macos-apple-silicon
|
||||||
- os: windows-2019
|
- os: windows-latest
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: windows
|
name: windows
|
||||||
- os: macos-12
|
- os: macos-15-intel
|
||||||
args: --debug
|
args: --debug
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos-debug-intel
|
name: macos-debug-intel
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: macos-debug-apple-silicon
|
name: macos-debug-apple-silicon
|
||||||
- os: windows-2019
|
- os: windows-latest
|
||||||
args: --debug
|
args: --debug
|
||||||
toxenv: build-release
|
toxenv: build-release
|
||||||
name: windows-debug
|
name: windows-debug
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.13"
|
||||||
|
- name: Install nsis
|
||||||
|
if: "matrix.os == 'windows-latest'"
|
||||||
|
run: |
|
||||||
|
irm get.scoop.sh | iex
|
||||||
|
scoop update
|
||||||
|
scoop bucket add extras
|
||||||
|
scoop install nsis
|
||||||
|
Add-Content $env:GITHUB_PATH "C:\Users\runneradmin\scoop\shims"
|
||||||
|
shell: pwsh
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install -U pip
|
python -m pip install -U pip
|
||||||
|
|
@ -77,7 +72,7 @@ jobs:
|
||||||
echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: "qutebrowser-nightly-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.name }}"
|
name: "qutebrowser-nightly-${{ steps.info.outputs.date }}-${{ steps.info.outputs.sha_short }}-${{ matrix.name }}"
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -89,7 +84,7 @@ jobs:
|
||||||
irc:
|
irc:
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
needs: [pyinstaller]
|
needs: [pyinstaller]
|
||||||
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
if: "always() && github.repository == 'qutebrowser/qutebrowser'"
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -18,20 +18,20 @@ jobs:
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Set up Python 3.8
|
- name: Set up Python 3.9
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: '3.8'
|
python-version: '3.9'
|
||||||
- name: Recompile requirements
|
- name: Recompile requirements
|
||||||
run: "python3 scripts/dev/recompile_requirements.py ${{ github.event.input.environments }}"
|
run: "python3 scripts/dev/recompile_requirements.py ${{ github.event.input.environments }}"
|
||||||
id: requirements
|
id: requirements
|
||||||
- name: Install apt dependencies
|
- name: Install apt dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends libyaml-dev libegl1-mesa libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 asciidoc python3-venv xvfb
|
sudo apt-get install --no-install-recommends libyaml-dev libegl1 libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-shape0 libxcb-cursor0 asciidoc python3-venv xvfb
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install -U pip
|
python -m pip install -U pip
|
||||||
|
|
@ -41,7 +41,7 @@ jobs:
|
||||||
- name: Run qutebrowser smoke test
|
- name: Run qutebrowser smoke test
|
||||||
run: "xvfb-run .venv/bin/python3 -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ':later 500 quit'"
|
run: "xvfb-run .venv/bin/python3 -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ':later 500 quit'"
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@v6
|
uses: peter-evans/create-pull-request@v8
|
||||||
with:
|
with:
|
||||||
committer: qutebrowser bot <bot@qutebrowser.org>
|
committer: qutebrowser bot <bot@qutebrowser.org>
|
||||||
author: qutebrowser bot <bot@qutebrowser.org>
|
author: qutebrowser bot <bot@qutebrowser.org>
|
||||||
|
|
|
||||||
|
|
@ -12,30 +12,33 @@ on:
|
||||||
- 'patch'
|
- 'patch'
|
||||||
- 'minor'
|
- 'minor'
|
||||||
- 'major'
|
- 'major'
|
||||||
|
- 'reupload' # reupload last release
|
||||||
# FIXME do we want a possibility to do prereleases here?
|
# FIXME do we want a possibility to do prereleases here?
|
||||||
python_version:
|
python_version:
|
||||||
description: 'Python version'
|
description: 'Python version'
|
||||||
required: true
|
required: true
|
||||||
default: '3.12'
|
default: '3.14'
|
||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- '3.8'
|
|
||||||
- '3.9'
|
- '3.9'
|
||||||
- '3.10'
|
- '3.10'
|
||||||
- '3.11'
|
- '3.11'
|
||||||
- '3.12'
|
- '3.12'
|
||||||
|
- '3.13'
|
||||||
|
- '3.14'
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.bump.outputs.version }}
|
version: ${{ steps.bump.outputs.version }}
|
||||||
release_id: ${{ steps.create-release.outputs.id }}
|
version_x: ${{ steps.bump.outputs.version_x }}
|
||||||
|
release_id: ${{ inputs.release_type == 'reupload' && steps.find-release.outputs.result || steps.create-release.outputs.id }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # To push release commit/tag
|
contents: write # To push release commit/tag
|
||||||
steps:
|
steps:
|
||||||
- name: Find release branch
|
- name: Find release branch
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
id: find-branch
|
id: find-branch
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
|
|
@ -59,9 +62,9 @@ jobs:
|
||||||
console.log(`sorted: ${sorted}`);
|
console.log(`sorted: ${sorted}`);
|
||||||
return sorted.at(-1);
|
return sorted.at(-1);
|
||||||
result-encoding: string
|
result-encoding: string
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
# Doesn't really matter what we prepare the release with, but let's
|
# Doesn't really matter what we prepare the release with, but let's
|
||||||
# use the same version for consistency.
|
# use the same version for consistency.
|
||||||
|
|
@ -75,7 +78,7 @@ jobs:
|
||||||
git config --global user.name "qutebrowser bot"
|
git config --global user.name "qutebrowser bot"
|
||||||
git config --global user.email "bot@qutebrowser.org"
|
git config --global user.email "bot@qutebrowser.org"
|
||||||
- name: Switch to release branch
|
- name: Switch to release branch
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ steps.find-branch.outputs.result }}
|
ref: ${{ steps.find-branch.outputs.result }}
|
||||||
- name: Import GPG Key
|
- name: Import GPG Key
|
||||||
|
|
@ -83,9 +86,9 @@ jobs:
|
||||||
gpg --import <<< "${{ secrets.QUTEBROWSER_BOT_GPGKEY }}"
|
gpg --import <<< "${{ secrets.QUTEBROWSER_BOT_GPGKEY }}"
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
id: bump
|
id: bump
|
||||||
run: "tox -e update-version -- ${{ github.event.inputs.release_type }}"
|
run: "tox -e update-version -- ${{ inputs.release_type }}"
|
||||||
- name: Check milestone
|
- name: Check milestone
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const milestones = await github.paginate(github.rest.issues.listMilestones, {
|
const milestones = await github.paginate(github.rest.issues.listMilestones, {
|
||||||
|
|
@ -100,49 +103,74 @@ jobs:
|
||||||
core.setFailed(`Found open milestone ${milestone.title} with ${milestone.open_issues} open and ${milestone.closed_issues} closed issues!`);
|
core.setFailed(`Found open milestone ${milestone.title} with ${milestone.open_issues} open and ${milestone.closed_issues} closed issues!`);
|
||||||
}
|
}
|
||||||
- name: Push release commit/tag
|
- name: Push release commit/tag
|
||||||
|
if: ${{ inputs.release_type != 'reupload' }}
|
||||||
run: |
|
run: |
|
||||||
git push origin ${{ steps.find-branch.outputs.result }}
|
git push origin ${{ steps.find-branch.outputs.result }}
|
||||||
git push origin v${{ steps.bump.outputs.version }}
|
git push origin v${{ steps.bump.outputs.version }}
|
||||||
- name: Cherry-pick release commit
|
- name: Cherry-pick release commit
|
||||||
if: ${{ github.event.inputs.release_type == 'patch' }}
|
if: ${{ inputs.release_type == 'patch' }}
|
||||||
run: |
|
run: |
|
||||||
|
git fetch origin main
|
||||||
git checkout main
|
git checkout main
|
||||||
git cherry-pick -x v${{ steps.bump.outputs.version }}
|
git cherry-pick -x v${{ steps.bump.outputs.version }}
|
||||||
git push origin main
|
git push origin main
|
||||||
git checkout v${{ steps.bump.outputs.version_x }}
|
git checkout v${{ steps.bump.outputs.version_x }}
|
||||||
- name: Create release branch
|
- name: Create release branch
|
||||||
if: ${{ github.event.inputs.release_type != 'patch' }}
|
if: ${{ inputs.release_type == 'minor' || inputs.release_type == 'major' }}
|
||||||
run: |
|
run: |
|
||||||
git checkout -b v${{ steps.bump.outputs.version_x }}
|
git checkout -b v${{ steps.bump.outputs.version_x }}
|
||||||
git push --set-upstream origin v${{ steps.bump.outputs.version_x }}
|
git push --set-upstream origin v${{ steps.bump.outputs.version_x }}
|
||||||
- name: Create GitHub draft release
|
- name: Create GitHub draft release
|
||||||
|
if: ${{ inputs.release_type != 'reupload' }}
|
||||||
id: create-release
|
id: create-release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: v${{ steps.bump.outputs.version }}
|
tag_name: v${{ steps.bump.outputs.version }}
|
||||||
draft: true
|
draft: true
|
||||||
body: "*Release artifacts for this release are currently being uploaded...*"
|
body: "*Release artifacts for this release are currently being uploaded...*"
|
||||||
|
- name: Find GitHub draft release
|
||||||
|
if: ${{ inputs.release_type == 'reupload' }}
|
||||||
|
id: find-release
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const releases = await github.paginate(github.rest.repos.listReleases, {
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
});
|
||||||
|
const names = releases.map(release => release.name);
|
||||||
|
console.log(`releases: ${names}`);
|
||||||
|
|
||||||
|
const release = releases.find(release => release.tag_name === "v${{ steps.bump.outputs.version }}");
|
||||||
|
if (release === undefined) {
|
||||||
|
core.setFailed(`No release found with tag v${{ steps.bump.outputs.version }}!`);
|
||||||
|
}
|
||||||
|
if (!release.draft) {
|
||||||
|
core.setFailed(`Release ${release.tag_name} is not a draft release!`);
|
||||||
|
}
|
||||||
|
return release.id;
|
||||||
|
result-encoding: string
|
||||||
release:
|
release:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: macos-12
|
- os: macos-14-large # Intel
|
||||||
- os: macos-14
|
- os: macos-14 # Apple Silicon
|
||||||
- os: windows-2019
|
- os: windows-2022
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-24.04
|
||||||
runs-on: "${{ matrix.os }}"
|
runs-on: "${{ matrix.os }}"
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
needs: [prepare]
|
needs: [prepare]
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # To upload release artifacts
|
contents: write # To upload release artifacts
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: v${{ needs.prepare.outputs.version }}
|
ref: v${{ inputs.release_type == 'reupload' && needs.prepare.outputs.version_x || needs.prepare.outputs.version }}
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: ${{ github.event.inputs.python_version }}
|
python-version: ${{ inputs.python_version }}
|
||||||
- name: Import GPG Key
|
- name: Import GPG Key
|
||||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -159,7 +187,7 @@ jobs:
|
||||||
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
if: ${{ startsWith(matrix.os, 'ubuntu-') }}
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install --no-install-recommends libegl1-mesa libxml2-utils docbook-xml xsltproc docbook-xsl
|
sudo apt-get install --no-install-recommends libegl1 libxml2-utils docbook-xml xsltproc docbook-xsl
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install -U pip
|
python -m pip install -U pip
|
||||||
|
|
@ -167,20 +195,20 @@ jobs:
|
||||||
# FIXME consider switching to trusted publishers:
|
# FIXME consider switching to trusted publishers:
|
||||||
# https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
|
# https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
|
||||||
- name: Build and upload release
|
- name: Build and upload release
|
||||||
run: "tox -e build-release -- --upload --no-confirm"
|
run: "tox -e build-release -- --upload --no-confirm ${{ inputs.release_type == 'reupload' && '--reupload' || '' }}"
|
||||||
env:
|
env:
|
||||||
TWINE_USERNAME: __token__
|
TWINE_USERNAME: __token__
|
||||||
TWINE_PASSWORD: ${{ secrets.QUTEBROWSER_BOT_PYPI_TOKEN }}
|
TWINE_PASSWORD: ${{ secrets.QUTEBROWSER_BOT_PYPI_TOKEN }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
finalize:
|
finalize:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
timeout-minutes: 5
|
timeout-minutes: 5
|
||||||
needs: [prepare, release]
|
needs: [prepare, release]
|
||||||
permissions:
|
permissions:
|
||||||
contents: write # To change release
|
contents: write # To change release
|
||||||
steps:
|
steps:
|
||||||
- name: Publish final release
|
- name: Publish final release
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
await github.rest.repos.updateRelease({
|
await github.rest.repos.updateRelease({
|
||||||
|
|
@ -193,7 +221,7 @@ jobs:
|
||||||
irc:
|
irc:
|
||||||
timeout-minutes: 2
|
timeout-minutes: 2
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
needs: [prepare, release, finalize]
|
needs: [prepare, release, finalize]
|
||||||
if: "${{ always() }}"
|
if: "${{ always() }}"
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[mypy]
|
[mypy]
|
||||||
python_version = 3.8
|
python_version = 3.9
|
||||||
|
|
||||||
### --strict
|
### --strict
|
||||||
warn_unused_configs = True
|
warn_unused_configs = True
|
||||||
|
|
@ -20,6 +20,7 @@ strict_equality = True
|
||||||
warn_unreachable = True
|
warn_unreachable = True
|
||||||
disallow_any_unimported = True
|
disallow_any_unimported = True
|
||||||
enable_error_code = ignore-without-code
|
enable_error_code = ignore-without-code
|
||||||
|
strict_bytes = True
|
||||||
|
|
||||||
### Output
|
### Output
|
||||||
show_error_context = True
|
show_error_context = True
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ load-plugins=qute_pylint.config,
|
||||||
pylint.extensions.dunder
|
pylint.extensions.dunder
|
||||||
|
|
||||||
persistent=n
|
persistent=n
|
||||||
py-version=3.8
|
py-version=3.9
|
||||||
|
|
||||||
[MESSAGES CONTROL]
|
[MESSAGES CONTROL]
|
||||||
enable=all
|
enable=all
|
||||||
|
|
@ -71,7 +71,8 @@ argument-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||||
docstring-min-length=3
|
docstring-min-length=3
|
||||||
no-docstring-rgx=(^_|^main$)
|
no-docstring-rgx=(^_|^main$)
|
||||||
class-const-naming-style = snake_case
|
class-const-naming-style=snake_case
|
||||||
|
max-positional-arguments=7
|
||||||
|
|
||||||
[FORMAT]
|
[FORMAT]
|
||||||
# FIXME:v4 (lint) down to 88 again once we use black
|
# FIXME:v4 (lint) down to 88 again once we use black
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ image:doc/img/hints.png["screenshot 4",width=300,link="doc/img/hints.png"]
|
||||||
Downloads
|
Downloads
|
||||||
---------
|
---------
|
||||||
|
|
||||||
See the https://github.com/qutebrowser/qutebrowser/releases[github releases
|
See the https://github.com/qutebrowser/qutebrowser/releases[GitHub releases
|
||||||
page] for available downloads and the link:doc/install.asciidoc[INSTALL] file for
|
page] for available downloads and the link:doc/install.asciidoc[INSTALL] file for
|
||||||
detailed instructions on how to get qutebrowser running on various platforms.
|
detailed instructions on how to get qutebrowser running on various platforms.
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ Requirements
|
||||||
|
|
||||||
The following software and libraries are required to run qutebrowser:
|
The following software and libraries are required to run qutebrowser:
|
||||||
|
|
||||||
* https://www.python.org/[Python] 3.8 or newer
|
* https://www.python.org/[Python] 3.9 or newer
|
||||||
* https://www.qt.io/[Qt], either 6.2.0 or newer, or 5.15.0 or newer, with the following modules:
|
* https://www.qt.io/[Qt], either 6.2.0 or newer, or 5.15.0 or newer, with the following modules:
|
||||||
- QtCore / qtbase
|
- QtCore / qtbase
|
||||||
- QtQuick (part of qtbase or qtdeclarative in some distributions)
|
- QtQuick (part of qtbase or qtdeclarative in some distributions)
|
||||||
|
|
@ -105,10 +105,6 @@ websites and using it for transmission of sensitive data._
|
||||||
* https://palletsprojects.com/p/jinja/[jinja2]
|
* https://palletsprojects.com/p/jinja/[jinja2]
|
||||||
* https://github.com/yaml/pyyaml[PyYAML]
|
* https://github.com/yaml/pyyaml[PyYAML]
|
||||||
|
|
||||||
On Python 3.8, the following backport is also required:
|
|
||||||
|
|
||||||
* https://importlib-resources.readthedocs.io/[importlib_resources]
|
|
||||||
|
|
||||||
On macOS, the following libraries are also required:
|
On macOS, the following libraries are also required:
|
||||||
|
|
||||||
* https://pyobjc.readthedocs.io/en/latest/[pyobjc-core and pyobjc-framework-Cocoa]
|
* https://pyobjc.readthedocs.io/en/latest/[pyobjc-core and pyobjc-framework-Cocoa]
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,279 @@ breaking changes (such as renamed commands) can happen in minor releases.
|
||||||
// `Fixed` for any bug fixes.
|
// `Fixed` for any bug fixes.
|
||||||
// `Security` to invite users to upgrade in case of vulnerabilities.
|
// `Security` to invite users to upgrade in case of vulnerabilities.
|
||||||
|
|
||||||
|
[[v3.6.4]]
|
||||||
|
v3.6.4 (unreleased)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- datalist dropdowns not opening correctly on Wayland/Sway (#8831).
|
||||||
|
This was caused by an old workaround for a different QtWebEngine issue,
|
||||||
|
which is now disabled for QtWebEngine 6.6.3 and newer.
|
||||||
|
|
||||||
|
[[v3.6.3]]
|
||||||
|
v3.6.3 (2025-11-30)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- New `qt.workarounds.disable_accessibility` setting, which disables Chromium
|
||||||
|
accessibility support. By default, is it set to `auto`, which only disables
|
||||||
|
accessibility on Qt versions with known issues. This works around a bug in Qt
|
||||||
|
6.10.1 causing frequent segfaults (#8797).
|
||||||
|
|
||||||
|
[[v3.6.2]]
|
||||||
|
v3.6.2 (2025-11-27)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Changed
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
* Windows and macOS releases now ship with Qt 6.10.1, which include
|
||||||
|
security patches up to Chromium 142.0.7444.162.
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- The version info now includes the Wayland compositor name if wayland-client is
|
||||||
|
available under a different name than `libwayland-client.so` (#8771).
|
||||||
|
- The list of Chromium extensions in `--version` / `:version` now uses the
|
||||||
|
correct Chromium data profile, also fixing a crash with Qt 6.10.1 (#8785).
|
||||||
|
- With Qt 6.10.1, `qt.workarounds.disable_hangouts_extension` now doesn't apply
|
||||||
|
on private profiles, avoiding a Qt bug leading to a crash (#8785).
|
||||||
|
|
||||||
|
[[v3.6.1]]
|
||||||
|
v3.6.1 (2025-11-03)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- A regression in v3.6.0 where the page didn't have keyboard focus after closing
|
||||||
|
the completion, so e.g. typing in an input field after hinting didn't work.
|
||||||
|
(#8750)
|
||||||
|
|
||||||
|
[[v3.6.0]]
|
||||||
|
v3.6.0 (2025-10-24)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Added
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- The `:version` info now shows additional information:
|
||||||
|
* The X11 window manager / Wayland compositor name (mostly useful for
|
||||||
|
bug/crash reports).
|
||||||
|
* Loaded WebExtensions (partial support landed in QtWebEngine 6.10, no
|
||||||
|
official qutebrowser support yet).
|
||||||
|
- Support for hinting elements which are part of an (open) shadow DOM.
|
||||||
|
|
||||||
|
Changed
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- The `qutedmenu` userscript now sorts history by the last access time.
|
||||||
|
- Hardware accelerated 2D canvas is now enabled by default on Qt 6.8.2+,
|
||||||
|
as graphic glitches with e.g. PDF.js and Google Sheets should be fixed
|
||||||
|
nowadays. If you still run into issues, please report them and set
|
||||||
|
`qt.workarounds.disable_accelerated_2d_canvas` to `always` to disable it
|
||||||
|
again.
|
||||||
|
- Changes to binary releases:
|
||||||
|
* Windows and macOS releases are now built with Qt 6.10.0, which is based
|
||||||
|
on Chromium 134.0.6998.208 with security patches up to 140.0.7339.207.
|
||||||
|
* Windows and macOS releases are now built with Python 3.14.
|
||||||
|
* Windows releases are now built on Windows Server 2022 (previously 2019),
|
||||||
|
which might break compatibility with older Windows releases (untested).
|
||||||
|
* If using `mkvenv.py` on Linux, note that Qt now requires glibc v2.34 (v2.28
|
||||||
|
previously). This is available down to Ubuntu 22.04 LTS and Debian Bookworm
|
||||||
|
(oldstable), so this should not affect most users of desktop distributions.
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- Fixed crash if two new downloads start while a download prompt is already open
|
||||||
|
(#8674).
|
||||||
|
- Fixed exception when closing a qutebrowser window while a download prompt is
|
||||||
|
still open.
|
||||||
|
- Hopefully proper fix for some web pages jumping to the top when the statusbar
|
||||||
|
is hidden (#8223).
|
||||||
|
- Fix for the page header being shown on YouTube after the fullscreen
|
||||||
|
notification was hidden (#8625).
|
||||||
|
- Fix for videos losing keyboard focus when the fullscreen notification shows
|
||||||
|
(#8174).
|
||||||
|
- The workaround for microphone/camera permissions not being requested with
|
||||||
|
QtWebEngine 6.9 on Google Meet, Zoom, or other pages using the new
|
||||||
|
`<permission>` element now got extended to Qt 6.9.1+ as it's still not fixed
|
||||||
|
upstream. (#8612)
|
||||||
|
- The package version for Jinja 3.3+ is now correctly displayed in `:version`.
|
||||||
|
- Fixed crash with Qt 6.10 (and possibly older Qt versions) when navigating
|
||||||
|
from a `qute://` page to a web page, e.g. when searching on `qute://start`.
|
||||||
|
- On Wayland with Qt <= 6.9, `EGL_PLATFORM=wayland` is now set by qutebrowser to
|
||||||
|
get hardware rendering. Qt 6.10 includes an equivalent fix (#8637).
|
||||||
|
- Added workaround for per-domain User-Agent header not being used on redirects
|
||||||
|
(#8679).
|
||||||
|
- Added site-specific quirk for gitlab.gnome.org agressively blocking old
|
||||||
|
Chromium versions (and thus QtWebEngine) (#8509).
|
||||||
|
- Using `:config-list-remove` with an invalid value for the respective option
|
||||||
|
type now corrently displays an error instead of crashing.
|
||||||
|
|
||||||
|
[[v3.5.1]]
|
||||||
|
v3.5.1 (2025-06-05)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Deprecated
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
- QtWebKit (legacy) support got removed from CI and is now untested. If it
|
||||||
|
breaks, it's not going to be fixed, and support will be removed over the next
|
||||||
|
releases.
|
||||||
|
- Qt 5 support is currently still tested, but is also planned to get removed
|
||||||
|
over the next releases. Same goes for support for older Qt 6 versions (likely
|
||||||
|
6.2/6.3/6.4 and perhaps 6.5, see https://github.com/qutebrowser/qutebrowser/issues/8464[#8464]).
|
||||||
|
|
||||||
|
Changed
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- Windows/macOS releases now bundle Qt 6.9.1, including many graphics-related
|
||||||
|
bugfixes, as well as security patches up to Chromium 136.0.7103.114.
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- A bogus "wildcard call disconnects from destroyed signal" warning from Qt is
|
||||||
|
now suppressed.
|
||||||
|
- PDF.js now loads correctly on Windows installations with broken mimetype
|
||||||
|
configurations.
|
||||||
|
- A "Ignoring new child ..." debug log message which got spammy with Qt 6.9 is
|
||||||
|
now removed.
|
||||||
|
- A unknown crash (possibly related to using devtools) due to weird (Py)Qt
|
||||||
|
behavior now has a workaround.
|
||||||
|
- No "QtWebEngine version mismatch" warning is now logged anymore with newer Qt
|
||||||
|
5.15 releases (but you should still stop using Qt 5).
|
||||||
|
- The PDF.js version can now correctly be extracted/displayed with newer PDF.js
|
||||||
|
versions.
|
||||||
|
- The `qute-bitwarden`, `-lastpass` and `-pass` userscripts now properly avoid
|
||||||
|
a `DeprecationWarning` from the upcoming 6.0 release of `tldextract`. The
|
||||||
|
previous fix in v3.5.1 was insufficient.
|
||||||
|
|
||||||
|
[[v3.5.0]]
|
||||||
|
v3.5.0 (2025-04-12)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Changed
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- Windows/macOS releases are now built with Qt 6.9.0
|
||||||
|
* Based on Chromium 130.0.6723.192
|
||||||
|
* Security fixes up to Chromium 133.0.6943.141
|
||||||
|
* Also fixes issues with opening links on macOS
|
||||||
|
- The `content.headers.user_agent` setting now has a new
|
||||||
|
`{upstream_browser_version_short}` template field, which is the
|
||||||
|
upstream/Chromium version but shortened to only major version.
|
||||||
|
- The default user agent now uses the shortened Chromium version and doesn't
|
||||||
|
expose the `QtWebEngine/...` part anymore, thus making it equal to the
|
||||||
|
corresponding Chromium user agent. This increases compatibilty due to various
|
||||||
|
overzealous "security" products used by a variety of websites that block
|
||||||
|
QtWebEngine, presumably as a bot (known issues existed with Whatsapp Web, UPS,
|
||||||
|
Digitec Galaxus).
|
||||||
|
- Changed features in userscripts:
|
||||||
|
* `qute-bitwarden` now passes your password to the subprocess in an
|
||||||
|
environment variable when unlocking your vault, instead of as a command
|
||||||
|
line argument. (#7781)
|
||||||
|
- New `-D no-system-pdfjs` debug flag to ignore system-wide PDF.js installations
|
||||||
|
for testing.
|
||||||
|
- Polyfill for missing `URL.parse` with PDF.js v5 and QtWebEngine < 6.9. Note
|
||||||
|
this is a "best effort" fix and you should be using the "older browsers"
|
||||||
|
("legacy") build of PDF.js instead.
|
||||||
|
|
||||||
|
Removed
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- The `ua-slack` site-specific quirk, as things seem to work better nowadays
|
||||||
|
without a quirk needed.
|
||||||
|
- The `ua-whatsapp` site-specific quirk, as it's unneeded with the default UA
|
||||||
|
change described above.
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- Crash when trying to use the `DocumentPictureInPicture` JS API, such as done
|
||||||
|
by the new Google Workspaces Huddle feature. The API is unsupported by
|
||||||
|
QtWebEngine and now correctly disabled on the JS side. (#8449)
|
||||||
|
- Crash when a buggy notification presenter returns a duplicate ID (now an
|
||||||
|
error is shown instead).
|
||||||
|
- Crashes when running `:tab-move` or `:yank title` at startup, before a tab is
|
||||||
|
available.
|
||||||
|
- Crash with `input.insert_mode.auto_load`, when closing a new tab quickly after
|
||||||
|
opening it, but before it was fully loaded. (#3895, #8400)
|
||||||
|
- Workaround for microphone/camera permissions not being requested with
|
||||||
|
QtWebEngine 6.9.0 on Google Meet, Zoom, or other pages using the new
|
||||||
|
`<permission>` element. (#8539)
|
||||||
|
- Resolved issues in userscripts:
|
||||||
|
* `qute-bitwarden` will now prompt a re-login if its cached session has
|
||||||
|
been invalidated since last used. (#8456)
|
||||||
|
* `qute-bitwarden`, `-lastpass` and `-pass` now avoid a
|
||||||
|
`DeprecationWarning` from the upcoming 6.0 release of `tldextract`
|
||||||
|
|
||||||
|
[[v3.4.0]]
|
||||||
|
v3.4.0 (2024-12-14)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Removed
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- Support for Python 3.8 is dropped, and Python 3.9 is now required. (#8325)
|
||||||
|
- Support for macOS 12 Monterey is now dropped, and binaries will be built on
|
||||||
|
macOS 13 Ventura. (#8327)
|
||||||
|
- When using the installer on Windows 10, build 1809 or newer is now required
|
||||||
|
(previous versions required 1607 or newer, but that's not officialy supported by
|
||||||
|
Qt upstream). (#8336)
|
||||||
|
|
||||||
|
Changed
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
- Windows/macOS binaries are now built with Qt 6.8.1. (#8242)
|
||||||
|
- Based on Chromium 122.0.6261.171
|
||||||
|
- With security patches up to 131.0.6778.70
|
||||||
|
- Windows/macOS binaries are now using Python 3.13. (#8205)
|
||||||
|
- The `.desktop` file now also declares qutebrowser as a valid viewer for
|
||||||
|
`image/webp`. (#8340)
|
||||||
|
- Updated mimetype information for getting a suitable extension when downloading
|
||||||
|
a `data:` URL.
|
||||||
|
- The `content.javascript.clipboard` setting now defaults to "ask", which on
|
||||||
|
Qt 6.8+ will prompt the user to grant clipboard access. On older Qt versions,
|
||||||
|
this is still equivalent to `"none"` and needs to be set manually. (#8348)
|
||||||
|
- If a XHR request made via JS sets a custom `Accept-Language` header, it now
|
||||||
|
correctly has precedence over the global `content.headers.accept_language`
|
||||||
|
setting (but not per-domain overrides). This fixes subtle JS issues on
|
||||||
|
websites that rely on the custom header being sent for those requests, and
|
||||||
|
e.g. block the requests server-side otherwise. (#8370)
|
||||||
|
- Our packaging scripts now prefer the "legacy"/"for older browsers" PDF.js
|
||||||
|
build as their normal release only supports the latest Chromium version and
|
||||||
|
might break in qutebrowser on updates. **Note to packagers:** If there's a
|
||||||
|
PDF.js package in your distribution as an (optional) qutebrowser dependency,
|
||||||
|
consider also switching to this variant (same code, built differently).
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- Crash with recent Jinja/Markupsafe versions when viewing a finished userscript
|
||||||
|
(or potentially editor) process via `:process`.
|
||||||
|
- `scripts/open_url_in_instance.sh` now avoids `echo -n`, thus running
|
||||||
|
correctly on POSIX sh. (#8409)
|
||||||
|
- Added a workaround for a bogus QtWebEngine warning about missing spell
|
||||||
|
checking dictionaries. (#8330)
|
||||||
|
|
||||||
|
|
||||||
|
[[v3.3.1]]
|
||||||
|
v3.3.1 (2024-10-12)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Fixed
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- Updated the workaround for Google sign-in issues.
|
||||||
|
|
||||||
[[v3.3.0]]
|
[[v3.3.0]]
|
||||||
v3.3.0 (2024-10-12)
|
v3.3.0 (2024-10-12)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
@ -24,16 +297,16 @@ Added
|
||||||
|
|
||||||
- Added the `qt.workarounds.disable_hangouts_extension` setting,
|
- Added the `qt.workarounds.disable_hangouts_extension` setting,
|
||||||
for disabling the Google Hangouts extension built into Chromium/QtWebEngine.
|
for disabling the Google Hangouts extension built into Chromium/QtWebEngine.
|
||||||
|
- Failed end2end tests will now save screenshots of the browser window when
|
||||||
|
run under xvfb (the default on linux). Screenshots will be under
|
||||||
|
`$TEMP/pytest-current/pytest-screenshots/` or attached to the GitHub actions
|
||||||
|
run as an artifact. (#7625)
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
- Support for macOS 11 Big Sur is dropped. Binaries are now built on macOS 12
|
- Support for macOS 11 Big Sur is dropped. Binaries are now built on macOS 12
|
||||||
Monterey and are unlikely to still run on older macOS versions.
|
Monterey and are unlikely to still run on older macOS versions.
|
||||||
- Failed end2end tests will now save screenshots of the browser window when
|
|
||||||
run under xvfb (the default on linux). Screenshots will be under
|
|
||||||
`$TEMP/pytest-current/pytest-screenshots/` or attached to the GitHub actions
|
|
||||||
run as an artifact. (#7625)
|
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
@ -45,6 +318,8 @@ Changed
|
||||||
respected when yanking any URL (for example, through hints with `hint links
|
respected when yanking any URL (for example, through hints with `hint links
|
||||||
yank`). The `{url:yank}` substitution has also been added as a version of
|
yank`). The `{url:yank}` substitution has also been added as a version of
|
||||||
`{url}` that respects ignored URL query parameters. (#7879)
|
`{url}` that respects ignored URL query parameters. (#7879)
|
||||||
|
- Windows and macOS releases now bundle Qt 6.7.3, which includes security fixes
|
||||||
|
up to Chromium 129.0.6668.58.
|
||||||
|
|
||||||
Fixed
|
Fixed
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
@ -55,6 +330,7 @@ Fixed
|
||||||
documentation in our settings docs now loads a live page again. (#8268)
|
documentation in our settings docs now loads a live page again. (#8268)
|
||||||
- A rare crash when on Qt 6, a renderer process terminates with an unknown
|
- A rare crash when on Qt 6, a renderer process terminates with an unknown
|
||||||
termination reason.
|
termination reason.
|
||||||
|
- Updated the workaround for Google sign-in issues.
|
||||||
|
|
||||||
[[v3.2.1]]
|
[[v3.2.1]]
|
||||||
v3.2.1 (2024-06-25)
|
v3.2.1 (2024-06-25)
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ If you want to find something useful to do, check the
|
||||||
https://github.com/qutebrowser/qutebrowser/issues[issue tracker]. Some
|
https://github.com/qutebrowser/qutebrowser/issues[issue tracker]. Some
|
||||||
pointers:
|
pointers:
|
||||||
|
|
||||||
* https://github.com/qutebrowser/qutebrowser/labels/easy[Issues which should
|
* https://github.com/qutebrowser/qutebrowser/contribute[Issues which should
|
||||||
be easy to solve]
|
be easy to solve]
|
||||||
* https://github.com/qutebrowser/qutebrowser/labels/component%3A%20docs[Documentation issues which require little/no coding]
|
* https://github.com/qutebrowser/qutebrowser/labels/component%3A%20docs[Documentation issues which require little/no coding]
|
||||||
|
|
||||||
|
|
@ -111,9 +111,9 @@ unittests and several linters/checkers.
|
||||||
Currently, the following tox environments are available:
|
Currently, the following tox environments are available:
|
||||||
|
|
||||||
* Tests using https://www.pytest.org[pytest]:
|
* Tests using https://www.pytest.org[pytest]:
|
||||||
- `py38`, `py39`, ...: Run pytest for python 3.8/3.9/... with the system-wide PyQt.
|
- `py39`, `py310`, ...: Run pytest for python 3.9/3.10/... with the system-wide PyQt.
|
||||||
- `py38-pyqt515`, ..., `py38-pyqt65`: Run pytest with the given PyQt version (`py39-*` etc. also works).
|
- `py39-pyqt515`, ..., `py39-pyqt65`: Run pytest with the given PyQt version (`py310-*` etc. also works).
|
||||||
- `py38-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
|
- `py39-pyqt515-cov`: Run with coverage support (other Python/PyQt versions work too).
|
||||||
* `flake8`: Run various linting checks via https://pypi.python.org/pypi/flake8[flake8].
|
* `flake8`: Run various linting checks via https://pypi.python.org/pypi/flake8[flake8].
|
||||||
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
|
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
|
||||||
unused code portions.
|
unused code portions.
|
||||||
|
|
@ -121,8 +121,6 @@ Currently, the following tox environments are available:
|
||||||
* `pyroma`: Check packaging practices with
|
* `pyroma`: Check packaging practices with
|
||||||
https://pypi.python.org/pypi/pyroma/[pyroma].
|
https://pypi.python.org/pypi/pyroma/[pyroma].
|
||||||
* `eslint`: Run https://eslint.org/[ESLint] javascript checker.
|
* `eslint`: Run https://eslint.org/[ESLint] javascript checker.
|
||||||
* `check-manifest`: Check MANIFEST.in completeness with
|
|
||||||
https://github.com/mgedmin/check-manifest[check-manifest].
|
|
||||||
* `mkvenv`: Bootstrap a virtualenv for testing.
|
* `mkvenv`: Bootstrap a virtualenv for testing.
|
||||||
* `misc`: Run `scripts/misc_checks.py` to check for:
|
* `misc`: Run `scripts/misc_checks.py` to check for:
|
||||||
- untracked git files
|
- untracked git files
|
||||||
|
|
@ -171,16 +169,16 @@ Examples:
|
||||||
|
|
||||||
----
|
----
|
||||||
# run only pytest tests which failed in last run:
|
# run only pytest tests which failed in last run:
|
||||||
tox -e py38 -- --lf
|
tox -e py39 -- --lf
|
||||||
|
|
||||||
# run only the end2end feature tests:
|
# run only the end2end feature tests:
|
||||||
tox -e py38 -- tests/end2end/features
|
tox -e py39 -- tests/end2end/features
|
||||||
|
|
||||||
# run everything with undo in the generated name, based on the scenario text
|
# run everything with undo in the generated name, based on the scenario text
|
||||||
tox -e py38 -- tests/end2end/features/test_tabs_bdd.py -k undo
|
tox -e py39 -- tests/end2end/features/test_tabs_bdd.py -k undo
|
||||||
|
|
||||||
# run coverage test for specific file (updates htmlcov/index.html)
|
# run coverage test for specific file (updates htmlcov/index.html)
|
||||||
tox -e py38-cov -- tests/unit/browser/test_webelem.py
|
tox -e py39-cov -- tests/unit/browser/test_webelem.py
|
||||||
----
|
----
|
||||||
|
|
||||||
Specifying the backend for tests
|
Specifying the backend for tests
|
||||||
|
|
@ -604,6 +602,7 @@ Info pages:
|
||||||
- chrome://device-log/ (QtWebEngine >= 6.3)
|
- chrome://device-log/ (QtWebEngine >= 6.3)
|
||||||
- chrome://gpu/
|
- chrome://gpu/
|
||||||
- chrome://sandbox/ (Linux only)
|
- chrome://sandbox/ (Linux only)
|
||||||
|
- chrome://qt/ (QtWebEngine >= 6.7)
|
||||||
|
|
||||||
Misc. / Debugging pages:
|
Misc. / Debugging pages:
|
||||||
|
|
||||||
|
|
@ -614,6 +613,7 @@ Misc. / Debugging pages:
|
||||||
- chrome://ukm/ (QtWebEngine >= 5.15.3)
|
- chrome://ukm/ (QtWebEngine >= 5.15.3)
|
||||||
- chrome://user-actions/ (QtWebEngine >= 5.15.3)
|
- chrome://user-actions/ (QtWebEngine >= 5.15.3)
|
||||||
- chrome://webrtc-logs/ (QtWebEngine >= 5.15.3)
|
- chrome://webrtc-logs/ (QtWebEngine >= 5.15.3)
|
||||||
|
- chrome://extensions/ (QtWebEngine >= 6.10)
|
||||||
|
|
||||||
Internals pages:
|
Internals pages:
|
||||||
|
|
||||||
|
|
@ -789,10 +789,12 @@ New PyQt release
|
||||||
qutebrowser release
|
qutebrowser release
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
* Make sure there are no unstaged changes and the tests are green.
|
* Make sure there are no unstaged or unpushed changes.
|
||||||
|
* Make sure CI is reasonably green.
|
||||||
* Make sure all issues with the related milestone are closed.
|
* Make sure all issues with the related milestone are closed.
|
||||||
* Mark the https://github.com/qutebrowser/qutebrowser/milestones[milestone] as closed.
|
* Mark the https://github.com/qutebrowser/qutebrowser/milestones[milestone] as closed.
|
||||||
* Consider updating the completions for `content.headers.user_agent` in `configdata.yml`.
|
* Consider updating the completions for `content.headers.user_agent` in `configdata.yml`
|
||||||
|
and the Firefox UA in `qutebrowser/browser/webengine/webenginesettings.py`.
|
||||||
* Minor release: Consider updating some files from main:
|
* Minor release: Consider updating some files from main:
|
||||||
- `misc/requirements/` and `requirements.txt`
|
- `misc/requirements/` and `requirements.txt`
|
||||||
- `scripts/`
|
- `scripts/`
|
||||||
|
|
@ -802,7 +804,8 @@ qutebrowser release
|
||||||
**Automatic release via GitHub Actions (starting with v3.0.0):**
|
**Automatic release via GitHub Actions (starting with v3.0.0):**
|
||||||
|
|
||||||
* Double check Python version in `.github/workflows/release.yml`
|
* Double check Python version in `.github/workflows/release.yml`
|
||||||
* Run the `release` workflow on the `main` branch, e.g. via `gh workflow run release -f release_type=major` (`release_type` can be `major`, `minor` or `patch`; you can also override `python_version`)
|
* Run the `release` workflow on the `main` branch, e.g. via `gh workflow run release -f release_type=minor` (`release_type` can be `major`, `minor` or `patch`; you can also override `python_version`)
|
||||||
|
* Consider running `gh run watch` or `gh run view --web` to watch the progress
|
||||||
|
|
||||||
**Manual release:**
|
**Manual release:**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ Why Python?::
|
||||||
point, I wasn't comfortable with C++ so that wasn't an alternative.
|
point, I wasn't comfortable with C++ so that wasn't an alternative.
|
||||||
|
|
||||||
But isn't Python too slow for a browser?::
|
But isn't Python too slow for a browser?::
|
||||||
https://www.infoworld.com/d/application-development/van-rossum-python-not-too-slow-188715[It's generally less of a problem than one would expect.]
|
https://www.infoworld.com/article/2303031/van-rossum-python-is-not-too-slow-2.html[It's generally less of a problem than one would expect.]
|
||||||
Most of the heavy lifting of qutebrowser is done by Qt and
|
Most of the heavy lifting of qutebrowser is done by Qt and
|
||||||
QtWebKit/QtWebEngine in C++, with the
|
QtWebKit/QtWebEngine in C++, with the
|
||||||
https://wiki.python.org/moin/GlobalInterpreterLock[GIL] released.
|
https://wiki.python.org/moin/GlobalInterpreterLock[GIL] released.
|
||||||
|
|
@ -141,7 +141,7 @@ The comma prefix is used to make sure user-defined bindings don't conflict with
|
||||||
the built-in ones.
|
the built-in ones.
|
||||||
+
|
+
|
||||||
Note that you might need an additional package (e.g.
|
Note that you might need an additional package (e.g.
|
||||||
https://www.archlinux.org/packages/community/any/youtube-dl/[youtube-dl] on
|
https://archlinux.org/packages/extra/any/yt-dlp/[yt-dlp] on
|
||||||
Archlinux) to play web videos with mpv.
|
Archlinux) to play web videos with mpv.
|
||||||
+
|
+
|
||||||
There is a very useful script for mpv, which emulates "unique application"
|
There is a very useful script for mpv, which emulates "unique application"
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,7 @@ customizable for a given <<patterns,URL patterns>>.
|
||||||
|
|
||||||
[source,python]
|
[source,python]
|
||||||
----
|
----
|
||||||
config.set('content.images', False, '*://example.com/')
|
config.set('content.images', False, '*://example.com/*')
|
||||||
----
|
----
|
||||||
|
|
||||||
Alternatively, you can use `with config.pattern(...) as p:` to get a shortcut
|
Alternatively, you can use `with config.pattern(...) as p:` to get a shortcut
|
||||||
|
|
@ -187,7 +187,7 @@ similar to `c.` which is scoped to the given domain:
|
||||||
|
|
||||||
[source,python]
|
[source,python]
|
||||||
----
|
----
|
||||||
with config.pattern('*://example.com/') as p:
|
with config.pattern('*://example.com/*') as p:
|
||||||
p.content.images = False
|
p.content.images = False
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
@ -416,6 +416,8 @@ Pre-built colorschemes
|
||||||
- https://github.com/gicrisf/qute-city-lights[City Lights (matte dark)]
|
- https://github.com/gicrisf/qute-city-lights[City Lights (matte dark)]
|
||||||
- https://github.com/catppuccin/qutebrowser[Catppuccin]
|
- https://github.com/catppuccin/qutebrowser[Catppuccin]
|
||||||
- https://github.com/iruzo/matrix-qutebrowser[Matrix]
|
- https://github.com/iruzo/matrix-qutebrowser[Matrix]
|
||||||
|
- https://github.com/harmtemolder/qutebrowser-solarized[Solarized]
|
||||||
|
- https://github.com/Rehpotsirhc-z/qutebrowser-doom-one[Doom One]
|
||||||
|
|
||||||
Avoiding flake8 errors
|
Avoiding flake8 errors
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -452,7 +454,7 @@ Various emacs/conkeror-like keybinding configs exist:
|
||||||
- https://gitlab.com/Kaligule/qutebrowser-emacs-config/blob/master/config.py[Kaligule]
|
- https://gitlab.com/Kaligule/qutebrowser-emacs-config/blob/master/config.py[Kaligule]
|
||||||
- https://web.archive.org/web/20210512185023/https://me0w.net/pit/1540882719[nm0i]
|
- https://web.archive.org/web/20210512185023/https://me0w.net/pit/1540882719[nm0i]
|
||||||
- https://www.reddit.com/r/qutebrowser/comments/eh10i7/config_share_qute_with_emacs_keybindings/[jasonsun0310]
|
- https://www.reddit.com/r/qutebrowser/comments/eh10i7/config_share_qute_with_emacs_keybindings/[jasonsun0310]
|
||||||
- https://git.sr.ht/~willvaughn/dots/tree/mjolnir/item/.config/qutebrowser/qutemacs.py[willvaughn]
|
- https://git.sr.ht/~willvaughn/dots/tree/main/item/.config/qutebrowser/qutemacs.py[willvaughn]
|
||||||
|
|
||||||
It's also mostly possible to get rid of modal keybindings by setting
|
It's also mostly possible to get rid of modal keybindings by setting
|
||||||
`input.insert_mode.auto_enter` to `false`, and `input.forward_unbound_keys` to
|
`input.insert_mode.auto_enter` to `false`, and `input.forward_unbound_keys` to
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Getting help
|
||||||
You can get help in the IRC channel
|
You can get help in the IRC channel
|
||||||
link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
|
link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
|
||||||
https://libera.chat/[Libera Chat]
|
https://libera.chat/[Libera Chat]
|
||||||
(https://web.libera.chat/#qutebrowser[webchat], https://matrix.to/#qutebrowser:libera.chat[via Matrix]),
|
(https://web.libera.chat/#qutebrowser[webchat]),
|
||||||
or by writing a message to the
|
or by writing a message to the
|
||||||
https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
|
https://listi.jpberlin.de/mailman/listinfo/qutebrowser[mailinglist] at
|
||||||
mailto:qutebrowser@lists.qutebrowser.org[].
|
mailto:qutebrowser@lists.qutebrowser.org[].
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,7 @@
|
||||||
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|
|<<qt.force_software_rendering,qt.force_software_rendering>>|Force software rendering for QtWebEngine.
|
||||||
|<<qt.highdpi,qt.highdpi>>|Turn on Qt HighDPI scaling.
|
|<<qt.highdpi,qt.highdpi>>|Turn on Qt HighDPI scaling.
|
||||||
|<<qt.workarounds.disable_accelerated_2d_canvas,qt.workarounds.disable_accelerated_2d_canvas>>|Disable accelerated 2d canvas to avoid graphical glitches.
|
|<<qt.workarounds.disable_accelerated_2d_canvas,qt.workarounds.disable_accelerated_2d_canvas>>|Disable accelerated 2d canvas to avoid graphical glitches.
|
||||||
|
|<<qt.workarounds.disable_accessibility,qt.workarounds.disable_accessibility>>|Disable accessibility to avoid crashes on Qt 6.10.1.
|
||||||
|<<qt.workarounds.disable_hangouts_extension,qt.workarounds.disable_hangouts_extension>>|Disable the Hangouts extension.
|
|<<qt.workarounds.disable_hangouts_extension,qt.workarounds.disable_hangouts_extension>>|Disable the Hangouts extension.
|
||||||
|<<qt.workarounds.locale,qt.workarounds.locale>>|Work around locale parsing issues in QtWebEngine 5.15.3.
|
|<<qt.workarounds.locale,qt.workarounds.locale>>|Work around locale parsing issues in QtWebEngine 5.15.3.
|
||||||
|<<qt.workarounds.remove_service_workers,qt.workarounds.remove_service_workers>>|Delete the QtWebEngine Service Worker directory on every start.
|
|<<qt.workarounds.remove_service_workers,qt.workarounds.remove_service_workers>>|Delete the QtWebEngine Service Worker directory on every start.
|
||||||
|
|
@ -2275,21 +2276,22 @@ The following placeholders are defined:
|
||||||
* `{upstream_browser_key}`: "Version" for QtWebKit, "Chrome" for
|
* `{upstream_browser_key}`: "Version" for QtWebKit, "Chrome" for
|
||||||
QtWebEngine.
|
QtWebEngine.
|
||||||
* `{upstream_browser_version}`: The corresponding Safari/Chrome version.
|
* `{upstream_browser_version}`: The corresponding Safari/Chrome version.
|
||||||
|
* `{upstream_browser_version_short}`: The corresponding Safari/Chrome
|
||||||
|
version, but only with its major version.
|
||||||
* `{qutebrowser_version}`: The currently running qutebrowser version.
|
* `{qutebrowser_version}`: The currently running qutebrowser version.
|
||||||
|
|
||||||
The default value is equal to the unchanged user agent of
|
The default value is equal to the default user agent of
|
||||||
QtWebKit/QtWebEngine.
|
QtWebKit/QtWebEngine, but with the `QtWebEngine/...` part removed for
|
||||||
|
increased compatibility.
|
||||||
|
|
||||||
Note that the value read from JavaScript is always the global value. With
|
Note that the value read from JavaScript is always the global value.
|
||||||
QtWebEngine between 5.12 and 5.14 (inclusive), changing the value exposed
|
|
||||||
to JavaScript requires a restart.
|
|
||||||
|
|
||||||
|
|
||||||
This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
|
This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
|
||||||
|
|
||||||
Type: <<types,FormatString>>
|
Type: <<types,FormatString>>
|
||||||
|
|
||||||
Default: +pass:[Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, like Gecko) {qt_key}/{qt_version} {upstream_browser_key}/{upstream_browser_version} Safari/{webkit_version}]+
|
Default: +pass:[Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, like Gecko) {upstream_browser_key}/{upstream_browser_version_short} Safari/{webkit_version}]+
|
||||||
|
|
||||||
[[content.hyperlink_auditing]]
|
[[content.hyperlink_auditing]]
|
||||||
=== content.hyperlink_auditing
|
=== content.hyperlink_auditing
|
||||||
|
|
@ -2345,18 +2347,20 @@ Default: +pass:[false]+
|
||||||
=== content.javascript.clipboard
|
=== content.javascript.clipboard
|
||||||
Allow JavaScript to read from or write to the clipboard.
|
Allow JavaScript to read from or write to the clipboard.
|
||||||
With QtWebEngine, writing the clipboard as response to a user interaction is always allowed.
|
With QtWebEngine, writing the clipboard as response to a user interaction is always allowed.
|
||||||
|
On Qt < 6.8, the `ask` setting is equivalent to `none` and permission needs to be granted manually via this setting.
|
||||||
|
|
||||||
This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
|
This setting supports link:configuring{outfilesuffix}#patterns[URL patterns].
|
||||||
|
|
||||||
Type: <<types,String>>
|
Type: <<types,JSClipboardPermission>>
|
||||||
|
|
||||||
Valid values:
|
Valid values:
|
||||||
|
|
||||||
* +none+: Disable access to clipboard.
|
* +none+: Disable access to clipboard.
|
||||||
* +access+: Allow reading from and writing to the clipboard.
|
* +access+: Allow reading from and writing to the clipboard.
|
||||||
* +access-paste+: Allow accessing the clipboard and pasting clipboard content.
|
* +access-paste+: Allow accessing the clipboard and pasting clipboard content.
|
||||||
|
* +ask+: Prompt when requested (grants 'access-paste' permission).
|
||||||
|
|
||||||
Default: +pass:[none]+
|
Default: +pass:[ask]+
|
||||||
|
|
||||||
[[content.javascript.enabled]]
|
[[content.javascript.enabled]]
|
||||||
=== content.javascript.enabled
|
=== content.javascript.enabled
|
||||||
|
|
@ -2765,10 +2769,9 @@ Type: <<types,FlagList>>
|
||||||
|
|
||||||
Valid values:
|
Valid values:
|
||||||
|
|
||||||
* +ua-whatsapp+
|
|
||||||
* +ua-google+
|
* +ua-google+
|
||||||
* +ua-slack+
|
|
||||||
* +ua-googledocs+
|
* +ua-googledocs+
|
||||||
|
* +ua-gnome-gitlab+
|
||||||
* +js-whatsapp-web+
|
* +js-whatsapp-web+
|
||||||
* +js-discord+
|
* +js-discord+
|
||||||
* +js-string-replaceall+
|
* +js-string-replaceall+
|
||||||
|
|
@ -3891,7 +3894,7 @@ Chromium has various sandboxing layers, which should be enabled for normal brows
|
||||||
Open `chrome://sandbox` to see the current sandbox status.
|
Open `chrome://sandbox` to see the current sandbox status.
|
||||||
Changing this setting is only recommended if you know what you're doing, as it **disables one of Chromium's security layers**. To avoid sandboxing being accidentally disabled persistently, this setting can only be set via `config.py`, not via `:set`.
|
Changing this setting is only recommended if you know what you're doing, as it **disables one of Chromium's security layers**. To avoid sandboxing being accidentally disabled persistently, this setting can only be set via `config.py`, not via `:set`.
|
||||||
See the Chromium documentation for more details:
|
See the Chromium documentation for more details:
|
||||||
- https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/linux/sandboxing.md[Linux] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
|
- https://chromium.googlesource.com/chromium/src/\+/HEAD/sandbox/linux/README.md[Linux] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox.md[Windows] - https://chromium.googlesource.com/chromium/src/\+/HEAD/sandbox/mac/README.md[Mac] - https://chromium.googlesource.com/chromium/src/\+/HEAD/docs/design/sandbox_faq.md[FAQ (Windows-centric)]
|
||||||
|
|
||||||
This setting requires a restart.
|
This setting requires a restart.
|
||||||
|
|
||||||
|
|
@ -3989,11 +3992,29 @@ Type: <<types,String>>
|
||||||
Valid values:
|
Valid values:
|
||||||
|
|
||||||
* +always+: Disable accelerated 2d canvas
|
* +always+: Disable accelerated 2d canvas
|
||||||
* +auto+: Disable on Qt6 < 6.6.0, enable otherwise
|
* +auto+: Disable on Qt versions with known issues, enable otherwise
|
||||||
* +never+: Enable accelerated 2d canvas
|
* +never+: Enable accelerated 2d canvas
|
||||||
|
|
||||||
Default: +pass:[auto]+
|
Default: +pass:[auto]+
|
||||||
|
|
||||||
|
[[qt.workarounds.disable_accessibility]]
|
||||||
|
=== qt.workarounds.disable_accessibility
|
||||||
|
Disable accessibility to avoid crashes on Qt 6.10.1.
|
||||||
|
|
||||||
|
This setting requires a restart.
|
||||||
|
|
||||||
|
This setting is only available with the QtWebEngine backend.
|
||||||
|
|
||||||
|
Type: <<types,String>>
|
||||||
|
|
||||||
|
Valid values:
|
||||||
|
|
||||||
|
* +always+: Disable renderer accessibility
|
||||||
|
* +auto+: Disable on Qt versions with known issues, enable otherwise
|
||||||
|
* +never+: Enable renderer accessibility
|
||||||
|
|
||||||
|
Default: +pass:[auto]+
|
||||||
|
|
||||||
[[qt.workarounds.disable_hangouts_extension]]
|
[[qt.workarounds.disable_hangouts_extension]]
|
||||||
=== qt.workarounds.disable_hangouts_extension
|
=== qt.workarounds.disable_hangouts_extension
|
||||||
Disable the Hangouts extension.
|
Disable the Hangouts extension.
|
||||||
|
|
@ -4844,6 +4865,7 @@ Lists with duplicate flags are invalid. Each item is checked against the valid v
|
||||||
|FuzzyUrl|A URL which gets interpreted as search if needed.
|
|FuzzyUrl|A URL which gets interpreted as search if needed.
|
||||||
|IgnoreCase|Whether to search case insensitively.
|
|IgnoreCase|Whether to search case insensitively.
|
||||||
|Int|Base class for an integer setting.
|
|Int|Base class for an integer setting.
|
||||||
|
|JSClipboardPermission|Permission for page JS to access the system clipboard.
|
||||||
|Key|A name of a key.
|
|Key|A name of a key.
|
||||||
|List|A list of values.
|
|List|A list of values.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ newer qutebrowser running with:
|
||||||
Note you'll need some basic libraries to use the virtualenv-installed PyQt:
|
Note you'll need some basic libraries to use the virtualenv-installed PyQt:
|
||||||
|
|
||||||
----
|
----
|
||||||
# apt install --no-install-recommends git ca-certificates python3 python3-venv libgl1 libxkbcommon-x11-0 libegl1-mesa libfontconfig1 libglib2.0-0 libdbus-1-3 libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-shape0 libnss3 libxcomposite1 libxdamage1 libxrender1 libxrandr2 libxtst6 libxi6 libasound2
|
# apt install --no-install-recommends git ca-certificates python3 python3-venv libgl1 libxkbcommon-x11-0 libegl1 libfontconfig1 libglib2.0-0 libdbus-1-3 libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-shape0 libnss3 libxcomposite1 libxdamage1 libxrender1 libxrandr2 libxtst6 libxi6 libasound2
|
||||||
----
|
----
|
||||||
|
|
||||||
Additional hints
|
Additional hints
|
||||||
|
|
@ -103,12 +103,18 @@ To be able to play videos with proprietary codecs with QtWebEngine, you will
|
||||||
need to install an additional package from the RPM Fusion Free repository.
|
need to install an additional package from the RPM Fusion Free repository.
|
||||||
For more information see https://rpmfusion.org/Configuration.
|
For more information see https://rpmfusion.org/Configuration.
|
||||||
|
|
||||||
|
With Qt 6 (recommended):
|
||||||
|
|
||||||
|
-----
|
||||||
|
# dnf install libavcodec-freeworld
|
||||||
|
-----
|
||||||
|
|
||||||
|
With Qt 5:
|
||||||
|
|
||||||
-----
|
-----
|
||||||
# dnf install qt5-qtwebengine-freeworld
|
# dnf install qt5-qtwebengine-freeworld
|
||||||
-----
|
-----
|
||||||
|
|
||||||
It's currently unknown what the Qt 6 equivalent of this is.
|
|
||||||
|
|
||||||
On Archlinux
|
On Archlinux
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
@ -438,7 +444,7 @@ This installs all needed Python dependencies in a `.venv` subfolder
|
||||||
This comes with an up-to-date Qt/PyQt including a pre-compiled QtWebEngine
|
This comes with an up-to-date Qt/PyQt including a pre-compiled QtWebEngine
|
||||||
binary, but has a few caveats:
|
binary, but has a few caveats:
|
||||||
|
|
||||||
- Make sure your `python3` is Python 3.8 or newer, otherwise you'll get a "No
|
- Make sure your `python3` is Python 3.9 or newer, otherwise you'll get a "No
|
||||||
matching distribution found" error and/or qutebrowser will not run.
|
matching distribution found" error and/or qutebrowser will not run.
|
||||||
- It only works on 64-bit x86 systems, with other architectures you'll get the
|
- It only works on 64-bit x86 systems, with other architectures you'll get the
|
||||||
same error.
|
same error.
|
||||||
|
|
|
||||||
|
|
@ -432,32 +432,18 @@ Function .onInit
|
||||||
StrCpy $KeepReg 1
|
StrCpy $KeepReg 1
|
||||||
|
|
||||||
; OS version check
|
; OS version check
|
||||||
${If} ${RunningX64}
|
; https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa#remarks
|
||||||
; https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoa#remarks
|
; https://learn.microsoft.com/en-us/windows/release-health/release-information
|
||||||
GetWinVer $R0 Major
|
; https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information
|
||||||
!if "${QT5}" == "True"
|
${If} ${AtLeastWin11}
|
||||||
IntCmpU $R0 6 0 _os_check_fail _os_check_pass
|
Goto _os_check_pass
|
||||||
GetWinVer $R1 Minor
|
${ElseIf} ${IsNativeAMD64} ; Windows 10 has no x86_64 emulation on arm64
|
||||||
IntCmpU $R1 2 _os_check_pass _os_check_fail _os_check_pass
|
${AndIf} ${AtLeastWin10}
|
||||||
!else
|
${AndIf} ${AtLeastBuild} 17763 ; Windows 10 1809 (also in error message below)
|
||||||
IntCmpU $R0 10 0 _os_check_fail _os_check_pass
|
Goto _os_check_pass
|
||||||
GetWinVer $R1 Build
|
|
||||||
${If} $R1 >= 22000 ; Windows 11 21H2
|
|
||||||
Goto _os_check_pass
|
|
||||||
${ElseIf} $R1 >= 14393 ; Windows 10 1607
|
|
||||||
${AndIf} ${IsNativeAMD64} ; Windows 10 has no x86_64 emulation on arm64
|
|
||||||
Goto _os_check_pass
|
|
||||||
${EndIf}
|
|
||||||
!endif
|
|
||||||
${EndIf}
|
${EndIf}
|
||||||
_os_check_fail:
|
MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\
|
||||||
!if "${QT5}" == "True"
|
version of Windows 10 1809 or later."
|
||||||
MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\
|
|
||||||
version of Windows 8 or later."
|
|
||||||
!else
|
|
||||||
MessageBox MB_OK|MB_ICONSTOP "This version of ${PRODUCT_NAME} requires a 64-bit$\r$\n\
|
|
||||||
version of Windows 10 1607 or later."
|
|
||||||
!endif
|
|
||||||
Abort
|
Abort
|
||||||
_os_check_pass:
|
_os_check_pass:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -131,9 +131,6 @@ ShowUninstDetails hide
|
||||||
!define /ifndef DIST_DIR ".\..\..\dist\${PRODUCT_NAME}-${VERSION}"
|
!define /ifndef DIST_DIR ".\..\..\dist\${PRODUCT_NAME}-${VERSION}"
|
||||||
!endif
|
!endif
|
||||||
|
|
||||||
; If not defined, assume Qt6 (requires a more recent windows version)
|
|
||||||
!define /ifndef QT5 "False"
|
|
||||||
|
|
||||||
; Pack the exe header with upx if UPX is defined.
|
; Pack the exe header with upx if UPX is defined.
|
||||||
!ifdef UPX
|
!ifdef UPX
|
||||||
!packhdr "$%TEMP%\exehead.tmp" '"upx" "--ultra-brute" "$%TEMP%\exehead.tmp"'
|
!packhdr "$%TEMP%\exehead.tmp" '"upx" "--ultra-brute" "$%TEMP%\exehead.tmp"'
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,14 @@
|
||||||
</content_rating>
|
</content_rating>
|
||||||
<releases>
|
<releases>
|
||||||
<!-- Add new releases here -->
|
<!-- Add new releases here -->
|
||||||
|
<release version='3.6.3' date='2025-11-30'/>
|
||||||
|
<release version='3.6.2' date='2025-11-27'/>
|
||||||
|
<release version='3.6.1' date='2025-11-03'/>
|
||||||
|
<release version='3.6.0' date='2025-10-24'/>
|
||||||
|
<release version='3.5.1' date='2025-06-05'/>
|
||||||
|
<release version='3.5.0' date='2025-04-12'/>
|
||||||
|
<release version="3.4.0" date="2024-12-14"/>
|
||||||
|
<release version="3.3.1" date="2024-10-12"/>
|
||||||
<release version="3.3.0" date="2024-10-12"/>
|
<release version="3.3.0" date="2024-10-12"/>
|
||||||
<release version="3.2.1" date="2024-06-25"/>
|
<release version="3.2.1" date="2024-06-25"/>
|
||||||
<release version="3.2.0" date="2024-06-03"/>
|
<release version="3.2.0" date="2024-06-03"/>
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Categories=Network;WebBrowser;
|
||||||
Exec=qutebrowser --untrusted-args %u
|
Exec=qutebrowser --untrusted-args %u
|
||||||
Terminal=false
|
Terminal=false
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute;
|
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/rdf+xml;image/gif;image/webp;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/qute;
|
||||||
Keywords=Browser
|
Keywords=Browser
|
||||||
Actions=new-window;preferences;
|
Actions=new-window;preferences;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,24 +25,66 @@ INFO_PLIST_UPDATES = {
|
||||||
"CFBundleURLName": "local file URL",
|
"CFBundleURLName": "local file URL",
|
||||||
"CFBundleURLSchemes": ["file"]
|
"CFBundleURLSchemes": ["file"]
|
||||||
}],
|
}],
|
||||||
'CFBundleDocumentTypes': [{
|
'CFBundleDocumentTypes': [
|
||||||
"CFBundleTypeExtensions": ["html", "htm"],
|
{
|
||||||
"CFBundleTypeMIMETypes": ["text/html"],
|
"CFBundleTypeIconFile": "document.icns",
|
||||||
"CFBundleTypeName": "HTML document",
|
"CFBundleTypeName": name,
|
||||||
"CFBundleTypeOSTypes": ["HTML"],
|
"CFBundleTypeRole": "Viewer",
|
||||||
"CFBundleTypeRole": "Viewer",
|
"LSItemContentTypes": [content_type],
|
||||||
}, {
|
}
|
||||||
"CFBundleTypeExtensions": ["xhtml"],
|
for name, content_type in [
|
||||||
"CFBundleTypeMIMETypes": ["text/xhtml"],
|
("GIF image", "com.compuserve.gif"),
|
||||||
"CFBundleTypeName": "XHTML document",
|
("HTML document", "public.html"),
|
||||||
"CFBundleTypeRole": "Viewer",
|
("XHTML document", "public.xhtml"),
|
||||||
}, {
|
("JavaScript script", "com.netscape.javascript-source"),
|
||||||
"CFBundleTypeExtensions": ["mhtml"],
|
("JPEG image", "public.jpeg"),
|
||||||
"CFBundleTypeMIMETypes": ["multipart/related", "application/x-mimearchive", "message/rfc822"],
|
("MHTML document", "org.ietf.mhtml"),
|
||||||
"CFBundleTypeName": "MHTML document",
|
("HTML5 Audio (Ogg)", "org.xiph.ogg-audio"),
|
||||||
"CFBundleTypeRole": "Viewer",
|
("HTML5 Video (Ogg)", "org.xiph.oggv"),
|
||||||
}],
|
("PNG image", "public.png"),
|
||||||
|
("SVG document", "public.svg-image"),
|
||||||
|
("Plain text document", "public.text"),
|
||||||
|
("HTML5 Video (WebM)", "org.webmproject.webm"),
|
||||||
|
("WebP image", "org.webmproject.webp"),
|
||||||
|
("PDF Document", "com.adobe.pdf"),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'UTImportedTypeDeclarations': [
|
||||||
|
{
|
||||||
|
"UTTypeConformsTo": ["public.data", "public.content"],
|
||||||
|
"UTTypeDescription": "MIME HTML document",
|
||||||
|
"UTTypeIconFile": "document.icns",
|
||||||
|
"UTTypeIdentifier": "org.ietf.mhtml",
|
||||||
|
"UTTypeReferenceURL": "https://www.ietf.org/rfc/rfc2557",
|
||||||
|
"UTTypeTagSpecification": {
|
||||||
|
"com.apple.ostype": "MHTM",
|
||||||
|
"public.filename-extension": ["mht", "mhtml"],
|
||||||
|
"public.mime-type": ["multipart/related", "application/x-mimearchive"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UTTypeConformsTo": ["public.audio"],
|
||||||
|
"UTTypeDescription": "Ogg Audio",
|
||||||
|
"UTTypeIconFile": "document.icns",
|
||||||
|
"UTTypeIdentifier": "org.xiph.ogg-audio",
|
||||||
|
"UTTypeReferenceURL": "https://xiph.org/ogg/",
|
||||||
|
"UTTypeTagSpecification": {
|
||||||
|
"public.filename-extension": ["ogg", "oga"],
|
||||||
|
"public.mime-type": ["audio/ogg"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"UTTypeConformsTo": ["public.movie"],
|
||||||
|
"UTTypeDescription": "Ogg Video",
|
||||||
|
"UTTypeIconFile": "document.icns",
|
||||||
|
"UTTypeIdentifier": "org.xiph.ogv",
|
||||||
|
"UTTypeReferenceURL": "https://xiph.org/ogg/",
|
||||||
|
"UTTypeTagSpecification": {
|
||||||
|
"public.filename-extension": ["ogm", "ogv"],
|
||||||
|
"public.mime-type": ["video/ogg"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
# https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_macos
|
# https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/requesting_authorization_for_media_capture_on_macos
|
||||||
#
|
#
|
||||||
# Keys based on Google Chrome's .app, except Bluetooth keys which seem to
|
# Keys based on Google Chrome's .app, except Bluetooth keys which seem to
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
|
||||||
|
|
||||||
build==1.2.2
|
|
||||||
check-manifest==0.49
|
|
||||||
importlib_metadata==8.5.0
|
|
||||||
packaging==24.1
|
|
||||||
pyproject_hooks==1.2.0
|
|
||||||
tomli==2.0.1
|
|
||||||
zipp==3.20.2
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
check-manifest
|
|
||||||
|
|
@ -1,54 +1,73 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
|
annotated-types==0.7.0
|
||||||
|
anyio==4.12.0
|
||||||
autocommand==2.2.2
|
autocommand==2.2.2
|
||||||
backports.tarfile==1.2.0
|
backports.tarfile==1.2.0
|
||||||
build==1.2.2
|
bracex==2.6
|
||||||
bump2version==1.0.1
|
build==1.3.0
|
||||||
certifi==2024.8.30
|
bump-my-version==1.2.5
|
||||||
cffi==1.17.1
|
certifi==2025.11.12
|
||||||
charset-normalizer==3.3.2
|
cffi==2.0.0
|
||||||
cryptography==43.0.1
|
charset-normalizer==3.4.4
|
||||||
docutils==0.20.1
|
click==8.1.8
|
||||||
|
cryptography==46.0.3
|
||||||
|
docutils==0.22.3
|
||||||
|
exceptiongroup==1.3.1
|
||||||
github3.py==4.0.1
|
github3.py==4.0.1
|
||||||
hunter==3.7.0
|
h11==0.16.0
|
||||||
idna==3.10
|
httpcore==1.0.9
|
||||||
importlib_metadata==8.5.0
|
httpx==0.28.1
|
||||||
importlib_resources==6.4.5
|
hunter==3.9.0
|
||||||
|
id==1.5.0
|
||||||
|
idna==3.11
|
||||||
|
importlib_metadata==8.7.0
|
||||||
|
importlib_resources==6.5.2
|
||||||
inflect==7.3.1
|
inflect==7.3.1
|
||||||
jaraco.classes==3.4.0
|
jaraco.classes==3.4.0
|
||||||
jaraco.collections==5.1.0
|
jaraco.collections==5.1.0
|
||||||
jaraco.context==6.0.1
|
jaraco.context==6.0.1
|
||||||
jaraco.functools==4.1.0
|
jaraco.functools==4.0.1
|
||||||
jaraco.text==3.12.1
|
jaraco.text==3.12.1
|
||||||
jeepney==0.8.0
|
jeepney==0.9.0
|
||||||
keyring==25.4.1
|
keyring==25.7.0
|
||||||
manhole==1.8.1
|
manhole==1.8.1
|
||||||
markdown-it-py==3.0.0
|
markdown-it-py==3.0.0
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
more-itertools==10.5.0
|
more-itertools==10.8.0
|
||||||
nh3==0.2.18
|
nh3==0.3.2
|
||||||
packaging==24.1
|
packaging==25.0
|
||||||
pkginfo==1.10.0
|
platformdirs==4.4.0
|
||||||
platformdirs==4.3.6
|
prompt_toolkit==3.0.52
|
||||||
pycparser==2.22
|
pycparser==2.23
|
||||||
Pygments==2.18.0
|
pydantic==2.12.5
|
||||||
PyJWT==2.9.0
|
pydantic-settings==2.11.0
|
||||||
|
pydantic_core==2.41.5
|
||||||
|
Pygments==2.19.2
|
||||||
|
PyJWT==2.10.1
|
||||||
Pympler==1.1
|
Pympler==1.1
|
||||||
pyproject_hooks==1.2.0
|
pyproject_hooks==1.2.0
|
||||||
PyQt-builder==1.16.4
|
PyQt-builder==1.19.1
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
readme_renderer==43.0
|
python-dotenv==1.2.1
|
||||||
requests==2.32.3
|
questionary==2.1.1
|
||||||
|
readme_renderer==44.0
|
||||||
|
requests==2.32.5
|
||||||
requests-toolbelt==1.0.0
|
requests-toolbelt==1.0.0
|
||||||
rfc3986==2.0.0
|
rfc3986==2.0.0
|
||||||
rich==13.8.1
|
rich==14.2.0
|
||||||
|
rich-click==1.9.4
|
||||||
SecretStorage==3.3.3
|
SecretStorage==3.3.3
|
||||||
sip==6.8.6
|
sip==6.14.0
|
||||||
six==1.16.0
|
six==1.17.0
|
||||||
tomli==2.0.1
|
tomli==2.3.0
|
||||||
twine==5.1.1
|
tomlkit==0.13.3
|
||||||
|
twine==6.2.0
|
||||||
typeguard==4.3.0
|
typeguard==4.3.0
|
||||||
typing_extensions==4.12.2
|
typing-inspection==0.4.2
|
||||||
uritemplate==4.1.1
|
typing_extensions==4.15.0
|
||||||
# urllib3==2.2.3
|
uritemplate==4.2.0
|
||||||
zipp==3.20.2
|
# urllib3==2.6.2
|
||||||
|
wcmatch==10.1
|
||||||
|
wcwidth==0.2.14
|
||||||
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
hunter
|
hunter
|
||||||
pympler
|
pympler
|
||||||
github3.py
|
github3.py
|
||||||
bump2version
|
bump-my-version
|
||||||
requests
|
requests
|
||||||
pyqt-builder
|
pyqt-builder
|
||||||
build
|
build
|
||||||
|
|
@ -9,6 +9,7 @@ twine
|
||||||
|
|
||||||
# Included to override setuptools' vendored version that is being included in
|
# Included to override setuptools' vendored version that is being included in
|
||||||
# the lock file by pip freeze.
|
# the lock file by pip freeze.
|
||||||
|
importlib_resources
|
||||||
platformdirs
|
platformdirs
|
||||||
|
|
||||||
# Already included via test requirements
|
# Already included via test requirements
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,23 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
attrs==24.2.0
|
attrs==25.4.0
|
||||||
flake8==7.1.1
|
flake8==7.3.0
|
||||||
flake8-bugbear==24.8.19
|
flake8-bugbear==24.12.12
|
||||||
flake8-builtins==2.5.0
|
flake8-builtins==3.0.0
|
||||||
flake8-comprehensions==3.15.0
|
flake8-comprehensions==3.17.0
|
||||||
flake8-debugger==4.1.2
|
flake8-debugger==4.1.2
|
||||||
flake8-deprecated==2.2.1
|
flake8-deprecated==2.2.1
|
||||||
flake8-docstrings==1.7.0
|
flake8-docstrings==1.7.0
|
||||||
flake8-future-import==0.4.7
|
flake8-future-import==0.4.7
|
||||||
flake8-plugin-utils==1.3.3
|
flake8-plugin-utils==1.3.3
|
||||||
flake8-pytest-style==2.0.0
|
flake8-pytest-style==2.1.0
|
||||||
flake8-string-format==0.3.0
|
flake8-string-format==0.3.0
|
||||||
flake8-tidy-imports==4.10.0
|
flake8-tidy-imports==4.12.0
|
||||||
flake8-tuple==0.4.1
|
flake8-tuple==0.4.1
|
||||||
mccabe==0.7.0
|
mccabe==0.7.0
|
||||||
pep8-naming==0.14.1
|
pep8-naming==0.15.1
|
||||||
pycodestyle==2.12.1
|
pycodestyle==2.14.0
|
||||||
pydocstyle==6.3.0
|
pydocstyle==6.3.0
|
||||||
pyflakes==3.2.0
|
pyflakes==3.4.0
|
||||||
six==1.16.0
|
six==1.17.0
|
||||||
snowballstemmer==2.2.0
|
snowballstemmer==3.0.1
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,20 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
chardet==5.2.0
|
chardet==5.2.0
|
||||||
diff_cover==9.2.0
|
diff_cover==10.0.0
|
||||||
importlib_resources==6.4.5
|
Jinja2==3.1.6
|
||||||
Jinja2==3.1.4
|
librt==0.7.3
|
||||||
lxml==5.3.0
|
lxml==6.0.2
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==3.0.3
|
||||||
mypy==1.11.2
|
mypy==1.19.0
|
||||||
mypy-extensions==1.0.0
|
mypy_extensions==1.1.0
|
||||||
pluggy==1.5.0
|
pathspec==0.12.1
|
||||||
Pygments==2.18.0
|
pluggy==1.6.0
|
||||||
|
Pygments==2.19.2
|
||||||
PyQt5-stubs==5.15.6.0
|
PyQt5-stubs==5.15.6.0
|
||||||
tomli==2.0.1
|
tomli==2.3.0
|
||||||
types-colorama==0.4.15.20240311
|
types-colorama==0.4.15.20250801
|
||||||
types-docutils==0.21.0.20240907
|
types-docutils==0.22.3.20251115
|
||||||
types-Pygments==2.18.0.20240506
|
types-Pygments==2.19.0.20251121
|
||||||
types-PyYAML==6.0.12.20240917
|
types-PyYAML==6.0.12.20250915
|
||||||
types-setuptools==75.1.0.20240917
|
typing_extensions==4.15.0
|
||||||
typing_extensions==4.12.2
|
|
||||||
zipp==3.20.2
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,3 @@ PyQt5-stubs
|
||||||
types-PyYAML
|
types-PyYAML
|
||||||
types-colorama
|
types-colorama
|
||||||
types-Pygments
|
types-Pygments
|
||||||
|
|
||||||
# So stubs are available even on newer Python versions
|
|
||||||
importlib_resources
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
altgraph==0.17.4
|
altgraph==0.17.5
|
||||||
importlib_metadata==8.5.0
|
importlib_metadata==8.7.0
|
||||||
packaging==24.1
|
packaging==25.0
|
||||||
pyinstaller==6.10.0
|
pyinstaller==6.17.0
|
||||||
pyinstaller-hooks-contrib==2024.8
|
pyinstaller-hooks-contrib==2025.10
|
||||||
zipp==3.20.2
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,28 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
astroid==3.2.4
|
astroid==3.3.11
|
||||||
certifi==2024.8.30
|
certifi==2025.11.12
|
||||||
cffi==1.17.1
|
cffi==2.0.0
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.4.4
|
||||||
cryptography==43.0.1
|
cryptography==46.0.3
|
||||||
dill==0.3.9
|
dill==0.4.0
|
||||||
github3.py==4.0.1
|
github3.py==4.0.1
|
||||||
idna==3.10
|
idna==3.11
|
||||||
isort==5.13.2
|
importlib_metadata==8.7.0
|
||||||
|
isort==6.1.0
|
||||||
mccabe==0.7.0
|
mccabe==0.7.0
|
||||||
pefile==2024.8.26
|
pefile==2024.8.26
|
||||||
platformdirs==4.3.6
|
platformdirs==4.4.0
|
||||||
pycparser==2.22
|
pycparser==2.23
|
||||||
PyJWT==2.9.0
|
PyJWT==2.10.1
|
||||||
pylint==3.2.7
|
pylint==3.3.9
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
./scripts/dev/pylint_checkers
|
./scripts/dev/pylint_checkers
|
||||||
requests==2.32.3
|
requests==2.32.5
|
||||||
six==1.16.0
|
six==1.17.0
|
||||||
tomli==2.0.1
|
tomli==2.3.0
|
||||||
tomlkit==0.13.2
|
tomlkit==0.13.3
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.15.0
|
||||||
uritemplate==4.1.1
|
uritemplate==4.2.0
|
||||||
# urllib3==2.2.3
|
# urllib3==2.6.2
|
||||||
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ pefile
|
||||||
|
|
||||||
# fix qute-pylint location
|
# fix qute-pylint location
|
||||||
#@ replace: qute[_-]pylint.* ./scripts/dev/pylint_checkers
|
#@ replace: qute[_-]pylint.* ./scripts/dev/pylint_checkers
|
||||||
#@ markers: typed-ast python_version<"3.8"
|
|
||||||
|
|
||||||
# Already included via test requirements
|
# Already included via test requirements
|
||||||
#@ ignore: urllib3
|
#@ ignore: urllib3
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt5==5.15.2 # rq.filter: == 5.15.2
|
PyQt5==5.15.2 # rq.filter: == 5.15.2
|
||||||
PyQt5_sip==12.15.0
|
PyQt5_sip==12.17.1
|
||||||
PyQtWebEngine==5.15.2 # rq.filter: == 5.15.2
|
PyQtWebEngine==5.15.2 # rq.filter: == 5.15.2
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt5==5.15.11 # rq.filter: < 5.16
|
PyQt5==5.15.11 # rq.filter: < 5.16
|
||||||
PyQt5-Qt5==5.15.15
|
PyQt5-Qt5==5.15.18
|
||||||
PyQt5_sip==12.15.0
|
PyQt5_sip==12.17.1
|
||||||
PyQtWebEngine==5.15.7 # rq.filter: < 5.16
|
PyQtWebEngine==5.15.7 # rq.filter: < 5.16
|
||||||
PyQtWebEngine-Qt5==5.15.15
|
PyQtWebEngine-Qt5==5.15.18
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt5==5.15.11
|
PyQt5==5.15.11
|
||||||
PyQt5-Qt5==5.15.15
|
PyQt5-Qt5==5.15.18
|
||||||
PyQt5_sip==12.15.0
|
PyQt5_sip==12.17.1
|
||||||
PyQtWebEngine==5.15.7
|
PyQtWebEngine==5.15.7
|
||||||
PyQtWebEngine-Qt5==5.15.15
|
PyQtWebEngine-Qt5==5.15.18
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
|
PyQt6==6.10.1
|
||||||
|
PyQt6-Qt6==6.10.1
|
||||||
|
PyQt6-WebEngine==6.10.0
|
||||||
|
PyQt6-WebEngine-Qt6==6.10.1
|
||||||
|
PyQt6_sip==13.10.2
|
||||||
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
PyQt6 >= 6.10, < 6.11
|
||||||
|
PyQt6-Qt6 >= 6.10, < 6.11
|
||||||
|
PyQt6-WebEngine >= 6.10, < 6.11
|
||||||
|
PyQt6-WebEngine-Qt6 >= 6.10, < 6.11
|
||||||
|
|
||||||
|
# WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2025-October/046347.html
|
||||||
|
#@ add: --extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
@ -4,4 +4,4 @@ PyQt6==6.2.3
|
||||||
PyQt6-Qt6==6.2.4
|
PyQt6-Qt6==6.2.4
|
||||||
PyQt6-WebEngine==6.2.1
|
PyQt6-WebEngine==6.2.1
|
||||||
PyQt6-WebEngine-Qt6==6.2.4
|
PyQt6-WebEngine-Qt6==6.2.4
|
||||||
PyQt6_sip==13.8.0
|
PyQt6_sip==13.10.2
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@ PyQt6==6.3.1
|
||||||
PyQt6-Qt6==6.3.2
|
PyQt6-Qt6==6.3.2
|
||||||
PyQt6-WebEngine==6.3.1
|
PyQt6-WebEngine==6.3.1
|
||||||
PyQt6-WebEngine-Qt6==6.3.2
|
PyQt6-WebEngine-Qt6==6.3.2
|
||||||
PyQt6_sip==13.8.0
|
PyQt6_sip==13.10.2
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@ PyQt6==6.4.2
|
||||||
PyQt6-Qt6==6.4.3
|
PyQt6-Qt6==6.4.3
|
||||||
PyQt6-WebEngine==6.4.0
|
PyQt6-WebEngine==6.4.0
|
||||||
PyQt6-WebEngine-Qt6==6.4.3
|
PyQt6-WebEngine-Qt6==6.4.3
|
||||||
PyQt6_sip==13.8.0
|
PyQt6_sip==13.10.2
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@ PyQt6==6.5.3
|
||||||
PyQt6-Qt6==6.5.3
|
PyQt6-Qt6==6.5.3
|
||||||
PyQt6-WebEngine==6.5.0
|
PyQt6-WebEngine==6.5.0
|
||||||
PyQt6-WebEngine-Qt6==6.5.3
|
PyQt6-WebEngine-Qt6==6.5.3
|
||||||
PyQt6_sip==13.8.0
|
PyQt6_sip==13.10.2
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,4 @@ PyQt6==6.6.1
|
||||||
PyQt6-Qt6==6.6.3
|
PyQt6-Qt6==6.6.3
|
||||||
PyQt6-WebEngine==6.6.0
|
PyQt6-WebEngine==6.6.0
|
||||||
PyQt6-WebEngine-Qt6==6.6.3
|
PyQt6-WebEngine-Qt6==6.6.3
|
||||||
PyQt6_sip==13.8.0
|
PyQt6_sip==13.10.2
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ PyQt6-Qt6==6.7.3
|
||||||
PyQt6-WebEngine==6.7.0
|
PyQt6-WebEngine==6.7.0
|
||||||
PyQt6-WebEngine-Qt6==6.7.3
|
PyQt6-WebEngine-Qt6==6.7.3
|
||||||
PyQt6-WebEngineSubwheel-Qt6==6.7.3
|
PyQt6-WebEngineSubwheel-Qt6==6.7.3
|
||||||
PyQt6_sip==13.8.0
|
PyQt6_sip==13.10.2
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
|
PyQt6==6.8.1
|
||||||
|
PyQt6-Qt6==6.8.2
|
||||||
|
PyQt6-WebEngine==6.8.0
|
||||||
|
PyQt6-WebEngine-Qt6==6.8.2
|
||||||
|
PyQt6_sip==13.10.2
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
PyQt6 >= 6.8, < 6.9
|
||||||
|
PyQt6-Qt6 >= 6.8, < 6.9
|
||||||
|
PyQt6-WebEngine >= 6.8, < 6.9
|
||||||
|
PyQt6-WebEngine-Qt6 >= 6.8, < 6.9
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
|
PyQt6==6.9.1
|
||||||
|
PyQt6-Qt6==6.9.2
|
||||||
|
PyQt6-WebEngine==6.9.0
|
||||||
|
PyQt6-WebEngine-Qt6==6.9.2
|
||||||
|
PyQt6_sip==13.10.2
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
PyQt6 >= 6.9, < 6.10
|
||||||
|
PyQt6-Qt6 >= 6.9, < 6.10
|
||||||
|
PyQt6-WebEngine >= 6.9, < 6.10
|
||||||
|
PyQt6-WebEngine-Qt6 >= 6.9, < 6.10
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt6==6.7.1
|
PyQt6==6.10.1
|
||||||
PyQt6-Qt6==6.7.3
|
PyQt6-Qt6==6.10.1
|
||||||
PyQt6-WebEngine==6.7.0
|
PyQt6-WebEngine==6.10.0
|
||||||
PyQt6-WebEngine-Qt6==6.7.3
|
PyQt6-WebEngine-Qt6==6.10.1
|
||||||
PyQt6-WebEngineSubwheel-Qt6==6.7.3
|
PyQt6_sip==13.10.2
|
||||||
PyQt6_sip==13.8.0
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,7 @@ PyQt6
|
||||||
PyQt6-Qt6
|
PyQt6-Qt6
|
||||||
PyQt6-WebEngine
|
PyQt6-WebEngine
|
||||||
PyQt6-WebEngine-Qt6
|
PyQt6-WebEngine-Qt6
|
||||||
|
|
||||||
|
# WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2025-October/046347.html
|
||||||
|
#@ add: --extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
PyQt6==6.7.1
|
PyQt6==6.10.1
|
||||||
PyQt6-Qt6==6.7.3
|
PyQt6-Qt6==6.10.1
|
||||||
PyQt6-WebEngine==6.7.0
|
PyQt6-WebEngine==6.10.0
|
||||||
PyQt6-WebEngine-Qt6==6.7.3
|
PyQt6-WebEngine-Qt6==6.10.1
|
||||||
PyQt6-WebEngineSubwheel-Qt6==6.7.3
|
PyQt6_sip==13.10.2
|
||||||
PyQt6_sip==13.8.0
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,7 @@ PyQt6
|
||||||
PyQt6-Qt6
|
PyQt6-Qt6
|
||||||
PyQt6-WebEngine
|
PyQt6-WebEngine
|
||||||
PyQt6-WebEngine-Qt6
|
PyQt6-WebEngine-Qt6
|
||||||
|
|
||||||
|
# WORKAROUND for https://www.riverbankcomputing.com/pipermail/pyqt/2025-October/046347.html
|
||||||
|
#@ add: --extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
--extra-index-url https://www.riverbankcomputing.com/pypi/simple/
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
build==1.2.2
|
build==1.3.0
|
||||||
certifi==2024.8.30
|
certifi==2025.11.12
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.4.4
|
||||||
docutils==0.20.1
|
check-manifest==0.51
|
||||||
idna==3.10
|
docutils==0.22.3
|
||||||
importlib_metadata==8.5.0
|
idna==3.11
|
||||||
packaging==24.1
|
importlib_metadata==8.7.0
|
||||||
Pygments==2.18.0
|
packaging==25.0
|
||||||
|
Pygments==2.19.2
|
||||||
pyproject_hooks==1.2.0
|
pyproject_hooks==1.2.0
|
||||||
pyroma==4.2
|
pyroma==5.0.1
|
||||||
requests==2.32.3
|
requests==2.32.5
|
||||||
tomli==2.0.1
|
tomli==2.3.0
|
||||||
trove-classifiers==2024.9.12
|
trove-classifiers==2025.12.1.14
|
||||||
urllib3==2.2.3
|
urllib3==2.6.2
|
||||||
zipp==3.20.2
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
pyroma
|
pyroma
|
||||||
|
check-manifest
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,7 @@ PyYAML
|
||||||
#@ add: pyobjc-core ; sys_platform=="darwin"
|
#@ add: pyobjc-core ; sys_platform=="darwin"
|
||||||
#@ add: pyobjc-framework-Cocoa ; sys_platform=="darwin"
|
#@ add: pyobjc-framework-Cocoa ; sys_platform=="darwin"
|
||||||
|
|
||||||
## stdlib backports
|
|
||||||
importlib_resources
|
|
||||||
|
|
||||||
## Optional dependencies
|
## Optional dependencies
|
||||||
Pygments # For :view-source --pygments or on QtWebKit
|
Pygments # For :view-source --pygments or on QtWebKit
|
||||||
colorama # Colored log output on Windows
|
colorama # Colored log output on Windows
|
||||||
adblock # Improved adblocking
|
adblock # Improved adblocking
|
||||||
|
|
||||||
#@ markers: importlib_resources python_version=="3.8.*"
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
alabaster==0.7.13
|
alabaster==0.7.16
|
||||||
babel==2.16.0
|
babel==2.17.0
|
||||||
certifi==2024.8.30
|
certifi==2025.11.12
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.4.4
|
||||||
docutils==0.20.1
|
docutils==0.21.2
|
||||||
idna==3.10
|
idna==3.11
|
||||||
imagesize==1.4.1
|
imagesize==1.4.1
|
||||||
importlib_metadata==8.5.0
|
importlib_metadata==8.7.0
|
||||||
Jinja2==3.1.4
|
Jinja2==3.1.6
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==3.0.3
|
||||||
packaging==24.1
|
packaging==25.0
|
||||||
Pygments==2.18.0
|
Pygments==2.19.2
|
||||||
pytz==2024.2
|
requests==2.32.5
|
||||||
requests==2.32.3
|
snowballstemmer==3.0.1
|
||||||
snowballstemmer==2.2.0
|
Sphinx==7.4.7
|
||||||
Sphinx==7.1.2
|
sphinxcontrib-applehelp==2.0.0
|
||||||
sphinxcontrib-applehelp==1.0.4
|
sphinxcontrib-devhelp==2.0.0
|
||||||
sphinxcontrib-devhelp==1.0.2
|
sphinxcontrib-htmlhelp==2.1.0
|
||||||
sphinxcontrib-htmlhelp==2.0.1
|
|
||||||
sphinxcontrib-jsmath==1.0.1
|
sphinxcontrib-jsmath==1.0.1
|
||||||
sphinxcontrib-qthelp==1.0.3
|
sphinxcontrib-qthelp==2.0.0
|
||||||
sphinxcontrib-serializinghtml==1.1.5
|
sphinxcontrib-serializinghtml==2.0.0
|
||||||
urllib3==2.2.3
|
tomli==2.3.0
|
||||||
zipp==3.20.2
|
urllib3==2.6.2
|
||||||
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@
|
||||||
# bzr+lp:beautifulsoup
|
# bzr+lp:beautifulsoup
|
||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
git+https://github.com/cherrypy/cheroot.git
|
git+https://github.com/cherrypy/cheroot.git
|
||||||
git+https://github.com/nedbat/coveragepy.git#egg=coverage[toml]
|
coverage[toml] @ git+https://github.com/nedbat/coveragepy.git
|
||||||
git+https://github.com/pallets/flask.git
|
git+https://github.com/pallets/flask.git
|
||||||
git+https://github.com/pallets/werkzeug.git # transitive dep, but needed to work
|
git+https://github.com/pallets/werkzeug.git # transitive dep, but needed to work
|
||||||
git+https://github.com/HypothesisWorks/hypothesis.git#subdirectory=hypothesis-python
|
git+https://github.com/HypothesisWorks/hypothesis.git#subdirectory=hypothesis-python
|
||||||
git+https://github.com/pytest-dev/pytest.git
|
git+https://github.com/pytest-dev/pytest.git
|
||||||
git+https://github.com/pytest-dev/pytest-bdd.git
|
git+https://github.com/pytest-dev/pytest-bdd.git
|
||||||
|
gherkin-official<31.0.0 # https://github.com/cucumber/gherkin/issues/373
|
||||||
git+https://github.com/ionelmc/pytest-benchmark.git
|
git+https://github.com/ionelmc/pytest-benchmark.git
|
||||||
git+https://github.com/pytest-dev/pytest-instafail.git
|
git+https://github.com/pytest-dev/pytest-instafail.git
|
||||||
git+https://github.com/pytest-dev/pytest-mock.git
|
git+https://github.com/pytest-dev/pytest-mock.git
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,67 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
attrs==24.2.0
|
attrs==25.4.0
|
||||||
autocommand==2.2.2
|
autocommand==2.2.2
|
||||||
backports.tarfile==1.2.0
|
backports.tarfile==1.2.0
|
||||||
beautifulsoup4==4.12.3
|
beautifulsoup4==4.14.3
|
||||||
blinker==1.8.2
|
blinker==1.9.0
|
||||||
certifi==2024.8.30
|
certifi==2025.11.12
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.4.4
|
||||||
cheroot==10.0.1
|
cheroot==11.1.2
|
||||||
click==8.1.7
|
click==8.1.8
|
||||||
coverage==7.6.1
|
coverage==7.10.7
|
||||||
exceptiongroup==1.2.2
|
exceptiongroup==1.3.1
|
||||||
execnet==2.1.1
|
execnet==2.1.2
|
||||||
filelock==3.16.1
|
filelock==3.19.1
|
||||||
Flask==3.0.3
|
Flask==3.1.2
|
||||||
hunter==3.7.0
|
gherkin-official==29.0.0
|
||||||
hypothesis==6.112.2
|
hunter==3.9.0
|
||||||
idna==3.10
|
hypothesis==6.141.1
|
||||||
importlib_metadata==8.5.0
|
idna==3.11
|
||||||
importlib_resources==6.4.5 ; python_version=="3.8.*"
|
importlib_metadata==8.7.0
|
||||||
|
importlib_resources==6.5.2
|
||||||
inflect==7.3.1
|
inflect==7.3.1
|
||||||
iniconfig==2.0.0
|
iniconfig==2.1.0
|
||||||
itsdangerous==2.2.0
|
itsdangerous==2.2.0
|
||||||
jaraco.collections==5.1.0
|
jaraco.collections==5.1.0
|
||||||
jaraco.context==6.0.1
|
jaraco.context==6.0.1
|
||||||
jaraco.functools==4.1.0
|
jaraco.functools==4.0.1
|
||||||
jaraco.text==3.12.1
|
jaraco.text==3.12.1
|
||||||
# Jinja2==3.1.4
|
# Jinja2==3.1.6
|
||||||
Mako==1.3.5
|
Mako==1.3.10
|
||||||
manhole==1.8.1
|
manhole==1.8.1
|
||||||
# MarkupSafe==2.1.5
|
# MarkupSafe==3.0.3
|
||||||
more-itertools==10.5.0
|
more-itertools==10.8.0
|
||||||
packaging==24.1
|
packaging==25.0
|
||||||
parse==1.20.2
|
parse==1.20.2
|
||||||
parse_type==0.6.3
|
parse_type==0.6.6
|
||||||
platformdirs==4.3.6
|
pillow==11.3.0
|
||||||
pillow==10.4.0
|
platformdirs==4.4.0
|
||||||
pluggy==1.5.0
|
pluggy==1.6.0
|
||||||
py-cpuinfo==9.0.0
|
py-cpuinfo==9.0.0
|
||||||
Pygments==2.18.0
|
Pygments==2.19.2
|
||||||
pytest==8.3.3
|
pytest==8.4.2
|
||||||
pytest-bdd==7.3.0
|
pytest-bdd==8.1.0
|
||||||
pytest-benchmark==4.0.0
|
pytest-benchmark==5.2.3
|
||||||
pytest-cov==5.0.0
|
pytest-cov==7.0.0
|
||||||
pytest-instafail==0.5.0
|
pytest-instafail==0.5.0
|
||||||
pytest-mock==3.14.0
|
pytest-mock==3.15.1
|
||||||
pytest-qt==4.4.0
|
pytest-qt==4.5.0
|
||||||
pytest-repeat==0.9.3
|
pytest-repeat==0.9.4
|
||||||
pytest-rerunfailures==14.0
|
pytest-rerunfailures==16.0.1
|
||||||
pytest-xdist==3.6.1
|
pytest-xdist==3.8.0
|
||||||
pytest-xvfb==3.0.0
|
pytest-xvfb==3.1.1
|
||||||
PyVirtualDisplay==3.0
|
PyVirtualDisplay==3.0
|
||||||
requests==2.32.3
|
requests==2.32.5
|
||||||
requests-file==2.1.0
|
requests-file==3.0.1
|
||||||
six==1.16.0
|
six==1.17.0
|
||||||
sortedcontainers==2.4.0
|
sortedcontainers==2.4.0
|
||||||
soupsieve==2.6
|
soupsieve==2.8
|
||||||
tldextract==5.1.2
|
tldextract==5.3.0
|
||||||
tomli==2.0.1
|
tomli==2.3.0
|
||||||
typeguard==4.3.0
|
typeguard==4.3.0
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.15.0
|
||||||
urllib3==2.2.3
|
urllib3==2.6.2
|
||||||
vulture==2.12
|
vulture==2.14
|
||||||
Werkzeug==3.0.4
|
Werkzeug==3.1.4
|
||||||
zipp==3.20.2
|
zipp==3.23.0
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ tldextract
|
||||||
# Include them here even though we don't need them to make sure we at least
|
# Include them here even though we don't need them to make sure we at least
|
||||||
# get an up to date version.
|
# get an up to date version.
|
||||||
importlib_resources
|
importlib_resources
|
||||||
#@ markers: importlib_resources python_version=="3.8.*"
|
|
||||||
jaraco.context
|
jaraco.context
|
||||||
platformdirs
|
platformdirs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,19 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
cachetools==5.5.0
|
cachetools==6.2.3
|
||||||
chardet==5.2.0
|
chardet==5.2.0
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
distlib==0.3.8
|
distlib==0.4.0
|
||||||
filelock==3.16.1
|
filelock==3.19.1
|
||||||
packaging==24.1
|
packaging==25.0
|
||||||
pip==24.2
|
pip==25.3
|
||||||
platformdirs==4.3.6
|
platformdirs==4.4.0
|
||||||
pluggy==1.5.0
|
pluggy==1.6.0
|
||||||
pyproject-api==1.8.0
|
pyproject-api==1.9.1
|
||||||
setuptools==75.1.0
|
setuptools==80.9.0
|
||||||
tomli==2.0.1
|
tomli==2.3.0
|
||||||
tox==4.20.0
|
tox==4.30.3 ; python_full_version!="3.14.0b1"
|
||||||
virtualenv==20.26.6
|
typing_extensions==4.15.0
|
||||||
wheel==0.44.0
|
virtualenv==20.35.4
|
||||||
|
wheel==0.45.1
|
||||||
|
tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1"
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,5 @@
|
||||||
tox
|
tox
|
||||||
wheel
|
wheel
|
||||||
|
|
||||||
|
#@ markers: tox python_full_version!="3.14.0b1"
|
||||||
|
#@ add: tox @ git+https://github.com/tox-dev/tox ; python_full_version=="3.14.0b1"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
tomli==2.0.1
|
tomli==2.3.0
|
||||||
vulture==2.12
|
vulture==2.14
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||||
|
|
||||||
pathspec==0.12.1
|
pathspec==0.12.1
|
||||||
PyYAML==6.0.2
|
PyYAML==6.0.3
|
||||||
yamllint==1.35.1
|
yamllint==1.37.1
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,8 @@ The following userscripts can be found on their own repositories.
|
||||||
More powerfully manage single window sessions
|
More powerfully manage single window sessions
|
||||||
- [qutebrowser-url-mutator](https://codeberg.org/mister_monster/qutebrowser-url-mutator):
|
- [qutebrowser-url-mutator](https://codeberg.org/mister_monster/qutebrowser-url-mutator):
|
||||||
automatically mutates input URLs based on configurable rules
|
automatically mutates input URLs based on configurable rules
|
||||||
|
- [qute-translate-popup](https://github.com/JohnBardoe/qute-translate-popup):
|
||||||
|
selected text translation, with a qute popup!
|
||||||
|
|
||||||
[Zotero]: https://www.zotero.org/
|
[Zotero]: https://www.zotero.org/
|
||||||
[Pocket]: https://getpocket.com/
|
[Pocket]: https://getpocket.com/
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ no_entries_found() {
|
||||||
# expected to write the username of that entry to the $username variable and
|
# expected to write the username of that entry to the $username variable and
|
||||||
# the corresponding password to $password
|
# the corresponding password to $password
|
||||||
|
|
||||||
# shellcheck disable=SC2317
|
# shellcheck disable=SC2329
|
||||||
reset_backend() {
|
reset_backend() {
|
||||||
init() { true ; }
|
init() { true ; }
|
||||||
query_entries() { true ; }
|
query_entries() { true ; }
|
||||||
|
|
@ -199,7 +199,8 @@ choose_entry_zenity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
choose_entry_zenity_radio() {
|
choose_entry_zenity_radio() {
|
||||||
zenity_helper() { # shellcheck disable=SC2317
|
# shellcheck disable=SC2329
|
||||||
|
zenity_helper() {
|
||||||
awk '{ print $0 ; print $0 }' \
|
awk '{ print $0 ; print $0 }' \
|
||||||
| zenity --list --radiolist \
|
| zenity --list --radiolist \
|
||||||
--title "qutebrowser password fill" \
|
--title "qutebrowser password fill" \
|
||||||
|
|
@ -279,7 +280,7 @@ pass_backend() {
|
||||||
|
|
||||||
# =======================================================
|
# =======================================================
|
||||||
# backend: secret
|
# backend: secret
|
||||||
# shellcheck disable=SC2317
|
# shellcheck disable=SC2329
|
||||||
secret_backend() {
|
secret_backend() {
|
||||||
init() {
|
init() {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,8 @@ def ask_password(password_prompt_invocation):
|
||||||
raise Exception('Could not unlock vault')
|
raise Exception('Could not unlock vault')
|
||||||
master_pass = process.stdout.strip()
|
master_pass = process.stdout.strip()
|
||||||
return subprocess.check_output(
|
return subprocess.check_output(
|
||||||
['bw', 'unlock', '--raw', master_pass],
|
['bw', 'unlock', '--raw', '--passwordenv', 'BW_MASTERPASS'],
|
||||||
|
env={**os.environ, 'BW_MASTERPASS': master_pass},
|
||||||
text=True,
|
text=True,
|
||||||
).strip()
|
).strip()
|
||||||
|
|
||||||
|
|
@ -132,7 +133,7 @@ def get_session_key(auto_lock, password_prompt_invocation):
|
||||||
def pass_(domain, encoding, auto_lock, password_prompt_invocation):
|
def pass_(domain, encoding, auto_lock, password_prompt_invocation):
|
||||||
session_key = get_session_key(auto_lock, password_prompt_invocation)
|
session_key = get_session_key(auto_lock, password_prompt_invocation)
|
||||||
process = subprocess.run(
|
process = subprocess.run(
|
||||||
['bw', 'list', 'items', '--session', session_key, '--url', domain],
|
['bw', 'list', 'items', '--nointeraction', '--session', session_key, '--url', domain],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -141,6 +142,10 @@ def pass_(domain, encoding, auto_lock, password_prompt_invocation):
|
||||||
msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain, err)
|
msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain, err)
|
||||||
stderr(msg)
|
stderr(msg)
|
||||||
|
|
||||||
|
if "Vault is locked" in err:
|
||||||
|
stderr("Bitwarden Vault got locked, trying again with clean session")
|
||||||
|
return pass_(domain, encoding, 0, password_prompt_invocation)
|
||||||
|
|
||||||
if process.returncode:
|
if process.returncode:
|
||||||
return '[]'
|
return '[]'
|
||||||
|
|
||||||
|
|
@ -152,7 +157,7 @@ def pass_(domain, encoding, auto_lock, password_prompt_invocation):
|
||||||
def get_totp_code(selection_id, domain_name, encoding, auto_lock, password_prompt_invocation):
|
def get_totp_code(selection_id, domain_name, encoding, auto_lock, password_prompt_invocation):
|
||||||
session_key = get_session_key(auto_lock, password_prompt_invocation)
|
session_key = get_session_key(auto_lock, password_prompt_invocation)
|
||||||
process = subprocess.run(
|
process = subprocess.run(
|
||||||
['bw', 'get', 'totp', '--session', session_key, selection_id],
|
['bw', 'get', 'totp', '--nointeraction', '--session', session_key, selection_id],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -162,6 +167,10 @@ def get_totp_code(selection_id, domain_name, encoding, auto_lock, password_promp
|
||||||
msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain_name, err)
|
msg = 'Bitwarden CLI returned for {:s} - {:s}'.format(domain_name, err)
|
||||||
stderr(msg)
|
stderr(msg)
|
||||||
|
|
||||||
|
if "Vault is locked" in err:
|
||||||
|
stderr("Bitwarden Vault got locked, trying again with clean session")
|
||||||
|
return get_totp_code(selection_id, domain_name, encoding, 0, password_prompt_invocation)
|
||||||
|
|
||||||
if process.returncode:
|
if process.returncode:
|
||||||
return '[]'
|
return '[]'
|
||||||
|
|
||||||
|
|
@ -195,12 +204,20 @@ def main(arguments):
|
||||||
# the registered domain name and finally: the IPv4 address if that's what
|
# the registered domain name and finally: the IPv4 address if that's what
|
||||||
# the URL represents
|
# the URL represents
|
||||||
candidates = []
|
candidates = []
|
||||||
for target in filter(None, [
|
for target in filter(
|
||||||
extract_result.fqdn,
|
None,
|
||||||
extract_result.registered_domain,
|
[
|
||||||
extract_result.subdomain + '.' + extract_result.domain,
|
extract_result.fqdn,
|
||||||
extract_result.domain,
|
(
|
||||||
extract_result.ipv4]):
|
extract_result.top_domain_under_public_suffix
|
||||||
|
if hasattr(extract_result, "top_domain_under_public_suffix")
|
||||||
|
else extract_result.registered_domain
|
||||||
|
),
|
||||||
|
extract_result.subdomain + "." + extract_result.domain,
|
||||||
|
extract_result.domain,
|
||||||
|
extract_result.ipv4,
|
||||||
|
],
|
||||||
|
):
|
||||||
target_candidates = json.loads(
|
target_candidates = json.loads(
|
||||||
pass_(
|
pass_(
|
||||||
target,
|
target,
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,20 @@ def main(arguments):
|
||||||
# the URL represents
|
# the URL represents
|
||||||
candidates = []
|
candidates = []
|
||||||
seen_id = set()
|
seen_id = set()
|
||||||
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.subdomain + extract_result.domain, extract_result.domain, extract_result.ipv4]):
|
for target in filter(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
extract_result.fqdn,
|
||||||
|
(
|
||||||
|
extract_result.top_domain_under_public_suffix
|
||||||
|
if hasattr(extract_result, "top_domain_under_public_suffix")
|
||||||
|
else extract_result.registered_domain
|
||||||
|
),
|
||||||
|
extract_result.subdomain + extract_result.domain,
|
||||||
|
extract_result.domain,
|
||||||
|
extract_result.ipv4,
|
||||||
|
],
|
||||||
|
):
|
||||||
target_candidates, err = pass_(target, arguments.io_encoding)
|
target_candidates, err = pass_(target, arguments.io_encoding)
|
||||||
if err:
|
if err:
|
||||||
stderr("LastPass CLI returned for {:s} - {:s}".format(target, err))
|
stderr("LastPass CLI returned for {:s} - {:s}".format(target, err))
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,20 @@ def main(arguments):
|
||||||
|
|
||||||
netloc = urlparse(arguments.url).netloc
|
netloc = urlparse(arguments.url).netloc
|
||||||
|
|
||||||
for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.ipv4, private_domain, netloc]):
|
for target in filter(
|
||||||
|
None,
|
||||||
|
[
|
||||||
|
extract_result.fqdn,
|
||||||
|
(
|
||||||
|
extract_result.top_domain_under_public_suffix
|
||||||
|
if hasattr(extract_result, "top_domain_under_public_suffix")
|
||||||
|
else extract_result.registered_domain
|
||||||
|
),
|
||||||
|
extract_result.ipv4,
|
||||||
|
private_domain,
|
||||||
|
netloc,
|
||||||
|
],
|
||||||
|
):
|
||||||
attempted_targets.append(target)
|
attempted_targets.append(target)
|
||||||
target_candidates = find_pass_candidates(target, unfiltered=arguments.unfiltered)
|
target_candidates = find_pass_candidates(target, unfiltered=arguments.unfiltered)
|
||||||
if not target_candidates:
|
if not target_candidates:
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ create_menu() {
|
||||||
done < "$QUTE_CONFIG_DIR"/bookmarks/urls
|
done < "$QUTE_CONFIG_DIR"/bookmarks/urls
|
||||||
|
|
||||||
# Finally history
|
# Finally history
|
||||||
printf -- '%s\n' "$(sqlite3 -separator ' ' "$QUTE_DATA_DIR/history.sqlite" 'select title, url from CompletionHistory')"
|
printf -- '%s\n' "$(sqlite3 -separator ' ' "$QUTE_DATA_DIR/history.sqlite" 'select title, url from CompletionHistory ORDER BY last_atime DESC')"
|
||||||
}
|
}
|
||||||
|
|
||||||
get_selection() {
|
get_selection() {
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,16 @@
|
||||||
# :spawn --userscript ripbang amazon maps
|
# :spawn --userscript ripbang amazon maps
|
||||||
#
|
#
|
||||||
|
|
||||||
import os, re, requests, sys
|
import os, requests, sys
|
||||||
from urllib.parse import urlparse, parse_qs
|
|
||||||
|
|
||||||
for argument in sys.argv[1:]:
|
for argument in sys.argv[1:]:
|
||||||
bang = '!' + argument
|
bang = '!' + argument
|
||||||
r = requests.get('https://duckduckgo.com/',
|
r = requests.get('https://html.duckduckgo.com/html/',
|
||||||
|
allow_redirects=False,
|
||||||
params={'q': bang + ' SEARCHTEXT'},
|
params={'q': bang + ' SEARCHTEXT'},
|
||||||
headers={'user-agent': 'qutebrowser ripbang'})
|
headers={'user-agent': 'qutebrowser ripbang'})
|
||||||
|
|
||||||
searchengine = re.search("url=([^']+)", r.text).group(1)
|
searchengine = r.headers['location']
|
||||||
searchengine = urlparse(searchengine).query
|
|
||||||
searchengine = parse_qs(searchengine)['uddg'][0]
|
|
||||||
searchengine = searchengine.replace('SEARCHTEXT', '{}')
|
searchengine = searchengine.replace('SEARCHTEXT', '{}')
|
||||||
|
|
||||||
if os.getenv('QUTE_FIFO'):
|
if os.getenv('QUTE_FIFO'):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.check-manifest]
|
||||||
|
ignore = [
|
||||||
|
"qutebrowser/git-commit-id",
|
||||||
|
"qutebrowser/html/doc",
|
||||||
|
"qutebrowser/html/doc/*",
|
||||||
|
"qutebrowser/html/doc/img/cheatsheet-*.png",
|
||||||
|
"*/__pycache__",
|
||||||
|
]
|
||||||
27
pytest.ini
27
pytest.ini
|
|
@ -1,4 +1,5 @@
|
||||||
[pytest]
|
[pytest]
|
||||||
|
pythonpath = .
|
||||||
log_level = NOTSET
|
log_level = NOTSET
|
||||||
addopts = --strict-markers --strict-config --instafail --benchmark-columns=Min,Max,Median
|
addopts = --strict-markers --strict-config --instafail --benchmark-columns=Min,Max,Median
|
||||||
testpaths = tests
|
testpaths = tests
|
||||||
|
|
@ -19,6 +20,7 @@ markers =
|
||||||
not_frozen: Tests which can't be run if sys.frozen is True.
|
not_frozen: Tests which can't be run if sys.frozen is True.
|
||||||
not_flatpak: Tests which can't be run if running with Flatpak.
|
not_flatpak: Tests which can't be run if running with Flatpak.
|
||||||
no_xvfb: Tests which can't be run with Xvfb.
|
no_xvfb: Tests which can't be run with Xvfb.
|
||||||
|
no_offscreen: Tests which can't be run with the offscreen platform plugin.
|
||||||
frozen: Tests which can only be run if sys.frozen is True.
|
frozen: Tests which can only be run if sys.frozen is True.
|
||||||
integration: Tests which test a bigger portion of code
|
integration: Tests which test a bigger portion of code
|
||||||
end2end: End to end tests which run qutebrowser as subprocess
|
end2end: End to end tests which run qutebrowser as subprocess
|
||||||
|
|
@ -41,7 +43,8 @@ markers =
|
||||||
qt6_only: Tests which should only run with Qt 6
|
qt6_only: Tests which should only run with Qt 6
|
||||||
qt5_xfail: Tests which fail with Qt 5
|
qt5_xfail: Tests which fail with Qt 5
|
||||||
qt6_xfail: Tests which fail with Qt 6
|
qt6_xfail: Tests which fail with Qt 6
|
||||||
qt68_beta4_skip: Fails on Qt 6.8 beta 4
|
qt69_ci_flaky: Tests which are flaky with Qt 6.9+ on CI
|
||||||
|
qt69_ci_skip: Tests which should be skipped with Qt 6.9+ on CI
|
||||||
qt_log_level_fail = WARNING
|
qt_log_level_fail = WARNING
|
||||||
qt_log_ignore =
|
qt_log_ignore =
|
||||||
# GitHub Actions
|
# GitHub Actions
|
||||||
|
|
@ -77,13 +80,29 @@ qt_log_ignore =
|
||||||
# model, for example, when no completion function is available for the
|
# model, for example, when no completion function is available for the
|
||||||
# current text pattern.
|
# current text pattern.
|
||||||
QItemSelectionModel: Selecting when no model has been set will result in a no-op.
|
QItemSelectionModel: Selecting when no model has been set will result in a no-op.
|
||||||
^QSaveFile::commit: File \(.*/test_failing_flush0/foo\) is not open$
|
^QSaveFile::commit: File \(.*[/\\]test_failing_flush0[/\\]foo\) is not open$
|
||||||
^The following paths were searched for Qt WebEngine dictionaries:.*
|
^The following paths were searched for Qt WebEngine dictionaries:.*
|
||||||
|
# Qt 6.9 with Xvfb
|
||||||
|
^Backend texture is not a Vulkan texture\.$
|
||||||
|
^Compositor returned null texture$
|
||||||
|
# With offscreen platform plugin
|
||||||
|
^This plugin does not support (raise\(\)|propagateSizeHints\(\)|createPlatformVulkanInstance|grabbing the keyboard)$
|
||||||
|
^QRhiGles2: Failed to create (temporary )?context$
|
||||||
|
^QVulkanInstance: Failed to initialize Vulkan$
|
||||||
|
^Unable to detect GPU vendor\.$
|
||||||
|
# Qt 5 on CI with WebKit
|
||||||
|
^qglx_findConfig: Failed to finding matching FBConfig for QSurfaceFormat\(version 2\.0, options QFlags<QSurfaceFormat::FormatOption>\(\), depthBufferSize -1, redBufferSize 1, greenBufferSize 1, blueBufferSize 1, alphaBufferSize -1, stencilBufferSize -1, samples -1, swapBehavior QSurfaceFormat::SingleBuffer, swapInterval 1, colorSpace QSurfaceFormat::DefaultColorSpace, profile QSurfaceFormat::NoProfile\)$
|
||||||
|
# Qt 6.8+ debug build
|
||||||
|
# https://github.com/qutebrowser/qutebrowser/issues/8069#issuecomment-2017644465
|
||||||
|
^QObject::connect: Connecting from COMPAT signal \(QWebEnginePage::featurePermissionRequest(ed|Canceled)\(QUrl,QWebEnginePage::Feature\)\)
|
||||||
xfail_strict = true
|
xfail_strict = true
|
||||||
filterwarnings =
|
filterwarnings =
|
||||||
error
|
error
|
||||||
default:Test process .* failed to terminate!:UserWarning
|
default:Test process .* failed to terminate!:UserWarning
|
||||||
# Python 3.12: https://github.com/ionelmc/pytest-benchmark/issues/240 (fixed but not released)
|
# https://github.com/cucumber/gherkin/commit/2f4830093149eae7ff7bd82f683b3d3bb7320d39
|
||||||
ignore:(datetime\.)?datetime\.utcnow\(\) is deprecated and scheduled for removal in a future version\. Use timezone-aware objects to represent datetimes in UTC. (datetime\.)?datetime\.now\(datetime\.UTC\)\.:DeprecationWarning:pytest_benchmark\.utils
|
# https://github.com/pytest-dev/pytest-bdd/issues/752
|
||||||
|
ignore:'maxsplit' is passed as positional argument:DeprecationWarning:gherkin.gherkin_line
|
||||||
|
# https://github.com/ionelmc/pytest-benchmark/issues/283
|
||||||
|
ignore:FileType is deprecated\. Simply open files after parsing arguments\.:PendingDeprecationWarning:pytest_benchmark.plugin
|
||||||
faulthandler_timeout = 90
|
faulthandler_timeout = 90
|
||||||
xvfb_colordepth = 24
|
xvfb_colordepth = 24
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ _year = datetime.date.today().year
|
||||||
|
|
||||||
__author__ = "Florian Bruhin"
|
__author__ = "Florian Bruhin"
|
||||||
__copyright__ = "Copyright 2013-{} Florian Bruhin (The Compiler)".format(_year)
|
__copyright__ = "Copyright 2013-{} Florian Bruhin (The Compiler)".format(_year)
|
||||||
__license__ = "GPL"
|
__license__ = "GPL-3.0-or-later"
|
||||||
__maintainer__ = __author__
|
__maintainer__ = __author__
|
||||||
__email__ = "mail@qutebrowser.org"
|
__email__ = "mail@qutebrowser.org"
|
||||||
__version__ = "3.3.0"
|
__version__ = "3.6.3"
|
||||||
__version_info__ = tuple(int(part) for part in __version__.split('.'))
|
__version_info__ = tuple(int(part) for part in __version__.split('.'))
|
||||||
__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."
|
__description__ = "A keyboard-driven, vim-like browser based on Python and Qt."
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,8 @@ Possible values:
|
||||||
|
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
from typing import Any, Callable, Iterable, Protocol, Optional, Dict, cast
|
from typing import Any, Protocol, Optional, cast
|
||||||
|
from collections.abc import Iterable, Callable
|
||||||
|
|
||||||
from qutebrowser.utils import qtutils
|
from qutebrowser.utils import qtutils
|
||||||
from qutebrowser.commands import command, cmdexc
|
from qutebrowser.commands import command, cmdexc
|
||||||
|
|
@ -101,7 +102,7 @@ class _CmdHandlerType(Protocol):
|
||||||
Below, we cast the decorated function to _CmdHandlerType to make mypy aware of this.
|
Below, we cast the decorated function to _CmdHandlerType to make mypy aware of this.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
qute_args: Optional[Dict[str, 'command.ArgInfo']]
|
qute_args: Optional[dict[str, 'command.ArgInfo']]
|
||||||
|
|
||||||
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
...
|
...
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
"""Hooks for extensions."""
|
"""Hooks for extensions."""
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
from typing import Callable, Any
|
from typing import Any
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
|
||||||
from qutebrowser.extensions import loader
|
from qutebrowser.extensions import loader
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,8 @@ import tempfile
|
||||||
import pathlib
|
import pathlib
|
||||||
import datetime
|
import datetime
|
||||||
import argparse
|
import argparse
|
||||||
from typing import Iterable, Optional, List, Tuple
|
from typing import Optional
|
||||||
|
from collections.abc import Iterable
|
||||||
|
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
from qutebrowser.qt.widgets import QApplication, QWidget
|
from qutebrowser.qt.widgets import QApplication, QWidget
|
||||||
|
|
@ -330,7 +331,7 @@ def _open_special_pages(args):
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
window='last-focused')
|
window='last-focused')
|
||||||
|
|
||||||
pages: List[Tuple[str, bool, str]] = [
|
pages: list[tuple[str, bool, str]] = [
|
||||||
# state, condition, URL
|
# state, condition, URL
|
||||||
('quickstart-done',
|
('quickstart-done',
|
||||||
True,
|
True,
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ import pathlib
|
||||||
import itertools
|
import itertools
|
||||||
import functools
|
import functools
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import (cast, TYPE_CHECKING, Any, Callable, Iterable, List, Optional,
|
from typing import (cast, TYPE_CHECKING, Any, Optional, Union)
|
||||||
Sequence, Set, Type, Union, Tuple)
|
from collections.abc import Iterable, Sequence, Callable
|
||||||
|
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
from qutebrowser.qt.core import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt,
|
from qutebrowser.qt.core import (pyqtSignal, pyqtSlot, QUrl, QObject, QSizeF, Qt,
|
||||||
|
|
@ -29,7 +29,7 @@ if TYPE_CHECKING:
|
||||||
from qutebrowser.keyinput import modeman
|
from qutebrowser.keyinput import modeman
|
||||||
from qutebrowser.config import config, websettings
|
from qutebrowser.config import config, websettings
|
||||||
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
|
from qutebrowser.utils import (utils, objreg, usertypes, log, qtutils,
|
||||||
urlutils, message, jinja, version)
|
urlutils, message, jinja)
|
||||||
from qutebrowser.misc import miscwidgets, objects, sessions
|
from qutebrowser.misc import miscwidgets, objects, sessions
|
||||||
from qutebrowser.browser import eventfilter, inspector
|
from qutebrowser.browser import eventfilter, inspector
|
||||||
from qutebrowser.qt import sip
|
from qutebrowser.qt import sip
|
||||||
|
|
@ -60,7 +60,7 @@ def create(win_id: int,
|
||||||
mode_manager = modeman.instance(win_id)
|
mode_manager = modeman.instance(win_id)
|
||||||
if objects.backend == usertypes.Backend.QtWebEngine:
|
if objects.backend == usertypes.Backend.QtWebEngine:
|
||||||
from qutebrowser.browser.webengine import webenginetab
|
from qutebrowser.browser.webengine import webenginetab
|
||||||
tab_class: Type[AbstractTab] = webenginetab.WebEngineTab
|
tab_class: type[AbstractTab] = webenginetab.WebEngineTab
|
||||||
elif objects.backend == usertypes.Backend.QtWebKit:
|
elif objects.backend == usertypes.Backend.QtWebKit:
|
||||||
from qutebrowser.browser.webkit import webkittab
|
from qutebrowser.browser.webkit import webkittab
|
||||||
tab_class = webkittab.WebKitTab
|
tab_class = webkittab.WebKitTab
|
||||||
|
|
@ -142,7 +142,7 @@ class AbstractAction:
|
||||||
|
|
||||||
"""Attribute ``action`` of AbstractTab for Qt WebActions."""
|
"""Attribute ``action`` of AbstractTab for Qt WebActions."""
|
||||||
|
|
||||||
action_base: Type[Union['QWebPage.WebAction', 'QWebEnginePage.WebAction']]
|
action_base: type[Union['QWebPage.WebAction', 'QWebEnginePage.WebAction']]
|
||||||
|
|
||||||
def __init__(self, tab: 'AbstractTab') -> None:
|
def __init__(self, tab: 'AbstractTab') -> None:
|
||||||
self._widget = cast(_WidgetType, None)
|
self._widget = cast(_WidgetType, None)
|
||||||
|
|
@ -639,7 +639,7 @@ class AbstractScroller(QObject):
|
||||||
def pos_px(self) -> QPoint:
|
def pos_px(self) -> QPoint:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def pos_perc(self) -> Tuple[int, int]:
|
def pos_perc(self) -> tuple[int, int]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def to_perc(self, x: float = None, y: float = None) -> None:
|
def to_perc(self, x: float = None, y: float = None) -> None:
|
||||||
|
|
@ -765,10 +765,10 @@ class AbstractHistory:
|
||||||
def _go_to_item(self, item: Any) -> None:
|
def _go_to_item(self, item: Any) -> None:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def back_items(self) -> List[Any]:
|
def back_items(self) -> list[Any]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def forward_items(self) -> List[Any]:
|
def forward_items(self) -> list[Any]:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1018,7 +1018,7 @@ class AbstractTab(QWidget):
|
||||||
# Note that we remember hosts here, without scheme/port:
|
# Note that we remember hosts here, without scheme/port:
|
||||||
# QtWebEngine/Chromium also only remembers hostnames, and certificates are
|
# QtWebEngine/Chromium also only remembers hostnames, and certificates are
|
||||||
# for a given hostname anyways.
|
# for a given hostname anyways.
|
||||||
_insecure_hosts: Set[str] = set()
|
_insecure_hosts: set[str] = set()
|
||||||
|
|
||||||
# Sub-APIs initialized by subclasses
|
# Sub-APIs initialized by subclasses
|
||||||
history: AbstractHistory
|
history: AbstractHistory
|
||||||
|
|
@ -1177,37 +1177,6 @@ class AbstractTab(QWidget):
|
||||||
navigation.url.errorString()))
|
navigation.url.errorString()))
|
||||||
navigation.accepted = False
|
navigation.accepted = False
|
||||||
|
|
||||||
# WORKAROUND for QtWebEngine >= 6.2 not allowing form requests from
|
|
||||||
# qute:// to outside domains.
|
|
||||||
needs_load_workarounds = (
|
|
||||||
objects.backend == usertypes.Backend.QtWebEngine and
|
|
||||||
version.qtwebengine_versions().webengine >= utils.VersionNumber(6, 2)
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
needs_load_workarounds and
|
|
||||||
self.url() == QUrl("qute://start/") and
|
|
||||||
navigation.navigation_type == navigation.Type.form_submitted and
|
|
||||||
navigation.url.matches(
|
|
||||||
QUrl(config.val.url.searchengines['DEFAULT']),
|
|
||||||
urlutils.FormatOption.REMOVE_QUERY)
|
|
||||||
):
|
|
||||||
log.webview.debug(
|
|
||||||
"Working around qute://start loading issue for "
|
|
||||||
f"{navigation.url.toDisplayString()}")
|
|
||||||
navigation.accepted = False
|
|
||||||
self.load_url(navigation.url)
|
|
||||||
|
|
||||||
if (
|
|
||||||
needs_load_workarounds and
|
|
||||||
self.url() == QUrl("qute://bookmarks/") and
|
|
||||||
navigation.navigation_type == navigation.Type.back_forward
|
|
||||||
):
|
|
||||||
log.webview.debug(
|
|
||||||
"Working around qute://bookmarks loading issue for "
|
|
||||||
f"{navigation.url.toDisplayString()}")
|
|
||||||
navigation.accepted = False
|
|
||||||
self.load_url(navigation.url)
|
|
||||||
|
|
||||||
@pyqtSlot(bool)
|
@pyqtSlot(bool)
|
||||||
def _on_load_finished(self, ok: bool) -> None:
|
def _on_load_finished(self, ok: bool) -> None:
|
||||||
assert self._widget is not None
|
assert self._widget is not None
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,15 @@
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
# pylint: disable=too-many-positional-arguments
|
||||||
|
|
||||||
"""Command dispatcher for TabbedBrowser."""
|
"""Command dispatcher for TabbedBrowser."""
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import shlex
|
import shlex
|
||||||
import functools
|
import functools
|
||||||
from typing import cast, Callable, Dict, Union, Optional
|
from typing import cast, Union, Optional
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
from qutebrowser.qt.widgets import QApplication, QTabBar
|
from qutebrowser.qt.widgets import QApplication, QTabBar
|
||||||
from qutebrowser.qt.core import Qt, QUrl, QEvent, QUrlQuery
|
from qutebrowser.qt.core import Qt, QUrl, QEvent, QUrlQuery
|
||||||
|
|
@ -68,7 +71,10 @@ class CommandDispatcher:
|
||||||
|
|
||||||
def _current_index(self):
|
def _current_index(self):
|
||||||
"""Convenience method to get the current widget index."""
|
"""Convenience method to get the current widget index."""
|
||||||
return self._tabbed_browser.widget.currentIndex()
|
current_index = self._tabbed_browser.widget.currentIndex()
|
||||||
|
if current_index == -1:
|
||||||
|
raise cmdutils.CommandError("No WebView available yet!")
|
||||||
|
return current_index
|
||||||
|
|
||||||
def _current_url(self):
|
def _current_url(self):
|
||||||
"""Convenience method to get the current url."""
|
"""Convenience method to get the current url."""
|
||||||
|
|
@ -636,7 +642,7 @@ class CommandDispatcher:
|
||||||
widget = self._current_widget()
|
widget = self._current_widget()
|
||||||
url = self._current_url()
|
url = self._current_url()
|
||||||
|
|
||||||
handlers: Dict[str, Callable[..., QUrl]] = {
|
handlers: dict[str, Callable[..., QUrl]] = {
|
||||||
'prev': functools.partial(navigate.prevnext, prev=True),
|
'prev': functools.partial(navigate.prevnext, prev=True),
|
||||||
'next': functools.partial(navigate.prevnext, prev=False),
|
'next': functools.partial(navigate.prevnext, prev=False),
|
||||||
'up': navigate.path_up,
|
'up': navigate.path_up,
|
||||||
|
|
@ -862,10 +868,6 @@ class CommandDispatcher:
|
||||||
Args:
|
Args:
|
||||||
count: How many tabs to switch back.
|
count: How many tabs to switch back.
|
||||||
"""
|
"""
|
||||||
if self._count() == 0:
|
|
||||||
# Running :tab-prev after last tab was closed
|
|
||||||
# See https://github.com/qutebrowser/qutebrowser/issues/1448
|
|
||||||
return
|
|
||||||
newidx = self._current_index() - count
|
newidx = self._current_index() - count
|
||||||
if newidx >= 0:
|
if newidx >= 0:
|
||||||
self._set_current_index(newidx)
|
self._set_current_index(newidx)
|
||||||
|
|
@ -882,10 +884,6 @@ class CommandDispatcher:
|
||||||
Args:
|
Args:
|
||||||
count: How many tabs to switch forward.
|
count: How many tabs to switch forward.
|
||||||
"""
|
"""
|
||||||
if self._count() == 0:
|
|
||||||
# Running :tab-next after last tab was closed
|
|
||||||
# See https://github.com/qutebrowser/qutebrowser/issues/1448
|
|
||||||
return
|
|
||||||
newidx = self._current_index() + count
|
newidx = self._current_index() + count
|
||||||
if newidx < self._count():
|
if newidx < self._count():
|
||||||
self._set_current_index(newidx)
|
self._set_current_index(newidx)
|
||||||
|
|
@ -1136,8 +1134,7 @@ class CommandDispatcher:
|
||||||
else:
|
else:
|
||||||
cmd = os.path.expanduser(cmd)
|
cmd = os.path.expanduser(cmd)
|
||||||
proc = guiprocess.GUIProcess(what='command', verbose=verbose,
|
proc = guiprocess.GUIProcess(what='command', verbose=verbose,
|
||||||
output_messages=output_messages,
|
output_messages=output_messages)
|
||||||
parent=self._tabbed_browser)
|
|
||||||
if detach:
|
if detach:
|
||||||
ok = proc.start_detached(cmd, args)
|
ok = proc.start_detached(cmd, args)
|
||||||
if not ok:
|
if not ok:
|
||||||
|
|
@ -1166,7 +1163,7 @@ class CommandDispatcher:
|
||||||
if count is not None:
|
if count is not None:
|
||||||
env['QUTE_COUNT'] = str(count)
|
env['QUTE_COUNT'] = str(count)
|
||||||
|
|
||||||
idx = self._current_index()
|
idx = self._tabbed_browser.widget.currentIndex()
|
||||||
if idx != -1:
|
if idx != -1:
|
||||||
env['QUTE_TAB_INDEX'] = str(idx + 1)
|
env['QUTE_TAB_INDEX'] = str(idx + 1)
|
||||||
env['QUTE_TITLE'] = self._tabbed_browser.widget.page_title(idx)
|
env['QUTE_TITLE'] = self._tabbed_browser.widget.page_title(idx)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,8 @@ import functools
|
||||||
import pathlib
|
import pathlib
|
||||||
import tempfile
|
import tempfile
|
||||||
import enum
|
import enum
|
||||||
from typing import Any, Dict, IO, List, MutableSequence, Optional, Union
|
from typing import Any, IO, Optional, Union
|
||||||
|
from collections.abc import MutableSequence
|
||||||
|
|
||||||
from qutebrowser.qt.core import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
|
from qutebrowser.qt.core import (pyqtSlot, pyqtSignal, Qt, QObject, QModelIndex,
|
||||||
QTimer, QAbstractListModel, QUrl)
|
QTimer, QAbstractListModel, QUrl)
|
||||||
|
|
@ -187,15 +188,22 @@ def transform_path(path):
|
||||||
"""
|
"""
|
||||||
if not utils.is_windows:
|
if not utils.is_windows:
|
||||||
return path
|
return path
|
||||||
|
|
||||||
path = utils.expand_windows_drive(path)
|
path = utils.expand_windows_drive(path)
|
||||||
# Drive dependent working directories are not supported, e.g.
|
# Drive dependent working directories are not supported, e.g.
|
||||||
# E:filename is invalid
|
# E:filename is invalid
|
||||||
if re.search(r'^[A-Z]:[^\\]', path, re.IGNORECASE):
|
if re.search(r'^[A-Z]:[^\\]', path, re.IGNORECASE):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Paths like COM1, ...
|
# Paths like COM1, ...
|
||||||
# See https://github.com/qutebrowser/qutebrowser/issues/82
|
# See https://github.com/qutebrowser/qutebrowser/issues/82
|
||||||
if pathlib.Path(path).is_reserved():
|
if sys.version_info[:2] >= (3, 13):
|
||||||
return None
|
if os.path.isreserved(path): # pylint: disable=no-member
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
if pathlib.Path(path).is_reserved(): # pylint: disable=else-if-used
|
||||||
|
return None
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -447,7 +455,7 @@ class AbstractDownloadItem(QObject):
|
||||||
UnsupportedAttribute, IO[bytes], None
|
UnsupportedAttribute, IO[bytes], None
|
||||||
] = UnsupportedAttribute()
|
] = UnsupportedAttribute()
|
||||||
self.raw_headers: Union[
|
self.raw_headers: Union[
|
||||||
UnsupportedAttribute, Dict[bytes, bytes]
|
UnsupportedAttribute, dict[bytes, bytes]
|
||||||
] = UnsupportedAttribute()
|
] = UnsupportedAttribute()
|
||||||
|
|
||||||
self._filename: Optional[str] = None
|
self._filename: Optional[str] = None
|
||||||
|
|
@ -899,7 +907,7 @@ class AbstractDownloadManager(QObject):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.downloads: List[AbstractDownloadItem] = []
|
self.downloads: list[AbstractDownloadItem] = []
|
||||||
self._update_timer = usertypes.Timer(self, 'download-update')
|
self._update_timer = usertypes.Timer(self, 'download-update')
|
||||||
self._update_timer.timeout.connect(self._update_gui)
|
self._update_timer.timeout.connect(self._update_gui)
|
||||||
self._update_timer.setInterval(_REFRESH_INTERVAL)
|
self._update_timer.setInterval(_REFRESH_INTERVAL)
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
"""The ListView to display downloads in."""
|
"""The ListView to display downloads in."""
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
from typing import Callable, MutableSequence, Tuple, Union
|
from typing import Union
|
||||||
|
from collections.abc import MutableSequence, Callable
|
||||||
|
|
||||||
from qutebrowser.qt.core import pyqtSlot, QSize, Qt
|
from qutebrowser.qt.core import pyqtSlot, QSize, Qt
|
||||||
from qutebrowser.qt.widgets import QListView, QSizePolicy, QMenu, QStyleFactory
|
from qutebrowser.qt.widgets import QListView, QSizePolicy, QMenu, QStyleFactory
|
||||||
|
|
@ -17,8 +18,8 @@ from qutebrowser.utils import qtutils, utils
|
||||||
|
|
||||||
_ActionListType = MutableSequence[
|
_ActionListType = MutableSequence[
|
||||||
Union[
|
Union[
|
||||||
Tuple[None, None], # separator
|
tuple[None, None], # separator
|
||||||
Tuple[str, Callable[[], None]],
|
tuple[str, Callable[[], None]],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,12 @@
|
||||||
|
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
from qutebrowser.qt.core import QObject, QEvent, Qt, QTimer
|
from qutebrowser.qt.core import QObject, QEvent, Qt, QTimer
|
||||||
|
from qutebrowser.qt.gui import QKeyEvent
|
||||||
from qutebrowser.qt.widgets import QWidget
|
from qutebrowser.qt.widgets import QWidget
|
||||||
|
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
from qutebrowser.utils import log, message, usertypes, qtutils
|
from qutebrowser.utils import log, message, usertypes, qtutils, version, utils
|
||||||
from qutebrowser.keyinput import modeman
|
from qutebrowser.keyinput import modeman, keyutils
|
||||||
|
|
||||||
|
|
||||||
class ChildEventFilter(QObject):
|
class ChildEventFilter(QObject):
|
||||||
|
|
@ -37,8 +38,8 @@ class ChildEventFilter(QObject):
|
||||||
if event.type() == QEvent.Type.ChildAdded:
|
if event.type() == QEvent.Type.ChildAdded:
|
||||||
child = event.child()
|
child = event.child()
|
||||||
if not isinstance(child, QWidget):
|
if not isinstance(child, QWidget):
|
||||||
# Can e.g. happen when dragging text
|
# Can e.g. happen when dragging text, or accessibility tree
|
||||||
log.misc.debug(f"Ignoring new child {qtutils.qobj_repr(child)}")
|
# nodes since Qt 6.9
|
||||||
return False
|
return False
|
||||||
|
|
||||||
log.misc.debug(
|
log.misc.debug(
|
||||||
|
|
@ -54,21 +55,30 @@ class ChildEventFilter(QObject):
|
||||||
# - This is a child event filter on a tab (self._widget is not None)
|
# - This is a child event filter on a tab (self._widget is not None)
|
||||||
# - We find an old existing child which is a QQuickWidget and is
|
# - We find an old existing child which is a QQuickWidget and is
|
||||||
# currently focused.
|
# currently focused.
|
||||||
# - We're using QtWebEngine >= 6.4 (older versions are not affected)
|
# - We're using an affected QtWebEngine version
|
||||||
children = [
|
children = [
|
||||||
c for c in self._widget.findChildren(
|
c for c in self._widget.findChildren(
|
||||||
QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
|
QWidget, "", Qt.FindChildOption.FindDirectChildrenOnly)
|
||||||
if c is not child and
|
if c is not child and
|
||||||
c.hasFocus() and
|
c.hasFocus() and
|
||||||
c.metaObject() is not None and
|
c.metaObject() is not None and
|
||||||
c.metaObject().className() == "QQuickWidget"
|
c.metaObject().className() == "QQuickWidget" # Qt 6.4+
|
||||||
]
|
]
|
||||||
if children:
|
if children and version.qtwebengine_versions().webengine < utils.VersionNumber(6, 6, 3):
|
||||||
log.misc.debug("Focusing new child")
|
log.misc.debug("Focusing new child")
|
||||||
child.setFocus()
|
child.setFocus()
|
||||||
|
|
||||||
child.installEventFilter(self._filter)
|
child.installEventFilter(self._filter)
|
||||||
elif event.type() == QEvent.Type.ChildRemoved:
|
elif event.type() == QEvent.Type.ChildRemoved:
|
||||||
|
if isinstance(event, QKeyEvent):
|
||||||
|
# WORKAROUND for unknown (Py)Qt bug
|
||||||
|
info = keyutils.KeyInfo.from_event(event)
|
||||||
|
log.misc.warning(
|
||||||
|
f"ChildEventFilter: ignoring key event {info} "
|
||||||
|
f"on {qtutils.qobj_repr(obj)}"
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
child = event.child()
|
child = event.child()
|
||||||
log.misc.debug(
|
log.misc.debug(
|
||||||
f"{qtutils.qobj_repr(obj)}: removed child {qtutils.qobj_repr(child)}")
|
f"{qtutils.qobj_repr(obj)}: removed child {qtutils.qobj_repr(child)}")
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ import functools
|
||||||
import glob
|
import glob
|
||||||
import textwrap
|
import textwrap
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import cast, List, Sequence, Tuple, Optional
|
from typing import cast, Optional
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from qutebrowser.qt.core import pyqtSignal, QObject, QUrl
|
from qutebrowser.qt.core import pyqtSignal, QObject, QUrl
|
||||||
|
|
||||||
|
|
@ -207,9 +208,9 @@ class MatchingScripts:
|
||||||
"""All userscripts registered to run on a particular url."""
|
"""All userscripts registered to run on a particular url."""
|
||||||
|
|
||||||
url: QUrl
|
url: QUrl
|
||||||
start: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
start: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||||
end: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
end: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||||
idle: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
idle: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
@dataclasses.dataclass
|
||||||
|
|
@ -217,8 +218,8 @@ class LoadResults:
|
||||||
|
|
||||||
"""The results of loading all Greasemonkey scripts."""
|
"""The results of loading all Greasemonkey scripts."""
|
||||||
|
|
||||||
successful: List[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
successful: list[GreasemonkeyScript] = dataclasses.field(default_factory=list)
|
||||||
errors: List[Tuple[str, str]] = dataclasses.field(default_factory=list)
|
errors: list[tuple[str, str]] = dataclasses.field(default_factory=list)
|
||||||
|
|
||||||
def successful_str(self) -> str:
|
def successful_str(self) -> str:
|
||||||
"""Get a string with all successfully loaded scripts.
|
"""Get a string with all successfully loaded scripts.
|
||||||
|
|
@ -294,10 +295,10 @@ class GreasemonkeyManager(QObject):
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._run_start: List[GreasemonkeyScript] = []
|
self._run_start: list[GreasemonkeyScript] = []
|
||||||
self._run_end: List[GreasemonkeyScript] = []
|
self._run_end: list[GreasemonkeyScript] = []
|
||||||
self._run_idle: List[GreasemonkeyScript] = []
|
self._run_idle: list[GreasemonkeyScript] = []
|
||||||
self._in_progress_dls: List[downloads.AbstractDownloadItem] = []
|
self._in_progress_dls: list[downloads.AbstractDownloadItem] = []
|
||||||
|
|
||||||
def load_scripts(self, *, force: bool = False) -> LoadResults:
|
def load_scripts(self, *, force: bool = False) -> LoadResults:
|
||||||
"""Re-read Greasemonkey scripts from disk.
|
"""Re-read Greasemonkey scripts from disk.
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,15 @@ import html
|
||||||
import enum
|
import enum
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from string import ascii_lowercase
|
from string import ascii_lowercase
|
||||||
from typing import (TYPE_CHECKING, Callable, Dict, Iterable, Iterator, List, Mapping,
|
from typing import (TYPE_CHECKING, Optional)
|
||||||
MutableSequence, Optional, Sequence, Set)
|
from collections.abc import (
|
||||||
|
Iterable,
|
||||||
|
Iterator,
|
||||||
|
Mapping,
|
||||||
|
MutableSequence,
|
||||||
|
Sequence,
|
||||||
|
Callable,
|
||||||
|
)
|
||||||
|
|
||||||
from qutebrowser.qt.core import pyqtSignal, pyqtSlot, QObject, Qt, QUrl
|
from qutebrowser.qt.core import pyqtSignal, pyqtSlot, QObject, Qt, QUrl
|
||||||
from qutebrowser.qt.widgets import QLabel
|
from qutebrowser.qt.widgets import QLabel
|
||||||
|
|
@ -175,11 +182,11 @@ class HintContext:
|
||||||
add_history: bool
|
add_history: bool
|
||||||
first: bool
|
first: bool
|
||||||
baseurl: QUrl
|
baseurl: QUrl
|
||||||
args: List[str]
|
args: list[str]
|
||||||
group: str
|
group: str
|
||||||
|
|
||||||
all_labels: List[HintLabel] = dataclasses.field(default_factory=list)
|
all_labels: list[HintLabel] = dataclasses.field(default_factory=list)
|
||||||
labels: Dict[str, HintLabel] = dataclasses.field(default_factory=dict)
|
labels: dict[str, HintLabel] = dataclasses.field(default_factory=dict)
|
||||||
to_follow: Optional[str] = None
|
to_follow: Optional[str] = None
|
||||||
first_run: bool = True
|
first_run: bool = True
|
||||||
filterstr: Optional[str] = None
|
filterstr: Optional[str] = None
|
||||||
|
|
@ -1033,7 +1040,7 @@ class WordHinter:
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
# will be initialized on first use.
|
# will be initialized on first use.
|
||||||
self.words: Set[str] = set()
|
self.words: set[str] = set()
|
||||||
self.dictionary = None
|
self.dictionary = None
|
||||||
|
|
||||||
def ensure_initialized(self) -> None:
|
def ensure_initialized(self) -> None:
|
||||||
|
|
@ -1143,7 +1150,7 @@ class WordHinter:
|
||||||
"""
|
"""
|
||||||
self.ensure_initialized()
|
self.ensure_initialized()
|
||||||
hints = []
|
hints = []
|
||||||
used_hints: Set[str] = set()
|
used_hints: set[str] = set()
|
||||||
words = iter(self.words)
|
words = iter(self.words)
|
||||||
for elem in elems:
|
for elem in elems:
|
||||||
hint = self.new_hint_for(elem, used_hints, words)
|
hint = self.new_hint_for(elem, used_hints, words)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ import os
|
||||||
import time
|
import time
|
||||||
import contextlib
|
import contextlib
|
||||||
import pathlib
|
import pathlib
|
||||||
from typing import cast, Mapping, MutableSequence, Optional
|
from typing import cast, Optional
|
||||||
|
from collections.abc import Mapping, MutableSequence
|
||||||
|
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
from qutebrowser.qt.core import pyqtSlot, QUrl, QObject, pyqtSignal
|
from qutebrowser.qt.core import pyqtSlot, QUrl, QObject, pyqtSignal
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import posixpath
|
import posixpath
|
||||||
from typing import Optional, Set
|
from typing import Optional
|
||||||
|
|
||||||
from qutebrowser.qt.core import QUrl
|
from qutebrowser.qt.core import QUrl
|
||||||
|
|
||||||
|
|
@ -79,7 +79,7 @@ def incdec(url, count, inc_or_dec):
|
||||||
inc_or_dec: Either 'increment' or 'decrement'.
|
inc_or_dec: Either 'increment' or 'decrement'.
|
||||||
"""
|
"""
|
||||||
urlutils.ensure_valid(url)
|
urlutils.ensure_valid(url)
|
||||||
segments: Optional[Set[str]] = (
|
segments: Optional[set[str]] = (
|
||||||
set(config.val.url.incdec_segments)
|
set(config.val.url.incdec_segments)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ class PACFetcher(QObject):
|
||||||
pac_prefix = "pac+"
|
pac_prefix = "pac+"
|
||||||
|
|
||||||
assert url.scheme().startswith(pac_prefix)
|
assert url.scheme().startswith(pac_prefix)
|
||||||
url.setScheme(url.scheme()[len(pac_prefix):])
|
url.setScheme(url.scheme().removeprefix(pac_prefix))
|
||||||
|
|
||||||
self._pac_url = url
|
self._pac_url = url
|
||||||
with qtlog.disable_qt_msghandler():
|
with qtlog.disable_qt_msghandler():
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
"""Handling of proxies."""
|
"""Handling of proxies."""
|
||||||
|
|
||||||
from typing import Optional, List
|
from typing import Optional
|
||||||
|
|
||||||
from qutebrowser.qt.core import QUrl, pyqtSlot
|
from qutebrowser.qt.core import QUrl, pyqtSlot
|
||||||
from qutebrowser.qt.network import QNetworkProxy, QNetworkProxyFactory, QNetworkProxyQuery
|
from qutebrowser.qt.network import QNetworkProxy, QNetworkProxyFactory, QNetworkProxyQuery
|
||||||
|
|
@ -71,7 +71,7 @@ class ProxyFactory(QNetworkProxyFactory):
|
||||||
capabilities &= ~lookup_cap
|
capabilities &= ~lookup_cap
|
||||||
proxy.setCapabilities(capabilities)
|
proxy.setCapabilities(capabilities)
|
||||||
|
|
||||||
def queryProxy(self, query: QNetworkProxyQuery = QNetworkProxyQuery()) -> List[QNetworkProxy]:
|
def queryProxy(self, query: QNetworkProxyQuery = QNetworkProxyQuery()) -> list[QNetworkProxy]:
|
||||||
"""Get the QNetworkProxies for a query.
|
"""Get the QNetworkProxies for a query.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from qutebrowser.qt.core import QUrl, QUrlQuery
|
||||||
|
|
||||||
from qutebrowser.utils import resources, javascript, jinja, standarddir, log, urlutils
|
from qutebrowser.utils import resources, javascript, jinja, standarddir, log, urlutils
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
|
from qutebrowser.misc import objects
|
||||||
|
|
||||||
|
|
||||||
_SYSTEM_PATHS = [
|
_SYSTEM_PATHS = [
|
||||||
|
|
@ -69,19 +70,8 @@ def generate_pdfjs_page(filename, url):
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
|
||||||
def _generate_polyfills():
|
def _get_polyfills() -> str:
|
||||||
return """
|
return resources.read_file("javascript/pdfjs_polyfills.js")
|
||||||
if (typeof Promise.withResolvers === 'undefined') {
|
|
||||||
Promise.withResolvers = function () {
|
|
||||||
let resolve, reject
|
|
||||||
const promise = new Promise((res, rej) => {
|
|
||||||
resolve = res
|
|
||||||
reject = rej
|
|
||||||
})
|
|
||||||
return { promise, resolve, reject }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def _generate_pdfjs_script(filename):
|
def _generate_pdfjs_script(filename):
|
||||||
|
|
@ -121,7 +111,7 @@ def _generate_pdfjs_script(filename):
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
""").render(url=js_url, polyfills=_generate_polyfills())
|
""").render(url=js_url, polyfills=_get_polyfills())
|
||||||
|
|
||||||
|
|
||||||
def get_pdfjs_res_and_path(path):
|
def get_pdfjs_res_and_path(path):
|
||||||
|
|
@ -138,7 +128,12 @@ def get_pdfjs_res_and_path(path):
|
||||||
content = None
|
content = None
|
||||||
file_path = None
|
file_path = None
|
||||||
|
|
||||||
system_paths = _SYSTEM_PATHS + [
|
if 'no-system-pdfjs' in objects.debug_flags:
|
||||||
|
system_paths = []
|
||||||
|
else:
|
||||||
|
system_paths = _SYSTEM_PATHS[:]
|
||||||
|
|
||||||
|
system_paths += [
|
||||||
# fallback
|
# fallback
|
||||||
os.path.join(standarddir.data(), 'pdfjs'),
|
os.path.join(standarddir.data(), 'pdfjs'),
|
||||||
# hardcoded fallback for --temp-basedir
|
# hardcoded fallback for --temp-basedir
|
||||||
|
|
@ -168,7 +163,7 @@ def get_pdfjs_res_and_path(path):
|
||||||
if path == "build/pdf.worker.mjs":
|
if path == "build/pdf.worker.mjs":
|
||||||
content = b"\n".join(
|
content = b"\n".join(
|
||||||
[
|
[
|
||||||
_generate_polyfills().encode("ascii"),
|
_get_polyfills().encode("ascii"),
|
||||||
content,
|
content,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import os.path
|
||||||
import shutil
|
import shutil
|
||||||
import functools
|
import functools
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import Dict, IO, Optional
|
from typing import IO, Optional
|
||||||
|
|
||||||
from qutebrowser.qt.core import pyqtSlot, pyqtSignal, QTimer, QUrl
|
from qutebrowser.qt.core import pyqtSlot, pyqtSignal, QTimer, QUrl
|
||||||
from qutebrowser.qt.widgets import QApplication
|
from qutebrowser.qt.widgets import QApplication
|
||||||
|
|
@ -73,7 +73,7 @@ class DownloadItem(downloads.AbstractDownloadItem):
|
||||||
"""
|
"""
|
||||||
super().__init__(manager=manager, parent=manager)
|
super().__init__(manager=manager, parent=manager)
|
||||||
self.fileobj: Optional[IO[bytes]] = None
|
self.fileobj: Optional[IO[bytes]] = None
|
||||||
self.raw_headers: Dict[bytes, bytes] = {}
|
self.raw_headers: dict[bytes, bytes] = {}
|
||||||
|
|
||||||
self._autoclose = True
|
self._autoclose = True
|
||||||
self._retry_info = None
|
self._retry_info = None
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ import textwrap
|
||||||
import urllib
|
import urllib
|
||||||
import collections
|
import collections
|
||||||
import secrets
|
import secrets
|
||||||
from typing import TypeVar, Callable, Dict, List, Optional, Union, Sequence, Tuple
|
from typing import TypeVar, Optional, Union
|
||||||
|
from collections.abc import Sequence, Callable
|
||||||
|
|
||||||
from qutebrowser.qt.core import QUrlQuery, QUrl
|
from qutebrowser.qt.core import QUrlQuery, QUrl
|
||||||
|
|
||||||
|
|
@ -35,7 +36,7 @@ pyeval_output = ":pyeval was never called"
|
||||||
csrf_token: Optional[str] = None
|
csrf_token: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
_HANDLERS: Dict[str, "_HandlerCallable"] = {}
|
_HANDLERS: dict[str, "_HandlerCallable"] = {}
|
||||||
|
|
||||||
|
|
||||||
class Error(Exception):
|
class Error(Exception):
|
||||||
|
|
@ -77,7 +78,7 @@ class Redirect(Exception):
|
||||||
|
|
||||||
|
|
||||||
# Return value: (mimetype, data) (encoded as utf-8 if a str is returned)
|
# Return value: (mimetype, data) (encoded as utf-8 if a str is returned)
|
||||||
_HandlerRet = Tuple[str, Union[str, bytes]]
|
_HandlerRet = tuple[str, Union[str, bytes]]
|
||||||
_HandlerCallable = Callable[[QUrl], _HandlerRet]
|
_HandlerCallable = Callable[[QUrl], _HandlerRet]
|
||||||
_Handler = TypeVar('_Handler', bound=_HandlerCallable)
|
_Handler = TypeVar('_Handler', bound=_HandlerCallable)
|
||||||
|
|
||||||
|
|
@ -105,7 +106,7 @@ class add_handler: # noqa: N801,N806 pylint: disable=invalid-name
|
||||||
return self._function(url)
|
return self._function(url)
|
||||||
|
|
||||||
|
|
||||||
def data_for_url(url: QUrl) -> Tuple[str, bytes]:
|
def data_for_url(url: QUrl) -> tuple[str, bytes]:
|
||||||
"""Get the data to show for the given URL.
|
"""Get the data to show for the given URL.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -122,25 +123,24 @@ def data_for_url(url: QUrl) -> Tuple[str, bytes]:
|
||||||
|
|
||||||
path = url.path()
|
path = url.path()
|
||||||
host = url.host()
|
host = url.host()
|
||||||
query = url.query()
|
|
||||||
# A url like "qute:foo" is split as "scheme:path", not "scheme:host".
|
# A url like "qute:foo" is split as "scheme:path", not "scheme:host".
|
||||||
log.misc.debug("url: {}, path: {}, host {}".format(
|
log.misc.debug("url: {}, path: {}, host {}".format(
|
||||||
url.toDisplayString(), path, host))
|
url.toDisplayString(), path, host))
|
||||||
if not path or not host:
|
|
||||||
new_url = QUrl()
|
|
||||||
new_url.setScheme('qute')
|
|
||||||
# When path is absent, e.g. qute://help (with no trailing slash)
|
|
||||||
if host:
|
|
||||||
new_url.setHost(host)
|
|
||||||
# When host is absent, e.g. qute:help
|
|
||||||
else:
|
|
||||||
new_url.setHost(path)
|
|
||||||
|
|
||||||
|
if not host:
|
||||||
|
# Redirect qute:help -> qute://help/
|
||||||
|
new_url = QUrl(url)
|
||||||
|
new_url.setHost(path)
|
||||||
new_url.setPath('/')
|
new_url.setPath('/')
|
||||||
if query:
|
if not new_url.host(): # Valid path but not valid host
|
||||||
new_url.setQuery(query)
|
raise UrlInvalidError(f"Invalid host (from path): {path!r}")
|
||||||
if new_url.host(): # path was a valid host
|
raise Redirect(new_url)
|
||||||
raise Redirect(new_url)
|
|
||||||
|
if not path:
|
||||||
|
# Redirect qute://help -> qute://help/
|
||||||
|
new_url = QUrl(url)
|
||||||
|
new_url.setPath('/')
|
||||||
|
raise Redirect(new_url)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
handler = _HANDLERS[host]
|
handler = _HANDLERS[host]
|
||||||
|
|
@ -180,7 +180,7 @@ def qute_bookmarks(_url: QUrl) -> _HandlerRet:
|
||||||
@add_handler('tabs')
|
@add_handler('tabs')
|
||||||
def qute_tabs(_url: QUrl) -> _HandlerRet:
|
def qute_tabs(_url: QUrl) -> _HandlerRet:
|
||||||
"""Handler for qute://tabs. Display information about all open tabs."""
|
"""Handler for qute://tabs. Display information about all open tabs."""
|
||||||
tabs: Dict[str, List[Tuple[str, str]]] = collections.defaultdict(list)
|
tabs: dict[str, list[tuple[str, str]]] = collections.defaultdict(list)
|
||||||
for win_id, window in objreg.window_registry.items():
|
for win_id, window in objreg.window_registry.items():
|
||||||
if sip.isdeleted(window):
|
if sip.isdeleted(window):
|
||||||
continue
|
continue
|
||||||
|
|
@ -201,7 +201,7 @@ def qute_tabs(_url: QUrl) -> _HandlerRet:
|
||||||
def history_data(
|
def history_data(
|
||||||
start_time: float,
|
start_time: float,
|
||||||
offset: int = None
|
offset: int = None
|
||||||
) -> Sequence[Dict[str, Union[str, int]]]:
|
) -> Sequence[dict[str, Union[str, int]]]:
|
||||||
"""Return history data.
|
"""Return history data.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,12 @@ import html
|
||||||
import enum
|
import enum
|
||||||
import netrc
|
import netrc
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Callable, Mapping, List, Optional, Iterable, Iterator
|
from typing import Optional
|
||||||
|
from collections.abc import Mapping, Iterable, Iterator, Callable
|
||||||
|
|
||||||
from qutebrowser.qt.core import QUrl, pyqtBoundSignal
|
from qutebrowser.qt.core import QUrl, pyqtBoundSignal
|
||||||
|
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config, configtypes
|
||||||
from qutebrowser.utils import (usertypes, message, log, objreg, jinja, utils,
|
from qutebrowser.utils import (usertypes, message, log, objreg, jinja, utils,
|
||||||
qtutils, version, urlutils)
|
qtutils, version, urlutils)
|
||||||
from qutebrowser.mainwindow import mainwindow
|
from qutebrowser.mainwindow import mainwindow
|
||||||
|
|
@ -25,8 +26,15 @@ class CallSuper(Exception):
|
||||||
"""Raised when the caller should call the superclass instead."""
|
"""Raised when the caller should call the superclass instead."""
|
||||||
|
|
||||||
|
|
||||||
def custom_headers(url):
|
def custom_headers(
|
||||||
"""Get the combined custom headers."""
|
url: QUrl, *, fallback_accept_language: bool = True
|
||||||
|
) -> list[tuple[bytes, bytes]]:
|
||||||
|
"""Get the combined custom headers.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
fallback_accept_language: Whether to include the global (rather than
|
||||||
|
per-domain override) accept language header as well.
|
||||||
|
"""
|
||||||
headers = {}
|
headers = {}
|
||||||
|
|
||||||
dnt_config = config.instance.get('content.headers.do_not_track', url=url)
|
dnt_config = config.instance.get('content.headers.do_not_track', url=url)
|
||||||
|
|
@ -40,9 +48,17 @@ def custom_headers(url):
|
||||||
encoded_value = b"" if value is None else value.encode('ascii')
|
encoded_value = b"" if value is None else value.encode('ascii')
|
||||||
headers[encoded_header] = encoded_value
|
headers[encoded_header] = encoded_value
|
||||||
|
|
||||||
|
# On QtWebEngine, we have fallback_accept_language set to False here for XHR
|
||||||
|
# requests, so that we don't end up overriding headers that are set via the XHR API.
|
||||||
|
#
|
||||||
|
# The global Accept-Language header is set via
|
||||||
|
# QWebEngineProfile::setHttpAcceptLanguage already anyways, so we only need
|
||||||
|
# to take care of URL pattern overrides here.
|
||||||
|
#
|
||||||
|
# note: Once we drop QtWebKit, we could hardcode fallback_accept_language to False.
|
||||||
accept_language = config.instance.get('content.headers.accept_language',
|
accept_language = config.instance.get('content.headers.accept_language',
|
||||||
url=url)
|
url=url, fallback=fallback_accept_language)
|
||||||
if accept_language is not None:
|
if accept_language is not None and not isinstance(accept_language, usertypes.Unset):
|
||||||
headers[b'Accept-Language'] = accept_language.encode('ascii')
|
headers[b'Accept-Language'] = accept_language.encode('ascii')
|
||||||
|
|
||||||
return sorted(headers.items())
|
return sorted(headers.items())
|
||||||
|
|
@ -303,6 +319,7 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on,
|
||||||
None otherwise.
|
None otherwise.
|
||||||
"""
|
"""
|
||||||
config_val = config.instance.get(option, url=url)
|
config_val = config.instance.get(option, url=url)
|
||||||
|
opt = config.instance.get_opt(option)
|
||||||
if config_val == 'ask':
|
if config_val == 'ask':
|
||||||
if url.isValid():
|
if url.isValid():
|
||||||
urlstr = url.toString(QUrl.UrlFormattingOption.RemovePassword | QUrl.ComponentFormattingOption.FullyEncoded)
|
urlstr = url.toString(QUrl.UrlFormattingOption.RemovePassword | QUrl.ComponentFormattingOption.FullyEncoded)
|
||||||
|
|
@ -328,12 +345,21 @@ def feature_permission(url, option, msg, yes_action, no_action, abort_on,
|
||||||
cancel_action=no_action, abort_on=abort_on,
|
cancel_action=no_action, abort_on=abort_on,
|
||||||
title='Permission request', text=text, url=urlstr,
|
title='Permission request', text=text, url=urlstr,
|
||||||
option=option)
|
option=option)
|
||||||
elif config_val:
|
|
||||||
|
if isinstance(opt.typ, configtypes.AsBool):
|
||||||
|
config_val = opt.typ.to_bool(config_val)
|
||||||
|
|
||||||
|
if config_val is True:
|
||||||
yes_action()
|
yes_action()
|
||||||
return None
|
return None
|
||||||
else:
|
elif config_val is False:
|
||||||
no_action()
|
no_action()
|
||||||
return None
|
return None
|
||||||
|
else:
|
||||||
|
raise AssertionError(
|
||||||
|
f"Unsupported value for permission prompt setting ({option}), expected boolean or "
|
||||||
|
f"'ask', got: {config_val} ({type(config_val)})"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_tab(win_id, target):
|
def get_tab(win_id, target):
|
||||||
|
|
@ -445,7 +471,7 @@ class FileSelectionMode(enum.Enum):
|
||||||
folder = enum.auto()
|
folder = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
def choose_file(qb_mode: FileSelectionMode) -> List[str]:
|
def choose_file(qb_mode: FileSelectionMode) -> list[str]:
|
||||||
"""Select file(s)/folder for up-/downloading, using an external command.
|
"""Select file(s)/folder for up-/downloading, using an external command.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -485,10 +511,10 @@ def choose_file(qb_mode: FileSelectionMode) -> List[str]:
|
||||||
|
|
||||||
|
|
||||||
def _execute_fileselect_command(
|
def _execute_fileselect_command(
|
||||||
command: List[str],
|
command: list[str],
|
||||||
qb_mode: FileSelectionMode,
|
qb_mode: FileSelectionMode,
|
||||||
tmpfilename: Optional[str] = None
|
tmpfilename: Optional[str] = None
|
||||||
) -> List[str]:
|
) -> list[str]:
|
||||||
"""Execute external command to choose file.
|
"""Execute external command to choose file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -522,7 +548,7 @@ def _execute_fileselect_command(
|
||||||
|
|
||||||
def _validated_selected_files(
|
def _validated_selected_files(
|
||||||
qb_mode: FileSelectionMode,
|
qb_mode: FileSelectionMode,
|
||||||
selected_files: List[str],
|
selected_files: list[str],
|
||||||
) -> Iterator[str]:
|
) -> Iterator[str]:
|
||||||
"""Validates selected files if they are.
|
"""Validates selected files if they are.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import os.path
|
||||||
import html
|
import html
|
||||||
import functools
|
import functools
|
||||||
import collections
|
import collections
|
||||||
from typing import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
|
|
||||||
from qutebrowser.qt.core import pyqtSignal, QUrl, QObject
|
from qutebrowser.qt.core import pyqtSignal, QUrl, QObject
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
"""Generic web element related code."""
|
"""Generic web element related code."""
|
||||||
|
|
||||||
from typing import Iterator, Optional, Set, TYPE_CHECKING, Union, Dict
|
from typing import Optional, TYPE_CHECKING, Union
|
||||||
|
from collections.abc import Iterator
|
||||||
import collections.abc
|
import collections.abc
|
||||||
|
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
|
|
@ -93,7 +94,7 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
|
||||||
"""Get the geometry for this element."""
|
"""Get the geometry for this element."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def classes(self) -> Set[str]:
|
def classes(self) -> set[str]:
|
||||||
"""Get a set of classes assigned to this element."""
|
"""Get a set of classes assigned to this element."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
@ -336,7 +337,7 @@ class AbstractWebElement(collections.abc.MutableMapping): # type: ignore[type-a
|
||||||
log.webelem.debug("Sending fake click to {!r} at position {} with "
|
log.webelem.debug("Sending fake click to {!r} at position {} with "
|
||||||
"target {}".format(self, pos, click_target))
|
"target {}".format(self, pos, click_target))
|
||||||
|
|
||||||
target_modifiers: Dict[usertypes.ClickTarget, KeyboardModifierType] = {
|
target_modifiers: dict[usertypes.ClickTarget, KeyboardModifierType] = {
|
||||||
usertypes.ClickTarget.normal: Qt.KeyboardModifier.NoModifier,
|
usertypes.ClickTarget.normal: Qt.KeyboardModifier.NoModifier,
|
||||||
usertypes.ClickTarget.window: Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.ShiftModifier,
|
usertypes.ClickTarget.window: Qt.KeyboardModifier.AltModifier | Qt.KeyboardModifier.ShiftModifier,
|
||||||
usertypes.ClickTarget.tab: Qt.KeyboardModifier.ControlModifier,
|
usertypes.ClickTarget.tab: Qt.KeyboardModifier.ControlModifier,
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,8 @@ import copy
|
||||||
import enum
|
import enum
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import collections
|
import collections
|
||||||
from typing import (Any, Iterator, Mapping, MutableMapping, Optional, Set, Tuple, Union,
|
from typing import (Any, Optional, Union)
|
||||||
Sequence, List)
|
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
|
||||||
|
|
||||||
from qutebrowser.config import config
|
from qutebrowser.config import config
|
||||||
from qutebrowser.utils import usertypes, utils, log, version
|
from qutebrowser.utils import usertypes, utils, log, version
|
||||||
|
|
@ -212,7 +212,7 @@ class _Setting:
|
||||||
return str(value)
|
return str(value)
|
||||||
return str(self.mapping[value])
|
return str(self.mapping[value])
|
||||||
|
|
||||||
def chromium_tuple(self, value: Any) -> Optional[Tuple[str, str]]:
|
def chromium_tuple(self, value: Any) -> Optional[tuple[str, str]]:
|
||||||
"""Get the Chromium key and value, or None if no value should be set."""
|
"""Get the Chromium key and value, or None if no value should be set."""
|
||||||
if self.mapping is not None and self.mapping[value] is None:
|
if self.mapping is not None and self.mapping[value] is None:
|
||||||
return None
|
return None
|
||||||
|
|
@ -242,7 +242,7 @@ class _Definition:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*args: _Setting,
|
*args: _Setting,
|
||||||
mandatory: Set[str],
|
mandatory: set[str],
|
||||||
prefix: str,
|
prefix: str,
|
||||||
switch_names: Mapping[Optional[str], str] = None,
|
switch_names: Mapping[Optional[str], str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
@ -255,7 +255,7 @@ class _Definition:
|
||||||
else:
|
else:
|
||||||
self._switch_names = {None: _BLINK_SETTINGS}
|
self._switch_names = {None: _BLINK_SETTINGS}
|
||||||
|
|
||||||
def prefixed_settings(self) -> Iterator[Tuple[str, _Setting]]:
|
def prefixed_settings(self) -> Iterator[tuple[str, _Setting]]:
|
||||||
"""Get all "prepared" settings.
|
"""Get all "prepared" settings.
|
||||||
|
|
||||||
Yields tuples which contain the Chromium setting key (e.g. 'blink-settings' or
|
Yields tuples which contain the Chromium setting key (e.g. 'blink-settings' or
|
||||||
|
|
@ -399,7 +399,7 @@ def settings(
|
||||||
*,
|
*,
|
||||||
versions: version.WebEngineVersions,
|
versions: version.WebEngineVersions,
|
||||||
special_flags: Sequence[str],
|
special_flags: Sequence[str],
|
||||||
) -> Mapping[str, Sequence[Tuple[str, str]]]:
|
) -> Mapping[str, Sequence[tuple[str, str]]]:
|
||||||
"""Get necessary blink settings to configure dark mode for QtWebEngine.
|
"""Get necessary blink settings to configure dark mode for QtWebEngine.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -413,12 +413,12 @@ def settings(
|
||||||
variant = _variant(versions)
|
variant = _variant(versions)
|
||||||
log.init.debug(f"Darkmode variant: {variant.name}")
|
log.init.debug(f"Darkmode variant: {variant.name}")
|
||||||
|
|
||||||
result: Mapping[str, List[Tuple[str, str]]] = collections.defaultdict(list)
|
result: Mapping[str, list[tuple[str, str]]] = collections.defaultdict(list)
|
||||||
|
|
||||||
blink_settings_flag = f'--{_BLINK_SETTINGS}='
|
blink_settings_flag = f'--{_BLINK_SETTINGS}='
|
||||||
for flag in special_flags:
|
for flag in special_flags:
|
||||||
if flag.startswith(blink_settings_flag):
|
if flag.startswith(blink_settings_flag):
|
||||||
for pair in flag[len(blink_settings_flag):].split(','):
|
for pair in flag.removeprefix(blink_settings_flag).split(','):
|
||||||
key, val = pair.split('=', maxsplit=1)
|
key, val = pair.split('=', maxsplit=1)
|
||||||
result[_BLINK_SETTINGS].append((key, val))
|
result[_BLINK_SETTINGS].append((key, val))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
||||||
}
|
}
|
||||||
new_types = {
|
new_types = {
|
||||||
"WebSocket": interceptors.ResourceType.websocket, # added in Qt 6.4
|
"WebSocket": interceptors.ResourceType.websocket, # added in Qt 6.4
|
||||||
|
"Json": interceptors.ResourceType.json, # added in Qt 6.8
|
||||||
}
|
}
|
||||||
for qt_name, qb_value in new_types.items():
|
for qt_name, qb_value in new_types.items():
|
||||||
qt_value = getattr(
|
qt_value = getattr(
|
||||||
|
|
@ -187,7 +188,9 @@ class RequestInterceptor(QWebEngineUrlRequestInterceptor):
|
||||||
if request.is_blocked:
|
if request.is_blocked:
|
||||||
info.block(True)
|
info.block(True)
|
||||||
|
|
||||||
for header, value in shared.custom_headers(url=url):
|
for header, value in shared.custom_headers(
|
||||||
|
url=url, fallback_accept_language=not is_xhr
|
||||||
|
):
|
||||||
if header.lower() == b'accept' and is_xhr:
|
if header.lower() == b'accept' and is_xhr:
|
||||||
# https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader
|
# https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader
|
||||||
# says: "If no Accept header has been set using this, an Accept header
|
# says: "If no Accept header has been set using this, an Accept header
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@ import dataclasses
|
||||||
import itertools
|
import itertools
|
||||||
import functools
|
import functools
|
||||||
import subprocess
|
import subprocess
|
||||||
from typing import Any, List, Dict, Optional, Iterator, Type, TYPE_CHECKING
|
from typing import Any, Optional, TYPE_CHECKING
|
||||||
|
from collections.abc import Iterator
|
||||||
|
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
from qutebrowser.qt.core import (Qt, QObject, QVariant, QMetaType, QByteArray, pyqtSlot,
|
from qutebrowser.qt.core import (Qt, QObject, QVariant, QMetaType, QByteArray, pyqtSlot,
|
||||||
|
|
@ -195,7 +196,7 @@ class NotificationBridgePresenter(QObject):
|
||||||
def __init__(self, parent: QObject = None) -> None:
|
def __init__(self, parent: QObject = None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
self._active_notifications: Dict[int, 'QWebEngineNotification'] = {}
|
self._active_notifications: dict[int, 'QWebEngineNotification'] = {}
|
||||||
self._adapter: Optional[AbstractNotificationAdapter] = None
|
self._adapter: Optional[AbstractNotificationAdapter] = None
|
||||||
|
|
||||||
config.instance.changed.connect(self._init_adapter)
|
config.instance.changed.connect(self._init_adapter)
|
||||||
|
|
@ -232,8 +233,8 @@ class NotificationBridgePresenter(QObject):
|
||||||
def _get_adapter_candidates(
|
def _get_adapter_candidates(
|
||||||
self,
|
self,
|
||||||
setting: str,
|
setting: str,
|
||||||
) -> List[Type[AbstractNotificationAdapter]]:
|
) -> list[type[AbstractNotificationAdapter]]:
|
||||||
candidates: Dict[str, List[Type[AbstractNotificationAdapter]]] = {
|
candidates: dict[str, list[type[AbstractNotificationAdapter]]] = {
|
||||||
"libnotify": [
|
"libnotify": [
|
||||||
DBusNotificationAdapter,
|
DBusNotificationAdapter,
|
||||||
SystrayNotificationAdapter,
|
SystrayNotificationAdapter,
|
||||||
|
|
@ -285,7 +286,10 @@ class NotificationBridgePresenter(QObject):
|
||||||
|
|
||||||
if replaces_id is None:
|
if replaces_id is None:
|
||||||
if notification_id in self._active_notifications:
|
if notification_id in self._active_notifications:
|
||||||
raise Error(f"Got duplicate id {notification_id}")
|
message.error(f"Got duplicate notification id {notification_id} "
|
||||||
|
f"from {self._adapter.NAME}")
|
||||||
|
self._drop_adapter()
|
||||||
|
return
|
||||||
|
|
||||||
qt_notification.show()
|
qt_notification.show()
|
||||||
self._active_notifications[notification_id] = qt_notification
|
self._active_notifications[notification_id] = qt_notification
|
||||||
|
|
@ -665,7 +669,7 @@ class _ServerCapabilities:
|
||||||
kde_origin_name: bool
|
kde_origin_name: bool
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_list(cls, capabilities: List[str]) -> "_ServerCapabilities":
|
def from_list(cls, capabilities: list[str]) -> "_ServerCapabilities":
|
||||||
return cls(
|
return cls(
|
||||||
actions='actions' in capabilities,
|
actions='actions' in capabilities,
|
||||||
body_markup='body-markup' in capabilities,
|
body_markup='body-markup' in capabilities,
|
||||||
|
|
@ -951,10 +955,10 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
|
||||||
qtutils.extract_enum_val(QMetaType.Type.QStringList),
|
qtutils.extract_enum_val(QMetaType.Type.QStringList),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_hints_arg(self, *, origin_url: QUrl, icon: QImage) -> Dict[str, Any]:
|
def _get_hints_arg(self, *, origin_url: QUrl, icon: QImage) -> dict[str, Any]:
|
||||||
"""Get the hints argument for present()."""
|
"""Get the hints argument for present()."""
|
||||||
origin_url_str = origin_url.toDisplayString()
|
origin_url_str = origin_url.toDisplayString()
|
||||||
hints: Dict[str, Any] = {
|
hints: dict[str, Any] = {
|
||||||
# Include the origin in case the user wants to do different things
|
# Include the origin in case the user wants to do different things
|
||||||
# with different origin's notifications.
|
# with different origin's notifications.
|
||||||
"x-qutebrowser-origin": origin_url_str,
|
"x-qutebrowser-origin": origin_url_str,
|
||||||
|
|
@ -984,7 +988,7 @@ class DBusNotificationAdapter(AbstractNotificationAdapter):
|
||||||
title: str,
|
title: str,
|
||||||
body: str,
|
body: str,
|
||||||
actions: QDBusArgument,
|
actions: QDBusArgument,
|
||||||
hints: Dict[str, Any],
|
hints: dict[str, Any],
|
||||||
timeout: int,
|
timeout: int,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""Wrapper around DBus call to use keyword args."""
|
"""Wrapper around DBus call to use keyword args."""
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@
|
||||||
"""QtWebEngine specific part of the web element API."""
|
"""QtWebEngine specific part of the web element API."""
|
||||||
|
|
||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING, Any, Callable, Dict, Iterator, Optional, Set, Tuple, Union)
|
TYPE_CHECKING, Any, Optional, Union)
|
||||||
|
from collections.abc import Iterator, Callable
|
||||||
|
|
||||||
from qutebrowser.qt.core import QRect, QEventLoop
|
from qutebrowser.qt.core import QRect, QEventLoop
|
||||||
from qutebrowser.qt.widgets import QApplication
|
from qutebrowser.qt.widgets import QApplication
|
||||||
|
|
@ -24,11 +25,11 @@ class WebEngineElement(webelem.AbstractWebElement):
|
||||||
|
|
||||||
_tab: "webenginetab.WebEngineTab"
|
_tab: "webenginetab.WebEngineTab"
|
||||||
|
|
||||||
def __init__(self, js_dict: Dict[str, Any],
|
def __init__(self, js_dict: dict[str, Any],
|
||||||
tab: 'webenginetab.WebEngineTab') -> None:
|
tab: 'webenginetab.WebEngineTab') -> None:
|
||||||
super().__init__(tab)
|
super().__init__(tab)
|
||||||
# Do some sanity checks on the data we get from JS
|
# Do some sanity checks on the data we get from JS
|
||||||
js_dict_types: Dict[str, Union[type, Tuple[type, ...]]] = {
|
js_dict_types: dict[str, Union[type, tuple[type, ...]]] = {
|
||||||
'id': int,
|
'id': int,
|
||||||
'text': str,
|
'text': str,
|
||||||
'value': (str, int, float),
|
'value': (str, int, float),
|
||||||
|
|
@ -105,7 +106,7 @@ class WebEngineElement(webelem.AbstractWebElement):
|
||||||
log.stub()
|
log.stub()
|
||||||
return QRect()
|
return QRect()
|
||||||
|
|
||||||
def classes(self) -> Set[str]:
|
def classes(self) -> set[str]:
|
||||||
"""Get a list of classes assigned to this element."""
|
"""Get a list of classes assigned to this element."""
|
||||||
return set(self._js_dict['class_name'].split())
|
return set(self._js_dict['class_name'].split())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ Module attributes:
|
||||||
import os
|
import os
|
||||||
import operator
|
import operator
|
||||||
import pathlib
|
import pathlib
|
||||||
from typing import cast, Any, List, Optional, Tuple, Union, TYPE_CHECKING
|
from typing import cast, Any, Optional, Union, TYPE_CHECKING
|
||||||
|
|
||||||
from qutebrowser.qt import machinery
|
from qutebrowser.qt import machinery
|
||||||
from qutebrowser.qt.gui import QFont
|
from qutebrowser.qt.gui import QFont
|
||||||
|
|
@ -26,7 +26,7 @@ from qutebrowser.config import config, websettings
|
||||||
from qutebrowser.config.websettings import AttributeInfo as Attr
|
from qutebrowser.config.websettings import AttributeInfo as Attr
|
||||||
from qutebrowser.misc import pakjoy
|
from qutebrowser.misc import pakjoy
|
||||||
from qutebrowser.utils import (standarddir, qtutils, message, log,
|
from qutebrowser.utils import (standarddir, qtutils, message, log,
|
||||||
urlmatch, usertypes, objreg, version)
|
urlmatch, usertypes, objreg, version, utils)
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from qutebrowser.browser.webengine import interceptor
|
from qutebrowser.browser.webengine import interceptor
|
||||||
|
|
||||||
|
|
@ -216,6 +216,10 @@ class WebEngineSettings(websettings.AbstractSettings):
|
||||||
QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: True,
|
QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: True,
|
||||||
QWebEngineSettings.WebAttribute.JavascriptCanPaste: True,
|
QWebEngineSettings.WebAttribute.JavascriptCanPaste: True,
|
||||||
},
|
},
|
||||||
|
'ask': {
|
||||||
|
QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard: False,
|
||||||
|
QWebEngineSettings.WebAttribute.JavascriptCanPaste: False,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def set_unknown_url_scheme_policy(
|
def set_unknown_url_scheme_policy(
|
||||||
|
|
@ -281,6 +285,7 @@ class ProfileSetter:
|
||||||
self._set_hardcoded_settings()
|
self._set_hardcoded_settings()
|
||||||
self.set_persistent_cookie_policy()
|
self.set_persistent_cookie_policy()
|
||||||
self.set_dictionary_language()
|
self.set_dictionary_language()
|
||||||
|
self.disable_persistent_permissions_policy()
|
||||||
|
|
||||||
def _set_hardcoded_settings(self):
|
def _set_hardcoded_settings(self):
|
||||||
"""Set up settings with a fixed value."""
|
"""Set up settings with a fixed value."""
|
||||||
|
|
@ -343,7 +348,23 @@ class ProfileSetter:
|
||||||
|
|
||||||
log.config.debug("Found dicts: {}".format(filenames))
|
log.config.debug("Found dicts: {}".format(filenames))
|
||||||
self._profile.setSpellCheckLanguages(filenames)
|
self._profile.setSpellCheckLanguages(filenames)
|
||||||
self._profile.setSpellCheckEnabled(bool(filenames))
|
|
||||||
|
should_enable = bool(filenames)
|
||||||
|
if self._profile.isSpellCheckEnabled() != should_enable:
|
||||||
|
# Only setting conditionally as a WORKAROUND for a bogus Qt error message:
|
||||||
|
# https://bugreports.qt.io/browse/QTBUG-131969
|
||||||
|
self._profile.setSpellCheckEnabled(should_enable)
|
||||||
|
|
||||||
|
def disable_persistent_permissions_policy(self):
|
||||||
|
"""Disable webengine's permission persistence."""
|
||||||
|
if machinery.IS_QT6: # for mypy
|
||||||
|
try:
|
||||||
|
# New in WebEngine 6.8.0
|
||||||
|
self._profile.setPersistentPermissionsPolicy(
|
||||||
|
QWebEngineProfile.PersistentPermissionsPolicy.AskEveryTime
|
||||||
|
)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _update_settings(option):
|
def _update_settings(option):
|
||||||
|
|
@ -357,6 +378,12 @@ def _update_settings(option):
|
||||||
def _init_user_agent_str(ua):
|
def _init_user_agent_str(ua):
|
||||||
global parsed_user_agent
|
global parsed_user_agent
|
||||||
parsed_user_agent = websettings.UserAgent.parse(ua)
|
parsed_user_agent = websettings.UserAgent.parse(ua)
|
||||||
|
if parsed_user_agent.upstream_browser_version.endswith(".0.0.0"):
|
||||||
|
# https://codereview.qt-project.org/c/qt/qtwebengine/+/616314
|
||||||
|
# but we still want the full version available to users if they want it.
|
||||||
|
qtwe_versions = version.qtwebengine_versions()
|
||||||
|
assert qtwe_versions.chromium is not None
|
||||||
|
parsed_user_agent.upstream_browser_version = qtwe_versions.chromium
|
||||||
|
|
||||||
|
|
||||||
def init_user_agent():
|
def init_user_agent():
|
||||||
|
|
@ -390,16 +417,70 @@ def _init_profile(profile: QWebEngineProfile) -> None:
|
||||||
lambda url: profile.clearVisitedLinks([url]))
|
lambda url: profile.clearVisitedLinks([url]))
|
||||||
|
|
||||||
_global_settings.init_settings()
|
_global_settings.init_settings()
|
||||||
|
_maybe_disable_hangouts_extension(profile)
|
||||||
|
|
||||||
|
|
||||||
|
def _maybe_disable_hangouts_extension(profile: QWebEngineProfile) -> None:
|
||||||
|
"""Disable the Hangouts extension for Qt 6.10+."""
|
||||||
|
if not config.val.qt.workarounds.disable_hangouts_extension:
|
||||||
|
return
|
||||||
|
|
||||||
|
if machinery.IS_QT6: # mypy
|
||||||
|
try:
|
||||||
|
ext_manager = profile.extensionManager()
|
||||||
|
except AttributeError:
|
||||||
|
return # added in QtWebEngine 6.10
|
||||||
|
|
||||||
|
qtwe_versions = version.qtwebengine_versions(avoid_init=True)
|
||||||
|
if (
|
||||||
|
qtwe_versions.webengine == utils.VersionNumber(6, 10, 1)
|
||||||
|
and profile.isOffTheRecord()
|
||||||
|
):
|
||||||
|
# WORKAROUND for https://github.com/qutebrowser/qutebrowser/issues/8785
|
||||||
|
log.misc.warning(
|
||||||
|
"Not disabling Hangouts extension on private profile to avoid "
|
||||||
|
"QtWebEngine crash with Qt 6.10.1")
|
||||||
|
return
|
||||||
|
|
||||||
|
assert ext_manager is not None # mypy
|
||||||
|
for info in ext_manager.extensions():
|
||||||
|
if info.id() == pakjoy.HANGOUTS_EXT_ID:
|
||||||
|
log.misc.debug(f"Disabling extension: {info.name()}")
|
||||||
|
# setExtensionEnabled(info, False) seems to segfault
|
||||||
|
ext_manager.unloadExtension(info)
|
||||||
|
|
||||||
|
|
||||||
|
def _clear_webengine_permissions_json():
|
||||||
|
"""Remove QtWebEngine's persistent permissions file, if present.
|
||||||
|
|
||||||
|
We have our own permissions feature and don't integrate with their one.
|
||||||
|
This only needs to be called when you are on Qt6.8 but PyQt<6.8, since if
|
||||||
|
we have access to the `setPersistentPermissionsPolicy()` we will use that
|
||||||
|
to disable the Qt feature.
|
||||||
|
This needs to be called before we call `setPersistentStoragePath()`
|
||||||
|
because Qt will load the file during that.
|
||||||
|
"""
|
||||||
|
permissions_file = pathlib.Path(standarddir.data()) / "webengine" / "permissions.json"
|
||||||
|
try:
|
||||||
|
permissions_file.unlink(missing_ok=True)
|
||||||
|
except OSError as err:
|
||||||
|
log.init.warning(
|
||||||
|
f"Error while cleaning up webengine permissions file: {err}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def default_qt_profile() -> QWebEngineProfile:
|
||||||
|
"""Get the default profile from Qt."""
|
||||||
|
if machinery.IS_QT6:
|
||||||
|
return QWebEngineProfile("Default")
|
||||||
|
else:
|
||||||
|
return QWebEngineProfile.defaultProfile()
|
||||||
|
|
||||||
|
|
||||||
def _init_default_profile():
|
def _init_default_profile():
|
||||||
"""Init the default QWebEngineProfile."""
|
"""Init the default QWebEngineProfile."""
|
||||||
global default_profile
|
global default_profile
|
||||||
|
default_profile = default_qt_profile()
|
||||||
if machinery.IS_QT6:
|
|
||||||
default_profile = QWebEngineProfile("Default")
|
|
||||||
else:
|
|
||||||
default_profile = QWebEngineProfile.defaultProfile()
|
|
||||||
assert not default_profile.isOffTheRecord()
|
assert not default_profile.isOffTheRecord()
|
||||||
|
|
||||||
assert parsed_user_agent is None # avoid earlier profile initialization
|
assert parsed_user_agent is None # avoid earlier profile initialization
|
||||||
|
|
@ -407,13 +488,25 @@ def _init_default_profile():
|
||||||
|
|
||||||
init_user_agent()
|
init_user_agent()
|
||||||
ua_version = version.qtwebengine_versions()
|
ua_version = version.qtwebengine_versions()
|
||||||
|
|
||||||
|
logger = log.init.warning
|
||||||
|
if machinery.IS_QT5:
|
||||||
|
# With Qt 5.15, we can't quite be sure about which QtWebEngine patch version
|
||||||
|
# we're getting, as ELF parsing might be broken and there's no other way.
|
||||||
|
# For most of the code, we don't really care about the patch version though.
|
||||||
|
assert (
|
||||||
|
non_ua_version.webengine.strip_patch() == ua_version.webengine.strip_patch()
|
||||||
|
), (non_ua_version, ua_version)
|
||||||
|
logger = log.init.debug
|
||||||
|
|
||||||
if ua_version.webengine != non_ua_version.webengine:
|
if ua_version.webengine != non_ua_version.webengine:
|
||||||
log.init.warning(
|
logger(
|
||||||
"QtWebEngine version mismatch - unexpected behavior might occur, "
|
"QtWebEngine version mismatch - unexpected behavior might occur, "
|
||||||
"please open a bug about this.\n"
|
"please open a bug about this.\n"
|
||||||
f" Early version: {non_ua_version}\n"
|
f" Early version: {non_ua_version}\n"
|
||||||
f" Real version: {ua_version}")
|
f" Real version: {ua_version}")
|
||||||
|
|
||||||
|
_clear_webengine_permissions_json()
|
||||||
default_profile.setCachePath(
|
default_profile.setCachePath(
|
||||||
os.path.join(standarddir.cache(), 'webengine'))
|
os.path.join(standarddir.cache(), 'webengine'))
|
||||||
default_profile.setPersistentStoragePath(
|
default_profile.setPersistentStoragePath(
|
||||||
|
|
@ -446,13 +539,23 @@ def _init_site_specific_quirks():
|
||||||
# default_ua = ("Mozilla/5.0 ({os_info}) "
|
# default_ua = ("Mozilla/5.0 ({os_info}) "
|
||||||
# "AppleWebKit/{webkit_version} (KHTML, like Gecko) "
|
# "AppleWebKit/{webkit_version} (KHTML, like Gecko) "
|
||||||
# "{qt_key}/{qt_version} "
|
# "{qt_key}/{qt_version} "
|
||||||
# "{upstream_browser_key}/{upstream_browser_version} "
|
# "{upstream_browser_key}/{upstream_browser_version_short} "
|
||||||
# "Safari/{webkit_version}")
|
# "Safari/{webkit_version}")
|
||||||
no_qtwe_ua = ("Mozilla/5.0 ({os_info}) "
|
firefox_ua = "Mozilla/5.0 ({os_info}; rv:145.0) Gecko/20100101 Firefox/145.0"
|
||||||
"AppleWebKit/{webkit_version} (KHTML, like Gecko) "
|
|
||||||
"{upstream_browser_key}/{upstream_browser_version} "
|
# Needed for gitlab.gnome.org which blocks old Chromium versions outright,
|
||||||
"Safari/{webkit_version}")
|
# except when QtWebEngine/... is in the UA.
|
||||||
firefox_ua = "Mozilla/5.0 ({os_info}; rv:90.0) Gecko/20100101 Firefox/90.0"
|
#
|
||||||
|
# We could further modify the UA to just "qutebrowser" or something so we don't get
|
||||||
|
# Anubis at all, but it looks like their Anubis triggers to more than just
|
||||||
|
# Mozilla/5.0 (also AppleWebKit/... and Chromium/... possibly?), so at that point
|
||||||
|
# I'm not sure if we can strip down the UA so much without breaking
|
||||||
|
# something in GitLab as well.
|
||||||
|
not_mozilla_ua = (
|
||||||
|
"Mozilla/5.0 ({os_info}) AppleWebKit/{webkit_version} (KHTML, like Gecko) "
|
||||||
|
"{qt_key}/{qt_version} {upstream_browser_key}/{upstream_browser_version_short} "
|
||||||
|
"Safari/{webkit_version}"
|
||||||
|
)
|
||||||
|
|
||||||
def maybe_newer_chrome_ua(at_least_version):
|
def maybe_newer_chrome_ua(at_least_version):
|
||||||
"""Return a new UA if our current chrome version isn't at least at_least_version."""
|
"""Return a new UA if our current chrome version isn't at least at_least_version."""
|
||||||
|
|
@ -467,23 +570,14 @@ def _init_site_specific_quirks():
|
||||||
"Safari/537.36"
|
"Safari/537.36"
|
||||||
)
|
)
|
||||||
|
|
||||||
user_agents = [
|
utils.unused(maybe_newer_chrome_ua)
|
||||||
# Needed to avoid a ""WhatsApp works with Google Chrome 36+" error
|
|
||||||
# page which doesn't allow to use WhatsApp Web at all. Also see the
|
|
||||||
# additional JS quirk: qutebrowser/javascript/quirks/whatsapp_web.user.js
|
|
||||||
# https://github.com/qutebrowser/qutebrowser/issues/4445
|
|
||||||
("ua-whatsapp", 'https://web.whatsapp.com/', no_qtwe_ua),
|
|
||||||
|
|
||||||
|
user_agents = [
|
||||||
# Needed to avoid a "you're using a browser [...] that doesn't allow us
|
# Needed to avoid a "you're using a browser [...] that doesn't allow us
|
||||||
# to keep your account secure" error.
|
# to keep your account secure" error.
|
||||||
# https://github.com/qutebrowser/qutebrowser/issues/5182
|
# https://github.com/qutebrowser/qutebrowser/issues/5182
|
||||||
("ua-google", 'https://accounts.google.com/*', firefox_ua),
|
("ua-google", "https://accounts.google.com/*", firefox_ua),
|
||||||
|
("ua-gnome-gitlab", "https://gitlab.gnome.org/*", not_mozilla_ua),
|
||||||
# Needed because Slack adds an error which prevents using it relatively
|
|
||||||
# aggressively, despite things actually working fine.
|
|
||||||
# October 2023: Slack claims they only support 112+. On #7951 at least
|
|
||||||
# one user claims it still works fine on 108 based Qt versions.
|
|
||||||
("ua-slack", 'https://*.slack.com/*', maybe_newer_chrome_ua(112)),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for name, pattern, ua in user_agents:
|
for name, pattern, ua in user_agents:
|
||||||
|
|
@ -509,7 +603,7 @@ def _init_default_settings():
|
||||||
- Make sure the devtools always get images/JS permissions.
|
- Make sure the devtools always get images/JS permissions.
|
||||||
- On Qt 6, make sure files in the data path can load external resources.
|
- On Qt 6, make sure files in the data path can load external resources.
|
||||||
"""
|
"""
|
||||||
devtools_settings: List[Tuple[str, Any]] = [
|
devtools_settings: list[tuple[str, Any]] = [
|
||||||
('content.javascript.enabled', True),
|
('content.javascript.enabled', True),
|
||||||
('content.images', True),
|
('content.images', True),
|
||||||
('content.cookies.accept', 'all'),
|
('content.cookies.accept', 'all'),
|
||||||
|
|
@ -522,7 +616,7 @@ def _init_default_settings():
|
||||||
hide_userconfig=True)
|
hide_userconfig=True)
|
||||||
|
|
||||||
if machinery.IS_QT6:
|
if machinery.IS_QT6:
|
||||||
userscripts_settings: List[Tuple[str, Any]] = [
|
userscripts_settings: list[tuple[str, Any]] = [
|
||||||
("content.local_content_can_access_remote_urls", True),
|
("content.local_content_can_access_remote_urls", True),
|
||||||
("content.local_content_can_access_file_urls", False),
|
("content.local_content_can_access_file_urls", False),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue