justhtml 0.24.0__py3-none-any.whl → 0.38.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of justhtml might be problematic. Click here for more details.
- justhtml/__init__.py +44 -2
- justhtml/__main__.py +45 -9
- justhtml/constants.py +12 -0
- justhtml/errors.py +8 -3
- justhtml/linkify.py +438 -0
- justhtml/node.py +54 -35
- justhtml/parser.py +105 -38
- justhtml/sanitize.py +511 -282
- justhtml/selector.py +3 -1
- justhtml/serialize.py +398 -72
- justhtml/tokenizer.py +121 -21
- justhtml/tokens.py +21 -3
- justhtml/transforms.py +2568 -0
- justhtml/treebuilder.py +247 -190
- justhtml/treebuilder_modes.py +108 -102
- {justhtml-0.24.0.dist-info → justhtml-0.38.0.dist-info}/METADATA +28 -7
- justhtml-0.38.0.dist-info/RECORD +26 -0
- {justhtml-0.24.0.dist-info → justhtml-0.38.0.dist-info}/licenses/LICENSE +1 -1
- justhtml-0.24.0.dist-info/RECORD +0 -24
- {justhtml-0.24.0.dist-info → justhtml-0.38.0.dist-info}/WHEEL +0 -0
- {justhtml-0.24.0.dist-info → justhtml-0.38.0.dist-info}/entry_points.txt +0 -0
justhtml/treebuilder_modes.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
7
7
|
|
|
8
8
|
from .constants import (
|
|
9
9
|
FORMAT_MARKER,
|
|
@@ -11,16 +11,22 @@ from .constants import (
|
|
|
11
11
|
HEADING_ELEMENTS,
|
|
12
12
|
)
|
|
13
13
|
from .node import SimpleDomNode, TemplateNode
|
|
14
|
-
from .tokens import CharacterTokens, CommentToken, EOFToken, Tag, TokenSinkResult
|
|
14
|
+
from .tokens import AnyToken, CharacterTokens, CommentToken, DoctypeToken, EOFToken, Tag, TokenSinkResult
|
|
15
15
|
from .treebuilder_utils import (
|
|
16
16
|
InsertionMode,
|
|
17
17
|
doctype_error_and_quirks,
|
|
18
18
|
is_all_whitespace,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Callable
|
|
23
|
+
|
|
24
|
+
ModeResultTuple = tuple[str, InsertionMode, AnyToken] | tuple[str, InsertionMode, AnyToken, bool]
|
|
25
|
+
"Result is (instruction, mode, token) or (instruction, mode, token, force_html)"
|
|
26
|
+
|
|
21
27
|
|
|
22
28
|
class TreeBuilderModesMixin:
|
|
23
|
-
def _handle_doctype(self, token:
|
|
29
|
+
def _handle_doctype(self, token: DoctypeToken) -> Literal[0]:
|
|
24
30
|
if self.mode != InsertionMode.INITIAL:
|
|
25
31
|
self._parse_error("unexpected-doctype")
|
|
26
32
|
return TokenSinkResult.Continue
|
|
@@ -38,7 +44,7 @@ class TreeBuilderModesMixin:
|
|
|
38
44
|
self.mode = InsertionMode.BEFORE_HTML
|
|
39
45
|
return TokenSinkResult.Continue
|
|
40
46
|
|
|
41
|
-
def _mode_initial(self, token: Any) ->
|
|
47
|
+
def _mode_initial(self, token: Any) -> ModeResultTuple | None:
|
|
42
48
|
if isinstance(token, CharacterTokens):
|
|
43
49
|
if is_all_whitespace(token.data):
|
|
44
50
|
return None
|
|
@@ -61,7 +67,7 @@ class TreeBuilderModesMixin:
|
|
|
61
67
|
self._set_quirks_mode("quirks")
|
|
62
68
|
return ("reprocess", InsertionMode.BEFORE_HTML, token)
|
|
63
69
|
|
|
64
|
-
def _mode_before_html(self, token:
|
|
70
|
+
def _mode_before_html(self, token: AnyToken) -> ModeResultTuple | None:
|
|
65
71
|
if isinstance(token, CharacterTokens) and is_all_whitespace(token.data):
|
|
66
72
|
return None
|
|
67
73
|
if isinstance(token, CommentToken):
|
|
@@ -94,7 +100,7 @@ class TreeBuilderModesMixin:
|
|
|
94
100
|
self.mode = InsertionMode.BEFORE_HEAD
|
|
95
101
|
return ("reprocess", InsertionMode.BEFORE_HEAD, token)
|
|
96
102
|
|
|
97
|
-
def _mode_before_head(self, token:
|
|
103
|
+
def _mode_before_head(self, token: AnyToken) -> ModeResultTuple | None:
|
|
98
104
|
if isinstance(token, CharacterTokens):
|
|
99
105
|
data = token.data or ""
|
|
100
106
|
if "\x00" in data:
|
|
@@ -137,7 +143,7 @@ class TreeBuilderModesMixin:
|
|
|
137
143
|
self.mode = InsertionMode.IN_HEAD
|
|
138
144
|
return ("reprocess", InsertionMode.IN_HEAD, token)
|
|
139
145
|
|
|
140
|
-
def _mode_in_head(self, token:
|
|
146
|
+
def _mode_in_head(self, token: AnyToken) -> ModeResultTuple | None:
|
|
141
147
|
if isinstance(token, CharacterTokens):
|
|
142
148
|
if is_all_whitespace(token.data):
|
|
143
149
|
self._append_text(token.data)
|
|
@@ -213,7 +219,7 @@ class TreeBuilderModesMixin:
|
|
|
213
219
|
self.mode = InsertionMode.AFTER_HEAD
|
|
214
220
|
return ("reprocess", InsertionMode.AFTER_HEAD, token)
|
|
215
221
|
|
|
216
|
-
def _mode_in_head_noscript(self, token:
|
|
222
|
+
def _mode_in_head_noscript(self, token: AnyToken) -> ModeResultTuple | None:
|
|
217
223
|
"""Handle tokens in 'in head noscript' insertion mode (scripting disabled)."""
|
|
218
224
|
if isinstance(token, CharacterTokens):
|
|
219
225
|
data = token.data or ""
|
|
@@ -262,7 +268,7 @@ class TreeBuilderModesMixin:
|
|
|
262
268
|
# All token types are handled above - CharacterTokens, CommentToken, Tag, EOFToken
|
|
263
269
|
return None # pragma: no cover
|
|
264
270
|
|
|
265
|
-
def _mode_after_head(self, token:
|
|
271
|
+
def _mode_after_head(self, token: AnyToken) -> ModeResultTuple | None:
|
|
266
272
|
if isinstance(token, CharacterTokens):
|
|
267
273
|
data = token.data or ""
|
|
268
274
|
if "\x00" in data:
|
|
@@ -351,7 +357,7 @@ class TreeBuilderModesMixin:
|
|
|
351
357
|
self._insert_body_if_missing()
|
|
352
358
|
return ("reprocess", InsertionMode.IN_BODY, token)
|
|
353
359
|
|
|
354
|
-
def _mode_text(self, token:
|
|
360
|
+
def _mode_text(self, token: AnyToken) -> ModeResultTuple | None:
|
|
355
361
|
if isinstance(token, CharacterTokens):
|
|
356
362
|
self._append_text(token.data)
|
|
357
363
|
return None
|
|
@@ -367,11 +373,11 @@ class TreeBuilderModesMixin:
|
|
|
367
373
|
self.mode = self.original_mode or InsertionMode.IN_BODY
|
|
368
374
|
return None
|
|
369
375
|
|
|
370
|
-
def _mode_in_body(self, token: Any) ->
|
|
376
|
+
def _mode_in_body(self, token: Any) -> ModeResultTuple | None:
|
|
371
377
|
handler = self._BODY_TOKEN_HANDLERS.get(type(token))
|
|
372
378
|
return handler(self, token) if handler else None
|
|
373
379
|
|
|
374
|
-
def _handle_characters_in_body(self, token:
|
|
380
|
+
def _handle_characters_in_body(self, token: CharacterTokens) -> None:
|
|
375
381
|
data = token.data or ""
|
|
376
382
|
if "\x00" in data:
|
|
377
383
|
self._parse_error("invalid-codepoint")
|
|
@@ -385,11 +391,11 @@ class TreeBuilderModesMixin:
|
|
|
385
391
|
self._append_text(data)
|
|
386
392
|
return
|
|
387
393
|
|
|
388
|
-
def _handle_comment_in_body(self, token:
|
|
394
|
+
def _handle_comment_in_body(self, token: CommentToken) -> None:
|
|
389
395
|
self._append_comment(token.data)
|
|
390
396
|
return
|
|
391
397
|
|
|
392
|
-
def _handle_tag_in_body(self, token:
|
|
398
|
+
def _handle_tag_in_body(self, token: Tag) -> ModeResultTuple | None:
|
|
393
399
|
if token.kind == Tag.START:
|
|
394
400
|
handler = self._BODY_START_HANDLERS.get(token.name)
|
|
395
401
|
if handler:
|
|
@@ -413,7 +419,7 @@ class TreeBuilderModesMixin:
|
|
|
413
419
|
self._any_other_end_tag(token.name)
|
|
414
420
|
return None
|
|
415
421
|
|
|
416
|
-
def _handle_eof_in_body(self, token:
|
|
422
|
+
def _handle_eof_in_body(self, token: EOFToken) -> ModeResultTuple | None:
|
|
417
423
|
# If we're in a template, handle EOF in template mode first
|
|
418
424
|
if self.template_modes:
|
|
419
425
|
return self._mode_in_template(token)
|
|
@@ -448,7 +454,7 @@ class TreeBuilderModesMixin:
|
|
|
448
454
|
# Body mode start tag handlers
|
|
449
455
|
# ---------------------
|
|
450
456
|
|
|
451
|
-
def _handle_body_start_html(self, token:
|
|
457
|
+
def _handle_body_start_html(self, token: Tag) -> None:
|
|
452
458
|
if self.template_modes:
|
|
453
459
|
self._parse_error("unexpected-start-tag", tag_name=token.name)
|
|
454
460
|
return
|
|
@@ -460,7 +466,7 @@ class TreeBuilderModesMixin:
|
|
|
460
466
|
self._add_missing_attributes(html, token.attrs)
|
|
461
467
|
return
|
|
462
468
|
|
|
463
|
-
def _handle_body_start_body(self, token:
|
|
469
|
+
def _handle_body_start_body(self, token: Tag) -> None:
|
|
464
470
|
if self.template_modes:
|
|
465
471
|
self._parse_error("unexpected-start-tag", tag_name=token.name)
|
|
466
472
|
return
|
|
@@ -474,19 +480,19 @@ class TreeBuilderModesMixin:
|
|
|
474
480
|
self.frameset_ok = False
|
|
475
481
|
return
|
|
476
482
|
|
|
477
|
-
def _handle_body_start_head(self, token:
|
|
483
|
+
def _handle_body_start_head(self, token: Tag) -> None:
|
|
478
484
|
self._parse_error("unexpected-start-tag", tag_name=token.name)
|
|
479
485
|
return
|
|
480
486
|
|
|
481
|
-
def _handle_body_start_in_head(self, token:
|
|
487
|
+
def _handle_body_start_in_head(self, token: Tag) -> ModeResultTuple | None:
|
|
482
488
|
return self._mode_in_head(token)
|
|
483
489
|
|
|
484
|
-
def _handle_body_start_block_with_p(self, token:
|
|
490
|
+
def _handle_body_start_block_with_p(self, token: Tag) -> None:
|
|
485
491
|
self._close_p_element()
|
|
486
492
|
self._insert_element(token, push=True)
|
|
487
493
|
return
|
|
488
494
|
|
|
489
|
-
def _handle_body_start_heading(self, token:
|
|
495
|
+
def _handle_body_start_heading(self, token: Tag) -> None:
|
|
490
496
|
self._close_p_element()
|
|
491
497
|
if self.open_elements and self.open_elements[-1].name in HEADING_ELEMENTS:
|
|
492
498
|
self._parse_error("unexpected-start-tag", tag_name=token.name)
|
|
@@ -495,14 +501,14 @@ class TreeBuilderModesMixin:
|
|
|
495
501
|
self.frameset_ok = False
|
|
496
502
|
return
|
|
497
503
|
|
|
498
|
-
def _handle_body_start_pre_listing(self, token:
|
|
504
|
+
def _handle_body_start_pre_listing(self, token: Tag) -> None:
|
|
499
505
|
self._close_p_element()
|
|
500
506
|
self._insert_element(token, push=True)
|
|
501
507
|
self.ignore_lf = True
|
|
502
508
|
self.frameset_ok = False
|
|
503
509
|
return
|
|
504
510
|
|
|
505
|
-
def _handle_body_start_form(self, token:
|
|
511
|
+
def _handle_body_start_form(self, token: Tag) -> None:
|
|
506
512
|
if self.form_element is not None:
|
|
507
513
|
self._parse_error("unexpected-start-tag", tag_name=token.name)
|
|
508
514
|
return
|
|
@@ -512,7 +518,7 @@ class TreeBuilderModesMixin:
|
|
|
512
518
|
self.frameset_ok = False
|
|
513
519
|
return
|
|
514
520
|
|
|
515
|
-
def _handle_body_start_button(self, token:
|
|
521
|
+
def _handle_body_start_button(self, token: Tag) -> None:
|
|
516
522
|
if self._has_in_scope("button"):
|
|
517
523
|
self._parse_error("unexpected-start-tag-implies-end-tag", tag_name=token.name)
|
|
518
524
|
self._close_element_by_name("button")
|
|
@@ -520,19 +526,19 @@ class TreeBuilderModesMixin:
|
|
|
520
526
|
self.frameset_ok = False
|
|
521
527
|
return
|
|
522
528
|
|
|
523
|
-
def _handle_body_start_paragraph(self, token:
|
|
529
|
+
def _handle_body_start_paragraph(self, token: Tag) -> None:
|
|
524
530
|
self._close_p_element()
|
|
525
531
|
self._insert_element(token, push=True)
|
|
526
532
|
return
|
|
527
533
|
|
|
528
|
-
def _handle_body_start_math(self, token:
|
|
534
|
+
def _handle_body_start_math(self, token: Tag) -> None:
|
|
529
535
|
self._reconstruct_active_formatting_elements()
|
|
530
536
|
attrs = self._prepare_foreign_attributes("math", token.attrs)
|
|
531
537
|
new_tag = Tag(Tag.START, token.name, attrs, token.self_closing)
|
|
532
538
|
self._insert_element(new_tag, push=not token.self_closing, namespace="math")
|
|
533
539
|
return
|
|
534
540
|
|
|
535
|
-
def _handle_body_start_svg(self, token:
|
|
541
|
+
def _handle_body_start_svg(self, token: Tag) -> None:
|
|
536
542
|
self._reconstruct_active_formatting_elements()
|
|
537
543
|
adjusted_name = self._adjust_svg_tag_name(token.name)
|
|
538
544
|
attrs = self._prepare_foreign_attributes("svg", token.attrs)
|
|
@@ -540,7 +546,7 @@ class TreeBuilderModesMixin:
|
|
|
540
546
|
self._insert_element(new_tag, push=not token.self_closing, namespace="svg")
|
|
541
547
|
return
|
|
542
548
|
|
|
543
|
-
def _handle_body_start_li(self, token:
|
|
549
|
+
def _handle_body_start_li(self, token: Tag) -> None:
|
|
544
550
|
self.frameset_ok = False
|
|
545
551
|
self._close_p_element()
|
|
546
552
|
if self._has_in_list_item_scope("li"):
|
|
@@ -548,7 +554,7 @@ class TreeBuilderModesMixin:
|
|
|
548
554
|
self._insert_element(token, push=True)
|
|
549
555
|
return
|
|
550
556
|
|
|
551
|
-
def _handle_body_start_dd_dt(self, token:
|
|
557
|
+
def _handle_body_start_dd_dt(self, token: Tag) -> None:
|
|
552
558
|
self.frameset_ok = False
|
|
553
559
|
self._close_p_element()
|
|
554
560
|
name = token.name
|
|
@@ -614,7 +620,7 @@ class TreeBuilderModesMixin:
|
|
|
614
620
|
if furthest_block is None:
|
|
615
621
|
# formatting_element is known to be on the stack
|
|
616
622
|
while True:
|
|
617
|
-
popped = self.
|
|
623
|
+
popped = self._pop_current()
|
|
618
624
|
if popped is formatting_element:
|
|
619
625
|
break
|
|
620
626
|
self._remove_formatting_entry(formatting_element_index)
|
|
@@ -721,7 +727,7 @@ class TreeBuilderModesMixin:
|
|
|
721
727
|
furthest_block_index = self.open_elements.index(furthest_block)
|
|
722
728
|
self.open_elements.insert(furthest_block_index + 1, new_formatting_element)
|
|
723
729
|
|
|
724
|
-
def _handle_body_start_a(self, token:
|
|
730
|
+
def _handle_body_start_a(self, token: Tag) -> None:
|
|
725
731
|
if self._has_active_formatting_entry("a"):
|
|
726
732
|
self._parse_error("unexpected-start-tag-implies-end-tag", tag_name=token.name)
|
|
727
733
|
self._adoption_agency("a")
|
|
@@ -732,7 +738,7 @@ class TreeBuilderModesMixin:
|
|
|
732
738
|
self._append_active_formatting_entry("a", token.attrs, node)
|
|
733
739
|
return
|
|
734
740
|
|
|
735
|
-
def _handle_body_start_formatting(self, token:
|
|
741
|
+
def _handle_body_start_formatting(self, token: Tag) -> None:
|
|
736
742
|
name = token.name
|
|
737
743
|
if name == "nobr" and self._in_scope("nobr"):
|
|
738
744
|
self._adoption_agency("nobr")
|
|
@@ -746,21 +752,21 @@ class TreeBuilderModesMixin:
|
|
|
746
752
|
self._append_active_formatting_entry(name, token.attrs, node)
|
|
747
753
|
return
|
|
748
754
|
|
|
749
|
-
def _handle_body_start_applet_like(self, token:
|
|
755
|
+
def _handle_body_start_applet_like(self, token: Tag) -> None:
|
|
750
756
|
self._reconstruct_active_formatting_elements()
|
|
751
757
|
self._insert_element(token, push=True)
|
|
752
758
|
self._push_formatting_marker()
|
|
753
759
|
self.frameset_ok = False
|
|
754
760
|
return
|
|
755
761
|
|
|
756
|
-
def _handle_body_start_br(self, token:
|
|
762
|
+
def _handle_body_start_br(self, token: Tag) -> None:
|
|
757
763
|
self._close_p_element()
|
|
758
764
|
self._reconstruct_active_formatting_elements()
|
|
759
765
|
self._insert_element(token, push=False)
|
|
760
766
|
self.frameset_ok = False
|
|
761
767
|
return
|
|
762
768
|
|
|
763
|
-
def _handle_body_start_frameset(self, token:
|
|
769
|
+
def _handle_body_start_frameset(self, token: Tag) -> None:
|
|
764
770
|
if not self.frameset_ok:
|
|
765
771
|
self._parse_error("unexpected-start-tag-ignored", tag_name=token.name)
|
|
766
772
|
return
|
|
@@ -785,17 +791,17 @@ class TreeBuilderModesMixin:
|
|
|
785
791
|
# Body mode end tag handlers
|
|
786
792
|
# ---------------------
|
|
787
793
|
|
|
788
|
-
def _handle_body_end_body(self, token:
|
|
794
|
+
def _handle_body_end_body(self, token: Tag) -> None:
|
|
789
795
|
if self._in_scope("body"):
|
|
790
796
|
self.mode = InsertionMode.AFTER_BODY
|
|
791
797
|
return
|
|
792
798
|
|
|
793
|
-
def _handle_body_end_html(self, token:
|
|
799
|
+
def _handle_body_end_html(self, token: Tag) -> ModeResultTuple | None:
|
|
794
800
|
if self._in_scope("body"):
|
|
795
801
|
return ("reprocess", InsertionMode.AFTER_BODY, token)
|
|
796
802
|
return None
|
|
797
803
|
|
|
798
|
-
def _handle_body_end_p(self, token:
|
|
804
|
+
def _handle_body_end_p(self, token: Tag) -> None:
|
|
799
805
|
if not self._close_p_element():
|
|
800
806
|
self._parse_error("unexpected-end-tag", tag_name=token.name)
|
|
801
807
|
phantom = Tag(Tag.START, "p", {}, False)
|
|
@@ -803,21 +809,21 @@ class TreeBuilderModesMixin:
|
|
|
803
809
|
self._close_p_element()
|
|
804
810
|
return
|
|
805
811
|
|
|
806
|
-
def _handle_body_end_li(self, token:
|
|
812
|
+
def _handle_body_end_li(self, token: Tag) -> None:
|
|
807
813
|
if not self._has_in_list_item_scope("li"):
|
|
808
814
|
self._parse_error("unexpected-end-tag", tag_name=token.name)
|
|
809
815
|
return
|
|
810
816
|
self._pop_until_any_inclusive({"li"})
|
|
811
817
|
return
|
|
812
818
|
|
|
813
|
-
def _handle_body_end_dd_dt(self, token:
|
|
819
|
+
def _handle_body_end_dd_dt(self, token: Tag) -> None:
|
|
814
820
|
name = token.name
|
|
815
821
|
if not self._has_in_definition_scope(name):
|
|
816
822
|
self._parse_error("unexpected-end-tag", tag_name=name)
|
|
817
823
|
return
|
|
818
824
|
self._pop_until_any_inclusive({"dd", "dt"})
|
|
819
825
|
|
|
820
|
-
def _handle_body_end_form(self, token:
|
|
826
|
+
def _handle_body_end_form(self, token: Tag) -> None:
|
|
821
827
|
if self.form_element is None:
|
|
822
828
|
self._parse_error("unexpected-end-tag", tag_name=token.name)
|
|
823
829
|
return
|
|
@@ -827,20 +833,20 @@ class TreeBuilderModesMixin:
|
|
|
827
833
|
self._parse_error("unexpected-end-tag", tag_name=token.name)
|
|
828
834
|
return
|
|
829
835
|
|
|
830
|
-
def _handle_body_end_applet_like(self, token:
|
|
836
|
+
def _handle_body_end_applet_like(self, token: Tag) -> None:
|
|
831
837
|
name = token.name
|
|
832
838
|
if not self._in_scope(name):
|
|
833
839
|
self._parse_error("unexpected-end-tag", tag_name=name)
|
|
834
840
|
return
|
|
835
841
|
# Element verified in scope above
|
|
836
842
|
while self.open_elements: # pragma: no branch
|
|
837
|
-
popped = self.
|
|
843
|
+
popped = self._pop_current()
|
|
838
844
|
if popped.name == name:
|
|
839
845
|
break
|
|
840
846
|
self._clear_active_formatting_up_to_marker()
|
|
841
847
|
return
|
|
842
848
|
|
|
843
|
-
def _handle_body_end_heading(self, token:
|
|
849
|
+
def _handle_body_end_heading(self, token: Tag) -> None:
|
|
844
850
|
name = token.name
|
|
845
851
|
if not self._has_any_in_scope(HEADING_ELEMENTS):
|
|
846
852
|
self._parse_error("unexpected-end-tag", tag_name=name)
|
|
@@ -850,12 +856,12 @@ class TreeBuilderModesMixin:
|
|
|
850
856
|
self._parse_error("end-tag-too-early", tag_name=name)
|
|
851
857
|
# Heading verified in scope by caller
|
|
852
858
|
while self.open_elements: # pragma: no branch
|
|
853
|
-
popped = self.
|
|
859
|
+
popped = self._pop_current()
|
|
854
860
|
if popped.name in HEADING_ELEMENTS:
|
|
855
861
|
break
|
|
856
862
|
return
|
|
857
863
|
|
|
858
|
-
def _handle_body_end_block(self, token:
|
|
864
|
+
def _handle_body_end_block(self, token: Tag) -> None:
|
|
859
865
|
name = token.name
|
|
860
866
|
if not self._in_scope(name):
|
|
861
867
|
self._parse_error("unexpected-end-tag", tag_name=name)
|
|
@@ -866,7 +872,7 @@ class TreeBuilderModesMixin:
|
|
|
866
872
|
self._pop_until_any_inclusive({name})
|
|
867
873
|
return
|
|
868
874
|
|
|
869
|
-
def _handle_body_end_template(self, token:
|
|
875
|
+
def _handle_body_end_template(self, token: Tag) -> None:
|
|
870
876
|
has_template = any(node.name == "template" for node in self.open_elements)
|
|
871
877
|
if not has_template:
|
|
872
878
|
self._parse_error("unexpected-end-tag", tag_name=token.name)
|
|
@@ -880,18 +886,18 @@ class TreeBuilderModesMixin:
|
|
|
880
886
|
self._reset_insertion_mode()
|
|
881
887
|
return
|
|
882
888
|
|
|
883
|
-
def _handle_body_start_structure_ignored(self, token:
|
|
889
|
+
def _handle_body_start_structure_ignored(self, token: Tag) -> None:
|
|
884
890
|
self._parse_error("unexpected-start-tag-ignored", tag_name=token.name)
|
|
885
891
|
return
|
|
886
892
|
|
|
887
|
-
def _handle_body_start_col_or_frame(self, token:
|
|
893
|
+
def _handle_body_start_col_or_frame(self, token: Tag) -> None:
|
|
888
894
|
if self.fragment_context is None:
|
|
889
895
|
self._parse_error("unexpected-start-tag-ignored", tag_name=token.name)
|
|
890
896
|
return
|
|
891
897
|
self._insert_element(token, push=False)
|
|
892
898
|
return
|
|
893
899
|
|
|
894
|
-
def _handle_body_start_image(self, token:
|
|
900
|
+
def _handle_body_start_image(self, token: Tag) -> None:
|
|
895
901
|
self._parse_error("image-start-tag", tag_name=token.name)
|
|
896
902
|
img_token = Tag(Tag.START, "img", token.attrs, token.self_closing)
|
|
897
903
|
self._reconstruct_active_formatting_elements()
|
|
@@ -899,17 +905,17 @@ class TreeBuilderModesMixin:
|
|
|
899
905
|
self.frameset_ok = False
|
|
900
906
|
return
|
|
901
907
|
|
|
902
|
-
def _handle_body_start_void_with_formatting(self, token:
|
|
908
|
+
def _handle_body_start_void_with_formatting(self, token: Tag) -> None:
|
|
903
909
|
self._reconstruct_active_formatting_elements()
|
|
904
910
|
self._insert_element(token, push=False)
|
|
905
911
|
self.frameset_ok = False
|
|
906
912
|
return
|
|
907
913
|
|
|
908
|
-
def _handle_body_start_simple_void(self, token:
|
|
914
|
+
def _handle_body_start_simple_void(self, token: Tag) -> None:
|
|
909
915
|
self._insert_element(token, push=False)
|
|
910
916
|
return
|
|
911
917
|
|
|
912
|
-
def _handle_body_start_input(self, token:
|
|
918
|
+
def _handle_body_start_input(self, token: Tag) -> None:
|
|
913
919
|
input_type = None
|
|
914
920
|
for name, value in token.attrs.items():
|
|
915
921
|
if name == "type":
|
|
@@ -920,7 +926,7 @@ class TreeBuilderModesMixin:
|
|
|
920
926
|
self.frameset_ok = False
|
|
921
927
|
return
|
|
922
928
|
|
|
923
|
-
def _handle_body_start_table(self, token:
|
|
929
|
+
def _handle_body_start_table(self, token: Tag) -> None:
|
|
924
930
|
if self.quirks_mode != "quirks":
|
|
925
931
|
self._close_p_element()
|
|
926
932
|
self._insert_element(token, push=True)
|
|
@@ -928,7 +934,7 @@ class TreeBuilderModesMixin:
|
|
|
928
934
|
self.mode = InsertionMode.IN_TABLE
|
|
929
935
|
return
|
|
930
936
|
|
|
931
|
-
def _handle_body_start_plaintext_xmp(self, token:
|
|
937
|
+
def _handle_body_start_plaintext_xmp(self, token: Tag) -> None:
|
|
932
938
|
self._close_p_element()
|
|
933
939
|
self._insert_element(token, push=True)
|
|
934
940
|
self.frameset_ok = False
|
|
@@ -940,58 +946,58 @@ class TreeBuilderModesMixin:
|
|
|
940
946
|
self.mode = InsertionMode.TEXT
|
|
941
947
|
return
|
|
942
948
|
|
|
943
|
-
def _handle_body_start_textarea(self, token:
|
|
949
|
+
def _handle_body_start_textarea(self, token: Tag) -> None:
|
|
944
950
|
self._insert_element(token, push=True)
|
|
945
951
|
self.ignore_lf = True
|
|
946
952
|
self.frameset_ok = False
|
|
947
953
|
return
|
|
948
954
|
|
|
949
|
-
def _handle_body_start_select(self, token:
|
|
955
|
+
def _handle_body_start_select(self, token: Tag) -> None:
|
|
950
956
|
self._reconstruct_active_formatting_elements()
|
|
951
957
|
self._insert_element(token, push=True)
|
|
952
958
|
self.frameset_ok = False
|
|
953
959
|
self._reset_insertion_mode()
|
|
954
960
|
return
|
|
955
961
|
|
|
956
|
-
def _handle_body_start_option(self, token:
|
|
962
|
+
def _handle_body_start_option(self, token: Tag) -> None:
|
|
957
963
|
if self.open_elements and self.open_elements[-1].name == "option":
|
|
958
|
-
self.
|
|
964
|
+
self._pop_current()
|
|
959
965
|
self._reconstruct_active_formatting_elements()
|
|
960
966
|
self._insert_element(token, push=True)
|
|
961
967
|
return
|
|
962
968
|
|
|
963
|
-
def _handle_body_start_optgroup(self, token:
|
|
969
|
+
def _handle_body_start_optgroup(self, token: Tag) -> None:
|
|
964
970
|
if self.open_elements and self.open_elements[-1].name == "option":
|
|
965
|
-
self.
|
|
971
|
+
self._pop_current()
|
|
966
972
|
self._reconstruct_active_formatting_elements()
|
|
967
973
|
self._insert_element(token, push=True)
|
|
968
974
|
return
|
|
969
975
|
|
|
970
|
-
def _handle_body_start_rp_rt(self, token:
|
|
976
|
+
def _handle_body_start_rp_rt(self, token: Tag) -> None:
|
|
971
977
|
self._generate_implied_end_tags(exclude="rtc")
|
|
972
978
|
self._insert_element(token, push=True)
|
|
973
979
|
return
|
|
974
980
|
|
|
975
|
-
def _handle_body_start_rb_rtc(self, token:
|
|
981
|
+
def _handle_body_start_rb_rtc(self, token: Tag) -> None:
|
|
976
982
|
if self.open_elements and self.open_elements[-1].name in {"rb", "rp", "rt", "rtc"}:
|
|
977
983
|
self._generate_implied_end_tags()
|
|
978
984
|
self._insert_element(token, push=True)
|
|
979
985
|
return
|
|
980
986
|
|
|
981
|
-
def _handle_body_start_table_parse_error(self, token:
|
|
987
|
+
def _handle_body_start_table_parse_error(self, token: Tag) -> None:
|
|
982
988
|
self._parse_error("unexpected-start-tag", tag_name=token.name)
|
|
983
989
|
return
|
|
984
990
|
|
|
985
|
-
def _handle_body_start_default(self, token:
|
|
991
|
+
def _handle_body_start_default(self, token: Tag) -> ModeResultTuple | None:
|
|
986
992
|
self._reconstruct_active_formatting_elements()
|
|
987
993
|
self._insert_element(token, push=True)
|
|
988
994
|
if token.self_closing:
|
|
989
995
|
self._parse_error("non-void-html-element-start-tag-with-trailing-solidus", tag_name=token.name)
|
|
990
996
|
# Elements reaching here have no handler - never in FRAMESET_NEUTRAL/FORMATTING_ELEMENTS
|
|
991
997
|
self.frameset_ok = False
|
|
992
|
-
return
|
|
998
|
+
return None
|
|
993
999
|
|
|
994
|
-
def _mode_in_table(self, token:
|
|
1000
|
+
def _mode_in_table(self, token: AnyToken) -> ModeResultTuple | None:
|
|
995
1001
|
if isinstance(token, CharacterTokens):
|
|
996
1002
|
data = token.data or ""
|
|
997
1003
|
if "\x00" in data:
|
|
@@ -1085,14 +1091,14 @@ class TreeBuilderModesMixin:
|
|
|
1085
1091
|
if input_type == "hidden":
|
|
1086
1092
|
self._parse_error("unexpected-hidden-input-in-table")
|
|
1087
1093
|
self._insert_element(token, push=True)
|
|
1088
|
-
self.
|
|
1094
|
+
self._pop_current() # push=True always adds to stack
|
|
1089
1095
|
return None
|
|
1090
1096
|
if name == "form":
|
|
1091
1097
|
self._parse_error("unexpected-form-in-table")
|
|
1092
1098
|
if self.form_element is None:
|
|
1093
1099
|
node = self._insert_element(token, push=True)
|
|
1094
1100
|
self.form_element = node
|
|
1095
|
-
self.
|
|
1101
|
+
self._pop_current() # push=True always adds to stack
|
|
1096
1102
|
return None
|
|
1097
1103
|
self._parse_error("foster-parenting-start-tag", tag_name=name)
|
|
1098
1104
|
previous = self.insert_from_table
|
|
@@ -1124,7 +1130,7 @@ class TreeBuilderModesMixin:
|
|
|
1124
1130
|
self._parse_error("eof-in-table")
|
|
1125
1131
|
return None
|
|
1126
1132
|
|
|
1127
|
-
def _mode_in_table_text(self, token:
|
|
1133
|
+
def _mode_in_table_text(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1128
1134
|
if isinstance(token, CharacterTokens):
|
|
1129
1135
|
# IN_TABLE mode guarantees non-empty data
|
|
1130
1136
|
data = token.data
|
|
@@ -1154,7 +1160,7 @@ class TreeBuilderModesMixin:
|
|
|
1154
1160
|
self.mode = original
|
|
1155
1161
|
return ("reprocess", original, token)
|
|
1156
1162
|
|
|
1157
|
-
def _mode_in_caption(self, token:
|
|
1163
|
+
def _mode_in_caption(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1158
1164
|
if isinstance(token, CharacterTokens):
|
|
1159
1165
|
return self._mode_in_body(token)
|
|
1160
1166
|
if isinstance(token, CommentToken):
|
|
@@ -1200,14 +1206,14 @@ class TreeBuilderModesMixin:
|
|
|
1200
1206
|
self._generate_implied_end_tags()
|
|
1201
1207
|
# Caption verified in scope above
|
|
1202
1208
|
while self.open_elements: # pragma: no branch
|
|
1203
|
-
node = self.
|
|
1209
|
+
node = self._pop_current()
|
|
1204
1210
|
if node.name == "caption":
|
|
1205
1211
|
break
|
|
1206
1212
|
self._clear_active_formatting_up_to_marker()
|
|
1207
1213
|
self.mode = InsertionMode.IN_TABLE
|
|
1208
1214
|
return True
|
|
1209
1215
|
|
|
1210
|
-
def _mode_in_column_group(self, token:
|
|
1216
|
+
def _mode_in_column_group(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1211
1217
|
current = self.open_elements[-1] if self.open_elements else None
|
|
1212
1218
|
if isinstance(token, CharacterTokens):
|
|
1213
1219
|
data = token.data or ""
|
|
@@ -1244,7 +1250,7 @@ class TreeBuilderModesMixin:
|
|
|
1244
1250
|
return self._mode_in_body(token)
|
|
1245
1251
|
if name == "col":
|
|
1246
1252
|
self._insert_element(token, push=True)
|
|
1247
|
-
self.
|
|
1253
|
+
self._pop_current() # push=True always adds to stack
|
|
1248
1254
|
return None
|
|
1249
1255
|
if name == "template":
|
|
1250
1256
|
# Template is handled by delegating to IN_HEAD
|
|
@@ -1302,7 +1308,7 @@ class TreeBuilderModesMixin:
|
|
|
1302
1308
|
return None
|
|
1303
1309
|
# Per spec: EOF when current is html - implicit None return
|
|
1304
1310
|
|
|
1305
|
-
def _mode_in_table_body(self, token:
|
|
1311
|
+
def _mode_in_table_body(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1306
1312
|
if isinstance(token, CharacterTokens) or isinstance(token, CommentToken):
|
|
1307
1313
|
return self._mode_in_table(token)
|
|
1308
1314
|
if isinstance(token, Tag):
|
|
@@ -1337,7 +1343,7 @@ class TreeBuilderModesMixin:
|
|
|
1337
1343
|
return None
|
|
1338
1344
|
# Pop tbody/tfoot/thead (stack always has elements here in normal parsing)
|
|
1339
1345
|
if self.open_elements:
|
|
1340
|
-
self.
|
|
1346
|
+
self._pop_current()
|
|
1341
1347
|
self.mode = InsertionMode.IN_TABLE
|
|
1342
1348
|
return ("reprocess", InsertionMode.IN_TABLE, token)
|
|
1343
1349
|
# Empty stack edge case - go directly to IN_TABLE without reprocess
|
|
@@ -1368,7 +1374,7 @@ class TreeBuilderModesMixin:
|
|
|
1368
1374
|
self._parse_error("unexpected-end-tag", tag_name=token.name)
|
|
1369
1375
|
return None
|
|
1370
1376
|
if current and current.name in {"tbody", "tfoot", "thead"}:
|
|
1371
|
-
self.
|
|
1377
|
+
self._pop_current()
|
|
1372
1378
|
self.mode = InsertionMode.IN_TABLE
|
|
1373
1379
|
return ("reprocess", InsertionMode.IN_TABLE, token)
|
|
1374
1380
|
if name in {"caption", "col", "colgroup", "td", "th", "tr"}:
|
|
@@ -1378,7 +1384,7 @@ class TreeBuilderModesMixin:
|
|
|
1378
1384
|
assert isinstance(token, EOFToken), f"Unexpected token type: {type(token)}"
|
|
1379
1385
|
return self._mode_in_table(token)
|
|
1380
1386
|
|
|
1381
|
-
def _mode_in_row(self, token:
|
|
1387
|
+
def _mode_in_row(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1382
1388
|
if isinstance(token, CharacterTokens) or isinstance(token, CommentToken):
|
|
1383
1389
|
return self._mode_in_table(token)
|
|
1384
1390
|
if isinstance(token, Tag):
|
|
@@ -1431,14 +1437,14 @@ class TreeBuilderModesMixin:
|
|
|
1431
1437
|
self._clear_stack_until({"tr", "template", "html"})
|
|
1432
1438
|
# Pop tr if on top (may not be if stack was exhausted)
|
|
1433
1439
|
if self.open_elements and self.open_elements[-1].name == "tr":
|
|
1434
|
-
self.
|
|
1440
|
+
self._pop_current()
|
|
1435
1441
|
# When in a template, restore template mode; otherwise use IN_TABLE_BODY
|
|
1436
1442
|
if self.template_modes:
|
|
1437
1443
|
self.mode = self.template_modes[-1]
|
|
1438
1444
|
else:
|
|
1439
1445
|
self.mode = InsertionMode.IN_TABLE_BODY
|
|
1440
1446
|
|
|
1441
|
-
def _mode_in_cell(self, token:
|
|
1447
|
+
def _mode_in_cell(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1442
1448
|
if isinstance(token, CharacterTokens):
|
|
1443
1449
|
previous = self.insert_from_table
|
|
1444
1450
|
self.insert_from_table = False
|
|
@@ -1492,7 +1498,7 @@ class TreeBuilderModesMixin:
|
|
|
1492
1498
|
return ("reprocess", self.mode, token)
|
|
1493
1499
|
return self._mode_in_table(token)
|
|
1494
1500
|
|
|
1495
|
-
def _mode_in_select(self, token:
|
|
1501
|
+
def _mode_in_select(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1496
1502
|
if isinstance(token, CharacterTokens):
|
|
1497
1503
|
data = token.data or ""
|
|
1498
1504
|
if "\x00" in data:
|
|
@@ -1511,15 +1517,15 @@ class TreeBuilderModesMixin:
|
|
|
1511
1517
|
return ("reprocess", InsertionMode.IN_BODY, token)
|
|
1512
1518
|
if name == "option":
|
|
1513
1519
|
if self.open_elements and self.open_elements[-1].name == "option":
|
|
1514
|
-
self.
|
|
1520
|
+
self._pop_current()
|
|
1515
1521
|
self._reconstruct_active_formatting_elements()
|
|
1516
1522
|
self._insert_element(token, push=True)
|
|
1517
1523
|
return None
|
|
1518
1524
|
if name == "optgroup":
|
|
1519
1525
|
if self.open_elements and self.open_elements[-1].name == "option":
|
|
1520
|
-
self.
|
|
1526
|
+
self._pop_current()
|
|
1521
1527
|
if self.open_elements and self.open_elements[-1].name == "optgroup":
|
|
1522
|
-
self.
|
|
1528
|
+
self._pop_current()
|
|
1523
1529
|
self._reconstruct_active_formatting_elements()
|
|
1524
1530
|
self._insert_element(token, push=True)
|
|
1525
1531
|
return None
|
|
@@ -1561,9 +1567,9 @@ class TreeBuilderModesMixin:
|
|
|
1561
1567
|
self._parse_error("unexpected-start-tag-in-select", tag_name=name)
|
|
1562
1568
|
# Per spec: pop option and optgroup before inserting hr (makes hr sibling, not child)
|
|
1563
1569
|
if self.open_elements and self.open_elements[-1].name == "option":
|
|
1564
|
-
self.
|
|
1570
|
+
self._pop_current()
|
|
1565
1571
|
if self.open_elements and self.open_elements[-1].name == "optgroup":
|
|
1566
|
-
self.
|
|
1572
|
+
self._pop_current()
|
|
1567
1573
|
self._reconstruct_active_formatting_elements()
|
|
1568
1574
|
self._insert_element(token, push=False)
|
|
1569
1575
|
return None
|
|
@@ -1594,15 +1600,15 @@ class TreeBuilderModesMixin:
|
|
|
1594
1600
|
return None
|
|
1595
1601
|
if name == "optgroup":
|
|
1596
1602
|
if self.open_elements and self.open_elements[-1].name == "option":
|
|
1597
|
-
self.
|
|
1603
|
+
self._pop_current()
|
|
1598
1604
|
if self.open_elements and self.open_elements[-1].name == "optgroup":
|
|
1599
|
-
self.
|
|
1605
|
+
self._pop_current()
|
|
1600
1606
|
else:
|
|
1601
1607
|
self._parse_error("unexpected-end-tag-in-select", tag_name=token.name)
|
|
1602
1608
|
return None
|
|
1603
1609
|
if name == "option":
|
|
1604
1610
|
if self.open_elements and self.open_elements[-1].name == "option":
|
|
1605
|
-
self.
|
|
1611
|
+
self._pop_current()
|
|
1606
1612
|
else:
|
|
1607
1613
|
self._parse_error("unexpected-end-tag-in-select", tag_name=token.name)
|
|
1608
1614
|
return None
|
|
@@ -1643,7 +1649,7 @@ class TreeBuilderModesMixin:
|
|
|
1643
1649
|
# i.e., the target is inside the select or there's no select
|
|
1644
1650
|
if target_idx is not None and (select_idx is None or target_idx > select_idx):
|
|
1645
1651
|
while True:
|
|
1646
|
-
popped = self.
|
|
1652
|
+
popped = self._pop_current()
|
|
1647
1653
|
if popped.name == name:
|
|
1648
1654
|
break
|
|
1649
1655
|
return None
|
|
@@ -1659,7 +1665,7 @@ class TreeBuilderModesMixin:
|
|
|
1659
1665
|
assert isinstance(token, EOFToken), f"Unexpected token type: {type(token)}"
|
|
1660
1666
|
return self._mode_in_body(token)
|
|
1661
1667
|
|
|
1662
|
-
def _mode_in_template(self, token:
|
|
1668
|
+
def _mode_in_template(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1663
1669
|
# § The "in template" insertion mode
|
|
1664
1670
|
# https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-intemplate
|
|
1665
1671
|
if isinstance(token, CharacterTokens):
|
|
@@ -1738,7 +1744,7 @@ class TreeBuilderModesMixin:
|
|
|
1738
1744
|
return ("reprocess", self.mode, token)
|
|
1739
1745
|
return None
|
|
1740
1746
|
|
|
1741
|
-
def _mode_after_body(self, token:
|
|
1747
|
+
def _mode_after_body(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1742
1748
|
if isinstance(token, CharacterTokens):
|
|
1743
1749
|
if is_all_whitespace(token.data):
|
|
1744
1750
|
# Whitespace is processed using InBody rules (appended to body)
|
|
@@ -1759,7 +1765,7 @@ class TreeBuilderModesMixin:
|
|
|
1759
1765
|
assert isinstance(token, EOFToken), f"Unexpected token type: {type(token)}"
|
|
1760
1766
|
return None
|
|
1761
1767
|
|
|
1762
|
-
def _mode_after_after_body(self, token:
|
|
1768
|
+
def _mode_after_after_body(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1763
1769
|
if isinstance(token, CharacterTokens):
|
|
1764
1770
|
if is_all_whitespace(token.data):
|
|
1765
1771
|
# Per spec: whitespace characters are inserted using the rules for the "in body" mode
|
|
@@ -1786,7 +1792,7 @@ class TreeBuilderModesMixin:
|
|
|
1786
1792
|
assert isinstance(token, EOFToken), f"Unexpected token type: {type(token)}"
|
|
1787
1793
|
return None
|
|
1788
1794
|
|
|
1789
|
-
def _mode_in_frameset(self, token:
|
|
1795
|
+
def _mode_in_frameset(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1790
1796
|
# Per HTML5 spec §13.2.6.4.16: In frameset insertion mode
|
|
1791
1797
|
if isinstance(token, CharacterTokens):
|
|
1792
1798
|
# Only whitespace characters allowed; ignore all others
|
|
@@ -1807,13 +1813,13 @@ class TreeBuilderModesMixin:
|
|
|
1807
1813
|
if self.open_elements and self.open_elements[-1].name == "html":
|
|
1808
1814
|
self._parse_error("unexpected-end-tag", tag_name=token.name)
|
|
1809
1815
|
return None
|
|
1810
|
-
self.
|
|
1816
|
+
self._pop_current()
|
|
1811
1817
|
if self.open_elements and self.open_elements[-1].name != "frameset":
|
|
1812
1818
|
self.mode = InsertionMode.AFTER_FRAMESET
|
|
1813
1819
|
return None
|
|
1814
1820
|
if token.kind == Tag.START and token.name == "frame":
|
|
1815
1821
|
self._insert_element(token, push=True)
|
|
1816
|
-
self.
|
|
1822
|
+
self._pop_current()
|
|
1817
1823
|
return None
|
|
1818
1824
|
if token.kind == Tag.START and token.name == "noframes":
|
|
1819
1825
|
# Per spec: use IN_HEAD rules but preserve current mode for TEXT restoration
|
|
@@ -1828,7 +1834,7 @@ class TreeBuilderModesMixin:
|
|
|
1828
1834
|
self._parse_error("unexpected-token-in-frameset")
|
|
1829
1835
|
return None
|
|
1830
1836
|
|
|
1831
|
-
def _mode_after_frameset(self, token:
|
|
1837
|
+
def _mode_after_frameset(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1832
1838
|
# Per HTML5 spec §13.2.6.4.17: After frameset insertion mode
|
|
1833
1839
|
if isinstance(token, CharacterTokens):
|
|
1834
1840
|
# Only whitespace characters allowed; non-whitespace is a parse error.
|
|
@@ -1863,7 +1869,7 @@ class TreeBuilderModesMixin:
|
|
|
1863
1869
|
self.mode = InsertionMode.IN_FRAMESET
|
|
1864
1870
|
return ("reprocess", InsertionMode.IN_FRAMESET, token)
|
|
1865
1871
|
|
|
1866
|
-
def _mode_after_after_frameset(self, token:
|
|
1872
|
+
def _mode_after_after_frameset(self, token: AnyToken) -> ModeResultTuple | None:
|
|
1867
1873
|
# Per HTML5 spec §13.2.6.4.18: After after frameset insertion mode
|
|
1868
1874
|
if isinstance(token, CharacterTokens):
|
|
1869
1875
|
# Whitespace is processed using InBody rules
|
|
@@ -1894,7 +1900,7 @@ class TreeBuilderModesMixin:
|
|
|
1894
1900
|
|
|
1895
1901
|
# Helpers ----------------------------------------------------------------
|
|
1896
1902
|
|
|
1897
|
-
_MODE_HANDLERS = [
|
|
1903
|
+
_MODE_HANDLERS: list[Callable[[TreeBuilderModesMixin, AnyToken], ModeResultTuple | None]] = [
|
|
1898
1904
|
_mode_initial,
|
|
1899
1905
|
_mode_before_html,
|
|
1900
1906
|
_mode_before_head,
|
|
@@ -1919,14 +1925,14 @@ class TreeBuilderModesMixin:
|
|
|
1919
1925
|
_mode_in_template,
|
|
1920
1926
|
]
|
|
1921
1927
|
|
|
1922
|
-
_BODY_TOKEN_HANDLERS = {
|
|
1928
|
+
_BODY_TOKEN_HANDLERS: dict[type[AnyToken], Callable[[TreeBuilderModesMixin, Any], ModeResultTuple | None]] = {
|
|
1923
1929
|
CharacterTokens: _handle_characters_in_body,
|
|
1924
1930
|
CommentToken: _handle_comment_in_body,
|
|
1925
1931
|
Tag: _handle_tag_in_body,
|
|
1926
1932
|
EOFToken: _handle_eof_in_body,
|
|
1927
1933
|
}
|
|
1928
1934
|
|
|
1929
|
-
_BODY_START_HANDLERS = {
|
|
1935
|
+
_BODY_START_HANDLERS: dict[str, Callable[[TreeBuilderModesMixin, Tag], ModeResultTuple | None]] = {
|
|
1930
1936
|
"a": _handle_body_start_a,
|
|
1931
1937
|
"address": _handle_body_start_block_with_p,
|
|
1932
1938
|
"applet": _handle_body_start_applet_like,
|
|
@@ -2031,7 +2037,7 @@ class TreeBuilderModesMixin:
|
|
|
2031
2037
|
"wbr": _handle_body_start_void_with_formatting,
|
|
2032
2038
|
"xmp": _handle_body_start_plaintext_xmp,
|
|
2033
2039
|
}
|
|
2034
|
-
_BODY_END_HANDLERS = {
|
|
2040
|
+
_BODY_END_HANDLERS: dict[str, Callable[[TreeBuilderModesMixin, Tag], ModeResultTuple | None]] = {
|
|
2035
2041
|
"address": _handle_body_end_block,
|
|
2036
2042
|
"applet": _handle_body_end_applet_like,
|
|
2037
2043
|
"article": _handle_body_end_block,
|