summaryrefslogtreecommitdiff
path: root/doc/contributing.asciidoc
blob: 89f68fec09eb39728c64aa78cc06400ae598450d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
Contributing to qutebrowser
===========================
The Compiler <mail@qutebrowser.org>
:icons:
:data-uri:
:toc:

IMPORTANT: Bandwidth for pull request review is currently quite limited. If you
want to contribute where it's most needed, please consider reviewing or testing
open pull requests.

I `&lt;3` footnote:[`<3` in HTML] contributors!

This document contains guidelines for contributing to qutebrowser, as well as
useful hints when doing so.

If anything mentioned here would prevent you from contributing, please let me
know, and contribute anyways! The guidelines are meant to make life easier for
me, but if you don't follow everything in here, I won't be mad at you. In
fact, I will probably change it for you.

If you have any problems, I'm more than happy to help! You can get help in
several ways:

* Send a mail to the mailing list at mailto:qutebrowser@lists.qutebrowser.org[]
(optionally
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[subscribe]
first).
* Join the IRC channel link:ircs://irc.libera.chat:6697/#qutebrowser[`#qutebrowser`] on
https://libera.chat/[Libera Chat] (https://web.libera.chat/#qutebrowser[webchat],
https://matrix.to/#qutebrowser:libera.chat[via Matrix]).

Finding something to work on
----------------------------

Chances are you already know something to improve or add when you're reading
this. It might be a good idea to ask on the mailing list or IRC channel to make
sure nobody else started working on the same thing already.

If you want to find something useful to do, check the
https://github.com/qutebrowser/qutebrowser/issues[issue tracker]. Some
pointers:

* https://github.com/qutebrowser/qutebrowser/labels/easy[Issues which should
be easy to solve]
* https://github.com/qutebrowser/qutebrowser/labels/component%3A%20docs[Documentation issues which require little/no coding]

If you prefer C++ or Javascript to Python, see the relevant issues which involve
work in those languages:

* https://github.com/qutebrowser/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3A%22language%3A+c%2B%2B%22[C++] (mostly work on Qt, the library behind qutebrowser)
* https://github.com/qutebrowser/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3A%22language%3A+javascript%22[JavaScript]

There are also some things to do if you don't want to write code:

* Help the community, e.g., on the mailinglist and the IRC channel.
* Improve the documentation.
* Help on the website and graphics (logo, etc.).

Using git
---------

qutebrowser uses https://git-scm.com/[git] for its development. You can clone
the repo like this:

----
git clone https://github.com/qutebrowser/qutebrowser.git
----

If you don't know git, a https://git-scm.com/[git cheatsheet] might come in
handy. Of course, if using git is the issue which prevents you from
contributing, feel free to send normal patches instead, e.g., generated via
`diff -Nur`.

Getting patches
~~~~~~~~~~~~~~~

The preferred way of submitting changes is to
https://help.github.com/articles/fork-a-repo/[fork the repository] and to
https://help.github.com/articles/creating-a-pull-request/[submit a pull
request].

If you prefer to send a patch to the mailinglist, you can generate a patch
based on your changes like this:

----
git format-patch origin/master <1>
----
<1> Replace `master` by the branch your work was based on, e.g.,
`origin/develop`.

Running qutebrowser
-------------------

After link:install{outfilesuffix}#tox[installing qutebrowser in a virtualenv],
you can run `.venv/bin/qutebrowser --debug --temp-basedir` to test your changes
with debug logging enabled and without affecting existing running instances.

Alternatively, you can install qutebrowser's dependencies system-wide and run
`python3 -m qutebrowser --debug --temp-basedir`.

Useful utilities
----------------

Checkers
~~~~~~~~

qutebrowser uses https://tox.readthedocs.io/en/latest/[tox] to run its
unittests and several linters/checkers.

Currently, the following tox environments are available:

* Tests using https://www.pytest.org[pytest]:
  - `py36`, `py37`, ...: Run pytest for python 3.6/3.7/... with the system-wide PyQt.
  - `py36-pyqt512`, ..., `py36-pyqt515`: Run pytest with the given PyQt version (`py35-*` also works).
  - `py36-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].
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
  unused code portions.
* `pylint`: Run https://pylint.org/[pylint] static code analysis.
* `pyroma`: Check packaging practices with
  https://pypi.python.org/pypi/pyroma/[pyroma].
* `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.
* `misc`: Run `scripts/misc_checks.py` to check for:
    - untracked git files
    - VCS conflict markers
    - common spelling mistakes

The default test suite is run with `tox`; the list of default
environments is obtained with `tox -l`.

Please make sure the checks run without any warnings on your new contributions.

There's always the possibility of false positives; the following
techniques are useful to handle these:

* Use `_foo` for unused parameters, with `foo` being a descriptive name. Using
`_` is discouraged.
* If you think you have a good reason to suppress a message, then add the
following comment:
+
----
# pylint: disable=message-name
----
+
Note you can add this per line, per function/class, or per file. Please use the
smallest scope which makes sense. Most of the time, this will be line scope.
+
* If you really think a check shouldn't be done globally as it yields a lot of
false-positives, let me know! I'm still tweaking the parameters.


Running Specific Tests
~~~~~~~~~~~~~~~~~~~~~~

While you are developing you often don't want to run the full test
suite each time.

Specific test environments can be run with `tox -e <envlist>`.

Additional parameters can be passed to the test scripts by separating
them from `tox` arguments with `--`.

Examples:

----
# run only pytest tests which failed in last run:
tox -e py35 -- --lf

# run only the end2end feature tests:
tox -e py35 -- tests/end2end/features

# run everything with undo in the generated name, based on the scenario text
tox -e py35 -- tests/end2end/features/test_tabs_bdd.py -k undo

# run coverage test for specific file (updates htmlcov/index.html)
tox -e py35-cov -- tests/unit/browser/test_webelem.py
----

Profiling
~~~~~~~~~

In the _scripts/dev/_ subfolder there's `run_profile.py` which profiles the
code and shows a graphical representation of what takes how much time.

It uses the built-in Python
https://docs.python.org/3/library/profile.html[cProfile] module. It launches a
qutebrowser instance, waits for it to exit and then shows the graph.

Available methods for visualization are:

* https://jiffyclub.github.io/snakeviz/[SnakeViz] (`--profile-tool=snakeviz`, the default)
* https://pypi.python.org/pypi/pyprof2calltree/[pyprof2calltree] and https://kcachegrind.github.io/[KCacheGrind] (`--profile-tool=kcachegrind`)
* https://github.com/jrfonseca/gprof2dot[gprof2dot] (`--profile-tool=gprof2dot`, needs `dot` from https://graphviz.org/[Graphviz] and https://feh.finalrewind.org/[feh])
* https://github.com/nschloe/tuna[tuna] (`--profile-tool=tuna`)

You can also save the binary profile data to a file (`--profile-tool=none`).

Debugging
~~~~~~~~~

There are some useful functions for debugging in the `qutebrowser.utils.debug`
module.

When starting qutebrowser with the `--debug` flag, you also get useful debug
logs. You can add +--logfilter _[!]category[,category,...]_+ to restrict
logging to the given categories.

With `--debug` there are also some additional +debug-_*_+ commands available,
for example `:debug-all-objects` and `:debug-all-widgets` which print a list of
all Qt objects/widgets to the debug log -- this is very useful for finding
memory leaks.

Useful websites
~~~~~~~~~~~~~~~

Some resources which might be handy:

* https://doc.qt.io/qt-5/classes.html[The Qt5 reference]
* https://docs.python.org/3/library/index.html[The Python reference]
* https://httpbin.org/[httpbin, a test service for HTTP requests/responses]
* https://requestbin.com/[RequestBin, a service to inspect HTTP requests]

Documentation of used Python libraries:

* https://jinja.palletsprojects.com/[jinja2]
* https://pygments.org/docs/[pygments]
* https://www.pyinstaller.org/[PyInstaller]
* https://pypi.python.org/pypi/colorama[colorama]

Related RFCs and standards:

HTTP
^^^^

* https://tools.ietf.org/html/rfc2616[RFC 2616 - Hypertext Transfer Protocol
-- HTTP/1.1]
(https://www.rfc-editor.org/errata_search.php?rfc=2616[Errata])
* https://tools.ietf.org/html/rfc7230[RFC 7230 - Hypertext Transfer Protocol
(HTTP/1.1): Message Syntax and Routing]
(https://www.rfc-editor.org/errata_search.php?rfc=7230[Errata])
* https://tools.ietf.org/html/rfc7231[RFC 7231 - Hypertext Transfer Protocol
(HTTP/1.1): Semantics and Content]
(https://www.rfc-editor.org/errata_search.php?rfc=7231[Errata])
* https://tools.ietf.org/html/rfc7232[RFC 7232 - Hypertext Transfer Protocol
(HTTP/1.1): Conditional Requests]
(https://www.rfc-editor.org/errata_search.php?rfc=7232[Errata])
* https://tools.ietf.org/html/rfc7233[RFC 7233 - Hypertext Transfer Protocol
(HTTP/1.1): Range Requests]
(https://www.rfc-editor.org/errata_search.php?rfc=7233[Errata])
* https://tools.ietf.org/html/rfc7234[RFC 7234 - Hypertext Transfer Protocol
(HTTP/1.1): Caching]
(https://www.rfc-editor.org/errata_search.php?rfc=7234[Errata])
* https://tools.ietf.org/html/rfc7235[RFC 7235 - Hypertext Transfer Protocol
(HTTP/1.1): Authentication]
(https://www.rfc-editor.org/errata_search.php?rfc=7235[Errata])
* https://tools.ietf.org/html/rfc5987[RFC 5987 - Character Set and Language
Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters]
(https://www.rfc-editor.org/errata_search.php?rfc=5987[Errata])
* https://tools.ietf.org/html/rfc6266[RFC 6266 - Use of the
Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)]
(https://www.rfc-editor.org/errata_search.php?rfc=6266[Errata])
* https://tools.ietf.org/html/rfc6265[RFC 6265 - HTTP State Management Mechanism
(Cookies)] (https://www.rfc-editor.org/errata_search.php?rfc=6265[Errata])
* http://www.cookiecentral.com/faq/#3.5[Netscape Cookie Format]

Other
^^^^^

* https://tools.ietf.org/html/rfc5646[RFC 5646 - Tags for Identifying
Languages] (https://www.rfc-editor.org/errata_search.php?rfc=5646[Errata])
* https://www.w3.org/TR/CSS2/[Cascading Style Sheets Level 2 Revision 1 (CSS
2.1) Specification]
* https://doc.qt.io/qt-5/stylesheet-reference.html[Qt Style Sheets Reference]
* https://mimesniff.spec.whatwg.org/[MIME Sniffing Standard]
* https://spec.whatwg.org/[WHATWG specifications]
* https://www.w3.org/html/wg/drafts/html/master/Overview.html[HTML 5.1 Nightly]
* https://www.w3.org/TR/webstorage/[Web Storage]
* https://bford.info/cachedir/[Cache directory tagging standard]
* https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html[XDG
basedir specification]

Hints
-----

Python and Qt objects
~~~~~~~~~~~~~~~~~~~~~

For many tasks, there are solutions available in both Qt and the Python
standard library.

In qutebrowser, the policy is usually to use the Python libraries, as they
provide exceptions and other benefits.

There are some exceptions to that:

* `QThread` is used instead of Python threads because it provides signals and
slots.
* `QProcess` is used instead of Python's `subprocess`.
* `QUrl` is used instead of storing URLs as string, see the
<<handling-urls,handling URLs>> section for details.

When using Qt objects, two issues must be taken care of:

* Methods of Qt objects report their status with their return values,
instead of using exceptions.
+
If a function gets or returns a Qt object which has an `.isValid()`
method such as `QUrl` or `QModelIndex`, there's a helper function
`ensure_valid` in `qutebrowser.utils.qtutils` which should get called
on all such objects. It will raise
`qutebrowser.utils.qtutils.QtValueError` if the value is not valid.
+
If a function returns something else on error, the return value should
carefully be checked.

* Methods of Qt objects have certain maximum values based on their
underlying C++ types.
+
To avoid passing too large of a numeric parameter to a Qt function, all
numbers should be range-checked using `qutebrowser.qtutils.check_overflow`,
or by other means (e.g. by setting a maximum value for a config object).

[[object-registry]]
The object registry
~~~~~~~~~~~~~~~~~~~

The object registry in `qutebrowser.utils.objreg` is a collection of
dictionaries which map object names to the actual long-living objects.

There are currently these object registries, also called 'scopes':

* The `global` scope, with objects which are used globally (`config`,
`cookie-jar`, etc.).
* The `tab` scope with objects which are per-tab (`hintmanager`, `webview`,
etc.). Passing this scope to `objreg.get()` selects the object in the currently
focused tab by default. A tab can be explicitly selected by passing
+tab=_tab-id_, window=_win-id_+ to it.

A new object can be registered by using
+objreg.register(_name_, _object_[, scope=_scope_, window=_win-id_,
tab=_tab-id_])+. An object should not be registered twice. To update it,
`update=True` has to be given.

An object can be retrieved by using +objreg.get(_name_[, scope=_scope_,
window=_win-id_, tab=_tab-id_])+. The default scope is `global`.

All objects can be printed by starting with the `--debug` flag and using the
`:debug-all-objects` command.

The registry is mainly used for <<commands,command handlers>>, but it can
also be useful in places where using Qt's
https://doc.qt.io/qt-5/signalsandslots.html[signals and slots] mechanism would
be difficult.

Logging
~~~~~~~

Logging is used at various places throughout the qutebrowser code. If you add a
new feature, you should also add some strategic debug logging.

Unlike other Python projects, qutebrowser doesn't use a logger per file,
instead it uses custom-named loggers.

The existing loggers are defined in `qutebrowser.utils.log`. If your feature
doesn't fit in any of the logging categories, simply add a new line like this:

[source,python]
----
foo = getLogger('foo')
----

Then in your source files, do this:

[source,python]
----
from qutebrowser.utils import log
...
log.foo.debug("Hello World")
----

The following logging levels are available for every logger:

[width="75%",cols="25%,75%"]
|=======================================================================
|critical  |Critical issue, qutebrowser can't continue to run.
|error     |There was an issue and some kind of operation was abandoned.
|warning   |There was an issue but the operation can continue running.
|info      |General informational messages.
|debug     |Verbose debugging information.
|=======================================================================

[[commands]]
Commands
~~~~~~~~

qutebrowser has the concept of functions which are exposed to the user as
commands.

Creating a new command is straightforward:

[source,python]
----
from qutebrowser.api import cmdutils

...

@cmdutils.register(...)
def foo():
    ...
----

The commands arguments are automatically deduced by inspecting your function.

If the function is a method of a class, the `@cmdutils.register` decorator
needs to have an `instance=...` parameter which points to the (single/main)
instance of the class.

The `instance` parameter is the name of an object in the object registry, which
then gets passed as the `self` parameter to the handler. The `scope` argument
selects which object registry (global, per-tab, etc.) to use.  See the
<<object-registry,object registry>> section for details.

There are also other arguments to customize the way the command is
registered; see the class documentation for `register` in
`qutebrowser.api.cmdutils` for details.

The types of the function arguments are inferred based on their default values,
e.g., an argument `foo=True` will be converted to a flag `-f`/`--foo` in
qutebrowser's commandline.

The type can be overridden using Python's
https://www.python.org/dev/peps/pep-3107/[function annotations]:

[source,python]
----
@cmdutils.register(...)
def foo(bar: int, baz=True):
    ...
----

Possible values:

- A callable (`int`, `float`, etc.): Gets called to validate/convert the value.
- A python enum type: All members of the enum are possible values.
- A `typing.Union` of multiple types above: Any of these types are valid
  values, e.g., `typing.Union[str, int]`.

You can customize how an argument is handled using the `@cmdutils.argument`
decorator *after* `@cmdutils.register`. This can, for example, be used to
customize the flag an argument should get:

[source,python]
----
@cmdutils.register(...)
@cmdutils.argument('bar', flag='c')
def foo(bar):
    ...
----

For a `str` argument, you can restrict the allowed strings using `choices`:

[source,python]
----
@cmdutils.register(...)
@cmdutils.argument('bar', choices=['val1', 'val2'])
def foo(bar: str):
    ...
----

For `typing.Union` types, the given `choices` are only checked if other types
(like `int`) don't match.

The following arguments are supported for `@cmdutils.argument`:

- `flag`: Customize the short flag (`-x`) the argument will get.
- `value`: Tell qutebrowser to fill the argument with special values:
   - `value=cmdutils.Value.count`: The `count` given by the user to the command.
   - `value=cmdutils.Value.win_id`: The window ID of the current window.
   - `value=cmdutils.Value.cur_tab`: The tab object which is currently focused.
- `completion`: A completion function (see `qutebrowser.completions.models.*`)
  to use when completing arguments for the given command.
- `choices`: The allowed string choices for the argument.

The name of an argument will always be the parameter name, with any trailing
underscores stripped and underscores replaced by dashes.

[[handling-urls]]
Handling URLs
~~~~~~~~~~~~~

qutebrowser handles two different types of URLs: URLs as a string, and URLs as
the Qt `QUrl` type. As this can get confusing quickly, please follow the
following guidelines:

* Convert a string to a QUrl object as early as possible, i.e., directly after
the user did enter it.
    - Use `utils.urlutils.fuzzy_url` if the URL is entered by the user
      somewhere.
    - Be sure you handle `utils.urlutils.FuzzyError` and display an error
      message to the user.
* Convert a `QUrl` object to a string as late as possible, i.e., before
displaying it to the user.
    - If you want to display the URL to the user, use `url.toDisplayString()`
      so password information is removed.
    - If you want to get the URL as string for some other reason, you most
      likely want to add the `QUrl.EncodeFully` and `QUrl.RemovePassword`
      flags.
* Name a string URL something like `urlstr`, and a `QUrl` something like `url`.
* Mention in the docstring whether your function needs a URL string or a
`QUrl`.
* Call `ensure_valid` from `utils.qtutils` whenever getting or creating a
`QUrl` and take appropriate action if not. Note the URL of the current page
always could be an invalid QUrl (if nothing is loaded yet).

Running valgrind on QtWebKit
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you want to run qutebrowser (and thus QtWebKit) with
https://valgrind.org/[valgrind], you'll need to pass `--smc-check=all` to it or
recompile QtWebKit with the Javascript JIT disabled.

This is needed so valgrind handles self-modifying code correctly:

[quote]
____
This option controls Valgrind's detection of self-modifying code. If no
checking is done and a program executes some code, overwrites it with new
code, and then executes the new code, Valgrind will continue to execute the
translations it made for the old code. This will likely lead to incorrect
behavior and/or crashes.

...

Note that the default option will catch the vast majority of cases. The main
case it will not catch is programs such as JIT compilers that dynamically
generate code and subsequently overwrite part or all of it. Running with all
will slow Valgrind down noticeably.
____

Setting up a Windows Development Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* Install https://www.python.org/downloads/release/python-362/[Python 3.6].
* Install PyQt via `pip install PyQt5`.
* Create a file at `C:\Windows\system32\python3.bat` with the following content (adjust the path as necessary):
  `@C:\Python36\python %*`.
  This will make the Python 3.6 interpreter available as `python3`, which is used by various development scripts.
* Install git from the https://git-scm.com/download/win[git-scm downloads page].
  Try not to enable `core.autocrlf`, since that will cause `flake8` to complain a lot. Use an editor that can deal with plain line feeds instead.
* Clone your favourite qutebrowser repository.
* To install tox, open an elevated cmd, enter your working directory and run `pip install -rmisc/requirements/requirements-tox.txt`.

Note that the `flake8` tox env might not run due to encoding errors despite having LANG/LC_* set correctly.

Rebuilding the website
~~~~~~~~~~~~~~~~~~~~~~

If you want to rebuild the website, run `./scripts/asciidoc2html.py --website <outputdir>`.

Chrome URLs
~~~~~~~~~~~

With the QtWebEngine backend, qutebrowser supports several chrome:// urls which
can be useful for debugging:

- chrome://appcache-internals/
- chrome://blob-internals/
- chrome://gpu/
- chrome://histograms/
- chrome://indexeddb-internals/
- chrome://media-internals/
- chrome://network-errors/
- chrome://serviceworker-internals/
- chrome://webrtc-internals/
- chrome://crash/ (crashes the current renderer process!)
- chrome://kill/ (kills the current renderer process!)
- chrome://gpucrash/ (crashes qutebrowser!)
- chrome://gpuhang/ (hangs qutebrowser!)
- chrome://gpuclean/ (crashes the current renderer process!)
- chrome://ppapiflashcrash/
- chrome://ppapiflashhang/
- chrome://quota-internals/
- chrome://taskscheduler-internals/
- chrome://sandbox/ (Linux only)

QtWebEngine internals
~~~~~~~~~~~~~~~~~~~~~

This is mostly useful for qutebrowser maintainers to work around issues in Qt - if you don't understand it, don't worry, just ignore it.

The hierarchy of widgets when QtWebEngine is involved looks like this:

- qutebrowser has a `WebEngineTab` object, which is its abstraction over QtWebKit/QtWebEngine.
- The `WebEngineTab` has a `_widget` attribute, which is the https://doc.qt.io/qt-5/qwebengineview.html[QWebEngineView]
- That view has a https://doc.qt.io/qt-5/qwebenginepage.html[QWebEnginePage] for everything which doesn't require rendering.
- The view also has a layout with exactly one element (which also is its `focusProxy()`)
- That element is the  https://code.qt.io/cgit/qt/qtwebengine.git/tree/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp[RenderWidgetHostViewQtDelegateWidget] (it inherits https://doc.qt.io/qt-5/qquickwidget.html[QQuickWidget]) - also often referred to as RWHV or RWHVQDW. It can be obtained via `sip.cast(tab._widget.focusProxy(), QQuickWidget)`.
- Calling `rootObject()` on that gives us the https://doc.qt.io/qt-5/qquickitem.html[QQuickItem] where Chromium renders into (?). With it, we can do things like `.setRotation(20)`.

Style conventions
-----------------

qutebrowser's coding conventions are based on
https://www.python.org/dev/peps/pep-0008/[PEP8] and the https://google.github.io/styleguide/pyguide.html[Google Python style guidelines] with some additions:

* The _Raise:_ section is not added to the docstring.
* Methods overriding Qt methods (obviously!) don't follow the naming schemes.
* Everything else does though, even slots.
* Docstrings should look like described in
https://www.python.org/dev/peps/pep-0257/[PEP257] and the google guidelines.
* Class docstrings have additional _Attributes:_, _Class attributes:_ and
  _Signals:_ sections.
* In docstrings of command handlers (registered via `@cmdutils.register`), the
description should be split into two parts by using `//` - the first part is
the description of the command like it will appear in the documentation, the
second part is "internal" documentation only relevant to people reading the
sourcecode.
+
Example for a class docstring:
+
[source,python]
----
"""Some object.

Attributes:
    blub: The current thing to handle.

Signals:
    valueChanged: Emitted when a value changed.
                  arg: The new value
"""
----
+
Example for a method/function docstring:
+
[source,python]
----
"""Do something special.

This will do something.

//

It is based on http://example.com/.

Args:
    foo: ...

Return:
    True if something, False if something else.
"""
----
+
* The layout of a module should be roughly like this:
  - Shebang (`#!/usr/bin/python`, if needed)
  - vim-modeline (`# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et`)
  - Copyright
  - GPL boilerplate
  - Module docstring
  - Python standard library imports
  - PyQt imports
  - qutebrowser imports
  - functions
  - classes
* The layout of a class should be like this:
  - docstring
  - `__magic__` methods
  - other methods
  - overrides of Qt methods

Checklists
----------

These are mainly intended for myself, but they also fit in here well.

New Qt release
~~~~~~~~~~~~~~

* Run all tests and check nothing is broken.
* Check the
https://bugreports.qt.io/issues/?jql=reporter%20%3D%20%22The%20Compiler%22%20ORDER%20BY%20fixVersion%20ASC[Qt bugtracker]
and make sure all bugs marked as resolved are actually fixed.
* Update own PKGBUILDs based on upstream Archlinux updates and rebuild.
* Update recommended Qt version in `README`.
* Grep for `WORKAROUND` in the code and test if fixed stuff works without the
workaround.
* Check relevant
https://github.com/qutebrowser/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3Aqt[qutebrowser
bugs] and check if they're fixed.

New PyQt release
~~~~~~~~~~~~~~~~

* See above.
* Update `tox.ini`/`.github/workflows/ci.yml` to test new versions.

qutebrowser release
~~~~~~~~~~~~~~~~~~~

* Make sure there are no unstaged changes and the tests are green.
* Make sure all issues with the related milestone are closed.
* Consider updating the completions for `content.headers.user_agent` in `configdata.yml`.
* Minor release: Consider updating some files from master:
  - `misc/requirements/` and `requirements.txt`
  - `scripts/`
* Make sure Python is up-to-date on build machines.
* Mark the milestone at https://github.com/qutebrowser/qutebrowser/milestones as closed.
* Update changelog in master branch
* If necessary: Update changelog in release branch from master.
* Run `./.venv/bin/python3 scripts/dev/update_version.py {major,minor,patch}`.
* Run the printed instructions accordingly.
* Update `qutebrowser-git` PKGBUILD if dependencies/install changed.
* Add unreleased future versions to changelog
* Update IRC topic
* Announce to qutebrowser and qutebrowser-announce mailinglist.
* Post announcement mail to subreddit