File tree 2 files changed +34
-2
lines changed
2 files changed +34
-2
lines changed Original file line number Diff line number Diff line change @@ -839,8 +839,22 @@ func (z *Tokenizer) readStartTag() TokenType {
839
839
if raw {
840
840
z .rawTag = strings .ToLower (string (z .buf [z .data .start :z .data .end ]))
841
841
}
842
- // Look for a self-closing token like "<br/>".
843
- if z .err == nil && z .buf [z .raw .end - 2 ] == '/' {
842
+ // Look for a self-closing token (e.g. <br/>).
843
+ //
844
+ // Originally, we did this by just checking that the last character of the
845
+ // tag (ignoring the closing bracket) was a solidus (/) character, but this
846
+ // is not always accurate.
847
+ //
848
+ // We need to be careful that we don't misinterpret a non-self-closing tag
849
+ // as self-closing, as can happen if the tag contains unquoted attribute
850
+ // values (i.e. <p a=/>).
851
+ //
852
+ // To avoid this, we check that the last non-bracket character of the tag
853
+ // (z.raw.end-2) isn't the same character as the last non-quote character of
854
+ // the last attribute of the tag (z.pendingAttr[1].end-1), if the tag has
855
+ // attributes.
856
+ nAttrs := len (z .attr )
857
+ if z .err == nil && z .buf [z .raw .end - 2 ] == '/' && (nAttrs == 0 || z .raw .end - 2 != z .attr [nAttrs - 1 ][1 ].end - 1 ) {
844
858
return SelfClosingTagToken
845
859
}
846
860
return StartTagToken
Original file line number Diff line number Diff line change @@ -616,6 +616,16 @@ var tokenTests = []tokenTest{
616
616
`<p a/ ="">` ,
617
617
`<p a="" =""="">` ,
618
618
},
619
+ {
620
+ "slash at end of unquoted attribute value" ,
621
+ `<p a="\">` ,
622
+ `<p a="\">` ,
623
+ },
624
+ {
625
+ "self-closing tag with attribute" ,
626
+ `<p a=/>` ,
627
+ `<p a="/">` ,
628
+ },
619
629
}
620
630
621
631
func TestTokenizer (t * testing.T ) {
@@ -815,6 +825,14 @@ func TestReaderEdgeCases(t *testing.T) {
815
825
}
816
826
}
817
827
828
+ func TestSelfClosingTagValueConfusion (t * testing.T ) {
829
+ z := NewTokenizer (strings .NewReader (`<p a=/>` ))
830
+ tok := z .Next ()
831
+ if tok != StartTagToken {
832
+ t .Fatalf ("unexpected token type: got %s, want %s" , tok , StartTagToken )
833
+ }
834
+ }
835
+
818
836
// zeroOneByteReader is like a strings.Reader that alternates between
819
837
// returning 0 bytes and 1 byte at a time.
820
838
type zeroOneByteReader struct {
You can’t perform that action at this time.
0 commit comments