/[suikacvs]/markup/html/whatpm/Whatpm/ContentChecker/HTML.pm
Suika

Contents of /markup/html/whatpm/Whatpm/ContentChecker/HTML.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.180 - (hide annotations) (download)
Sat Jul 25 04:05:42 2009 UTC (16 years ago) by wakaba
Branch: MAIN
Changes since 1.179: +4 -3 lines
++ whatpm/t/dom-conformance/ChangeLog	25 Jul 2009 04:03:26 -0000
	* html-interactive-1.dat, html-scripting-1.dat,
	html-metadata-1.dat: The |eventsource| element and the |onmessage|
	attribute have been dropped (HTML5 revision 2870).

2009-07-25  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	25 Jul 2009 03:55:06 -0000
	* HTML.pm.src: Marked where SVG |script| execution should be
	implemented (HTML5 revision 2904).

2009-07-25  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	25 Jul 2009 04:04:24 -0000
	* HTML.pm: The |eventsource| element and the |onmessage| attribute
	have been dropped (HTML5 revision 2870).

2009-07-25  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::ContentChecker;
2     use strict;
3     require Whatpm::ContentChecker;
4    
5 wakaba 1.117 use Char::Class::XML qw/InXML_NCNameStartChar10 InXMLNCNameChar10/;
6    
7 wakaba 1.1 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
8    
9 wakaba 1.174 ## --- Feature Status ---
10    
11 wakaba 1.89 sub FEATURE_HTML5_COMPLETE () {
12 wakaba 1.154 ## NOTE: Part of HTML5, the implemented status.
13 wakaba 1.89 Whatpm::ContentChecker::FEATURE_STATUS_REC |
14     Whatpm::ContentChecker::FEATURE_ALLOWED
15     }
16 wakaba 1.154 sub FEATURE_HTML5_CR () {
17     ## NOTE: Part of HTML5, the awaiting implementation feedback status.
18     Whatpm::ContentChecker::FEATURE_STATUS_CR |
19     Whatpm::ContentChecker::FEATURE_ALLOWED
20     }
21 wakaba 1.54 sub FEATURE_HTML5_LC () {
22 wakaba 1.154 ## NOTE: Part of HTML5, the last call of comments status.
23 wakaba 1.54 Whatpm::ContentChecker::FEATURE_STATUS_LC |
24     Whatpm::ContentChecker::FEATURE_ALLOWED
25     }
26     sub FEATURE_HTML5_AT_RISK () {
27 wakaba 1.154 ## NOTE: Part of HTML5, but in the being considered for removal
28     ## status.
29 wakaba 1.54 Whatpm::ContentChecker::FEATURE_STATUS_WD |
30     Whatpm::ContentChecker::FEATURE_ALLOWED
31     }
32     sub FEATURE_HTML5_WD () {
33 wakaba 1.154 ## NOTE: Part of HTML5, the working draft status.
34 wakaba 1.54 Whatpm::ContentChecker::FEATURE_STATUS_WD |
35     Whatpm::ContentChecker::FEATURE_ALLOWED
36     }
37     sub FEATURE_HTML5_FD () {
38 wakaba 1.154 ## NOTE: Part of HTML5, the first draft status.
39 wakaba 1.54 Whatpm::ContentChecker::FEATURE_STATUS_WD |
40     Whatpm::ContentChecker::FEATURE_ALLOWED
41     }
42     sub FEATURE_HTML5_DEFAULT () {
43 wakaba 1.154 ## NOTE: Part of HTML5, but not annotated.
44 wakaba 1.54 Whatpm::ContentChecker::FEATURE_STATUS_WD |
45     Whatpm::ContentChecker::FEATURE_ALLOWED
46 wakaba 1.49 }
47 wakaba 1.54 sub FEATURE_HTML5_DROPPED () {
48 wakaba 1.154 ## NOTE: Was part of HTML5, in a status before the last call of
49     ## comments, but then dropped.
50 wakaba 1.49 Whatpm::ContentChecker::FEATURE_STATUS_WD
51     }
52 wakaba 1.118 sub FEATURE_HTML5_LC_DROPPED () {
53 wakaba 1.154 ## NOTE: Was part of HTML5, in the last call of comments status, but
54     ## then dropped.
55 wakaba 1.118 Whatpm::ContentChecker::FEATURE_STATUS_LC
56     }
57 wakaba 1.154
58 wakaba 1.119 sub FEATURE_WF2X () {
59 wakaba 1.154 ## NOTE: Defined in WF2 (whether deprecated or not) and then
60     ## incorporated into the HTML5 spec.
61 wakaba 1.119 Whatpm::ContentChecker::FEATURE_STATUS_LC
62     }
63 wakaba 1.54 sub FEATURE_WF2 () {
64 wakaba 1.154 ## NOTE: Features introduced or modified in WF2, which were not
65     ## merged into HTML5.
66 wakaba 1.119 Whatpm::ContentChecker::FEATURE_STATUS_LC
67 wakaba 1.54 }
68 wakaba 1.126 sub FEATURE_WF2_INFORMATIVE () {
69 wakaba 1.154 ## NOTE: Features mentioned in WF2's informative appendix A, which
70     ## were not merged into HTML5.
71 wakaba 1.56 Whatpm::ContentChecker::FEATURE_STATUS_LC
72     }
73 wakaba 1.49
74 wakaba 1.154 sub FEATURE_RDFA_REC () {
75     Whatpm::ContentChecker::FEATURE_STATUS_REC
76 wakaba 1.121 }
77 wakaba 1.154 sub FEATURE_RDFA_LC_DROPPED () {
78     ## NOTE: The feature that was defined in a RDFa last call working
79     ## draft, but then dropped.
80 wakaba 1.61 Whatpm::ContentChecker::FEATURE_STATUS_LC
81     }
82 wakaba 1.58
83     ## NOTE: XHTML Role LCWD has almost no information on how the |role|
84     ## attribute can be used- the only requirements for that matter is:
85     ## "the attribute MUST be referenced using its namespace-qualified form" (and
86     ## this is a host language conformance!).
87 wakaba 1.82 sub FEATURE_ROLE_LC () {
88     Whatpm::ContentChecker::FEATURE_STATUS_LC
89     }
90    
91     sub FEATURE_XHTML2_ED () {
92 wakaba 1.154 ## NOTE: XHTML 2.0 Editor's Draft, in which the namespace URI is
93     ## "http://www.w3.org/1999/xhtml".
94 wakaba 1.82 Whatpm::ContentChecker::FEATURE_STATUS_WD
95     }
96 wakaba 1.58
97 wakaba 1.55 sub FEATURE_XHTMLBASIC11_CR () {
98 wakaba 1.154 ## NOTE: XHTML Basic 1.1 Recommendation, new features (not in XHTML
99     ## M12N).
100     Whatpm::ContentChecker::FEATURE_STATUS_REC
101 wakaba 1.55 }
102     sub FEATURE_XHTMLBASIC11_CR_DEPRECATED () {
103 wakaba 1.154 ## NOTE: XHTML Basic 1.1 Recommendation, new but deprecated
104     ## features.
105     Whatpm::ContentChecker::FEATURE_STATUS_REC |
106 wakaba 1.55 Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
107     }
108    
109 wakaba 1.154 sub FEATURE_RUBY_REC () {
110     Whatpm::ContentChecker::FEATURE_STATUS_CR
111 wakaba 1.82 }
112    
113 wakaba 1.154 sub FEATURE_M12N11_LC () {
114     ## NOTE: XHTML M12N 1.1 Recommendation, new features (not in 1.0).
115     Whatpm::ContentChecker::FEATURE_STATUS_REC;
116 wakaba 1.99 }
117    
118 wakaba 1.49 ## NOTE: M12N10 status is based on its abstract module definition.
119     ## It contains a number of problems. (However, again, it's a REC!)
120 wakaba 1.54 sub FEATURE_M12N10_REC () {
121 wakaba 1.154 ## NOTE: Oh, XHTML m12n 1.0 passed the CR phase! W3C Process sucks!
122 wakaba 1.54 Whatpm::ContentChecker::FEATURE_STATUS_REC
123     }
124     sub FEATURE_M12N10_REC_DEPRECATED () {
125     Whatpm::ContentChecker::FEATURE_STATUS_REC |
126     Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
127     }
128 wakaba 1.49
129     ## NOTE: XHTML10 status is based on its transitional and frameset DTDs
130     ## (second edition). Only missing attributes from M12N10 abstract
131     ## definition are added.
132 wakaba 1.54 sub FEATURE_XHTML10_REC () {
133     Whatpm::ContentChecker::FEATURE_STATUS_CR
134     }
135    
136 wakaba 1.61 ## NOTE: Diff from HTML4.
137     sub FEATURE_ISOHTML_PREPARATION () { ## Informative documentation
138     Whatpm::ContentChecker::FEATURE_STATUS_CR
139     }
140 wakaba 1.58
141 wakaba 1.49 ## NOTE: HTML4 status is based on its transitional and frameset DTDs (HTML
142     ## 4.01). Only missing attributes from XHTML10 are added.
143 wakaba 1.54 sub FEATURE_HTML4_REC_RESERVED () {
144     Whatpm::ContentChecker::FEATURE_STATUS_WD
145     }
146    
147     ## TODO: According to HTML4 definition, authors SHOULD use style sheets
148     ## rather than presentational attributes (deprecated or not deprecated).
149 wakaba 1.48
150 wakaba 1.61 ## NOTE: Diff from HTML4.
151     sub FEATURE_HTML32_REC_OBSOLETE () {
152     Whatpm::ContentChecker::FEATURE_STATUS_CR |
153     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD
154     ## NOTE: Lowercase normative "should".
155     }
156    
157     sub FEATURE_RFC2659 () { ## Experimental RFC
158     Whatpm::ContentChecker::FEATURE_STATUS_CR
159     }
160    
161     ## NOTE: HTML 2.x - diff from HTML 2.0 and not in newer versions.
162     sub FEATURE_HTML2X_RFC () { ## Proposed Standard, obsolete
163     Whatpm::ContentChecker::FEATURE_STATUS_CR
164     }
165    
166     ## NOTE: Diff from HTML 2.0.
167     sub FEATURE_RFC1942 () { ## Experimental RFC, obsolete
168     Whatpm::ContentChecker::FEATURE_STATUS_CR
169     }
170    
171     ## NOTE: Diff from HTML 3.2.
172     sub FEATURE_HTML20_RFC () { ## Proposed Standard, obsolete
173     Whatpm::ContentChecker::FEATURE_STATUS_CR
174     }
175 wakaba 1.58
176 wakaba 1.174 ## --- Content Model ---
177    
178 wakaba 1.29 ## December 2007 HTML5 Classification
179    
180     my $HTMLMetadataContent = {
181     $HTML_NS => {
182     title => 1, base => 1, link => 1, style => 1, script => 1, noscript => 1,
183 wakaba 1.118 'event-source' => 1, eventsource => 1,
184     command => 1, datatemplate => 1,
185 wakaba 1.29 ## NOTE: A |meta| with no |name| element is not allowed as
186     ## a metadata content other than |head| element.
187     meta => 1,
188     },
189     ## NOTE: RDF is mentioned in the HTML5 spec.
190     ## TODO: Other RDF elements?
191     q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {RDF => 1},
192     };
193    
194 wakaba 1.72 my $HTMLFlowContent = {
195 wakaba 1.29 $HTML_NS => {
196     section => 1, nav => 1, article => 1, blockquote => 1, aside => 1,
197     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
198     footer => 1, address => 1, p => 1, hr => 1, dialog => 1, pre => 1,
199     ol => 1, ul => 1, dl => 1, figure => 1, map => 1, table => 1,
200 wakaba 1.119 form => 1, fieldset => 1,
201 wakaba 1.72 details => 1, ## ISSUE: "Flow element" in spec.
202     datagrid => 1, ## ISSUE: "Flow element" in spec.
203 wakaba 1.29 datatemplate => 1,
204     div => 1, ## ISSUE: No category in spec.
205     ## NOTE: |style| is only allowed if |scoped| attribute is specified.
206     ## Additionally, it must be before any other element or
207     ## non-inter-element-whitespace text node.
208     style => 1,
209    
210 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
211 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
212     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
213 wakaba 1.99 b => 1, bdo => 1, ruby => 1,
214 wakaba 1.118 script => 1, noscript => 1, 'event-source' => 1, eventsource => 1,
215     command => 1, bb => 1,
216 wakaba 1.119 input => 1, button => 1, label => 1, select => 1, datalist => 1,
217 wakaba 1.121 textarea => 1, output => 1,
218 wakaba 1.29 datagrid => 1, ## ISSUE: "Interactive element" in the spec.
219     ## NOTE: |area| is allowed only as a descendant of |map|.
220     area => 1,
221    
222 wakaba 1.124 ## NOTE: Transparent.
223     a => 1, ins => 1, del => 1, font => 1,
224 wakaba 1.29
225 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
226 wakaba 1.29 menu => 1,
227    
228     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
229     canvas => 1,
230     },
231    
232     ## NOTE: Embedded
233     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
234     q<http://www.w3.org/2000/svg> => {svg => 1},
235     };
236    
237 wakaba 1.58 my $HTMLSectioningContent = {
238 wakaba 1.57 $HTML_NS => {
239     section => 1, nav => 1, article => 1, aside => 1,
240     ## NOTE: |body| is only allowed in |html| element.
241     body => 1,
242     },
243     };
244    
245 wakaba 1.58 my $HTMLSectioningRoot = {
246 wakaba 1.29 $HTML_NS => {
247 wakaba 1.58 blockquote => 1, datagrid => 1, figure => 1, td => 1,
248 wakaba 1.29 },
249     };
250    
251     my $HTMLHeadingContent = {
252     $HTML_NS => {
253     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
254     },
255     };
256    
257     my $HTMLPhrasingContent = {
258 wakaba 1.72 ## NOTE: All phrasing content is also flow content.
259 wakaba 1.29 $HTML_NS => {
260 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
261 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
262     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
263 wakaba 1.99 b => 1, bdo => 1, ruby => 1,
264 wakaba 1.118 script => 1, noscript => 1, 'event-source' => 1, eventsource => 1,
265     command => 1, bb => 1,
266 wakaba 1.119 input => 1, button => 1, label => 1, select => 1, datalist => 1,
267 wakaba 1.121 textarea => 1, output => 1,
268 wakaba 1.29 datagrid => 1, ## ISSUE: "Interactive element" in the spec.
269     ## NOTE: |area| is allowed only as a descendant of |map|.
270     area => 1,
271    
272     ## NOTE: Transparent.
273 wakaba 1.124 a => 1, ins => 1, del => 1, font => 1,
274 wakaba 1.29
275 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
276 wakaba 1.29 menu => 1,
277    
278     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
279     canvas => 1,
280     },
281    
282     ## NOTE: Embedded
283     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
284     q<http://www.w3.org/2000/svg> => {svg => 1},
285    
286     ## NOTE: And non-inter-element-whitespace text nodes.
287     };
288    
289 wakaba 1.40 ## $HTMLEmbeddedContent: See Whatpm::ContentChecker.
290 wakaba 1.29
291     my $HTMLInteractiveContent = {
292     $HTML_NS => {
293     a => 1,
294 wakaba 1.130 label => 1, input => 1, button => 1, select => 1, textarea => 1,
295     details => 1, datagrid => 1, bb => 1,
296    
297     ## NOTE: When "controls" attribute is specified.
298     video => 1, audio => 1,
299    
300     ## NOTE: When "type=toolbar" attribute is specified.
301     menu => 1,
302 wakaba 1.29 },
303     };
304    
305 wakaba 1.139 ## NOTE: Labelable form-associated element.
306     my $LabelableFAE = {
307     $HTML_NS => {
308     input => 1, button => 1, select => 1, textarea => 1,
309     },
310     };
311    
312 wakaba 1.130 our $IsInHTMLInteractiveContent; # See Whatpm::ContentChecker.
313    
314 wakaba 1.36 ## NOTE: $HTMLTransparentElements: See Whatpm::ContentChecker.
315     ## NOTE: Semi-transparent elements: See Whatpm::ContentChecker.
316    
317     ## -- Common attribute syntacx checkers
318    
319 wakaba 1.1 our $AttrChecker;
320 wakaba 1.82 our $AttrStatus;
321 wakaba 1.1
322     my $GetHTMLEnumeratedAttrChecker = sub {
323     my $states = shift; # {value => conforming ? 1 : -1}
324     return sub {
325     my ($self, $attr) = @_;
326     my $value = lc $attr->value; ## TODO: ASCII case insensitibility?
327     if ($states->{$value} > 0) {
328     #
329     } elsif ($states->{$value}) {
330 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'enumerated:non-conforming',
331     level => $self->{level}->{must});
332 wakaba 1.1 } else {
333 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'enumerated:invalid',
334     level => $self->{level}->{must});
335 wakaba 1.1 }
336     };
337     }; # $GetHTMLEnumeratedAttrChecker
338    
339     my $GetHTMLBooleanAttrChecker = sub {
340     my $local_name = shift;
341     return sub {
342     my ($self, $attr) = @_;
343 wakaba 1.88 my $value = lc $attr->value; ## TODO: case
344 wakaba 1.1 unless ($value eq $local_name or $value eq '') {
345 wakaba 1.88 $self->{onerror}->(node => $attr, type => 'boolean:invalid',
346 wakaba 1.104 level => $self->{level}->{must});
347 wakaba 1.1 }
348     };
349     }; # $GetHTMLBooleanAttrChecker
350    
351 wakaba 1.8 ## Unordered set of space-separated tokens
352 wakaba 1.92 my $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker = sub {
353     my $allowed_words = shift;
354     return sub {
355     my ($self, $attr) = @_;
356     my %word;
357 wakaba 1.132 for my $word (grep {length $_}
358     split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
359 wakaba 1.92 unless ($word{$word}) {
360     $word{$word} = 1;
361     if (not defined $allowed_words or
362     $allowed_words->{$word}) {
363     #
364     } else {
365 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'word not allowed',
366 wakaba 1.92 value => $word,
367 wakaba 1.104 level => $self->{level}->{must});
368 wakaba 1.92 }
369     } else {
370 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
371     value => $word,
372     level => $self->{level}->{must});
373 wakaba 1.92 }
374 wakaba 1.8 }
375 wakaba 1.92 };
376     }; # $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker
377 wakaba 1.8
378 wakaba 1.132 ## |rel| attribute (set of space separated tokens,
379 wakaba 1.1 ## whose allowed values are defined by the section on link types)
380     my $HTMLLinkTypesAttrChecker = sub {
381 wakaba 1.66 my ($a_or_area, $todo, $self, $attr, $item, $element_state) = @_;
382 wakaba 1.1 my %word;
383 wakaba 1.132 for my $word (grep {length $_}
384     split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
385 wakaba 1.1 unless ($word{$word}) {
386     $word{$word} = 1;
387 wakaba 1.18 } elsif ($word eq 'up') {
388     #
389 wakaba 1.1 } else {
390 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
391     value => $word,
392     level => $self->{level}->{must});
393 wakaba 1.1 }
394     }
395     ## NOTE: Case sensitive match (since HTML5 spec does not say link
396     ## types are case-insensitive and it says "The value should not
397     ## be confusingly similar to any other defined value (e.g.
398     ## differing only in case).").
399     ## NOTE: Though there is no explicit "MUST NOT" for undefined values,
400     ## "MAY"s and "only ... MAY" restrict non-standard non-registered
401     ## values to be used conformingly.
402 wakaba 1.66
403     my $is_hyperlink;
404     my $is_resource;
405 wakaba 1.1 require Whatpm::_LinkTypeList;
406     our $LinkType;
407     for my $word (keys %word) {
408     my $def = $LinkType->{$word};
409     if (defined $def) {
410     if ($def->{status} eq 'accepted') {
411     if (defined $def->{effect}->[$a_or_area]) {
412     #
413     } else {
414     $self->{onerror}->(node => $attr,
415 wakaba 1.104 type => 'link type:bad context',
416     value => $word,
417 wakaba 1.110 level => $self->{level}->{must});
418 wakaba 1.1 }
419     } elsif ($def->{status} eq 'proposal') {
420 wakaba 1.104 $self->{onerror}->(node => $attr,
421     type => 'link type:proposed',
422     value => $word,
423     level => $self->{level}->{should});
424 wakaba 1.20 if (defined $def->{effect}->[$a_or_area]) {
425     #
426     } else {
427     $self->{onerror}->(node => $attr,
428 wakaba 1.104 type => 'link type:bad context',
429     value => $word,
430     level => $self->{level}->{must});
431 wakaba 1.20 }
432 wakaba 1.1 } else { # rejected or synonym
433     $self->{onerror}->(node => $attr,
434 wakaba 1.104 type => 'link type:non-conforming',
435     value => $word,
436     level => $self->{level}->{must});
437 wakaba 1.1 }
438 wakaba 1.4 if (defined $def->{effect}->[$a_or_area]) {
439     if ($word eq 'alternate') {
440     #
441     } elsif ($def->{effect}->[$a_or_area] eq 'hyperlink') {
442 wakaba 1.66 $is_hyperlink = 1;
443 wakaba 1.4 }
444     }
445 wakaba 1.1 if ($def->{unique}) {
446     unless ($self->{has_link_type}->{$word}) {
447     $self->{has_link_type}->{$word} = 1;
448     } else {
449     $self->{onerror}->(node => $attr,
450 wakaba 1.104 type => 'link type:duplicate',
451     value => $word,
452     level => $self->{level}->{must});
453 wakaba 1.1 }
454     }
455 wakaba 1.66
456     if (defined $def->{effect}->[$a_or_area] and $word ne 'alternate') {
457     $is_hyperlink = 1 if $def->{effect}->[$a_or_area] eq 'hyperlink';
458     $is_resource = 1 if $def->{effect}->[$a_or_area] eq 'external resource';
459     }
460 wakaba 1.1 } else {
461 wakaba 1.104 $self->{onerror}->(node => $attr,
462     type => 'unknown link type',
463     value => $word,
464     level => $self->{level}->{uncertain});
465 wakaba 1.1 }
466     }
467 wakaba 1.66 $is_hyperlink = 1 if $word{alternate} and not $word{stylesheet};
468 wakaba 1.1 ## TODO: The Pingback 1.0 specification, which is referenced by HTML5,
469     ## says that using both X-Pingback: header field and HTML
470     ## <link rel=pingback> is deprecated and if both appears they
471     ## SHOULD contain exactly the same value.
472     ## ISSUE: Pingback 1.0 specification defines the exact representation
473     ## of its link element, which cannot be tested by the current arch.
474     ## ISSUE: Pingback 1.0 specification says that the document MUST NOT
475     ## include any string that matches to the pattern for the rel=pingback link,
476     ## which again inpossible to test.
477     ## ISSUE: rel=pingback href MUST NOT include entities other than predefined 4.
478 wakaba 1.12
479     ## NOTE: <link rel="up index"><link rel="up up index"> is not an error.
480 wakaba 1.17 ## NOTE: We can't check "If the page is part of multiple hierarchies,
481     ## then they SHOULD be described in different paragraphs.".
482 wakaba 1.66
483     $todo->{has_hyperlink_link_type} = 1 if $is_hyperlink;
484     if ($is_hyperlink or $a_or_area) {
485     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
486     }
487     if ($is_resource and not $a_or_area) {
488     $element_state->{uri_info}->{href}->{type}->{resource} = 1;
489     }
490 wakaba 1.96
491     $element_state->{link_rel} = \%word;
492 wakaba 1.1 }; # $HTMLLinkTypesAttrChecker
493 wakaba 1.20
494     ## TODO: "When an author uses a new type not defined by either this specification or the Wiki page, conformance checkers should offer to add the value to the Wiki, with the details described above, with the "proposal" status."
495 wakaba 1.1
496     ## URI (or IRI)
497     my $HTMLURIAttrChecker = sub {
498 wakaba 1.66 my ($self, $attr, $item, $element_state) = @_;
499 wakaba 1.1 ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.)
500     my $value = $attr->value;
501     Whatpm::URIChecker->check_iri_reference ($value, sub {
502 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
503 wakaba 1.106 }), $self->{level};
504 wakaba 1.17 $self->{has_uri_attr} = 1; ## TODO: <html manifest>
505 wakaba 1.66
506     my $attr_name = $attr->name;
507     $element_state->{uri_info}->{$attr_name}->{node} = $attr;
508     ## TODO: absolute
509     push @{$self->{return}->{uri}->{$value} ||= []},
510     $element_state->{uri_info}->{$attr_name};
511 wakaba 1.1 }; # $HTMLURIAttrChecker
512    
513     ## A space separated list of one or more URIs (or IRIs)
514     my $HTMLSpaceURIsAttrChecker = sub {
515     my ($self, $attr) = @_;
516 wakaba 1.66
517     my $type = {ping => 'action',
518     profile => 'namespace',
519     archive => 'resource'}->{$attr->name};
520    
521 wakaba 1.1 my $i = 0;
522 wakaba 1.132 for my $value (split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
523 wakaba 1.1 Whatpm::URIChecker->check_iri_reference ($value, sub {
524 wakaba 1.104 $self->{onerror}->(value => $value, @_, node => $attr, index => $i);
525 wakaba 1.106 }, $self->{level});
526 wakaba 1.66
527     ## TODO: absolute
528     push @{$self->{return}->{uri}->{$value} ||= []},
529 wakaba 1.67 {node => $attr, type => {$type => 1}};
530 wakaba 1.66
531 wakaba 1.1 $i++;
532     }
533 wakaba 1.67 ## ISSUE: Relative references? (especially, in profile="")
534 wakaba 1.1 ## ISSUE: Leading or trailing white spaces are conformant?
535     ## ISSUE: A sequence of white space characters are conformant?
536     ## ISSUE: A zero-length string is conformant? (It does contain a relative reference, i.e. same as base URI.)
537 wakaba 1.132 ## ISSUE: What is "space"?
538 wakaba 1.1 ## NOTE: Duplication seems not an error.
539 wakaba 1.4 $self->{has_uri_attr} = 1;
540 wakaba 1.1 }; # $HTMLSpaceURIsAttrChecker
541    
542 wakaba 1.156 my $ValidEmailAddress;
543     {
544     my $atext = qr[[A-Za-z0-9!#\$%&'*+/=?^_`{|}~-]];
545     my $dot_atom = qr/$atext+(?>\.$atext+)*/;
546     $ValidEmailAddress = qr/$dot_atom\@$dot_atom/;
547     }
548    
549 wakaba 1.168 ## Valid global date and time.
550     my $GetDateTimeAttrChecker = sub ($) {
551     my $type = shift;
552     return sub {
553     my ($self, $attr, $item, $element_state) = @_;
554    
555     my $range_error;
556    
557     require Message::Date;
558     my $dp = Message::Date->new;
559     $dp->{level} = $self->{level};
560     $dp->{onerror} = sub {
561     my %opt = @_;
562     unless ($opt{type} eq 'date value not supported') {
563     $self->{onerror}->(%opt, node => $attr);
564     $range_error = '';
565     }
566     };
567    
568     my $method = 'parse_' . $type;
569     my $d = $dp->$method ($attr->value);
570     $element_state->{date_value}->{$attr->name} = $d || $range_error;
571     };
572     }; # $GetDateTimeAttrChecker
573 wakaba 1.1
574     my $HTMLIntegerAttrChecker = sub {
575     my ($self, $attr) = @_;
576     my $value = $attr->value;
577     unless ($value =~ /\A-?[0-9]+\z/) {
578 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'integer:syntax error',
579     level => $self->{level}->{must});
580 wakaba 1.1 }
581     }; # $HTMLIntegerAttrChecker
582    
583     my $GetHTMLNonNegativeIntegerAttrChecker = sub {
584     my $range_check = shift;
585     return sub {
586     my ($self, $attr) = @_;
587     my $value = $attr->value;
588     if ($value =~ /\A[0-9]+\z/) {
589     unless ($range_check->($value + 0)) {
590 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'nninteger:out of range',
591     level => $self->{level}->{must});
592 wakaba 1.1 }
593     } else {
594     $self->{onerror}->(node => $attr,
595 wakaba 1.104 type => 'nninteger:syntax error',
596     level => $self->{level}->{must});
597 wakaba 1.1 }
598     };
599     }; # $GetHTMLNonNegativeIntegerAttrChecker
600    
601     my $GetHTMLFloatingPointNumberAttrChecker = sub {
602     my $range_check = shift;
603     return sub {
604 wakaba 1.168 my ($self, $attr, $item, $element_state) = @_;
605 wakaba 1.1 my $value = $attr->value;
606 wakaba 1.90 if ($value =~ /\A-?[0-9]+(?>\.[0-9]*)?\z/ or
607     $value =~ /\A-?\.[0-9]+\z/) {
608 wakaba 1.168 if ($range_check->($value + 0)) {
609     ## TODO: parse algorithm
610     $element_state->{number_value}->{$attr->name} = $value + 0;
611     } else {
612 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'float:out of range',
613     level => $self->{level}->{must});
614 wakaba 1.1 }
615     } else {
616     $self->{onerror}->(node => $attr,
617 wakaba 1.104 type => 'float:syntax error',
618     level => $self->{level}->{must});
619 wakaba 1.1 }
620     };
621 wakaba 1.144
622     ## TODO: scientific notation
623 wakaba 1.1 }; # $GetHTMLFloatingPointNumberAttrChecker
624    
625 wakaba 1.148 my $StepAttrChecker = sub {
626     ## NOTE: A valid floating point number (> 0), or ASCII
627     ## case-insensitive "any".
628    
629     my ($self, $attr) = @_;
630     my $value = $attr->value;
631     if ($value =~ /\A-?[0-9]+(?>\.[0-9]*)?\z/ or
632     $value =~ /\A-?\.[0-9]+\z/) {
633     unless ($value > 0) {
634     $self->{onerror}->(node => $attr, type => 'float:out of range',
635     level => $self->{level}->{must});
636     }
637     } elsif ($value =~ /\A[Aa][Nn][Yy]\z/) {
638     #
639     } else {
640     $self->{onerror}->(node => $attr,
641     type => 'float:syntax error',
642     level => $self->{level}->{must});
643     }
644    
645     ## TODO: scientific
646     }; # $StepAttrChecker
647    
648 wakaba 1.86 ## HTML4 %Length;
649     my $HTMLLengthAttrChecker = sub {
650     my ($self, $attr) = @_;
651     my $value = $attr->value;
652     unless ($value =~ /\A[0-9]+%?\z/) {
653     $self->{onerror}->(node => $attr, type => 'length:syntax error',
654 wakaba 1.104 level => $self->{level}->{must});
655 wakaba 1.86 }
656    
657     ## NOTE: HTML4 definition is too vague - it does not define the syntax
658     ## of percentage value at all (!).
659     }; # $HTMLLengthAttrChecker
660    
661 wakaba 1.161 my $MIMEToken = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
662     my $TypeOrSubtype = qr/[A-Za-z0-9!#\$&.+^_-]{1,127}/; # RFC 4288
663     my $IMTNoParameter = qr[($TypeOrSubtype)/($TypeOrSubtype)];
664    
665 wakaba 1.1 ## "A valid MIME type, optionally with parameters. [RFC 2046]"
666     ## ISSUE: RFC 2046 does not define syntax of media types.
667     ## ISSUE: The definition of "a valid MIME type" is unknown.
668     ## Syntactical correctness?
669     my $HTMLIMTAttrChecker = sub {
670     my ($self, $attr) = @_;
671     my $value = $attr->value;
672     ## ISSUE: RFC 2045 Content-Type header field allows insertion
673     ## of LWS/comments between tokens. Is it allowed in HTML? Maybe no.
674     ## ISSUE: RFC 2231 extension? Maybe no.
675     my $lws0 = qr/(?>(?>\x0D\x0A)?[\x09\x20])*/;
676     my $qs = qr/"(?>[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\x7E]|\x0D\x0A[\x09\x20]|\x5C[\x00-\x7F])*"/;
677 wakaba 1.161 if ($value =~ m#\A$lws0($MIMEToken)$lws0/$lws0($MIMEToken)$lws0((?>;$lws0$MIMEToken$lws0=$lws0(?>$MIMEToken|$qs)$lws0)*)\z#) {
678 wakaba 1.1 my @type = ($1, $2);
679     my $param = $3;
680 wakaba 1.161 while ($param =~ s/^;$lws0($MIMEToken)$lws0=$lws0(?>($MIMEToken)|($qs))$lws0//) {
681 wakaba 1.1 if (defined $2) {
682     push @type, $1 => $2;
683     } else {
684     my $n = $1;
685 wakaba 1.152 my $v = $3;
686 wakaba 1.1 $v =~ s/\\(.)/$1/gs;
687 wakaba 1.152 push @type, $n => substr ($v, 1, length ($v) - 2);
688 wakaba 1.1 }
689     }
690     require Whatpm::IMTChecker;
691 wakaba 1.109 my $ic = Whatpm::IMTChecker->new;
692     $ic->{level} = $self->{level};
693     $ic->check_imt (sub {
694 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
695 wakaba 1.1 }, @type);
696     } else {
697 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'IMT:syntax error',
698     level => $self->{level}->{must});
699 wakaba 1.1 }
700     }; # $HTMLIMTAttrChecker
701    
702     my $HTMLLanguageTagAttrChecker = sub {
703 wakaba 1.7 ## NOTE: See also $AtomLanguageTagAttrChecker in Atom.pm.
704    
705 wakaba 1.1 my ($self, $attr) = @_;
706 wakaba 1.6 my $value = $attr->value;
707     require Whatpm::LangTag;
708     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
709 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
710 wakaba 1.106 }, $self->{level});
711 wakaba 1.1 ## ISSUE: RFC 4646 (3066bis)?
712 wakaba 1.6
713     ## TODO: testdata
714 wakaba 1.1 }; # $HTMLLanguageTagAttrChecker
715    
716     ## "A valid media query [MQ]"
717     my $HTMLMQAttrChecker = sub {
718     my ($self, $attr) = @_;
719 wakaba 1.104 $self->{onerror}->(node => $attr,
720     type => 'media query',
721     level => $self->{level}->{uncertain});
722 wakaba 1.1 ## ISSUE: What is "a valid media query"?
723     }; # $HTMLMQAttrChecker
724    
725     my $HTMLEventHandlerAttrChecker = sub {
726     my ($self, $attr) = @_;
727 wakaba 1.104 $self->{onerror}->(node => $attr,
728     type => 'event handler',
729     level => $self->{level}->{uncertain});
730 wakaba 1.1 ## TODO: MUST contain valid ECMAScript code matching the
731     ## ECMAScript |FunctionBody| production. [ECMA262]
732     ## ISSUE: MUST be ES3? E4X? ES4? JS1.x?
733     ## ISSUE: Automatic semicolon insertion does not apply?
734     ## ISSUE: Other script languages?
735     }; # $HTMLEventHandlerAttrChecker
736    
737 wakaba 1.136 my $HTMLFormAttrChecker = sub {
738     my ($self, $attr) = @_;
739    
740     ## NOTE: MUST be the ID of a |form| element.
741    
742     my $value = $attr->value;
743 wakaba 1.138 push @{$self->{idref}}, ['form', $value => $attr];
744 wakaba 1.136
745     ## ISSUE: <form id=""><input form=""> (empty ID)?
746     }; # $HTMLFormAttrChecker
747    
748 wakaba 1.158 my $ListAttrChecker = sub {
749     my ($self, $attr) = @_;
750    
751     ## NOTE: MUST be the ID of a |datalist| element.
752    
753     push @{$self->{idref}}, ['datalist', $attr->value, $attr];
754    
755     ## TODO: Warn violation to control-dependent restrictions. For
756     ## example, |<input type=url maxlength=10 list=a> <datalist
757     ## id=a><option value=nonurlandtoolong></datalist>| should be
758     ## warned.
759     }; # $ListAttrChecker
760    
761 wakaba 1.160 my $PatternAttrChecker = sub {
762     my ($self, $attr) = @_;
763     $self->{onsubdoc}->({s => $attr->value,
764     container_node => $attr,
765     media_type => 'text/x-regexp-js',
766     is_char_string => 1});
767 wakaba 1.161
768     ## ISSUE: "value must match the Pattern production of ECMA 262's
769     ## grammar" - no additional constraints (e.g. {n,m} then n>=m).
770    
771     ## TODO: Warn if @value does not match @pattern.
772 wakaba 1.160 }; # $PatternAttrChecker
773    
774 wakaba 1.161 my $AcceptAttrChecker = sub {
775     my ($self, $attr) = @_;
776    
777     my $value = $attr->value;
778     $value =~ tr/A-Z/a-z/; ## ASCII case-insensitive
779     my @value = length $value ? split /,/, $value, -1 : ('');
780     my %has_value;
781     for my $v (@value) {
782     if ($has_value{$v}) {
783     $self->{onerror}->(node => $attr,
784     type => 'duplicate token',
785     value => $v,
786     level => $self->{level}->{must});
787     next;
788     }
789     $has_value{$v} = 1;
790    
791     if ($v eq 'audio/*' or $v eq 'video/*' or $v eq 'image/*') {
792     #
793     } elsif ($v =~ m[\A$IMTNoParameter\z]) {
794     ## ISSUE: HTML5 references RFC 2046, but maybe HTML5 should
795     ## define its own syntax citing RFC 4288.
796    
797     ## NOTE: Parameters not allowed.
798     require Whatpm::IMTChecker;
799     my $ic = Whatpm::IMTChecker->new;
800     $ic->{level} = $self->{level};
801     $ic->check_imt (sub {
802     $self->{onerror}->(@_, node => $attr);
803     }, $1, $2);
804     } else {
805     $self->{onerror}->(node => $attr,
806     type => 'IMTnp:syntax error', ## TODOC: type
807     value => $v,
808     level => $self->{level}->{must});
809     }
810     }
811     }; # $AcceptAttrChecker
812    
813 wakaba 1.165 my $FormControlNameAttrChecker = sub {
814     my ($self, $attr) = @_;
815    
816     unless (length $attr->value) {
817     $self->{onerror}->(node => $attr,
818     type => 'empty control name', ## TODOC: type
819     level => $self->{level}->{must});
820     }
821    
822     ## NOTE: No uniqueness constraint.
823     }; # $FormControlNameAttrChecker
824    
825     my $AutofocusAttrChecker = sub {
826     my ($self, $attr) = @_;
827    
828     $GetHTMLBooleanAttrChecker->('autofocus')->(@_);
829    
830     if ($self->{has_autofocus}) {
831     $self->{onerror}->(node => $attr,
832     type => 'duplicate autofocus', ## TODOC: type
833     level => $self->{level}->{must});
834     }
835     $self->{has_autofocus} = 1;
836     }; # $AutofocusAttrChekcer
837    
838 wakaba 1.1 my $HTMLUsemapAttrChecker = sub {
839     my ($self, $attr) = @_;
840 wakaba 1.100 ## MUST be a valid hash-name reference to a |map| element.
841 wakaba 1.1 my $value = $attr->value;
842     if ($value =~ s/^#//) {
843 wakaba 1.100 ## NOTE: |usemap="#"| is conforming, though it identifies no |map| element
844     ## according to the "rules for parsing a hash-name reference" algorithm.
845     ## The document is non-conforming anyway, since |<map name="">| (empty
846     ## name) is non-conforming.
847 wakaba 1.1 push @{$self->{usemap}}, [$value => $attr];
848     } else {
849 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'hashref:syntax error',
850     level => $self->{level}->{must});
851 wakaba 1.1 }
852 wakaba 1.100 ## NOTE: Space characters in hash-name references are conforming.
853 wakaba 1.1 ## ISSUE: UA algorithm for matching is case-insensitive; IDs only different in cases should be reported
854     }; # $HTMLUsemapAttrChecker
855    
856 wakaba 1.76 ## Valid browsing context name
857     my $HTMLBrowsingContextNameAttrChecker = sub {
858     my ($self, $attr) = @_;
859     my $value = $attr->value;
860     if ($value =~ /^_/) {
861     $self->{onerror}->(node => $attr, type => 'window name:reserved',
862 wakaba 1.104 level => $self->{level}->{must},
863 wakaba 1.76 value => $value);
864     } elsif (length $value) {
865     #
866     } else {
867     $self->{onerror}->(node => $attr, type => 'window name:empty',
868 wakaba 1.104 level => $self->{level}->{must});
869 wakaba 1.76 }
870     }; # $HTMLBrowsingContextNameAttrChecker
871    
872     ## Valid browsing context name or keyword
873 wakaba 1.1 my $HTMLTargetAttrChecker = sub {
874     my ($self, $attr) = @_;
875     my $value = $attr->value;
876     if ($value =~ /^_/) {
877     $value = lc $value; ## ISSUE: ASCII case-insentitive?
878     unless ({
879 wakaba 1.76 _blank => 1,_self => 1, _parent => 1, _top => 1,
880 wakaba 1.1 }->{$value}) {
881     $self->{onerror}->(node => $attr,
882 wakaba 1.76 type => 'window name:reserved',
883 wakaba 1.104 level => $self->{level}->{must},
884 wakaba 1.76 value => $value);
885 wakaba 1.1 }
886 wakaba 1.76 } elsif (length $value) {
887     #
888 wakaba 1.1 } else {
889 wakaba 1.76 $self->{onerror}->(node => $attr, type => 'window name:empty',
890 wakaba 1.104 level => $self->{level}->{must});
891 wakaba 1.1 }
892     }; # $HTMLTargetAttrChecker
893    
894 wakaba 1.23 my $HTMLSelectorsAttrChecker = sub {
895     my ($self, $attr) = @_;
896    
897     ## ISSUE: Namespace resolution?
898    
899     my $value = $attr->value;
900    
901     require Whatpm::CSS::SelectorsParser;
902     my $p = Whatpm::CSS::SelectorsParser->new;
903     $p->{pseudo_class}->{$_} = 1 for qw/
904     active checked disabled empty enabled first-child first-of-type
905     focus hover indeterminate last-child last-of-type link only-child
906     only-of-type root target visited
907     lang nth-child nth-last-child nth-of-type nth-last-of-type not
908     -manakai-contains -manakai-current
909     /;
910    
911     $p->{pseudo_element}->{$_} = 1 for qw/
912     after before first-letter first-line
913     /;
914    
915 wakaba 1.104 $p->{level} = $self->{level};
916 wakaba 1.23 $p->{onerror} = sub {
917 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
918 wakaba 1.23 };
919     $p->parse_string ($value);
920     }; # $HTMLSelectorsAttrChecker
921    
922 wakaba 1.129 my $HTMLCharsetChecker = sub ($$$;$) {
923     my ($charset_value, $self, $attr, $ascii_compat) = @_;
924    
925     ## NOTE: This code is used for |charset=""| attributes, |charset=|
926     ## portion of the |content=""| attributes, and |accept-charset=""|
927     ## attributes.
928 wakaba 1.91
929     ## NOTE: Though the case-sensitivility of |charset| attribute value
930     ## is not explicitly spelled in the HTML5 spec, the Character Set
931     ## registry of IANA, which is referenced from HTML5 spec, says that
932     ## charset name is case-insensitive.
933     $charset_value =~ tr/A-Z/a-z/; ## NOTE: ASCII Case-insensitive.
934    
935     require Message::Charset::Info;
936     my $charset = $Message::Charset::Info::IANACharset->{$charset_value};
937    
938     ## ISSUE: What is "valid character encoding name"? Syntactically valid?
939     ## Syntactically valid and registered? What about x-charset names?
940     unless (Message::Charset::Info::is_syntactically_valid_iana_charset_name
941     ($charset_value)) {
942     $self->{onerror}->(node => $attr,
943 wakaba 1.104 type => 'charset:syntax error',
944     value => $charset_value,
945     level => $self->{level}->{must});
946 wakaba 1.91 }
947    
948     if ($charset) {
949     ## ISSUE: What is "the preferred name for that encoding" (for a charset
950     ## with no "preferred MIME name" label)?
951     my $charset_status = $charset->{iana_names}->{$charset_value} || 0;
952     if (($charset_status &
953     Message::Charset::Info::PREFERRED_CHARSET_NAME ())
954     != Message::Charset::Info::PREFERRED_CHARSET_NAME ()) {
955     $self->{onerror}->(node => $attr,
956 wakaba 1.104 type => 'charset:not preferred',
957     value => $charset_value,
958     level => $self->{level}->{must});
959 wakaba 1.91 }
960 wakaba 1.129
961 wakaba 1.91 if (($charset_status &
962     Message::Charset::Info::REGISTERED_CHARSET_NAME ())
963     != Message::Charset::Info::REGISTERED_CHARSET_NAME ()) {
964     if ($charset_value =~ /^x-/) {
965     $self->{onerror}->(node => $attr,
966 wakaba 1.104 type => 'charset:private',
967     value => $charset_value,
968     level => $self->{level}->{good});
969 wakaba 1.91 } else {
970     $self->{onerror}->(node => $attr,
971 wakaba 1.104 type => 'charset:not registered',
972     value => $charset_value,
973     level => $self->{level}->{good});
974 wakaba 1.91 }
975     }
976 wakaba 1.129
977     if ($ascii_compat) {
978     if ($charset->{category} &
979     Message::Charset::Info::CHARSET_CATEGORY_ASCII_COMPAT ()) {
980     #
981     } else {
982     $self->{onerror}->(node => $attr,
983     type => 'charset:not ascii compat',
984     value => $charset_value,
985     level => $self->{level}->{must});
986     }
987     }
988    
989 wakaba 1.91 ## TODO: non-preferred-name error for following cases:
990     } elsif ($charset_value =~ /^x-/) {
991     $self->{onerror}->(node => $attr,
992 wakaba 1.104 type => 'charset:private',
993     value => $charset_value,
994     level => $self->{level}->{good});
995 wakaba 1.129
996     ## NOTE: Whether this is an ASCII-compatible character encoding or
997     ## not is unknown.
998 wakaba 1.91 } else {
999     $self->{onerror}->(node => $attr,
1000 wakaba 1.104 type => 'charset:not registered',
1001     value => $charset_value,
1002     level => $self->{level}->{good});
1003 wakaba 1.129
1004     ## NOTE: Whether this is an ASCII-compatible character encoding or
1005     ## not is unknown.
1006 wakaba 1.91 }
1007    
1008     return ($charset, $charset_value);
1009     }; # $HTMLCharsetChecker
1010    
1011 wakaba 1.129 ## NOTE: "An ordered set of space-separated tokens" where "each token
1012     ## MUST be the preferred name of an ASCII-compatible character
1013     ## encoding".
1014     my $HTMLCharsetsAttrChecker = sub {
1015     my ($self, $attr) = @_;
1016    
1017     ## ISSUE: "ordered set of space-separated tokens" is not defined.
1018    
1019 wakaba 1.132 my @value = grep {length $_} split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value;
1020 wakaba 1.129
1021 wakaba 1.176 ## XXX
1022 wakaba 1.129 ## ISSUE: Uniqueness is not enforced.
1023    
1024     for my $charset (@value) {
1025     $HTMLCharsetChecker->($charset, $self, $attr, 1);
1026     }
1027    
1028     ## ISSUE: Shift_JIS is ASCII-compatible? What about ISO-2022-JP?
1029     }; # $HTMLCharsetsAttrChecker
1030    
1031 wakaba 1.68 my $HTMLColorAttrChecker = sub {
1032     my ($self, $attr) = @_;
1033    
1034     ## NOTE: HTML4 "color" or |%Color;|
1035    
1036     my $value = $attr->value;
1037    
1038     if ($value !~ /\A(?>#[0-9A-F]+|black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)\z/i) {
1039 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'color:syntax error',
1040 wakaba 1.105 level => $self->{level}->{html4_fact});
1041 wakaba 1.68 }
1042    
1043     ## TODO: HTML4 has some guideline on usage of color.
1044     }; # $HTMLColorAttrChecker
1045    
1046 wakaba 1.79 my $HTMLRefOrTemplateAttrChecker = sub {
1047     my ($self, $attr) = @_;
1048     $HTMLURIAttrChecker->(@_);
1049    
1050     my $attr_name = $attr->name;
1051    
1052     if ($attr_name eq 'ref') {
1053     unless ($attr->owner_element->has_attribute_ns (undef, 'template')) {
1054     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1055 wakaba 1.104 level => $self->{level}->{must});
1056 wakaba 1.79 }
1057     }
1058 wakaba 1.155
1059     require Message::URL;
1060 wakaba 1.79 my $doc = $attr->owner_document;
1061     my $doc_uri = $doc->document_uri;
1062 wakaba 1.155 my $uri = Message::URL->new_abs ($attr->value, $doc_uri);
1063 wakaba 1.79 my $no_frag_uri = $uri->clone;
1064     $no_frag_uri->uri_fragment (undef);
1065     if ((defined $doc_uri and $doc_uri eq $no_frag_uri) or
1066     (not defined $doc_uri and $no_frag_uri eq '')) {
1067     my $fragid = $uri->uri_fragment;
1068     if (defined $fragid) {
1069     push @{$self->{$attr_name}}, [$fragid => $attr];
1070     } else {
1071     DOCEL: {
1072     last DOCEL unless $attr_name eq 'template';
1073    
1074     my $docel = $doc->document_element;
1075     if ($docel) {
1076     my $nsuri = $docel->namespace_uri;
1077     if (defined $nsuri and $nsuri eq $HTML_NS) {
1078     if ($docel->manakai_local_name eq 'datatemplate') {
1079     last DOCEL;
1080     }
1081     }
1082     }
1083    
1084     $self->{onerror}->(node => $attr, type => 'template:not template',
1085 wakaba 1.104 level => $self->{level}->{must});
1086 wakaba 1.79 } # DOCEL
1087     }
1088     } else {
1089     ## TODO: An external document is referenced.
1090     ## The document MUST be an HTML or XML document.
1091     ## If there is a fragment identifier, it MUST point a part of the doc.
1092     ## If the attribute is |template|, the pointed part MUST be a
1093     ## |datatemplat| element.
1094     ## If no fragment identifier is specified, the root element MUST be
1095     ## a |datatemplate| element when the attribute is |template|.
1096     }
1097     }; # $HTMLRefOrTemplateAttrChecker
1098    
1099 wakaba 1.83 my $HTMLRepeatIndexAttrChecker = sub {
1100     my ($self, $attr) = @_;
1101    
1102     if (defined $attr->namespace_uri) {
1103     my $oe = $attr->owner_element;
1104     my $oe_nsuri = $oe->namespace_uri;
1105 wakaba 1.128 if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) { ## TODO: wrong?
1106 wakaba 1.83 $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1107 wakaba 1.104 level => $self->{level}->{must});
1108 wakaba 1.83 }
1109     }
1110    
1111     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
1112     }; # $HTMLRepeatIndexAttrChecker
1113    
1114 wakaba 1.179 my $PlaceholderAttrChecker = sub {
1115     my ($self, $attr) = @_;
1116     if ($attr->value =~ /[\x0D\x0A]/) {
1117     $self->{onerror}->(node => $attr,
1118     type => 'newline in value', ## TODOC: type
1119     level => $self->{level}->{must});
1120     }
1121     }; # $PlaceholderAttrChecker
1122    
1123 wakaba 1.1 my $HTMLAttrChecker = {
1124 wakaba 1.176 accesskey => sub {
1125     my ($self, $attr) = @_;
1126    
1127     ## "Ordered set of unique space-separated tokens"
1128    
1129     my %keys;
1130     my @keys = grep {length} split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value;
1131    
1132     for my $key (@keys) {
1133     unless ($keys{$key}) {
1134     $keys{$key} = 1;
1135     if (length $key != 1) {
1136     $self->{onerror}->(node => $attr, type => 'char:syntax error',
1137     value => $key,
1138     level => $self->{level}->{must});
1139     }
1140     } else {
1141     $self->{onerror}->(node => $attr, type => 'duplicate token',
1142     value => $key,
1143     level => $self->{level}->{must});
1144     }
1145     }
1146     }, # accesskey
1147    
1148 wakaba 1.58 ## TODO: aria-* ## TODO: svg:*/@aria-* [HTML5ROLE] -> [STATES]
1149 wakaba 1.1 id => sub {
1150 wakaba 1.135 my ($self, $attr, $item, $element_state) = @_;
1151 wakaba 1.1 my $value = $attr->value;
1152     if (length $value > 0) {
1153     if ($self->{id}->{$value}) {
1154 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
1155     level => $self->{level}->{must});
1156 wakaba 1.1 push @{$self->{id}->{$value}}, $attr;
1157     } else {
1158     $self->{id}->{$value} = [$attr];
1159 wakaba 1.135 $self->{id_type}->{$value} = $element_state->{id_type} || '';
1160 wakaba 1.1 }
1161 wakaba 1.132 if ($value =~ /[\x09\x0A\x0C\x0D\x20]/) {
1162 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
1163     level => $self->{level}->{must});
1164 wakaba 1.1 }
1165     } else {
1166     ## NOTE: MUST contain at least one character
1167 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
1168     level => $self->{level}->{must});
1169 wakaba 1.1 }
1170     },
1171     title => sub {}, ## NOTE: No conformance creteria
1172     lang => sub {
1173     my ($self, $attr) = @_;
1174 wakaba 1.6 my $value = $attr->value;
1175     if ($value eq '') {
1176     #
1177     } else {
1178     require Whatpm::LangTag;
1179     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
1180 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
1181 wakaba 1.106 }, $self->{level});
1182 wakaba 1.6 }
1183 wakaba 1.1 ## ISSUE: RFC 4646 (3066bis)?
1184 wakaba 1.6
1185     ## TODO: test data
1186 wakaba 1.111
1187     ## NOTE: Inconsistency between |lang| and |xml:lang| attributes are
1188     ## non-conforming. Such errors are detected by the checkers of
1189     ## |{}xml:lang| and |{xml}:lang| attributes.
1190 wakaba 1.1 },
1191     dir => $GetHTMLEnumeratedAttrChecker->({ltr => 1, rtl => 1}),
1192     class => sub {
1193     my ($self, $attr) = @_;
1194 wakaba 1.132
1195     ## NOTE: "Unordered set of unique space-separated tokens".
1196    
1197 wakaba 1.1 my %word;
1198 wakaba 1.132 for my $word (grep {length $_}
1199     split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
1200 wakaba 1.1 unless ($word{$word}) {
1201     $word{$word} = 1;
1202     push @{$self->{return}->{class}->{$word}||=[]}, $attr;
1203     } else {
1204 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
1205     value => $word,
1206     level => $self->{level}->{must});
1207 wakaba 1.1 }
1208     }
1209     },
1210 wakaba 1.63 contenteditable => $GetHTMLEnumeratedAttrChecker->({
1211     true => 1, false => 1, '' => 1,
1212     }),
1213 wakaba 1.1 contextmenu => sub {
1214     my ($self, $attr) = @_;
1215     my $value = $attr->value;
1216 wakaba 1.138 push @{$self->{idref}}, ['menu', $value => $attr];
1217 wakaba 1.1 ## ISSUE: "The value must be the ID of a menu element in the DOM."
1218     ## What is "in the DOM"? A menu Element node that is not part
1219     ## of the Document tree is in the DOM? A menu Element node that
1220     ## belong to another Document tree is in the DOM?
1221     },
1222 wakaba 1.115 hidden => $GetHTMLBooleanAttrChecker->('hidden'),
1223 wakaba 1.60 irrelevant => $GetHTMLBooleanAttrChecker->('irrelevant'),
1224 wakaba 1.79 ref => $HTMLRefOrTemplateAttrChecker,
1225     registrationmark => sub {
1226     my ($self, $attr, $item, $element_state) = @_;
1227    
1228     ## NOTE: Any value is conforming.
1229    
1230     if ($self->{flag}->{in_rule}) {
1231     my $el = $attr->owner_element;
1232     my $ln = $el->manakai_local_name;
1233     if ($ln eq 'nest' or
1234     ($ln eq 'rule' and not $element_state->{in_rule_original})) {
1235     my $nsuri = $el->namespace_uri;
1236     if (defined $nsuri and $nsuri eq $HTML_NS) {
1237     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1238 wakaba 1.104 level => $self->{level}->{must});
1239 wakaba 1.79 }
1240     }
1241     } else {
1242     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1243 wakaba 1.104 level => $self->{level}->{must});
1244 wakaba 1.79 }
1245     },
1246 wakaba 1.80 repeat => sub {
1247     my ($self, $attr) = @_;
1248 wakaba 1.83
1249     if (defined $attr->namespace_uri) {
1250     my $oe = $attr->owner_element;
1251     my $oe_nsuri = $oe->namespace_uri;
1252     if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) {
1253     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1254 wakaba 1.104 level => $self->{level}->{must});
1255 wakaba 1.83 }
1256     }
1257    
1258 wakaba 1.80 my $value = $attr->value;
1259     if ($value eq 'template') {
1260     #
1261     } elsif ($value =~ /\A-?[0-9]+\z/) {
1262     #
1263     } else {
1264     $self->{onerror}->(node => $attr, type => 'repeat:syntax error',
1265 wakaba 1.104 level => $self->{level}->{must});
1266 wakaba 1.80 }
1267    
1268     ## ISSUE: "Repetition templates may occur anywhere." Does that mean
1269     ## that the attribute MAY be specified to any element, or that the
1270     ## element with that attribute (i.e. a repetition template) can be
1271     ## inserted anywhere in a document tree?
1272     },
1273 wakaba 1.83 'repeat-min' => $HTMLRepeatIndexAttrChecker,
1274     'repeat-max' => $HTMLRepeatIndexAttrChecker,
1275     'repeat-start' => $HTMLRepeatIndexAttrChecker,
1276 wakaba 1.80 'repeat-template' => sub {
1277 wakaba 1.83 my ($self, $attr) = @_;
1278    
1279     if (defined $attr->namespace_uri) {
1280     my $oe = $attr->owner_element;
1281     my $oe_nsuri = $oe->namespace_uri;
1282 wakaba 1.128 if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) { ## TODO: This condition is wrong?
1283 wakaba 1.83 $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1284 wakaba 1.104 level => $self->{level}->{must});
1285 wakaba 1.83 }
1286     }
1287    
1288 wakaba 1.80 ## ISSUE: This attribute has no conformance requirement.
1289     ## ISSUE: Repetition blocks MAY have this attribute. Then, is the
1290     ## attribute allowed on an element that is not a repetition block?
1291     },
1292 wakaba 1.58 ## TODO: role [HTML5ROLE] ## TODO: global @role [XHTML1ROLE]
1293 wakaba 1.128 style => sub {
1294     my ($self, $attr) = @_;
1295    
1296     $self->{onsubdoc}->({s => $attr->value,
1297     container_node => $attr,
1298     media_type => 'text/x-css-inline',
1299     is_char_string => 1});
1300    
1301     ## NOTE: "... MUST still be comprehensible and usable if those
1302     ## attributes were removed" is a semantic requirement, it cannot
1303     ## be tested.
1304     },
1305 wakaba 1.74 tabindex => $HTMLIntegerAttrChecker,
1306 wakaba 1.79 template => $HTMLRefOrTemplateAttrChecker,
1307 wakaba 1.111 'xml:lang' => sub {
1308     my ($self, $attr) = @_;
1309    
1310     if ($attr->owner_document->manakai_is_html) {
1311     $self->{onerror}->(type => 'in HTML:xml:lang',
1312     level => $self->{level}->{info},
1313     node => $attr);
1314     ## NOTE: This is not an error, but the attribute will be ignored.
1315     } else {
1316     $self->{onerror}->(type => 'in XML:xml:lang',
1317     level => $self->{level}->{html5_no_may},
1318     node => $attr);
1319     ## TODO: We need to add test for this error.
1320     }
1321    
1322     my $lang_attr = $attr->owner_element->get_attribute_node_ns
1323     (undef, 'lang');
1324     if ($lang_attr) {
1325     my $lang_attr_value = $lang_attr->value;
1326     $lang_attr_value =~ tr/A-Z/a-z/; ## ASCII case-insensitive
1327     my $value = $attr->value;
1328     $value =~ tr/A-Z/a-z/; ## ASCII case-insensitive
1329     if ($lang_attr_value ne $value) {
1330     $self->{onerror}->(type => 'xml:lang ne lang',
1331     level => $self->{level}->{must},
1332     node => $attr);
1333     }
1334     } else {
1335     $self->{onerror}->(type => 'xml:lang not allowed',
1336     level => $self->{level}->{must},
1337     node => $attr);
1338     ## TODO: We need to add test for <x {xml}:lang {}xml:lang>.
1339     }
1340     },
1341 wakaba 1.74 xmlns => sub {
1342     my ($self, $attr) = @_;
1343     my $value = $attr->value;
1344     unless ($value eq $HTML_NS) {
1345 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
1346     level => $self->{level}->{must});
1347 wakaba 1.74 ## TODO: Should be new "bad namespace" error?
1348     }
1349     unless ($attr->owner_document->manakai_is_html) {
1350 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'in XML:xmlns',
1351     level => $self->{level}->{must});
1352 wakaba 1.74 ## TODO: Test
1353     }
1354    
1355     ## TODO: Should be resolved?
1356     push @{$self->{return}->{uri}->{$value} ||= []},
1357     {node => $attr, type => {namespace => 1}};
1358     },
1359 wakaba 1.1 };
1360    
1361 wakaba 1.79 ## ISSUE: Shouldn't the same-origin policy applied to the datatemplate feature?
1362    
1363 wakaba 1.49 my %HTMLAttrStatus = (
1364 wakaba 1.176 accesskey => FEATURE_HTML5_FD,
1365 wakaba 1.153 class => FEATURE_HTML5_WD,
1366 wakaba 1.50 contenteditable => FEATURE_HTML5_DEFAULT,
1367     contextmenu => FEATURE_HTML5_WD,
1368 wakaba 1.153 dir => FEATURE_HTML5_WD,
1369 wakaba 1.50 draggable => FEATURE_HTML5_LC,
1370 wakaba 1.115 hidden => FEATURE_HTML5_DEFAULT,
1371 wakaba 1.153 id => FEATURE_HTML5_WD,
1372 wakaba 1.115 irrelevant => FEATURE_HTML5_DROPPED,
1373 wakaba 1.153 lang => FEATURE_HTML5_WD,
1374 wakaba 1.50 ref => FEATURE_HTML5_AT_RISK,
1375     registrationmark => FEATURE_HTML5_AT_RISK,
1376 wakaba 1.60 repeat => FEATURE_WF2,
1377     'repeat-max' => FEATURE_WF2,
1378     'repeat-min' => FEATURE_WF2,
1379     'repeat-start' => FEATURE_WF2,
1380     'repeat-template' => FEATURE_WF2,
1381 wakaba 1.154 role => 0,
1382 wakaba 1.153 style => FEATURE_HTML5_WD,
1383 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT,
1384     template => FEATURE_HTML5_AT_RISK,
1385 wakaba 1.153 title => FEATURE_HTML5_WD,
1386 wakaba 1.154 xmlns => FEATURE_HTML5_WD,
1387 wakaba 1.49 );
1388    
1389     my %HTMLM12NCommonAttrStatus = (
1390 wakaba 1.154 about => FEATURE_RDFA_REC,
1391 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
1392 wakaba 1.154 content => FEATURE_RDFA_REC,
1393     datatype => FEATURE_RDFA_REC,
1394 wakaba 1.153 dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
1395 wakaba 1.154 href => FEATURE_RDFA_REC,
1396 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
1397 wakaba 1.154 instanceof => FEATURE_RDFA_LC_DROPPED,
1398 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1399     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1400     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1401     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1402     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1403     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1404     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1405     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1406     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1407     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1408 wakaba 1.154 property => FEATURE_RDFA_REC,
1409     rel => FEATURE_RDFA_REC,
1410     resource => FEATURE_RDFA_REC,
1411     rev => FEATURE_RDFA_REC,
1412 wakaba 1.153 #style => FEATURE_HTML5_WD | FEATURE_XHTMLBASIC11_CR_DEPRECATED |
1413 wakaba 1.78 # FEATURE_M12N10_REC,
1414 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTMLBASIC11_CR |
1415 wakaba 1.55 FEATURE_M12N10_REC,
1416 wakaba 1.153 title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
1417 wakaba 1.154 typeof => FEATURE_RDFA_REC,
1418 wakaba 1.49 );
1419    
1420 wakaba 1.82 my %XHTML2CommonAttrStatus = (
1421     ## Core
1422 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED,
1423     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED,
1424 wakaba 1.82 #xml:id
1425     layout => FEATURE_XHTML2_ED,
1426 wakaba 1.153 title => FEATURE_HTML5_WD | FEATURE_XHTML2_ED,
1427 wakaba 1.82
1428     ## Hypertext
1429     cite => FEATURE_XHTML2_ED,
1430     href => FEATURE_XHTML2_ED,
1431     hreflang => FEATURE_XHTML2_ED,
1432     hrefmedia => FEATURE_XHTML2_ED,
1433     hreftype => FEATURE_XHTML2_ED,
1434     nextfocus => FEATURE_XHTML2_ED,
1435     prevfocus => FEATURE_XHTML2_ED,
1436     target => FEATURE_XHTML2_ED,
1437     #xml:base
1438    
1439     ## I18N
1440     #xml:lang
1441    
1442     ## Bi-directional
1443 wakaba 1.153 dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED,
1444 wakaba 1.82
1445     ## Edit
1446     edit => FEATURE_XHTML2_ED,
1447     datetime => FEATURE_XHTML2_ED,
1448    
1449     ## Embedding
1450     encoding => FEATURE_XHTML2_ED,
1451     src => FEATURE_XHTML2_ED,
1452     srctype => FEATURE_XHTML2_ED,
1453    
1454     ## Image Map
1455     usemap => FEATURE_XHTML2_ED,
1456     ismap => FEATURE_XHTML2_ED,
1457     shape => FEATURE_XHTML2_ED,
1458     coords => FEATURE_XHTML2_ED,
1459    
1460     ## Media
1461     media => FEATURE_XHTML2_ED,
1462    
1463     ## Metadata
1464     about => FEATURE_XHTML2_ED,
1465     content => FEATURE_XHTML2_ED,
1466     datatype => FEATURE_XHTML2_ED,
1467     instanceof => FEATURE_XHTML2_ED,
1468     property => FEATURE_XHTML2_ED,
1469     rel => FEATURE_XHTML2_ED,
1470     resource => FEATURE_XHTML2_ED,
1471     rev => FEATURE_XHTML2_ED,
1472    
1473     ## Role
1474 wakaba 1.154 role => FEATURE_XHTML2_ED,
1475 wakaba 1.82
1476     ## Style
1477 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTML2_ED, # "strongly discouraged"
1478 wakaba 1.82 );
1479    
1480     my %HTMLM12NXHTML2CommonAttrStatus = (
1481     %HTMLM12NCommonAttrStatus,
1482     %XHTML2CommonAttrStatus,
1483    
1484 wakaba 1.154 about => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
1485 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1486 wakaba 1.154 content => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
1487     datatype => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
1488 wakaba 1.153 dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1489 wakaba 1.154 href => FEATURE_RDFA_REC,
1490 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1491 wakaba 1.154 instanceof => FEATURE_RDFA_LC_DROPPED | FEATURE_XHTML2_ED,
1492     property => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
1493     rel => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
1494     resource => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
1495     rev => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
1496 wakaba 1.153 #style => FEATURE_HTML5_WD | FEATURE_XHTMLBASIC11_CR_DEPRECATED |
1497 wakaba 1.82 # FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1498 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTMLBASIC11_CR |
1499 wakaba 1.82 FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1500 wakaba 1.153 title => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1501 wakaba 1.154 typeof => FEATURE_RDFA_REC,
1502 wakaba 1.82 );
1503    
1504 wakaba 1.1 for (qw/
1505     onabort onbeforeunload onblur onchange onclick oncontextmenu
1506     ondblclick ondrag ondragend ondragenter ondragleave ondragover
1507     ondragstart ondrop onerror onfocus onkeydown onkeypress
1508 wakaba 1.180 onkeyup onload onmousedown onmousemove onmouseout
1509 wakaba 1.1 onmouseover onmouseup onmousewheel onresize onscroll onselect
1510 wakaba 1.77 onstorage onsubmit onunload
1511 wakaba 1.1 /) {
1512     $HTMLAttrChecker->{$_} = $HTMLEventHandlerAttrChecker;
1513 wakaba 1.50 $HTMLAttrStatus{$_} = FEATURE_HTML5_DEFAULT;
1514 wakaba 1.1 }
1515    
1516 wakaba 1.170 for (qw/
1517     ondataunavailable
1518 wakaba 1.180 onmessage
1519 wakaba 1.170 /) {
1520     $HTMLAttrChecker->{$_} = $HTMLEventHandlerAttrChecker;
1521     $HTMLAttrStatus{$_} = FEATURE_HTML5_DROPPED;
1522     }
1523    
1524 wakaba 1.82 ## NOTE: Non-standard global attributes in the HTML namespace.
1525     $AttrChecker->{$HTML_NS}->{''} = sub {}; # no syntactical checks
1526     $AttrStatus->{$HTML_NS}->{''} = 0; # disallowed and not part of any standard
1527    
1528     $AttrStatus->{$HTML_NS}->{active} = FEATURE_HTML5_DROPPED;
1529     for (qw/repeat repeat-max repeat-min repeat-start repeat-template/) {
1530     $AttrChecker->{$HTML_NS}->{$_} = $HTMLAttrChecker->{$_};
1531     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_WF2;
1532     }
1533    
1534 wakaba 1.120 for (qw/about content datatype property rel resource rev/) {
1535 wakaba 1.154 $AttrStatus->{$HTML_NS}->{$_} = FEATURE_RDFA_REC | FEATURE_XHTML2_ED;
1536 wakaba 1.82 }
1537 wakaba 1.154 $AttrStatus->{$HTML_NS}->{instanceof} = FEATURE_RDFA_LC_DROPPED | FEATURE_XHTML2_ED;
1538     $AttrStatus->{$HTML_NS}->{typeof} = FEATURE_RDFA_REC;
1539 wakaba 1.82 $AttrStatus->{$HTML_NS}->{role} = FEATURE_ROLE_LC;
1540     for (qw/cite coords datetime edit encoding href hreflang hrefmedia hreftype
1541     ismap layout media nextfocus prevfocus shape src srctype style
1542     target usemap/) {
1543     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_XHTML2_ED;
1544     }
1545     for (qw/class dir id title/) {
1546     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC | FEATURE_XHTML2_ED;
1547     }
1548     for (qw/onclick ondblclick onmousedown onmouseup onmouseover onmousemove
1549     onmouseout onkeypress onkeydown onkeyup/) {
1550     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC;
1551     }
1552    
1553 wakaba 1.73 my $HTMLDatasetAttrChecker = sub {
1554     ## NOTE: "Authors should ... when the attributes are ignored and
1555     ## any associated CSS dropped, the page is still usable." (semantic
1556     ## constraint.)
1557     }; # $HTMLDatasetAttrChecker
1558    
1559 wakaba 1.153 my $HTMLDatasetAttrStatus = FEATURE_HTML5_WD;
1560 wakaba 1.73
1561 wakaba 1.1 my $GetHTMLAttrsChecker = sub {
1562     my $element_specific_checker = shift;
1563 wakaba 1.49 my $element_specific_status = shift;
1564 wakaba 1.1 return sub {
1565 wakaba 1.40 my ($self, $item, $element_state) = @_;
1566     for my $attr (@{$item->{node}->attributes}) {
1567 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
1568     $attr_ns = '' unless defined $attr_ns;
1569     my $attr_ln = $attr->manakai_local_name;
1570     my $checker;
1571 wakaba 1.73 my $status;
1572 wakaba 1.1 if ($attr_ns eq '') {
1573 wakaba 1.122 if ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
1574     $attr_ln !~ /[A-Z]/) {
1575 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
1576     $status = $HTMLDatasetAttrStatus;
1577     } else {
1578     $checker = $element_specific_checker->{$attr_ln}
1579     || $HTMLAttrChecker->{$attr_ln};
1580     $status = $element_specific_status->{$attr_ln};
1581     }
1582 wakaba 1.1 }
1583     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
1584 wakaba 1.40 || $AttrChecker->{$attr_ns}->{''};
1585 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
1586     || $AttrStatus->{$attr_ns}->{''};
1587     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
1588 wakaba 1.1 if ($checker) {
1589 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
1590 wakaba 1.62 } elsif ($attr_ns eq '' and not $element_specific_status->{$attr_ln}) {
1591 wakaba 1.54 #
1592 wakaba 1.1 } else {
1593 wakaba 1.104 $self->{onerror}->(node => $attr,
1594     type => 'unknown attribute',
1595     level => $self->{level}->{uncertain});
1596 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
1597     }
1598 wakaba 1.82 $self->_attr_status_info ($attr, $status);
1599 wakaba 1.1 }
1600     };
1601     }; # $GetHTMLAttrsChecker
1602    
1603 wakaba 1.40 my %HTMLChecker = (
1604     %Whatpm::ContentChecker::AnyChecker,
1605 wakaba 1.79 check_start => sub {
1606     my ($self, $item, $element_state) = @_;
1607    
1608     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1609     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1610     },
1611 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, \%HTMLAttrStatus),
1612 wakaba 1.40 );
1613    
1614     my %HTMLEmptyChecker = (
1615     %HTMLChecker,
1616     check_child_element => sub {
1617     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1618     $child_is_transparent, $element_state) = @_;
1619 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1620     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1621 wakaba 1.40 $self->{onerror}->(node => $child_el,
1622     type => 'element not allowed:minus',
1623 wakaba 1.104 level => $self->{level}->{must});
1624 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1625     #
1626     } else {
1627     $self->{onerror}->(node => $child_el,
1628     type => 'element not allowed:empty',
1629 wakaba 1.104 level => $self->{level}->{must});
1630 wakaba 1.40 }
1631     },
1632     check_child_text => sub {
1633     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1634     if ($has_significant) {
1635     $self->{onerror}->(node => $child_node,
1636     type => 'character not allowed:empty',
1637 wakaba 1.104 level => $self->{level}->{must});
1638 wakaba 1.40 }
1639     },
1640     );
1641    
1642     my %HTMLTextChecker = (
1643     %HTMLChecker,
1644     check_child_element => sub {
1645     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1646     $child_is_transparent, $element_state) = @_;
1647 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1648     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1649 wakaba 1.40 $self->{onerror}->(node => $child_el,
1650     type => 'element not allowed:minus',
1651 wakaba 1.104 level => $self->{level}->{must});
1652 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1653     #
1654     } else {
1655 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed:text',
1656     level => $self->{level}->{must});
1657 wakaba 1.40 }
1658     },
1659     );
1660    
1661 wakaba 1.72 my %HTMLFlowContentChecker = (
1662 wakaba 1.40 %HTMLChecker,
1663     check_child_element => sub {
1664     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1665     $child_is_transparent, $element_state) = @_;
1666 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1667     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1668 wakaba 1.40 $self->{onerror}->(node => $child_el,
1669     type => 'element not allowed:minus',
1670 wakaba 1.104 level => $self->{level}->{must});
1671 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1672     #
1673     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1674     if ($element_state->{has_non_style} or
1675     not $child_el->has_attribute_ns (undef, 'scoped')) {
1676 wakaba 1.104 $self->{onerror}->(node => $child_el,
1677 wakaba 1.72 type => 'element not allowed:flow style',
1678 wakaba 1.104 level => $self->{level}->{must});
1679 wakaba 1.40 }
1680 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
1681 wakaba 1.43 $element_state->{has_non_style} = 1 unless $child_is_transparent;
1682 wakaba 1.40 } else {
1683     $element_state->{has_non_style} = 1;
1684 wakaba 1.104 $self->{onerror}->(node => $child_el,
1685 wakaba 1.72 type => 'element not allowed:flow',
1686 wakaba 1.104 level => $self->{level}->{must})
1687 wakaba 1.40 }
1688     },
1689     check_child_text => sub {
1690     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1691     if ($has_significant) {
1692     $element_state->{has_non_style} = 1;
1693     }
1694     },
1695     check_end => sub {
1696     my ($self, $item, $element_state) = @_;
1697 wakaba 1.95 ## NOTE: A modified copy of the code below is in |datagrid| checker.
1698 wakaba 1.40 if ($element_state->{has_significant}) {
1699 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
1700 wakaba 1.40 } elsif ($item->{transparent}) {
1701     #
1702     } else {
1703     $self->{onerror}->(node => $item->{node},
1704 wakaba 1.104 level => $self->{level}->{should},
1705 wakaba 1.40 type => 'no significant content');
1706     }
1707     },
1708     );
1709    
1710     my %HTMLPhrasingContentChecker = (
1711     %HTMLChecker,
1712     check_child_element => sub {
1713     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1714     $child_is_transparent, $element_state) = @_;
1715 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1716     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1717 wakaba 1.40 $self->{onerror}->(node => $child_el,
1718     type => 'element not allowed:minus',
1719 wakaba 1.104 level => $self->{level}->{must});
1720 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1721     #
1722     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
1723     #
1724     } else {
1725     $self->{onerror}->(node => $child_el,
1726     type => 'element not allowed:phrasing',
1727 wakaba 1.104 level => $self->{level}->{must});
1728 wakaba 1.40 }
1729     },
1730 wakaba 1.72 check_end => $HTMLFlowContentChecker{check_end},
1731 wakaba 1.40 ## NOTE: The definition for |li| assumes that the only differences
1732 wakaba 1.72 ## between flow and phrasing content checkers are |check_child_element|
1733 wakaba 1.40 ## and |check_child_text|.
1734     );
1735    
1736 wakaba 1.72 my %HTMLTransparentChecker = %HTMLFlowContentChecker;
1737 wakaba 1.40 ## ISSUE: Significant content rule should be applied to transparent element
1738 wakaba 1.46 ## with parent?
1739 wakaba 1.40
1740 wakaba 1.1 our $Element;
1741     our $ElementDefault;
1742    
1743     $Element->{$HTML_NS}->{''} = {
1744 wakaba 1.40 %HTMLChecker,
1745 wakaba 1.1 };
1746    
1747     $Element->{$HTML_NS}->{html} = {
1748 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1749 wakaba 1.1 is_root => 1,
1750 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
1751 wakaba 1.16 manifest => $HTMLURIAttrChecker,
1752 wakaba 1.67 version => sub {
1753     ## NOTE: According to HTML4 prose, this is a "cdata" attribute.
1754     ## Though DTDs of various versions of HTML define the attribute
1755     ## as |#FIXED|, this conformance checker does no check for
1756     ## the attribute value, since what kind of check should be done
1757     ## is unknown.
1758     },
1759 wakaba 1.49 }, {
1760     %HTMLAttrStatus,
1761 wakaba 1.82 %XHTML2CommonAttrStatus,
1762 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1763     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1764     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1765     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
1766     manifest => FEATURE_HTML5_WD,
1767 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1768 wakaba 1.82 version => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1769 wakaba 1.1 }),
1770 wakaba 1.40 check_start => sub {
1771     my ($self, $item, $element_state) = @_;
1772     $element_state->{phase} = 'before head';
1773 wakaba 1.79
1774 wakaba 1.66 $element_state->{uri_info}->{manifest}->{type}->{resource} = 1;
1775 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1776     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1777 wakaba 1.40 },
1778     check_child_element => sub {
1779     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1780     $child_is_transparent, $element_state) = @_;
1781 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1782     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1783 wakaba 1.40 $self->{onerror}->(node => $child_el,
1784     type => 'element not allowed:minus',
1785 wakaba 1.104 level => $self->{level}->{must});
1786 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1787     #
1788     } elsif ($element_state->{phase} eq 'before head') {
1789     if ($child_nsuri eq $HTML_NS and $child_ln eq 'head') {
1790     $element_state->{phase} = 'after head';
1791     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1792     $self->{onerror}->(node => $child_el,
1793 wakaba 1.104 type => 'ps element missing',
1794     text => 'head',
1795     level => $self->{level}->{must});
1796 wakaba 1.40 $element_state->{phase} = 'after body';
1797     } else {
1798     $self->{onerror}->(node => $child_el,
1799 wakaba 1.104 type => 'element not allowed',
1800     level => $self->{level}->{must});
1801 wakaba 1.40 }
1802     } elsif ($element_state->{phase} eq 'after head') {
1803     if ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1804     $element_state->{phase} = 'after body';
1805     } else {
1806     $self->{onerror}->(node => $child_el,
1807 wakaba 1.104 type => 'element not allowed',
1808     level => $self->{level}->{must});
1809 wakaba 1.40 }
1810     } elsif ($element_state->{phase} eq 'after body') {
1811     $self->{onerror}->(node => $child_el,
1812 wakaba 1.104 type => 'element not allowed',
1813     level => $self->{level}->{must});
1814 wakaba 1.40 } else {
1815     die "check_child_element: Bad |html| phase: $element_state->{phase}";
1816     }
1817     },
1818     check_child_text => sub {
1819     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1820     if ($has_significant) {
1821     $self->{onerror}->(node => $child_node,
1822 wakaba 1.104 type => 'character not allowed',
1823     level => $self->{level}->{must});
1824 wakaba 1.40 }
1825     },
1826     check_end => sub {
1827     my ($self, $item, $element_state) = @_;
1828     if ($element_state->{phase} eq 'after body') {
1829     #
1830     } elsif ($element_state->{phase} eq 'before head') {
1831     $self->{onerror}->(node => $item->{node},
1832 wakaba 1.104 type => 'child element missing',
1833     text => 'head',
1834     level => $self->{level}->{must});
1835 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1836 wakaba 1.104 type => 'child element missing',
1837     text => 'body',
1838     level => $self->{level}->{must});
1839 wakaba 1.40 } elsif ($element_state->{phase} eq 'after head') {
1840     $self->{onerror}->(node => $item->{node},
1841 wakaba 1.104 type => 'child element missing',
1842     text => 'body',
1843     level => $self->{level}->{must});
1844 wakaba 1.40 } else {
1845     die "check_end: Bad |html| phase: $element_state->{phase}";
1846     }
1847 wakaba 1.1
1848 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1849     },
1850     };
1851 wakaba 1.25
1852 wakaba 1.40 $Element->{$HTML_NS}->{head} = {
1853 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1854 wakaba 1.67 check_attrs => $GetHTMLAttrsChecker->({
1855     profile => $HTMLSpaceURIsAttrChecker, ## NOTE: MUST be profile URIs.
1856     }, {
1857 wakaba 1.49 %HTMLAttrStatus,
1858 wakaba 1.82 %XHTML2CommonAttrStatus,
1859 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1860     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1861     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1862     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
1863 wakaba 1.49 profile => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1864     }),
1865 wakaba 1.40 check_child_element => sub {
1866     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1867     $child_is_transparent, $element_state) = @_;
1868 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1869     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1870 wakaba 1.40 $self->{onerror}->(node => $child_el,
1871     type => 'element not allowed:minus',
1872 wakaba 1.104 level => $self->{level}->{must});
1873 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1874     #
1875     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'title') {
1876     unless ($element_state->{has_title}) {
1877     $element_state->{has_title} = 1;
1878     } else {
1879     $self->{onerror}->(node => $child_el,
1880     type => 'element not allowed:head title',
1881 wakaba 1.104 level => $self->{level}->{must});
1882 wakaba 1.40 }
1883     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1884     if ($child_el->has_attribute_ns (undef, 'scoped')) {
1885     $self->{onerror}->(node => $child_el,
1886     type => 'element not allowed:head style',
1887 wakaba 1.104 level => $self->{level}->{must});
1888 wakaba 1.1 }
1889 wakaba 1.40 } elsif ($HTMLMetadataContent->{$child_nsuri}->{$child_ln}) {
1890     #
1891    
1892     ## NOTE: |meta| is a metadata content. However, strictly speaking,
1893     ## a |meta| element with none of |charset|, |name|,
1894     ## or |http-equiv| attribute is not allowed. It is non-conforming
1895     ## anyway.
1896 wakaba 1.56
1897     ## TODO: |form| MUST be empty and in XML [WF2].
1898 wakaba 1.40 } else {
1899     $self->{onerror}->(node => $child_el,
1900     type => 'element not allowed:metadata',
1901 wakaba 1.104 level => $self->{level}->{must});
1902 wakaba 1.40 }
1903     $element_state->{in_head_original} = $self->{flag}->{in_head};
1904     $self->{flag}->{in_head} = 1;
1905     },
1906     check_child_text => sub {
1907     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1908     if ($has_significant) {
1909 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
1910     level => $self->{level}->{must});
1911 wakaba 1.1 }
1912 wakaba 1.40 },
1913     check_end => sub {
1914     my ($self, $item, $element_state) = @_;
1915     unless ($element_state->{has_title}) {
1916     $self->{onerror}->(node => $item->{node},
1917 wakaba 1.104 type => 'child element missing',
1918     text => 'title',
1919 wakaba 1.105 level => $self->{level}->{must});
1920 wakaba 1.1 }
1921 wakaba 1.40 $self->{flag}->{in_head} = $element_state->{in_head_original};
1922 wakaba 1.1
1923 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1924 wakaba 1.1 },
1925     };
1926    
1927 wakaba 1.40 $Element->{$HTML_NS}->{title} = {
1928     %HTMLTextChecker,
1929 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1930 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
1931     %HTMLAttrStatus,
1932 wakaba 1.82 %XHTML2CommonAttrStatus,
1933 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1934     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1935     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1936     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
1937 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1938 wakaba 1.49 }),
1939 wakaba 1.40 };
1940 wakaba 1.1
1941 wakaba 1.40 $Element->{$HTML_NS}->{base} = {
1942 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
1943 wakaba 1.40 %HTMLEmptyChecker,
1944     check_attrs => sub {
1945     my ($self, $item, $element_state) = @_;
1946 wakaba 1.1
1947 wakaba 1.40 if ($self->{has_base}) {
1948     $self->{onerror}->(node => $item->{node},
1949 wakaba 1.104 type => 'element not allowed:base',
1950     level => $self->{level}->{must});
1951 wakaba 1.40 } else {
1952     $self->{has_base} = 1;
1953 wakaba 1.29 }
1954    
1955 wakaba 1.40 my $has_href = $item->{node}->has_attribute_ns (undef, 'href');
1956     my $has_target = $item->{node}->has_attribute_ns (undef, 'target');
1957 wakaba 1.14
1958     if ($self->{has_uri_attr} and $has_href) {
1959 wakaba 1.4 ## ISSUE: Are these examples conforming?
1960     ## <head profile="a b c"><base href> (except for |profile|'s
1961     ## non-conformance)
1962     ## <title xml:base="relative"/><base href/> (maybe it should be)
1963     ## <unknown xmlns="relative"/><base href/> (assuming that
1964     ## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though))
1965     ## <style>@import 'relative';</style><base href>
1966     ## <script>location.href = 'relative';</script><base href>
1967 wakaba 1.14 ## NOTE: <html manifest=".."><head><base href=""/> is conforming as
1968     ## an exception.
1969 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1970 wakaba 1.104 type => 'basehref after URL attribute',
1971     level => $self->{level}->{must});
1972 wakaba 1.4 }
1973 wakaba 1.14 if ($self->{has_hyperlink_element} and $has_target) {
1974 wakaba 1.4 ## ISSUE: Are these examples conforming?
1975     ## <head><title xlink:href=""/><base target="name"/></head>
1976     ## <xbl:xbl>...<svg:a href=""/>...</xbl:xbl><base target="name"/>
1977     ## (assuming that |xbl:xbl| is allowed before |base|)
1978     ## NOTE: These are non-conformant anyway because of |head|'s content model:
1979     ## <link href=""/><base target="name"/>
1980     ## <link rel=unknown href=""><base target=name>
1981 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1982 wakaba 1.104 type => 'basetarget after hyperlink',
1983     level => $self->{level}->{must});
1984 wakaba 1.4 }
1985    
1986 wakaba 1.14 if (not $has_href and not $has_target) {
1987 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1988 wakaba 1.104 type => 'attribute missing:href|target',
1989     level => $self->{level}->{must});
1990 wakaba 1.14 }
1991    
1992 wakaba 1.66 $element_state->{uri_info}->{href}->{type}->{base} = 1;
1993    
1994 wakaba 1.4 return $GetHTMLAttrsChecker->({
1995     href => $HTMLURIAttrChecker,
1996     target => $HTMLTargetAttrChecker,
1997 wakaba 1.49 }, {
1998     %HTMLAttrStatus,
1999 wakaba 1.153 href => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2000     id => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2001     target => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2002 wakaba 1.40 })->($self, $item, $element_state);
2003 wakaba 1.4 },
2004 wakaba 1.1 };
2005    
2006     $Element->{$HTML_NS}->{link} = {
2007 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2008 wakaba 1.40 %HTMLEmptyChecker,
2009     check_attrs => sub {
2010     my ($self, $item, $element_state) = @_;
2011 wakaba 1.96 my $sizes_attr;
2012 wakaba 1.1 $GetHTMLAttrsChecker->({
2013 wakaba 1.91 charset => sub {
2014     my ($self, $attr) = @_;
2015     $HTMLCharsetChecker->($attr->value, @_);
2016     },
2017 wakaba 1.1 href => $HTMLURIAttrChecker,
2018 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(0, $item, @_) },
2019 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
2020 wakaba 1.1 media => $HTMLMQAttrChecker,
2021     hreflang => $HTMLLanguageTagAttrChecker,
2022 wakaba 1.96 sizes => sub {
2023     my ($self, $attr) = @_;
2024     $sizes_attr = $attr;
2025     my %word;
2026     for my $word (grep {length $_}
2027 wakaba 1.132 split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
2028 wakaba 1.96 unless ($word{$word}) {
2029     $word{$word} = 1;
2030     if ($word eq 'any' or $word =~ /\A[1-9][0-9]*x[1-9][0-9]*\z/) {
2031     #
2032     } else {
2033     $self->{onerror}->(node => $attr,
2034 wakaba 1.104 type => 'sizes:syntax error',
2035 wakaba 1.96 value => $word,
2036 wakaba 1.104 level => $self->{level}->{must});
2037 wakaba 1.96 }
2038     } else {
2039     $self->{onerror}->(node => $attr, type => 'duplicate token',
2040     value => $word,
2041 wakaba 1.104 level => $self->{level}->{must});
2042 wakaba 1.96 }
2043     }
2044     },
2045 wakaba 1.70 target => $HTMLTargetAttrChecker,
2046 wakaba 1.1 type => $HTMLIMTAttrChecker,
2047     ## NOTE: Though |title| has special semantics,
2048     ## syntactically same as the |title| as global attribute.
2049 wakaba 1.49 }, {
2050     %HTMLAttrStatus,
2051 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2052 wakaba 1.91 charset => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
2053     ## NOTE: |charset| attribute had been part of HTML5 spec though
2054     ## it had been commented out.
2055 wakaba 1.154 href => FEATURE_HTML5_LC | FEATURE_RDFA_REC | FEATURE_XHTML2_ED |
2056 wakaba 1.82 FEATURE_M12N10_REC,
2057 wakaba 1.153 hreflang => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2058     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2059     media => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2060 wakaba 1.61 methods => FEATURE_HTML20_RFC,
2061 wakaba 1.154 rel => FEATURE_HTML5_LC | FEATURE_RDFA_REC | FEATURE_XHTML2_ED |
2062 wakaba 1.153 FEATURE_M12N10_REC,
2063 wakaba 1.154 rev => FEATURE_RDFA_REC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2064 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2065 wakaba 1.153 sizes => FEATURE_HTML5_LC,
2066 wakaba 1.82 target => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2067 wakaba 1.153 # title: HTML5_WD | HTML5_LC | ...
2068     type => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
2069 wakaba 1.61 urn => FEATURE_HTML20_RFC,
2070 wakaba 1.40 })->($self, $item, $element_state);
2071 wakaba 1.96
2072 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'href')) {
2073     $self->{has_hyperlink_element} = 1 if $item->{has_hyperlink_link_type};
2074 wakaba 1.4 } else {
2075 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2076 wakaba 1.104 type => 'attribute missing',
2077     text => 'href',
2078     level => $self->{level}->{must});
2079 wakaba 1.1 }
2080 wakaba 1.96
2081 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'rel')) {
2082     $self->{onerror}->(node => $item->{node},
2083 wakaba 1.104 type => 'attribute missing',
2084     text => 'rel',
2085     level => $self->{level}->{must});
2086 wakaba 1.96 }
2087    
2088     if ($sizes_attr and not $element_state->{link_rel}->{icon}) {
2089     $self->{onerror}->(node => $sizes_attr,
2090     type => 'attribute not allowed',
2091 wakaba 1.104 level => $self->{level}->{must});
2092 wakaba 1.1 }
2093 wakaba 1.116
2094     if ($element_state->{link_rel}->{alternate} and
2095     $element_state->{link_rel}->{stylesheet}) {
2096     my $title_attr = $item->{node}->get_attribute_node_ns (undef, 'title');
2097     unless ($title_attr) {
2098     $self->{onerror}->(node => $item->{node},
2099     type => 'attribute missing',
2100     text => 'title',
2101     level => $self->{level}->{must});
2102     } elsif ($title_attr->value eq '') {
2103     $self->{onerror}->(node => $title_attr,
2104     type => 'empty style sheet title',
2105     level => $self->{level}->{must});
2106     }
2107     }
2108 wakaba 1.1 },
2109     };
2110    
2111     $Element->{$HTML_NS}->{meta} = {
2112 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2113 wakaba 1.40 %HTMLEmptyChecker,
2114     check_attrs => sub {
2115     my ($self, $item, $element_state) = @_;
2116 wakaba 1.1 my $name_attr;
2117     my $http_equiv_attr;
2118     my $charset_attr;
2119     my $content_attr;
2120 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2121 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2122     $attr_ns = '' unless defined $attr_ns;
2123     my $attr_ln = $attr->manakai_local_name;
2124     my $checker;
2125 wakaba 1.73 my $status;
2126 wakaba 1.1 if ($attr_ns eq '') {
2127 wakaba 1.73 $status = {
2128     %HTMLAttrStatus,
2129 wakaba 1.82 %XHTML2CommonAttrStatus,
2130 wakaba 1.153 charset => FEATURE_HTML5_WD,
2131     content => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2132     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2133     'http-equiv' => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2134     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
2135     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2136     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2137 wakaba 1.73 scheme => FEATURE_M12N10_REC,
2138     }->{$attr_ln};
2139    
2140 wakaba 1.1 if ($attr_ln eq 'content') {
2141     $content_attr = $attr;
2142     $checker = 1;
2143     } elsif ($attr_ln eq 'name') {
2144     $name_attr = $attr;
2145     $checker = 1;
2146     } elsif ($attr_ln eq 'http-equiv') {
2147     $http_equiv_attr = $attr;
2148     $checker = 1;
2149     } elsif ($attr_ln eq 'charset') {
2150     $charset_attr = $attr;
2151     $checker = 1;
2152 wakaba 1.67 } elsif ($attr_ln eq 'scheme') {
2153 wakaba 1.71 ## NOTE: <http://suika.fam.cx/2007/html/standards#html-meta-scheme>
2154 wakaba 1.67 $checker = sub {};
2155 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
2156     $attr_ln !~ /[A-Z]/) {
2157 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
2158     $status = $HTMLDatasetAttrStatus;
2159 wakaba 1.1 } else {
2160     $checker = $HTMLAttrChecker->{$attr_ln}
2161 wakaba 1.67 || $AttrChecker->{$attr_ns}->{$attr_ln}
2162 wakaba 1.1 || $AttrChecker->{$attr_ns}->{''};
2163     }
2164     } else {
2165     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2166 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
2167     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
2168     || $AttrStatus->{$attr_ns}->{''};
2169     $status = FEATURE_ALLOWED if not defined $status;
2170 wakaba 1.1 }
2171 wakaba 1.62
2172 wakaba 1.1 if ($checker) {
2173 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2174 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2175 wakaba 1.54 #
2176 wakaba 1.1 } else {
2177 wakaba 1.104 $self->{onerror}->(node => $attr,
2178     type => 'unknown attribute',
2179     level => $self->{level}->{uncertain});
2180 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
2181     }
2182    
2183 wakaba 1.82 $self->_attr_status_info ($attr, $status);
2184 wakaba 1.1 }
2185    
2186     if (defined $name_attr) {
2187     if (defined $http_equiv_attr) {
2188     $self->{onerror}->(node => $http_equiv_attr,
2189 wakaba 1.104 type => 'attribute not allowed',
2190     level => $self->{level}->{must});
2191 wakaba 1.1 } elsif (defined $charset_attr) {
2192     $self->{onerror}->(node => $charset_attr,
2193 wakaba 1.104 type => 'attribute not allowed',
2194     level => $self->{level}->{must});
2195 wakaba 1.1 }
2196     my $metadata_name = $name_attr->value;
2197     my $metadata_value;
2198     if (defined $content_attr) {
2199     $metadata_value = $content_attr->value;
2200     } else {
2201 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2202 wakaba 1.104 type => 'attribute missing',
2203     text => 'content',
2204     level => $self->{level}->{must});
2205 wakaba 1.1 $metadata_value = '';
2206     }
2207     } elsif (defined $http_equiv_attr) {
2208     if (defined $charset_attr) {
2209     $self->{onerror}->(node => $charset_attr,
2210 wakaba 1.104 type => 'attribute not allowed',
2211     level => $self->{level}->{must});
2212 wakaba 1.1 }
2213     unless (defined $content_attr) {
2214 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2215 wakaba 1.104 type => 'attribute missing',
2216     text => 'content',
2217     level => $self->{level}->{must});
2218 wakaba 1.1 }
2219     } elsif (defined $charset_attr) {
2220     if (defined $content_attr) {
2221     $self->{onerror}->(node => $content_attr,
2222 wakaba 1.104 type => 'attribute not allowed',
2223     level => $self->{level}->{must});
2224 wakaba 1.1 }
2225     } else {
2226     if (defined $content_attr) {
2227     $self->{onerror}->(node => $content_attr,
2228 wakaba 1.104 type => 'attribute not allowed',
2229     level => $self->{level}->{must});
2230 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2231 wakaba 1.104 type => 'attribute missing:name|http-equiv',
2232     level => $self->{level}->{must});
2233 wakaba 1.1 } else {
2234 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2235 wakaba 1.104 type => 'attribute missing:name|http-equiv|charset',
2236     level => $self->{level}->{must});
2237 wakaba 1.1 }
2238     }
2239    
2240 wakaba 1.32 my $check_charset_decl = sub () {
2241 wakaba 1.40 my $parent = $item->{node}->manakai_parent_element;
2242 wakaba 1.29 if ($parent and $parent eq $parent->owner_document->manakai_head) {
2243     for my $el (@{$parent->child_nodes}) {
2244     next unless $el->node_type == 1; # ELEMENT_NODE
2245 wakaba 1.40 unless ($el eq $item->{node}) {
2246 wakaba 1.29 ## NOTE: Not the first child element.
2247 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2248 wakaba 1.32 type => 'element not allowed:meta charset',
2249 wakaba 1.104 level => $self->{level}->{must});
2250 wakaba 1.29 }
2251     last;
2252     ## NOTE: Entity references are not supported.
2253     }
2254     } else {
2255 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2256 wakaba 1.32 type => 'element not allowed:meta charset',
2257 wakaba 1.104 level => $self->{level}->{must});
2258 wakaba 1.29 }
2259    
2260 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
2261     $self->{onerror}->(node => $item->{node},
2262 wakaba 1.32 type => 'in XML:charset',
2263 wakaba 1.104 level => $self->{level}->{must});
2264 wakaba 1.1 }
2265 wakaba 1.32 }; # $check_charset_decl
2266 wakaba 1.21
2267 wakaba 1.32 my $check_charset = sub ($$) {
2268     my ($attr, $charset_value) = @_;
2269 wakaba 1.21
2270 wakaba 1.91 my $charset;
2271     ($charset, $charset_value)
2272     = $HTMLCharsetChecker->($charset_value, $self, $attr);
2273    
2274 wakaba 1.40 my $ic = $item->{node}->owner_document->input_encoding;
2275 wakaba 1.21 if (defined $ic) {
2276     ## TODO: Test for this case
2277     my $ic_charset = $Message::Charset::Info::IANACharset->{$ic};
2278     if ($charset ne $ic_charset) {
2279 wakaba 1.32 $self->{onerror}->(node => $attr,
2280 wakaba 1.104 type => 'mismatched charset name',
2281 wakaba 1.106 text => $ic,
2282 wakaba 1.104 value => $charset_value,
2283     level => $self->{level}->{must});
2284 wakaba 1.21 }
2285     } else {
2286     ## NOTE: MUST, but not checkable, since the document is not originally
2287     ## in serialized form (or the parser does not preserve the input
2288     ## encoding information).
2289 wakaba 1.32 $self->{onerror}->(node => $attr,
2290 wakaba 1.104 type => 'mismatched charset name not checked',
2291     value => $charset_value,
2292     level => $self->{level}->{uncertain});
2293 wakaba 1.21 }
2294    
2295 wakaba 1.32 if ($attr->get_user_data ('manakai_has_reference')) {
2296     $self->{onerror}->(node => $attr,
2297 wakaba 1.104 type => 'charref in charset',
2298     level => $self->{level}->{must},
2299     layer => 'syntax');
2300 wakaba 1.22 }
2301 wakaba 1.32 }; # $check_charset
2302    
2303     ## TODO: metadata conformance
2304    
2305     ## TODO: pragma conformance
2306     if (defined $http_equiv_attr) { ## An enumerated attribute
2307     my $keyword = lc $http_equiv_attr->value; ## TODO: ascii case?
2308 wakaba 1.33
2309 wakaba 1.85 if ($self->{has_http_equiv}->{$keyword}) {
2310     $self->{onerror}->(type => 'duplicate http-equiv', value => $keyword,
2311     node => $http_equiv_attr,
2312 wakaba 1.104 level => $self->{level}->{must});
2313 wakaba 1.85 } else {
2314     $self->{has_http_equiv}->{$keyword} = 1;
2315     }
2316    
2317     if ($keyword eq 'content-type') {
2318 wakaba 1.58 ## TODO: refs in "text/html; charset=" are not disallowed since rev.1275.
2319 wakaba 1.33
2320 wakaba 1.32 $check_charset_decl->();
2321     if ($content_attr) {
2322     my $content = $content_attr->value;
2323 wakaba 1.58 if ($content =~ m!^[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll];
2324 wakaba 1.132 [\x09\x0A\x0C\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
2325 wakaba 1.58 =(.+)\z!sx) {
2326 wakaba 1.32 $check_charset->($content_attr, $1);
2327     } else {
2328     $self->{onerror}->(node => $content_attr,
2329     type => 'meta content-type syntax error',
2330 wakaba 1.104 level => $self->{level}->{must});
2331 wakaba 1.85 }
2332     }
2333     } elsif ($keyword eq 'default-style') {
2334     ## ISSUE: Not defined yet in the spec.
2335     } elsif ($keyword eq 'refresh') {
2336     if ($content_attr) {
2337     my $content = $content_attr->value;
2338     if ($content =~ /\A[0-9]+\z/) {
2339     ## NOTE: Valid non-negative integer.
2340     #
2341 wakaba 1.132 } elsif ($content =~ s/\A[0-9]+;[\x09\x0A\x0C\x0D\x20]+[Uu][Rr][Ll]=//) {
2342 wakaba 1.85 ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.)
2343     Whatpm::URIChecker->check_iri_reference ($content, sub {
2344 wakaba 1.104 $self->{onerror}->(value => $content, @_, node => $content_attr);
2345 wakaba 1.106 }, $self->{level});
2346 wakaba 1.85 $self->{has_uri_attr} = 1; ## NOTE: One of "attributes with URIs".
2347    
2348     $element_state->{uri_info}->{content}->{node} = $content_attr;
2349     $element_state->{uri_info}->{content}->{type}->{hyperlink} = 1;
2350     ## TODO: absolute
2351     push @{$self->{return}->{uri}->{$content} ||= []},
2352     $element_state->{uri_info}->{content};
2353     } else {
2354     $self->{onerror}->(node => $content_attr,
2355     type => 'refresh:syntax error',
2356 wakaba 1.104 level => $self->{level}->{must});
2357 wakaba 1.32 }
2358     }
2359     } else {
2360     $self->{onerror}->(node => $http_equiv_attr,
2361 wakaba 1.104 type => 'enumerated:invalid',
2362     level => $self->{level}->{must});
2363 wakaba 1.32 }
2364     }
2365    
2366     if (defined $charset_attr) {
2367     $check_charset_decl->();
2368     $check_charset->($charset_attr, $charset_attr->value);
2369 wakaba 1.1 }
2370     },
2371     };
2372    
2373     $Element->{$HTML_NS}->{style} = {
2374 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2375 wakaba 1.40 %HTMLChecker,
2376     check_attrs => $GetHTMLAttrsChecker->({
2377 wakaba 1.1 type => $HTMLIMTAttrChecker, ## TODO: MUST be a styling language
2378     media => $HTMLMQAttrChecker,
2379     scoped => $GetHTMLBooleanAttrChecker->('scoped'),
2380     ## NOTE: |title| has special semantics for |style|s, but is syntactically
2381     ## not different
2382 wakaba 1.49 }, {
2383     %HTMLAttrStatus,
2384 wakaba 1.82 %XHTML2CommonAttrStatus,
2385 wakaba 1.153 dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2386 wakaba 1.82 disabled => FEATURE_XHTML2_ED,
2387 wakaba 1.154 href => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
2388 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
2389     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2390     media => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2391     scoped => FEATURE_HTML5_FD,
2392     title => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2393     type => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2394 wakaba 1.1 }),
2395 wakaba 1.40 check_start => sub {
2396     my ($self, $item, $element_state) = @_;
2397    
2398 wakaba 1.27 ## NOTE: |html:style| itself has no conformance creteria on content model.
2399 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
2400 wakaba 1.93 $type = 'text/css' unless defined $type;
2401     if ($type =~ m[\A(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*/(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*\z]) {
2402     $type = "$1/$2";
2403     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
2404     } else {
2405     ## NOTE: We don't know how parameters are handled by UAs. According to
2406     ## HTML5 specification, <style> with unknown parameters in |type=""|
2407     ## must be ignored.
2408     undef $type;
2409     }
2410     if (not defined $type) {
2411     $element_state->{allow_element} = 1; # invalid type=""
2412     } elsif ($type eq 'text/css') {
2413 wakaba 1.40 $element_state->{allow_element} = 0;
2414 wakaba 1.93 #} elsif ($type =~ m![/+][Xx][Mm][Ll]\z!) {
2415     # ## NOTE: There is no definition for "XML-based styling language" in HTML5
2416     # $element_state->{allow_element} = 1;
2417 wakaba 1.40 } else {
2418     $element_state->{allow_element} = 1; # unknown
2419     }
2420 wakaba 1.93 $element_state->{style_type} = $type;
2421 wakaba 1.79
2422     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2423     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2424 wakaba 1.107
2425     $element_state->{text} = '';
2426 wakaba 1.40 },
2427     check_child_element => sub {
2428     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2429     $child_is_transparent, $element_state) = @_;
2430 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
2431     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
2432 wakaba 1.40 $self->{onerror}->(node => $child_el,
2433     type => 'element not allowed:minus',
2434 wakaba 1.104 level => $self->{level}->{must});
2435 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2436     #
2437     } elsif ($element_state->{allow_element}) {
2438     #
2439     } else {
2440 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2441     level => $self->{level}->{must});
2442 wakaba 1.40 }
2443     },
2444     check_child_text => sub {
2445     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2446 wakaba 1.115 $element_state->{text} .= $child_node->data;
2447 wakaba 1.40 },
2448     check_end => sub {
2449     my ($self, $item, $element_state) = @_;
2450 wakaba 1.93 if (not defined $element_state->{style_type}) {
2451     ## NOTE: Invalid type=""
2452     #
2453     } elsif ($element_state->{style_type} eq 'text/css') {
2454 wakaba 1.40 $self->{onsubdoc}->({s => $element_state->{text},
2455     container_node => $item->{node},
2456 wakaba 1.28 media_type => 'text/css', is_char_string => 1});
2457 wakaba 1.93 } elsif ($element_state->{style_type} =~ m![+/][Xx][Mm][Ll]\z!) {
2458     ## NOTE: XML content should be checked by THIS instance of checker
2459     ## as part of normal tree validation. However, we don't know of any
2460     ## XML-based styling language that can be used in HTML <style> element,
2461     ## such that we throw a "style language not supported" error.
2462 wakaba 1.104 $self->{onerror}->(node => $item->{node},
2463     type => 'XML style lang',
2464     text => $element_state->{style_type},
2465     level => $self->{level}->{uncertain});
2466 wakaba 1.93 } else {
2467     ## NOTE: Should we raise some kind of error for,
2468     ## say, <style type="text/plaion">?
2469     $self->{onsubdoc}->({s => $element_state->{text},
2470     container_node => $item->{node},
2471     media_type => $element_state->{style_type},
2472     is_char_string => 1});
2473 wakaba 1.27 }
2474 wakaba 1.40
2475     $HTMLChecker{check_end}->(@_);
2476 wakaba 1.1 },
2477     };
2478 wakaba 1.25 ## ISSUE: Relationship to significant content check?
2479 wakaba 1.1
2480     $Element->{$HTML_NS}->{body} = {
2481 wakaba 1.72 %HTMLFlowContentChecker,
2482 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2483 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2484     alink => $HTMLColorAttrChecker,
2485     background => $HTMLURIAttrChecker,
2486     bgcolor => $HTMLColorAttrChecker,
2487     link => $HTMLColorAttrChecker,
2488     text => $HTMLColorAttrChecker,
2489     vlink => $HTMLColorAttrChecker,
2490     }, {
2491 wakaba 1.49 %HTMLAttrStatus,
2492 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2493 wakaba 1.49 alink => FEATURE_M12N10_REC_DEPRECATED,
2494     background => FEATURE_M12N10_REC_DEPRECATED,
2495     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
2496 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2497 wakaba 1.49 link => FEATURE_M12N10_REC_DEPRECATED,
2498 wakaba 1.50 onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2499     onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2500 wakaba 1.49 text => FEATURE_M12N10_REC_DEPRECATED,
2501     vlink => FEATURE_M12N10_REC_DEPRECATED,
2502     }),
2503 wakaba 1.68 check_start => sub {
2504     my ($self, $item, $element_state) = @_;
2505    
2506     $element_state->{uri_info}->{background}->{type}->{embedded} = 1;
2507 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2508     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2509 wakaba 1.68 },
2510 wakaba 1.1 };
2511    
2512     $Element->{$HTML_NS}->{section} = {
2513 wakaba 1.72 %HTMLFlowContentChecker,
2514 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED,
2515 wakaba 1.82 check_attrs => $GetHTMLAttrsChecker->({
2516     }, {
2517     %HTMLAttrStatus,
2518     %XHTML2CommonAttrStatus,
2519     }),
2520 wakaba 1.1 };
2521    
2522     $Element->{$HTML_NS}->{nav} = {
2523 wakaba 1.153 status => FEATURE_HTML5_LC,
2524 wakaba 1.72 %HTMLFlowContentChecker,
2525 wakaba 1.1 };
2526    
2527     $Element->{$HTML_NS}->{article} = {
2528 wakaba 1.174 %HTMLFlowContentChecker,
2529 wakaba 1.153 status => FEATURE_HTML5_LC,
2530 wakaba 1.174 check_attrs => $GetHTMLAttrsChecker->({
2531     pubdate => $GetDateTimeAttrChecker->('global_date_and_time_string'),
2532     }, {
2533     %HTMLAttrStatus,
2534     # XXX cite
2535     pubdate => FEATURE_HTML5_LC,
2536     }),
2537     }; # article
2538 wakaba 1.1
2539     $Element->{$HTML_NS}->{blockquote} = {
2540 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2541 wakaba 1.72 %HTMLFlowContentChecker,
2542 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2543 wakaba 1.1 cite => $HTMLURIAttrChecker,
2544 wakaba 1.49 }, {
2545     %HTMLAttrStatus,
2546 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2547 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2548 wakaba 1.154 cite => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2549 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2550 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2551 wakaba 1.1 }),
2552 wakaba 1.66 check_start => sub {
2553     my ($self, $item, $element_state) = @_;
2554    
2555     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2556 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2557     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2558 wakaba 1.66 },
2559 wakaba 1.1 };
2560    
2561     $Element->{$HTML_NS}->{aside} = {
2562 wakaba 1.153 status => FEATURE_HTML5_LC,
2563 wakaba 1.72 %HTMLFlowContentChecker,
2564 wakaba 1.1 };
2565    
2566     $Element->{$HTML_NS}->{h1} = {
2567 wakaba 1.40 %HTMLPhrasingContentChecker,
2568 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2569 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2570     align => $GetHTMLEnumeratedAttrChecker->({
2571     left => 1, center => 1, right => 1, justify => 1,
2572     }),
2573     }, {
2574 wakaba 1.49 %HTMLAttrStatus,
2575 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2576 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2577 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2578 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2579 wakaba 1.49 }),
2580 wakaba 1.40 check_start => sub {
2581     my ($self, $item, $element_state) = @_;
2582     $self->{flag}->{has_hn} = 1;
2583 wakaba 1.79
2584     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2585     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2586 wakaba 1.1 },
2587     };
2588    
2589 wakaba 1.40 $Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}};
2590 wakaba 1.1
2591 wakaba 1.40 $Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}};
2592 wakaba 1.1
2593 wakaba 1.40 $Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}};
2594 wakaba 1.1
2595 wakaba 1.40 $Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}};
2596 wakaba 1.1
2597 wakaba 1.40 $Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}};
2598 wakaba 1.1
2599 wakaba 1.29 ## TODO: Explicit sectioning is "encouraged".
2600 wakaba 1.174
2601     # XXX footer in header is disallowed (HTML5 revision 3050)
2602 wakaba 1.29
2603 wakaba 1.1 $Element->{$HTML_NS}->{header} = {
2604 wakaba 1.153 status => FEATURE_HTML5_LC,
2605 wakaba 1.72 %HTMLFlowContentChecker,
2606 wakaba 1.40 check_start => sub {
2607     my ($self, $item, $element_state) = @_;
2608     $self->_add_minus_elements ($element_state,
2609     {$HTML_NS => {qw/header 1 footer 1/}},
2610 wakaba 1.58 $HTMLSectioningContent);
2611 wakaba 1.40 $element_state->{has_hn_original} = $self->{flag}->{has_hn};
2612     $self->{flag}->{has_hn} = 0;
2613 wakaba 1.79
2614     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2615     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2616 wakaba 1.40 },
2617     check_end => sub {
2618     my ($self, $item, $element_state) = @_;
2619     $self->_remove_minus_elements ($element_state);
2620     unless ($self->{flag}->{has_hn}) {
2621     $self->{onerror}->(node => $item->{node},
2622 wakaba 1.104 type => 'element missing:hn',
2623     level => $self->{level}->{must});
2624 wakaba 1.40 }
2625     $self->{flag}->{has_hn} ||= $element_state->{has_hn_original};
2626 wakaba 1.1
2627 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2628 wakaba 1.1 },
2629 wakaba 1.40 ## ISSUE: <header><del><h1>...</h1></del></header> is conforming?
2630 wakaba 1.1 };
2631    
2632     $Element->{$HTML_NS}->{footer} = {
2633 wakaba 1.153 status => FEATURE_HTML5_LC,
2634 wakaba 1.72 %HTMLFlowContentChecker,
2635 wakaba 1.40 check_start => sub {
2636     my ($self, $item, $element_state) = @_;
2637     $self->_add_minus_elements ($element_state,
2638 wakaba 1.177 {$HTML_NS => {header => 1, footer => 1}},
2639 wakaba 1.58 $HTMLSectioningContent,
2640 wakaba 1.57 $HTMLHeadingContent);
2641 wakaba 1.79
2642     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2643     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2644 wakaba 1.40 },
2645     check_end => sub {
2646     my ($self, $item, $element_state) = @_;
2647     $self->_remove_minus_elements ($element_state);
2648 wakaba 1.1
2649 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2650 wakaba 1.1 },
2651     };
2652    
2653     $Element->{$HTML_NS}->{address} = {
2654 wakaba 1.72 %HTMLFlowContentChecker,
2655 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2656 wakaba 1.110 check_attrs => $GetHTMLAttrsChecker->({
2657     ## TODO: add test
2658     #align => $GetHTMLEnumeratedAttrChecker->({
2659     # left => 1, center => 1, right => 1, justify => 1,
2660     #}),
2661     }, {
2662 wakaba 1.49 %HTMLAttrStatus,
2663 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2664 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2665 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2666 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2667     sdapref => FEATURE_HTML20_RFC,
2668 wakaba 1.49 }),
2669 wakaba 1.40 check_start => sub {
2670     my ($self, $item, $element_state) = @_;
2671 wakaba 1.177 $self->_add_minus_elements
2672     ($element_state,
2673     {$HTML_NS => {header => 1, footer => 1, address => 1}},
2674     $HTMLSectioningContent, $HTMLHeadingContent);
2675 wakaba 1.79
2676     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2677     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2678 wakaba 1.40 },
2679     check_end => sub {
2680     my ($self, $item, $element_state) = @_;
2681     $self->_remove_minus_elements ($element_state);
2682 wakaba 1.29
2683 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2684 wakaba 1.29 },
2685 wakaba 1.1 };
2686    
2687     $Element->{$HTML_NS}->{p} = {
2688 wakaba 1.40 %HTMLPhrasingContentChecker,
2689 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2690 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2691     align => $GetHTMLEnumeratedAttrChecker->({
2692     left => 1, center => 1, right => 1, justify => 1,
2693     }),
2694     }, {
2695 wakaba 1.49 %HTMLAttrStatus,
2696 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2697 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2698 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2699 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2700 wakaba 1.49 }),
2701 wakaba 1.1 };
2702    
2703     $Element->{$HTML_NS}->{hr} = {
2704 wakaba 1.40 %HTMLEmptyChecker,
2705 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
2706 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
2707     ## TODO: HTML4 |align|, |noshade|, |size|, |width|
2708     }, {
2709 wakaba 1.49 %HTMLAttrStatus,
2710     %HTMLM12NCommonAttrStatus,
2711     align => FEATURE_M12N10_REC_DEPRECATED,
2712 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2713 wakaba 1.49 noshade => FEATURE_M12N10_REC_DEPRECATED,
2714 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2715 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
2716     width => FEATURE_M12N10_REC_DEPRECATED,
2717     }),
2718 wakaba 1.1 };
2719    
2720     $Element->{$HTML_NS}->{br} = {
2721 wakaba 1.40 %HTMLEmptyChecker,
2722 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
2723 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2724     clear => $GetHTMLEnumeratedAttrChecker->({
2725     left => 1, all => 1, right => 1, none => 1,
2726     }),
2727     }, {
2728 wakaba 1.49 %HTMLAttrStatus,
2729 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2730 wakaba 1.49 clear => FEATURE_M12N10_REC_DEPRECATED,
2731 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2732 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2733 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2734     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2735 wakaba 1.49 }),
2736 wakaba 1.29 ## NOTE: Blank line MUST NOT be used for presentation purpose.
2737     ## (This requirement is semantic so that we cannot check.)
2738 wakaba 1.1 };
2739    
2740     $Element->{$HTML_NS}->{dialog} = {
2741 wakaba 1.153 status => FEATURE_HTML5_WD,
2742 wakaba 1.40 %HTMLChecker,
2743     check_start => sub {
2744     my ($self, $item, $element_state) = @_;
2745     $element_state->{phase} = 'before dt';
2746 wakaba 1.79
2747     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2748     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2749 wakaba 1.40 },
2750     check_child_element => sub {
2751     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2752     $child_is_transparent, $element_state) = @_;
2753 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
2754     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
2755 wakaba 1.40 $self->{onerror}->(node => $child_el,
2756     type => 'element not allowed:minus',
2757 wakaba 1.104 level => $self->{level}->{must});
2758 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2759     #
2760     } elsif ($element_state->{phase} eq 'before dt') {
2761     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2762     $element_state->{phase} = 'before dd';
2763     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2764     $self->{onerror}
2765 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2766     text => 'dt',
2767     level => $self->{level}->{must});
2768 wakaba 1.40 $element_state->{phase} = 'before dt';
2769     } else {
2770 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2771     level => $self->{level}->{must});
2772 wakaba 1.40 }
2773     } elsif ($element_state->{phase} eq 'before dd') {
2774     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2775     $element_state->{phase} = 'before dt';
2776     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2777     $self->{onerror}
2778 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2779     text => 'dd',
2780     level => $self->{level}->{must});
2781 wakaba 1.40 $element_state->{phase} = 'before dd';
2782     } else {
2783 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2784     level => $self->{level}->{must});
2785 wakaba 1.1 }
2786 wakaba 1.40 } else {
2787     die "check_child_element: Bad |dialog| phase: $element_state->{phase}";
2788     }
2789     },
2790     check_child_text => sub {
2791     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2792     if ($has_significant) {
2793 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2794     level => $self->{level}->{must});
2795 wakaba 1.1 }
2796 wakaba 1.40 },
2797     check_end => sub {
2798     my ($self, $item, $element_state) = @_;
2799     if ($element_state->{phase} eq 'before dd') {
2800     $self->{onerror}->(node => $item->{node},
2801 wakaba 1.104 type => 'child element missing',
2802     text => 'dd',
2803     level => $self->{level}->{must});
2804 wakaba 1.1 }
2805 wakaba 1.40
2806     $HTMLChecker{check_end}->(@_);
2807 wakaba 1.1 },
2808     };
2809    
2810     $Element->{$HTML_NS}->{pre} = {
2811 wakaba 1.40 %HTMLPhrasingContentChecker,
2812 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2813 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2814     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2815     }, {
2816 wakaba 1.49 %HTMLAttrStatus,
2817 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2818 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2819 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2820 wakaba 1.49 width => FEATURE_M12N10_REC_DEPRECATED,
2821     }),
2822 wakaba 1.101 check_end => sub {
2823     my ($self, $item, $element_state) = @_;
2824    
2825     ## TODO: Flag to enable/disable IDL checking?
2826 wakaba 1.145 my $class = $item->{node}->get_attribute_ns (undef, 'class');
2827 wakaba 1.102 if ($class =~ /\bidl(?>-code)?\b/) { ## TODO: use classList.has
2828     ## NOTE: pre.idl: WHATWG, XHR, Selectors API, CSSOM specs
2829     ## NOTE: pre.code > code.idl-code: WebIDL spec
2830     ## NOTE: pre.idl-code: DOM1 spec
2831     ## NOTE: div.idl-code > pre: DOM, ProgressEvent specs
2832     ## NOTE: pre.schema: ReSpec-generated specs
2833 wakaba 1.101 $self->{onsubdoc}->({s => $item->{node}->text_content,
2834     container_node => $item->{node},
2835     media_type => 'text/x-webidl',
2836     is_char_string => 1});
2837     }
2838    
2839 wakaba 1.110 $HTMLPhrasingContentChecker{check_end}->(@_);
2840 wakaba 1.101 },
2841 wakaba 1.1 };
2842    
2843     $Element->{$HTML_NS}->{ol} = {
2844 wakaba 1.40 %HTMLChecker,
2845 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2846 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2847 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
2848 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
2849 wakaba 1.1 start => $HTMLIntegerAttrChecker,
2850 wakaba 1.69 ## TODO: HTML4 |type|
2851 wakaba 1.49 }, {
2852     %HTMLAttrStatus,
2853 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2854 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2855 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2856 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2857     reversed => FEATURE_HTML5_WD,
2858 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2859 wakaba 1.153 #start => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
2860     start => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2861 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2862 wakaba 1.1 }),
2863 wakaba 1.40 check_child_element => sub {
2864     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2865     $child_is_transparent, $element_state) = @_;
2866 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
2867     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
2868 wakaba 1.40 $self->{onerror}->(node => $child_el,
2869     type => 'element not allowed:minus',
2870 wakaba 1.104 level => $self->{level}->{must});
2871 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2872     #
2873     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
2874     #
2875     } else {
2876 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2877     level => $self->{level}->{must});
2878 wakaba 1.1 }
2879 wakaba 1.40 },
2880     check_child_text => sub {
2881     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2882     if ($has_significant) {
2883 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2884     level => $self->{level}->{must});
2885 wakaba 1.1 }
2886     },
2887     };
2888    
2889     $Element->{$HTML_NS}->{ul} = {
2890 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
2891 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2892 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2893     compact => $GetHTMLBooleanAttrChecker->('compact'),
2894 wakaba 1.69 ## TODO: HTML4 |type|
2895     ## TODO: sdaform, align
2896 wakaba 1.68 }, {
2897 wakaba 1.49 %HTMLAttrStatus,
2898 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2899 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2900 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2901 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2902 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2903 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2904     }),
2905 wakaba 1.1 };
2906    
2907 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
2908     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
2909     %{$Element->{$HTML_NS}->{ul}},
2910     status => FEATURE_M12N10_REC_DEPRECATED,
2911 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2912     compact => $GetHTMLBooleanAttrChecker->('compact'),
2913     }, {
2914 wakaba 1.64 %HTMLAttrStatus,
2915     %HTMLM12NCommonAttrStatus,
2916     align => FEATURE_HTML2X_RFC,
2917     compact => FEATURE_M12N10_REC_DEPRECATED,
2918 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2919 wakaba 1.64 sdaform => FEATURE_HTML20_RFC,
2920     sdapref => FEATURE_HTML20_RFC,
2921     }),
2922     };
2923    
2924 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
2925 wakaba 1.72 %HTMLFlowContentChecker,
2926 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2927 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2928 wakaba 1.69 ## TODO: HTML4 |type|
2929 wakaba 1.49 value => sub {
2930 wakaba 1.1 my ($self, $attr) = @_;
2931 wakaba 1.152
2932     my $parent_is_ol;
2933 wakaba 1.1 my $parent = $attr->owner_element->manakai_parent_element;
2934     if (defined $parent) {
2935     my $parent_ns = $parent->namespace_uri;
2936     $parent_ns = '' unless defined $parent_ns;
2937     my $parent_ln = $parent->manakai_local_name;
2938 wakaba 1.152 $parent_is_ol = ($parent_ns eq $HTML_NS and $parent_ln eq 'ol');
2939     }
2940    
2941     unless ($parent_is_ol) {
2942     ## ISSUE: No "MUST" in the spec.
2943     $self->{onerror}->(node => $attr,
2944     type => 'non-ol li value',
2945     level => $self->{level}->{html5_fact});
2946 wakaba 1.1 }
2947 wakaba 1.152
2948 wakaba 1.1 $HTMLIntegerAttrChecker->($self, $attr);
2949 wakaba 1.131 },
2950 wakaba 1.49 }, {
2951     %HTMLAttrStatus,
2952 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2953 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2954 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2955 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2956 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2957 wakaba 1.154 #value => FEATURE_HTML5_LC | FEATURE_XHTMLBASIC11_CR |
2958 wakaba 1.55 # FEATURE_M12N10_REC_DEPRECATED,
2959 wakaba 1.154 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED |
2960 wakaba 1.82 FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2961 wakaba 1.1 }),
2962 wakaba 1.40 check_child_element => sub {
2963     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2964     $child_is_transparent, $element_state) = @_;
2965     if ($self->{flag}->{in_menu}) {
2966 wakaba 1.152 ## TODO: In <dir> element, then ...
2967 wakaba 1.40 $HTMLPhrasingContentChecker{check_child_element}->(@_);
2968     } else {
2969 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2970 wakaba 1.40 }
2971     },
2972     check_child_text => sub {
2973     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2974     if ($self->{flag}->{in_menu}) {
2975 wakaba 1.152 ## TODO: In <dir> element, then ...
2976 wakaba 1.40 $HTMLPhrasingContentChecker{check_child_text}->(@_);
2977 wakaba 1.1 } else {
2978 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2979 wakaba 1.1 }
2980     },
2981     };
2982    
2983     $Element->{$HTML_NS}->{dl} = {
2984 wakaba 1.40 %HTMLChecker,
2985 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2986 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2987     compact => $GetHTMLBooleanAttrChecker->('compact'),
2988     }, {
2989 wakaba 1.49 %HTMLAttrStatus,
2990 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2991 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2992 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2993 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2994     sdapref => FEATURE_HTML20_RFC,
2995 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2996     }),
2997 wakaba 1.40 check_start => sub {
2998     my ($self, $item, $element_state) = @_;
2999     $element_state->{phase} = 'before dt';
3000 wakaba 1.79
3001     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3002     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3003 wakaba 1.40 },
3004     check_child_element => sub {
3005     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3006     $child_is_transparent, $element_state) = @_;
3007 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
3008     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
3009 wakaba 1.40 $self->{onerror}->(node => $child_el,
3010     type => 'element not allowed:minus',
3011 wakaba 1.104 level => $self->{level}->{must});
3012 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3013     #
3014     } elsif ($element_state->{phase} eq 'in dds') {
3015     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
3016     #$element_state->{phase} = 'in dds';
3017     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
3018     $element_state->{phase} = 'in dts';
3019     } else {
3020 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
3021     level => $self->{level}->{must});
3022 wakaba 1.40 }
3023     } elsif ($element_state->{phase} eq 'in dts') {
3024     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
3025     #$element_state->{phase} = 'in dts';
3026     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
3027     $element_state->{phase} = 'in dds';
3028     } else {
3029 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
3030     level => $self->{level}->{must});
3031 wakaba 1.40 }
3032     } elsif ($element_state->{phase} eq 'before dt') {
3033     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
3034     $element_state->{phase} = 'in dts';
3035     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
3036     $self->{onerror}
3037 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
3038     text => 'dt',
3039     level => $self->{level}->{must});
3040 wakaba 1.40 $element_state->{phase} = 'in dds';
3041     } else {
3042 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
3043     level => $self->{level}->{must});
3044 wakaba 1.1 }
3045 wakaba 1.40 } else {
3046     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
3047 wakaba 1.1 }
3048 wakaba 1.40 },
3049     check_child_text => sub {
3050     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3051     if ($has_significant) {
3052 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
3053     level => $self->{level}->{must});
3054 wakaba 1.40 }
3055     },
3056     check_end => sub {
3057     my ($self, $item, $element_state) = @_;
3058     if ($element_state->{phase} eq 'in dts') {
3059     $self->{onerror}->(node => $item->{node},
3060 wakaba 1.104 type => 'child element missing',
3061     text => 'dd',
3062     level => $self->{level}->{must});
3063 wakaba 1.1 }
3064    
3065 wakaba 1.40 $HTMLChecker{check_end}->(@_);
3066 wakaba 1.1 },
3067     };
3068    
3069     $Element->{$HTML_NS}->{dt} = {
3070 wakaba 1.40 %HTMLPhrasingContentChecker,
3071 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3072 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3073     %HTMLAttrStatus,
3074 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3075 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3076 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3077 wakaba 1.49 }),
3078 wakaba 1.1 };
3079    
3080     $Element->{$HTML_NS}->{dd} = {
3081 wakaba 1.72 %HTMLFlowContentChecker,
3082 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3083 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3084     %HTMLAttrStatus,
3085 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3086 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3087 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3088 wakaba 1.49 }),
3089 wakaba 1.1 };
3090    
3091     $Element->{$HTML_NS}->{a} = {
3092 wakaba 1.123 %HTMLTransparentChecker,
3093 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3094 wakaba 1.40 check_attrs => sub {
3095     my ($self, $item, $element_state) = @_;
3096 wakaba 1.1 my %attr;
3097 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3098 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3099     $attr_ns = '' unless defined $attr_ns;
3100     my $attr_ln = $attr->manakai_local_name;
3101     my $checker;
3102 wakaba 1.73 my $status;
3103 wakaba 1.1 if ($attr_ns eq '') {
3104 wakaba 1.73 $status = {
3105     %HTMLAttrStatus,
3106 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3107 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
3108 wakaba 1.73 charset => FEATURE_M12N10_REC,
3109 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3110 wakaba 1.73 cryptopts => FEATURE_RFC2659,
3111     dn => FEATURE_RFC2659,
3112 wakaba 1.154 href => FEATURE_HTML5_WD | FEATURE_RDFA_REC | FEATURE_XHTML2_ED |
3113 wakaba 1.153 FEATURE_M12N10_REC,
3114     hreflang => FEATURE_HTML5_WD | FEATURE_XHTML2_ED |
3115     FEATURE_M12N10_REC,
3116     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3117     media => FEATURE_HTML5_WD | FEATURE_XHTML2_ED,
3118 wakaba 1.73 methods => FEATURE_HTML20_RFC,
3119     name => FEATURE_M12N10_REC_DEPRECATED,
3120     nonce => FEATURE_RFC2659,
3121     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3122     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3123 wakaba 1.153 ping => FEATURE_HTML5_WD,
3124 wakaba 1.154 rel => FEATURE_HTML5_WD | FEATURE_RDFA_REC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3125     rev => FEATURE_RDFA_REC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3126 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
3127 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3128 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3129 wakaba 1.153 target => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3130     type => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3131 wakaba 1.73 urn => FEATURE_HTML20_RFC,
3132     }->{$attr_ln};
3133    
3134 wakaba 1.1 $checker = {
3135 wakaba 1.91 charset => sub {
3136     my ($self, $attr) = @_;
3137     $HTMLCharsetChecker->($attr->value, @_);
3138     },
3139 wakaba 1.70 ## TODO: HTML4 |coords|
3140 wakaba 1.1 target => $HTMLTargetAttrChecker,
3141     href => $HTMLURIAttrChecker,
3142     ping => $HTMLSpaceURIsAttrChecker,
3143 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
3144 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
3145 wakaba 1.70 ## TODO: HTML4 |shape|
3146 wakaba 1.1 media => $HTMLMQAttrChecker,
3147 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
3148 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
3149     type => $HTMLIMTAttrChecker,
3150     }->{$attr_ln};
3151     if ($checker) {
3152     $attr{$attr_ln} = $attr;
3153 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
3154     $attr_ln !~ /[A-Z]/) {
3155 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
3156     $status = $HTMLDatasetAttrStatus;
3157 wakaba 1.1 } else {
3158     $checker = $HTMLAttrChecker->{$attr_ln};
3159     }
3160     }
3161     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3162     || $AttrChecker->{$attr_ns}->{''};
3163 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3164     || $AttrStatus->{$attr_ns}->{''};
3165     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3166 wakaba 1.62
3167 wakaba 1.1 if ($checker) {
3168 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
3169 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3170 wakaba 1.54 #
3171 wakaba 1.1 } else {
3172 wakaba 1.104 $self->{onerror}->(node => $attr,
3173     type => 'unknown attribute',
3174     level => $self->{level}->{uncertain});
3175 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
3176 wakaba 1.1 }
3177 wakaba 1.49
3178 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3179 wakaba 1.1 }
3180    
3181 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
3182 wakaba 1.4 if (defined $attr{href}) {
3183     $self->{has_hyperlink_element} = 1;
3184 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
3185 wakaba 1.4 } else {
3186 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
3187     if (defined $attr{$_}) {
3188     $self->{onerror}->(node => $attr{$_},
3189 wakaba 1.104 type => 'attribute not allowed',
3190     level => $self->{level}->{must});
3191 wakaba 1.1 }
3192     }
3193     }
3194 wakaba 1.66
3195     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
3196 wakaba 1.1 },
3197 wakaba 1.40 check_start => sub {
3198     my ($self, $item, $element_state) = @_;
3199     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
3200 wakaba 1.79
3201     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3202     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3203 wakaba 1.40 },
3204     check_end => sub {
3205     my ($self, $item, $element_state) = @_;
3206     $self->_remove_minus_elements ($element_state);
3207 wakaba 1.59 delete $self->{flag}->{in_a_href}
3208     unless $element_state->{in_a_href_original};
3209 wakaba 1.1
3210 wakaba 1.123 $HTMLTransparentChecker{check_end}->(@_);
3211 wakaba 1.1 },
3212     };
3213    
3214     $Element->{$HTML_NS}->{q} = {
3215 wakaba 1.153 status => FEATURE_HTML5_AT_RISK | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3216 wakaba 1.40 %HTMLPhrasingContentChecker,
3217     check_attrs => $GetHTMLAttrsChecker->({
3218 wakaba 1.50 cite => $HTMLURIAttrChecker,
3219     }, {
3220 wakaba 1.49 %HTMLAttrStatus,
3221 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3222 wakaba 1.153 cite => FEATURE_HTML5_AT_RISK | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3223     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3224 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3225     sdasuff => FEATURE_HTML2X_RFC,
3226 wakaba 1.1 }),
3227 wakaba 1.66 check_start => sub {
3228     my ($self, $item, $element_state) = @_;
3229    
3230     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3231 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3232     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3233 wakaba 1.66 },
3234 wakaba 1.1 };
3235 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
3236     ## placed inside the <code>q</code> element." Though we cannot test the
3237     ## element against this requirement since it incluides a semantic bit,
3238     ## it might be possible to inform of the existence of quotation marks OUTSIDE
3239     ## the |q| element.
3240 wakaba 1.1
3241     $Element->{$HTML_NS}->{cite} = {
3242 wakaba 1.40 %HTMLPhrasingContentChecker,
3243 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3244 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3245     %HTMLAttrStatus,
3246 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3247 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3248 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3249 wakaba 1.49 }),
3250 wakaba 1.1 };
3251    
3252     $Element->{$HTML_NS}->{em} = {
3253 wakaba 1.40 %HTMLPhrasingContentChecker,
3254 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3255 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3256     %HTMLAttrStatus,
3257 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3258 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3259 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3260 wakaba 1.49 }),
3261 wakaba 1.1 };
3262    
3263     $Element->{$HTML_NS}->{strong} = {
3264 wakaba 1.40 %HTMLPhrasingContentChecker,
3265 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3266 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3267     %HTMLAttrStatus,
3268 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3269 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3270 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3271 wakaba 1.49 }),
3272 wakaba 1.1 };
3273    
3274     $Element->{$HTML_NS}->{small} = {
3275 wakaba 1.40 %HTMLPhrasingContentChecker,
3276 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
3277 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3278     %HTMLAttrStatus,
3279     %HTMLM12NCommonAttrStatus,
3280 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3281 wakaba 1.49 }),
3282 wakaba 1.1 };
3283    
3284 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
3285     %HTMLPhrasingContentChecker,
3286     status => FEATURE_M12N10_REC,
3287     check_attrs => $GetHTMLAttrsChecker->({}, {
3288     %HTMLAttrStatus,
3289     %HTMLM12NCommonAttrStatus,
3290 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3291 wakaba 1.51 }),
3292     };
3293    
3294 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
3295 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3296 wakaba 1.40 %HTMLPhrasingContentChecker,
3297 wakaba 1.1 };
3298    
3299     $Element->{$HTML_NS}->{dfn} = {
3300 wakaba 1.40 %HTMLPhrasingContentChecker,
3301 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3302 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3303     %HTMLAttrStatus,
3304 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3305 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3306 wakaba 1.49 }),
3307 wakaba 1.40 check_start => sub {
3308     my ($self, $item, $element_state) = @_;
3309     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
3310 wakaba 1.1
3311 wakaba 1.40 my $node = $item->{node};
3312 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
3313     unless (defined $term) {
3314     for my $child (@{$node->child_nodes}) {
3315     if ($child->node_type == 1) { # ELEMENT_NODE
3316     if (defined $term) {
3317     undef $term;
3318     last;
3319     } elsif ($child->manakai_local_name eq 'abbr') {
3320     my $nsuri = $child->namespace_uri;
3321     if (defined $nsuri and $nsuri eq $HTML_NS) {
3322     my $attr = $child->get_attribute_node_ns (undef, 'title');
3323     if ($attr) {
3324     $term = $attr->value;
3325     }
3326     }
3327     }
3328     } elsif ($child->node_type == 3 or $child->node_type == 4) {
3329     ## TEXT_NODE or CDATA_SECTION_NODE
3330 wakaba 1.132 if ($child->data =~ /\A[\x09\x0A\x0C\x0D\x20]+\z/) { # Inter-element whitespace
3331 wakaba 1.1 next;
3332     }
3333     undef $term;
3334     last;
3335     }
3336     }
3337     unless (defined $term) {
3338     $term = $node->text_content;
3339     }
3340     }
3341     if ($self->{term}->{$term}) {
3342     push @{$self->{term}->{$term}}, $node;
3343     } else {
3344     $self->{term}->{$term} = [$node];
3345     }
3346 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
3347     ## |ruby| unless |dfn| has |title|.
3348 wakaba 1.79
3349     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3350     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3351 wakaba 1.40 },
3352     check_end => sub {
3353     my ($self, $item, $element_state) = @_;
3354     $self->_remove_minus_elements ($element_state);
3355 wakaba 1.1
3356 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3357 wakaba 1.1 },
3358     };
3359    
3360     $Element->{$HTML_NS}->{abbr} = {
3361 wakaba 1.40 %HTMLPhrasingContentChecker,
3362 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3363 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3364     %HTMLAttrStatus,
3365 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3366     full => FEATURE_XHTML2_ED,
3367 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3368 wakaba 1.49 }),
3369 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
3370     ## number (plural vs singular) must match the grammatical number of the
3371     ## contents of the element." Though this can be checked by machine,
3372     ## it requires language-specific knowledge and dictionary, such that
3373     ## we don't support the check of the requirement.
3374     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
3375 wakaba 1.49 };
3376    
3377     $Element->{$HTML_NS}->{acronym} = {
3378     %HTMLPhrasingContentChecker,
3379     status => FEATURE_M12N10_REC,
3380     check_attrs => $GetHTMLAttrsChecker->({}, {
3381     %HTMLAttrStatus,
3382     %HTMLM12NCommonAttrStatus,
3383 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3384 wakaba 1.49 }),
3385 wakaba 1.1 };
3386    
3387     $Element->{$HTML_NS}->{time} = {
3388 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3389 wakaba 1.40 %HTMLPhrasingContentChecker,
3390     check_attrs => $GetHTMLAttrsChecker->({
3391 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
3392 wakaba 1.49 }, {
3393     %HTMLAttrStatus,
3394     %HTMLM12NCommonAttrStatus,
3395 wakaba 1.72 datetime => FEATURE_HTML5_FD,
3396 wakaba 1.1 }),
3397 wakaba 1.168 ## TODO: Update definition
3398 wakaba 1.1 ## TODO: Write tests
3399 wakaba 1.40 check_end => sub {
3400     my ($self, $item, $element_state) = @_;
3401 wakaba 1.1
3402 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
3403 wakaba 1.1 my $input;
3404     my $reg_sp;
3405     my $input_node;
3406     if ($attr) {
3407     $input = $attr->value;
3408 wakaba 1.132 $reg_sp = qr/[\x09\x0A\x0C\x0D\x20]*/;
3409 wakaba 1.1 $input_node = $attr;
3410     } else {
3411 wakaba 1.40 $input = $item->{node}->text_content;
3412 wakaba 1.112 $reg_sp = qr/\p{WhiteSpace}*/;
3413 wakaba 1.40 $input_node = $item->{node};
3414 wakaba 1.1
3415     ## ISSUE: What is the definition for "successfully extracts a date
3416     ## or time"? If the algorithm says the string is invalid but
3417     ## return some date or time, is it "successfully"?
3418     }
3419    
3420     my $hour;
3421     my $minute;
3422     my $second;
3423     if ($input =~ /
3424     \A
3425 wakaba 1.112 $reg_sp
3426 wakaba 1.1 ([0-9]+) # 1
3427     (?>
3428     -([0-9]+) # 2
3429 wakaba 1.112 -((?>[0-9]+)) # 3 # Use (?>) such that yyyy-mm-ddhh:mm does not match
3430     $reg_sp
3431 wakaba 1.1 (?>
3432     T
3433 wakaba 1.112 $reg_sp
3434 wakaba 1.1 )?
3435     ([0-9]+) # 4
3436     :([0-9]+) # 5
3437     (?>
3438     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
3439     )?
3440 wakaba 1.112 $reg_sp
3441 wakaba 1.1 (?>
3442     Z
3443 wakaba 1.112 $reg_sp
3444 wakaba 1.1 |
3445     [+-]([0-9]+):([0-9]+) # 7, 8
3446 wakaba 1.112 $reg_sp
3447 wakaba 1.1 )?
3448     \z
3449     |
3450     :([0-9]+) # 9
3451     (?>
3452     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
3453     )?
3454 wakaba 1.112 $reg_sp
3455     \z
3456 wakaba 1.1 )
3457     /x) {
3458     if (defined $2) { ## YYYY-MM-DD T? hh:mm
3459     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
3460     length $4 != 2 or length $5 != 2) {
3461     $self->{onerror}->(node => $input_node,
3462 wakaba 1.104 type => 'dateortime:syntax error',
3463     level => $self->{level}->{must});
3464 wakaba 1.1 }
3465    
3466     if (1 <= $2 and $2 <= 12) {
3467 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3468     level => $self->{level}->{must})
3469 wakaba 1.1 if $3 < 1 or
3470     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
3471 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3472     level => $self->{level}->{must})
3473 wakaba 1.1 if $2 == 2 and $3 == 29 and
3474     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
3475     } else {
3476     $self->{onerror}->(node => $input_node,
3477 wakaba 1.104 type => 'datetime:bad month',
3478     level => $self->{level}->{must});
3479 wakaba 1.1 }
3480    
3481     ($hour, $minute, $second) = ($4, $5, $6);
3482    
3483     if (defined $7) { ## [+-]hh:mm
3484     if (length $7 != 2 or length $8 != 2) {
3485     $self->{onerror}->(node => $input_node,
3486 wakaba 1.104 type => 'dateortime:syntax error',
3487     level => $self->{level}->{must});
3488 wakaba 1.1 }
3489    
3490     $self->{onerror}->(node => $input_node,
3491 wakaba 1.104 type => 'datetime:bad timezone hour',
3492     level => $self->{level}->{must})
3493 wakaba 1.1 if $7 > 23;
3494     $self->{onerror}->(node => $input_node,
3495 wakaba 1.104 type => 'datetime:bad timezone minute',
3496     level => $self->{level}->{must})
3497 wakaba 1.1 if $8 > 59;
3498     }
3499     } else { ## hh:mm
3500     if (length $1 != 2 or length $9 != 2) {
3501     $self->{onerror}->(node => $input_node,
3502 wakaba 1.104 type => qq'dateortime:syntax error',
3503     level => $self->{level}->{must});
3504 wakaba 1.1 }
3505    
3506     ($hour, $minute, $second) = ($1, $9, $10);
3507     }
3508    
3509 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad hour',
3510     level => $self->{level}->{must}) if $hour > 23;
3511     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute',
3512     level => $self->{level}->{must}) if $minute > 59;
3513 wakaba 1.1
3514     if (defined $second) { ## s
3515     ## NOTE: Integer part of second don't have to have length of two.
3516    
3517     if (substr ($second, 0, 1) eq '.') {
3518     $self->{onerror}->(node => $input_node,
3519 wakaba 1.104 type => 'dateortime:syntax error',
3520     level => $self->{level}->{must});
3521 wakaba 1.1 }
3522    
3523 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad second',
3524     level => $self->{level}->{must}) if $second >= 60;
3525 wakaba 1.1 }
3526     } else {
3527     $self->{onerror}->(node => $input_node,
3528 wakaba 1.104 type => 'dateortime:syntax error',
3529     level => $self->{level}->{must});
3530 wakaba 1.1 }
3531    
3532 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3533 wakaba 1.1 },
3534     };
3535    
3536     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
3537 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
3538 wakaba 1.113 ## TODO: content checking
3539     ## TODO: content or value must contain number (rev 2053)
3540 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3541 wakaba 1.40 %HTMLPhrasingContentChecker,
3542     check_attrs => $GetHTMLAttrsChecker->({
3543 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3544     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3545     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3546     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3547     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3548     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3549 wakaba 1.50 }, {
3550     %HTMLAttrStatus,
3551     high => FEATURE_HTML5_DEFAULT,
3552     low => FEATURE_HTML5_DEFAULT,
3553     max => FEATURE_HTML5_DEFAULT,
3554     min => FEATURE_HTML5_DEFAULT,
3555     optimum => FEATURE_HTML5_DEFAULT,
3556     value => FEATURE_HTML5_DEFAULT,
3557 wakaba 1.1 }),
3558     };
3559    
3560     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3561 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3562 wakaba 1.40 %HTMLPhrasingContentChecker,
3563     check_attrs => $GetHTMLAttrsChecker->({
3564 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3565     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3566 wakaba 1.50 }, {
3567     %HTMLAttrStatus,
3568     max => FEATURE_HTML5_DEFAULT,
3569     value => FEATURE_HTML5_DEFAULT,
3570 wakaba 1.1 }),
3571     };
3572    
3573     $Element->{$HTML_NS}->{code} = {
3574 wakaba 1.40 %HTMLPhrasingContentChecker,
3575 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3576 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3577     %HTMLAttrStatus,
3578 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3579 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3580 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3581 wakaba 1.49 }),
3582 wakaba 1.1 };
3583    
3584     $Element->{$HTML_NS}->{var} = {
3585 wakaba 1.40 %HTMLPhrasingContentChecker,
3586 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3587 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3588     %HTMLAttrStatus,
3589 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3590 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3591 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3592 wakaba 1.49 }),
3593 wakaba 1.1 };
3594    
3595     $Element->{$HTML_NS}->{samp} = {
3596 wakaba 1.40 %HTMLPhrasingContentChecker,
3597 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3598 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3599     %HTMLAttrStatus,
3600 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3601 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3602 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3603 wakaba 1.49 }),
3604 wakaba 1.1 };
3605    
3606     $Element->{$HTML_NS}->{kbd} = {
3607 wakaba 1.40 %HTMLPhrasingContentChecker,
3608 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3609 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3610     %HTMLAttrStatus,
3611 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3612 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3613 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3614 wakaba 1.49 }),
3615 wakaba 1.1 };
3616    
3617     $Element->{$HTML_NS}->{sub} = {
3618 wakaba 1.40 %HTMLPhrasingContentChecker,
3619 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3620 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3621     %HTMLAttrStatus,
3622 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3623 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3624 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3625 wakaba 1.49 }),
3626 wakaba 1.1 };
3627    
3628 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3629 wakaba 1.1
3630     $Element->{$HTML_NS}->{span} = {
3631 wakaba 1.40 %HTMLPhrasingContentChecker,
3632 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3633 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3634     %HTMLAttrStatus,
3635 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3636 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3637     dataformatas => FEATURE_HTML4_REC_RESERVED,
3638     datasrc => FEATURE_HTML4_REC_RESERVED,
3639 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3640 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3641 wakaba 1.49 }),
3642 wakaba 1.1 };
3643    
3644     $Element->{$HTML_NS}->{i} = {
3645 wakaba 1.40 %HTMLPhrasingContentChecker,
3646 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3647     check_attrs => $GetHTMLAttrsChecker->({}, {
3648     %HTMLAttrStatus,
3649     %HTMLM12NCommonAttrStatus,
3650 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3651 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3652 wakaba 1.49 }),
3653 wakaba 1.1 };
3654    
3655 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3656    
3657 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3658     %HTMLPhrasingContentChecker,
3659     status => FEATURE_M12N10_REC,
3660     check_attrs => $GetHTMLAttrsChecker->({}, {
3661     %HTMLAttrStatus,
3662     %HTMLM12NCommonAttrStatus,
3663 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3664 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3665     }),
3666     };
3667 wakaba 1.51
3668     $Element->{$HTML_NS}->{s} = {
3669 wakaba 1.40 %HTMLPhrasingContentChecker,
3670 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3671 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3672     %HTMLAttrStatus,
3673     %HTMLM12NCommonAttrStatus,
3674 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3675 wakaba 1.49 }),
3676 wakaba 1.1 };
3677    
3678 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3679    
3680     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3681    
3682 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3683 wakaba 1.40 %HTMLPhrasingContentChecker,
3684 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3685 wakaba 1.40 check_attrs => sub {
3686     my ($self, $item, $element_state) = @_;
3687 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3688     %HTMLAttrStatus,
3689 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3690     dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3691     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3692     style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3693     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3694     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3695 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3696     sdasuff => FEATURE_HTML2X_RFC,
3697 wakaba 1.49 })->($self, $item, $element_state);
3698 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3699     $self->{onerror}->(node => $item->{node},
3700 wakaba 1.104 type => 'attribute missing',
3701     text => 'dir',
3702     level => $self->{level}->{must});
3703 wakaba 1.1 }
3704     },
3705     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3706     };
3707    
3708 wakaba 1.99 $Element->{$HTML_NS}->{ruby} = {
3709     %HTMLPhrasingContentChecker,
3710     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3711     check_attrs => $GetHTMLAttrsChecker->({}, {
3712     %HTMLAttrStatus,
3713     %HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0
3714 wakaba 1.153 lang => FEATURE_HTML5_WD,
3715 wakaba 1.99 }),
3716     check_start => sub {
3717     my ($self, $item, $element_state) = @_;
3718    
3719     $element_state->{phase} = 'before-rb';
3720     #$element_state->{has_sig}
3721 wakaba 1.100
3722     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3723     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3724 wakaba 1.99 },
3725     ## NOTE: (phrasing, (rt | (rp, rt, rp)))+
3726     check_child_element => sub {
3727     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3728     $child_is_transparent, $element_state) = @_;
3729 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
3730     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
3731 wakaba 1.99 $self->{onerror}->(node => $child_el,
3732     type => 'element not allowed:minus',
3733 wakaba 1.104 level => $self->{level}->{must});
3734 wakaba 1.99 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3735     #
3736     } elsif ($element_state->{phase} eq 'before-rb') {
3737     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3738     $element_state->{phase} = 'in-rb';
3739     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3740     $self->{onerror}->(node => $child_el,
3741 wakaba 1.104 level => $self->{level}->{should},
3742     type => 'no significant content before');
3743 wakaba 1.99 $element_state->{phase} = 'after-rt';
3744     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3745     $self->{onerror}->(node => $child_el,
3746 wakaba 1.104 level => $self->{level}->{should},
3747     type => 'no significant content before');
3748 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3749     } else {
3750     $self->{onerror}->(node => $child_el,
3751 wakaba 1.104 type => 'element not allowed:ruby base',
3752     level => $self->{level}->{must});
3753 wakaba 1.99 $element_state->{phase} = 'in-rb';
3754     }
3755     } elsif ($element_state->{phase} eq 'in-rb') {
3756     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3757     #$element_state->{phase} = 'in-rb';
3758     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3759     unless ($element_state->{has_significant}) {
3760     $self->{onerror}->(node => $child_el,
3761 wakaba 1.104 level => $self->{level}->{should},
3762     type => 'no significant content before');
3763 wakaba 1.99 }
3764     $element_state->{phase} = 'after-rt';
3765     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3766     unless ($element_state->{has_significant}) {
3767     $self->{onerror}->(node => $child_el,
3768 wakaba 1.104 level => $self->{level}->{should},
3769     type => 'no significant content before');
3770 wakaba 1.99 }
3771     $element_state->{phase} = 'after-rp1';
3772     } else {
3773     $self->{onerror}->(node => $child_el,
3774 wakaba 1.104 type => 'element not allowed:ruby base',
3775     level => $self->{level}->{must});
3776 wakaba 1.99 #$element_state->{phase} = 'in-rb';
3777     }
3778     } elsif ($element_state->{phase} eq 'after-rt') {
3779     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3780     if ($element_state->{has_significant}) {
3781     $element_state->{has_sig} = 1;
3782     delete $element_state->{has_significant};
3783     }
3784     $element_state->{phase} = 'in-rb';
3785     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3786     $self->{onerror}->(node => $child_el,
3787 wakaba 1.104 level => $self->{level}->{should},
3788     type => 'no significant content before');
3789 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3790     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3791     $self->{onerror}->(node => $child_el,
3792 wakaba 1.104 level => $self->{level}->{should},
3793     type => 'no significant content before');
3794 wakaba 1.99 #$element_state->{phase} = 'after-rt';
3795     } else {
3796     $self->{onerror}->(node => $child_el,
3797 wakaba 1.104 type => 'element not allowed:ruby base',
3798     level => $self->{level}->{must});
3799 wakaba 1.99 if ($element_state->{has_significant}) {
3800     $element_state->{has_sig} = 1;
3801     delete $element_state->{has_significant};
3802     }
3803     $element_state->{phase} = 'in-rb';
3804     }
3805     } elsif ($element_state->{phase} eq 'after-rp1') {
3806     if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3807     $element_state->{phase} = 'after-rp-rt';
3808     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3809     $self->{onerror}->(node => $child_el,
3810 wakaba 1.104 type => 'ps element missing',
3811     text => 'rt',
3812     level => $self->{level}->{must});
3813 wakaba 1.99 $element_state->{phase} = 'after-rp2';
3814     } else {
3815     $self->{onerror}->(node => $child_el,
3816 wakaba 1.104 type => 'ps element missing',
3817     text => 'rt',
3818     level => $self->{level}->{must});
3819 wakaba 1.99 $self->{onerror}->(node => $child_el,
3820 wakaba 1.104 type => 'ps element missing',
3821     text => 'rp',
3822     level => $self->{level}->{must});
3823 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3824     $self->{onerror}->(node => $child_el,
3825 wakaba 1.104 type => 'element not allowed:ruby base',
3826     level => $self->{level}->{must});
3827 wakaba 1.99 }
3828     if ($element_state->{has_significant}) {
3829     $element_state->{has_sig} = 1;
3830     delete $element_state->{has_significant};
3831     }
3832     $element_state->{phase} = 'in-rb';
3833     }
3834     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3835     if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3836     $element_state->{phase} = 'after-rp2';
3837     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3838     $self->{onerror}->(node => $child_el,
3839 wakaba 1.104 type => 'ps element missing',
3840     text => 'rp',
3841     level => $self->{level}->{must});
3842 wakaba 1.99 $self->{onerror}->(node => $child_el,
3843 wakaba 1.104 level => $self->{level}->{should},
3844     type => 'no significant content before');
3845 wakaba 1.99 $element_state->{phase} = 'after-rt';
3846     } else {
3847     $self->{onerror}->(node => $child_el,
3848 wakaba 1.104 type => 'ps element missing',
3849     text => 'rp',
3850     level => $self->{level}->{must});
3851 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3852     $self->{onerror}->(node => $child_el,
3853 wakaba 1.104 type => 'element not allowed:ruby base',
3854     level => $self->{level}->{must});
3855 wakaba 1.99 }
3856     if ($element_state->{has_significant}) {
3857     $element_state->{has_sig} = 1;
3858     delete $element_state->{has_significant};
3859     }
3860     $element_state->{phase} = 'in-rb';
3861     }
3862     } elsif ($element_state->{phase} eq 'after-rp2') {
3863     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3864     if ($element_state->{has_significant}) {
3865     $element_state->{has_sig} = 1;
3866     delete $element_state->{has_significant};
3867     }
3868     $element_state->{phase} = 'in-rb';
3869     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3870     $self->{onerror}->(node => $child_el,
3871 wakaba 1.104 level => $self->{level}->{should},
3872     type => 'no significant content before');
3873 wakaba 1.99 $element_state->{phase} = 'after-rt';
3874     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3875     $self->{onerror}->(node => $child_el,
3876 wakaba 1.104 level => $self->{level}->{should},
3877     type => 'no significant content before');
3878 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3879     } else {
3880     $self->{onerror}->(node => $child_el,
3881 wakaba 1.104 type => 'element not allowed:ruby base',
3882     level => $self->{level}->{must});
3883 wakaba 1.99 if ($element_state->{has_significant}) {
3884     $element_state->{has_sig} = 1;
3885     delete $element_state->{has_significant};
3886     }
3887     $element_state->{phase} = 'in-rb';
3888     }
3889     } else {
3890     die "check_child_element: Bad |ruby| phase: $element_state->{phase}";
3891     }
3892     },
3893     check_child_text => sub {
3894     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3895     if ($has_significant) {
3896     if ($element_state->{phase} eq 'before-rb') {
3897     $element_state->{phase} = 'in-rb';
3898     } elsif ($element_state->{phase} eq 'in-rb') {
3899     #
3900     } elsif ($element_state->{phase} eq 'after-rt' or
3901     $element_state->{phase} eq 'after-rp2') {
3902     $element_state->{phase} = 'in-rb';
3903     } elsif ($element_state->{phase} eq 'after-rp1') {
3904     $self->{onerror}->(node => $child_node,
3905 wakaba 1.104 type => 'ps element missing',
3906     text => 'rt',
3907     level => $self->{level}->{must});
3908 wakaba 1.99 $self->{onerror}->(node => $child_node,
3909 wakaba 1.104 type => 'ps element missing',
3910     text => 'rp',
3911     level => $self->{level}->{must});
3912 wakaba 1.99 $element_state->{phase} = 'in-rb';
3913     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3914     $self->{onerror}->(node => $child_node,
3915 wakaba 1.104 type => 'ps element missing',
3916     text => 'rp',
3917     level => $self->{level}->{must});
3918 wakaba 1.99 $element_state->{phase} = 'in-rb';
3919     } else {
3920     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3921     }
3922     }
3923     },
3924     check_end => sub {
3925     my ($self, $item, $element_state) = @_;
3926     $self->_remove_minus_elements ($element_state);
3927    
3928     if ($element_state->{phase} eq 'before-rb') {
3929     $self->{onerror}->(node => $item->{node},
3930 wakaba 1.104 level => $self->{level}->{should},
3931 wakaba 1.99 type => 'no significant content');
3932     $self->{onerror}->(node => $item->{node},
3933 wakaba 1.104 type => 'element missing',
3934     text => 'rt',
3935     level => $self->{level}->{must});
3936 wakaba 1.99 } elsif ($element_state->{phase} eq 'in-rb') {
3937     unless ($element_state->{has_significant}) {
3938     $self->{onerror}->(node => $item->{node},
3939 wakaba 1.104 level => $self->{level}->{should},
3940     type => 'no significant content at the end');
3941 wakaba 1.99 }
3942     $self->{onerror}->(node => $item->{node},
3943 wakaba 1.104 type => 'element missing',
3944     text => 'rt',
3945     level => $self->{level}->{must});
3946 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rt' or
3947     $element_state->{phase} eq 'after-rp2') {
3948     #
3949     } elsif ($element_state->{phase} eq 'after-rp1') {
3950     $self->{onerror}->(node => $item->{node},
3951 wakaba 1.104 type => 'element missing',
3952     text => 'rt',
3953     level => $self->{level}->{must});
3954 wakaba 1.99 $self->{onerror}->(node => $item->{node},
3955 wakaba 1.104 type => 'element missing',
3956     text => 'rp',
3957     level => $self->{level}->{must});
3958 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rp-rt') {
3959     $self->{onerror}->(node => $item->{node},
3960 wakaba 1.104 type => 'element missing',
3961     text => 'rp',
3962     level => $self->{level}->{must});
3963 wakaba 1.99 } else {
3964     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3965     }
3966    
3967     ## NOTE: A modified version of |check_end| of %AnyChecker.
3968     if ($element_state->{has_significant} or $element_state->{has_sig}) {
3969     $item->{real_parent_state}->{has_significant} = 1;
3970     }
3971     },
3972     };
3973    
3974     $Element->{$HTML_NS}->{rt} = {
3975     %HTMLPhrasingContentChecker,
3976     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3977     check_attrs => $GetHTMLAttrsChecker->({}, {
3978     %HTMLAttrStatus,
3979     %HTMLM12NXHTML2CommonAttrStatus,
3980 wakaba 1.153 lang => FEATURE_HTML5_WD,
3981 wakaba 1.99 }),
3982     };
3983    
3984     $Element->{$HTML_NS}->{rp} = {
3985 wakaba 1.171 %HTMLPhrasingContentChecker,
3986 wakaba 1.99 status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3987     check_attrs => $GetHTMLAttrsChecker->({}, {
3988     %HTMLAttrStatus,
3989     %HTMLM12NXHTML2CommonAttrStatus,
3990 wakaba 1.153 lang => FEATURE_HTML5_WD,
3991 wakaba 1.99 }),
3992 wakaba 1.171 }; # rp
3993 wakaba 1.99
3994 wakaba 1.29 =pod
3995    
3996     ## TODO:
3997    
3998     +
3999     + <p>Partly because of the confusion described above, authors are
4000     + strongly recommended to always mark up all paragraphs with the
4001     + <code>p</code> element, and to not have any <code>ins</code> or
4002     + <code>del</code> elements that cross across any <span
4003     + title="paragraph">implied paragraphs</span>.</p>
4004     +
4005     (An informative note)
4006    
4007     <p><code>ins</code> elements should not cross <span
4008     + title="paragraph">implied paragraph</span> boundaries.</p>
4009     (normative)
4010    
4011     + <p><code>del</code> elements should not cross <span
4012     + title="paragraph">implied paragraph</span> boundaries.</p>
4013     (normative)
4014    
4015     =cut
4016    
4017 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
4018 wakaba 1.40 %HTMLTransparentChecker,
4019 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4020 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4021 wakaba 1.1 cite => $HTMLURIAttrChecker,
4022 wakaba 1.168 datetime => $GetDateTimeAttrChecker->('global_date_and_time_string'),
4023 wakaba 1.49 }, {
4024     %HTMLAttrStatus,
4025     %HTMLM12NCommonAttrStatus,
4026 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4027 wakaba 1.153 datetime => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4028     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4029 wakaba 1.1 }),
4030 wakaba 1.66 check_start => sub {
4031     my ($self, $item, $element_state) = @_;
4032    
4033     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
4034 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4035     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4036 wakaba 1.66 },
4037 wakaba 1.1 };
4038    
4039     $Element->{$HTML_NS}->{del} = {
4040 wakaba 1.40 %HTMLTransparentChecker,
4041 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4042 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4043 wakaba 1.1 cite => $HTMLURIAttrChecker,
4044 wakaba 1.168 datetime => $GetDateTimeAttrChecker->('global_date_and_time_string'),
4045 wakaba 1.49 }, {
4046     %HTMLAttrStatus,
4047     %HTMLM12NCommonAttrStatus,
4048 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4049 wakaba 1.153 datetime => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4050     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4051 wakaba 1.1 }),
4052 wakaba 1.40 check_end => sub {
4053     my ($self, $item, $element_state) = @_;
4054     if ($element_state->{has_significant}) {
4055     ## NOTE: Significantness flag does not propagate.
4056     } elsif ($item->{transparent}) {
4057     #
4058     } else {
4059     $self->{onerror}->(node => $item->{node},
4060 wakaba 1.104 level => $self->{level}->{should},
4061 wakaba 1.40 type => 'no significant content');
4062     }
4063 wakaba 1.1 },
4064 wakaba 1.66 check_start => sub {
4065     my ($self, $item, $element_state) = @_;
4066    
4067     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
4068 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4069     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4070 wakaba 1.66 },
4071 wakaba 1.1 };
4072    
4073 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
4074 wakaba 1.72 %HTMLFlowContentChecker,
4075 wakaba 1.153 status => FEATURE_HTML5_WD,
4076 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
4077 wakaba 1.41 check_child_element => sub {
4078     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4079     $child_is_transparent, $element_state) = @_;
4080 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4081     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4082 wakaba 1.41 $self->{onerror}->(node => $child_el,
4083     type => 'element not allowed:minus',
4084 wakaba 1.104 level => $self->{level}->{must});
4085 wakaba 1.41 $element_state->{has_non_legend} = 1;
4086     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4087     #
4088     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
4089     if ($element_state->{has_legend_at_first}) {
4090     $self->{onerror}->(node => $child_el,
4091     type => 'element not allowed:figure legend',
4092 wakaba 1.104 level => $self->{level}->{must});
4093 wakaba 1.41 } elsif ($element_state->{has_legend}) {
4094     $self->{onerror}->(node => $element_state->{has_legend},
4095     type => 'element not allowed:figure legend',
4096 wakaba 1.104 level => $self->{level}->{must});
4097 wakaba 1.41 $element_state->{has_legend} = $child_el;
4098     } elsif ($element_state->{has_non_legend}) {
4099     $element_state->{has_legend} = $child_el;
4100     } else {
4101     $element_state->{has_legend_at_first} = 1;
4102 wakaba 1.35 }
4103 wakaba 1.41 delete $element_state->{has_non_legend};
4104     } else {
4105 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4106 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
4107 wakaba 1.41 }
4108     },
4109     check_child_text => sub {
4110     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4111     if ($has_significant) {
4112     $element_state->{has_non_legend} = 1;
4113 wakaba 1.35 }
4114 wakaba 1.170
4115     $element_state->{in_figure} = 1;
4116 wakaba 1.41 },
4117     check_end => sub {
4118     my ($self, $item, $element_state) = @_;
4119 wakaba 1.35
4120 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
4121     #
4122     } elsif ($element_state->{has_legend}) {
4123     if ($element_state->{has_non_legend}) {
4124     $self->{onerror}->(node => $element_state->{has_legend},
4125 wakaba 1.35 type => 'element not allowed:figure legend',
4126 wakaba 1.104 level => $self->{level}->{must});
4127 wakaba 1.35 }
4128     }
4129 wakaba 1.41
4130 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4131 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
4132 wakaba 1.35 },
4133     };
4134 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
4135 wakaba 1.1
4136 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
4137     my ($self, $attr) = @_;
4138 wakaba 1.104 $self->{onerror}->(node => $attr,
4139     type => 'unknown attribute',
4140     level => $self->{level}->{uncertain});
4141 wakaba 1.92 };
4142    
4143 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
4144 wakaba 1.40 %HTMLEmptyChecker,
4145 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4146 wakaba 1.40 check_attrs => sub {
4147     my ($self, $item, $element_state) = @_;
4148 wakaba 1.1 $GetHTMLAttrsChecker->({
4149 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4150     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4151     }),
4152 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
4153 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4154 wakaba 1.1 src => $HTMLURIAttrChecker,
4155     usemap => $HTMLUsemapAttrChecker,
4156 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4157 wakaba 1.1 ismap => sub {
4158 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
4159     if (not $self->{flag}->{in_a_href}) {
4160 wakaba 1.15 $self->{onerror}->(node => $attr,
4161 wakaba 1.59 type => 'attribute not allowed:ismap',
4162 wakaba 1.104 level => $self->{level}->{must});
4163 wakaba 1.1 }
4164 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
4165 wakaba 1.1 },
4166 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
4167     ## TODO: HTML4 |name|
4168 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4169 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4170 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4171 wakaba 1.49 }, {
4172     %HTMLAttrStatus,
4173 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4174 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4175 wakaba 1.153 alt => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4176 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
4177 wakaba 1.153 height => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4178 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
4179 wakaba 1.153 ismap => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4180     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4181 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
4182     name => FEATURE_M12N10_REC_DEPRECATED,
4183 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4184 wakaba 1.153 src => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4185     usemap => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4186 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
4187 wakaba 1.153 width => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4188 wakaba 1.66 })->($self, $item, $element_state);
4189 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
4190     $self->{onerror}->(node => $item->{node},
4191 wakaba 1.104 type => 'attribute missing',
4192     text => 'alt',
4193     level => $self->{level}->{should});
4194 wakaba 1.114 ## TODO: ...
4195 wakaba 1.1 }
4196 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4197     $self->{onerror}->(node => $item->{node},
4198 wakaba 1.104 type => 'attribute missing',
4199     text => 'src',
4200     level => $self->{level}->{must});
4201 wakaba 1.1 }
4202 wakaba 1.66
4203 wakaba 1.114 ## TODO: external resource check
4204    
4205 wakaba 1.66 $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4206     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
4207     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
4208     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
4209 wakaba 1.1 },
4210     };
4211    
4212     $Element->{$HTML_NS}->{iframe} = {
4213 wakaba 1.40 %HTMLTextChecker,
4214 wakaba 1.114 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4215 wakaba 1.49 ## NOTE: Not part of M12N10 Strict
4216 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4217 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4218 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4219 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
4220     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
4221     }),
4222     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
4223 wakaba 1.1 src => $HTMLURIAttrChecker,
4224 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4225 wakaba 1.49 }, {
4226     %HTMLAttrStatus,
4227     %HTMLM12NCommonAttrStatus,
4228     align => FEATURE_XHTML10_REC,
4229 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4230 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
4231 wakaba 1.153 height => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4232     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4233 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
4234     marginheight => FEATURE_M12N10_REC,
4235     marginwidth => FEATURE_M12N10_REC,
4236 wakaba 1.114 #name => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
4237     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4238     sandbox => FEATURE_HTML5_WD,
4239 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
4240 wakaba 1.114 seemless => FEATURE_HTML5_WD,
4241     src => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4242 wakaba 1.153 title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4243     width => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4244 wakaba 1.1 }),
4245 wakaba 1.66 check_start => sub {
4246     my ($self, $item, $element_state) = @_;
4247    
4248     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4249 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4250     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4251 wakaba 1.66 },
4252 wakaba 1.40 };
4253    
4254 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
4255 wakaba 1.40 %HTMLEmptyChecker,
4256 wakaba 1.98 status => FEATURE_HTML5_WD,
4257 wakaba 1.40 check_attrs => sub {
4258     my ($self, $item, $element_state) = @_;
4259 wakaba 1.1 my $has_src;
4260 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4261 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4262     $attr_ns = '' unless defined $attr_ns;
4263     my $attr_ln = $attr->manakai_local_name;
4264     my $checker;
4265 wakaba 1.73
4266     my $status = {
4267     %HTMLAttrStatus,
4268 wakaba 1.153 height => FEATURE_HTML5_LC,
4269 wakaba 1.98 src => FEATURE_HTML5_WD,
4270     type => FEATURE_HTML5_WD,
4271 wakaba 1.153 width => FEATURE_HTML5_LC,
4272 wakaba 1.73 }->{$attr_ln};
4273    
4274 wakaba 1.1 if ($attr_ns eq '') {
4275     if ($attr_ln eq 'src') {
4276     $checker = $HTMLURIAttrChecker;
4277     $has_src = 1;
4278     } elsif ($attr_ln eq 'type') {
4279     $checker = $HTMLIMTAttrChecker;
4280 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
4281 wakaba 1.178 $checker = $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 });
4282 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4283     $attr_ln !~ /[A-Z]/) {
4284 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4285     $status = $HTMLDatasetAttrStatus;
4286 wakaba 1.117 } elsif ($attr_ln !~ /^[Xx][Mm][Ll]/ and
4287 wakaba 1.118 $attr_ln !~ /[A-Z]/ and
4288 wakaba 1.117 $attr_ln =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
4289 wakaba 1.1 $checker = $HTMLAttrChecker->{$attr_ln}
4290     || sub { }; ## NOTE: Any local attribute is ok.
4291 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
4292 wakaba 1.117 } else {
4293     $checker = $HTMLAttrChecker->{$attr_ln};
4294 wakaba 1.1 }
4295     }
4296     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4297 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4298     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4299     || $AttrStatus->{$attr_ns}->{''};
4300     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4301 wakaba 1.62
4302 wakaba 1.1 if ($checker) {
4303 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
4304 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4305 wakaba 1.54 #
4306 wakaba 1.1 } else {
4307 wakaba 1.104 $self->{onerror}->(node => $attr,
4308     type => 'unknown attribute',
4309     level => $self->{level}->{uncertain});
4310 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
4311     }
4312    
4313 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4314 wakaba 1.1 }
4315    
4316     unless ($has_src) {
4317 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4318 wakaba 1.104 type => 'attribute missing',
4319     text => 'src',
4320 wakaba 1.114 level => $self->{level}->{info});
4321     ## NOTE: <embed> without src="" is allowed since revision 1929.
4322     ## We issues an informational message since <embed> w/o src=""
4323     ## is likely an authoring error.
4324 wakaba 1.1 }
4325 wakaba 1.114
4326     ## TODO: external resource check
4327 wakaba 1.66
4328     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4329 wakaba 1.1 },
4330     };
4331    
4332 wakaba 1.49 ## TODO:
4333     ## {applet} FEATURE_M12N10_REC_DEPRECATED
4334     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
4335    
4336 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
4337 wakaba 1.40 %HTMLTransparentChecker,
4338 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4339 wakaba 1.40 check_attrs => sub {
4340     my ($self, $item, $element_state) = @_;
4341 wakaba 1.1 $GetHTMLAttrsChecker->({
4342 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4343     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4344     }),
4345     archive => $HTMLSpaceURIsAttrChecker,
4346     ## TODO: Relative to @codebase
4347     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4348     classid => $HTMLURIAttrChecker,
4349     codebase => $HTMLURIAttrChecker,
4350     codetype => $HTMLIMTAttrChecker,
4351     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
4352 wakaba 1.1 data => $HTMLURIAttrChecker,
4353 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
4354     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
4355     ## [HTML4] but we don't know how to test this.
4356 wakaba 1.167 form => $HTMLFormAttrChecker,
4357 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4358 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4359 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4360 wakaba 1.167 ## NOTE: |name| attribute of the |object| element defines
4361     ## the name of the browsing context created by the element,
4362     ## if any, but is also used as the form control name of the
4363     ## form control provided by the plugin, if any.
4364 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
4365 wakaba 1.1 type => $HTMLIMTAttrChecker,
4366     usemap => $HTMLUsemapAttrChecker,
4367 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4368 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4369 wakaba 1.49 }, {
4370     %HTMLAttrStatus,
4371 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4372 wakaba 1.49 align => FEATURE_XHTML10_REC,
4373 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4374 wakaba 1.49 border => FEATURE_XHTML10_REC,
4375     classid => FEATURE_M12N10_REC,
4376     codebase => FEATURE_M12N10_REC,
4377     codetype => FEATURE_M12N10_REC,
4378 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
4379 wakaba 1.153 data => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4380 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4381     dataformatas => FEATURE_HTML4_REC_RESERVED,
4382     datasrc => FEATURE_HTML4_REC_RESERVED,
4383 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4384 wakaba 1.167 form => FEATURE_HTML5_DEFAULT,
4385 wakaba 1.153 height => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4386 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
4387 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4388     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4389 wakaba 1.49 standby => FEATURE_M12N10_REC,
4390 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4391 wakaba 1.153 type => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4392     usemap => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4393 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
4394 wakaba 1.153 width => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4395 wakaba 1.66 })->($self, $item, $element_state);
4396 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
4397     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
4398     $self->{onerror}->(node => $item->{node},
4399 wakaba 1.104 type => 'attribute missing:data|type',
4400     level => $self->{level}->{must});
4401 wakaba 1.1 }
4402     }
4403 wakaba 1.66
4404     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
4405     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
4406     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
4407     ## TODO: archive
4408     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4409 wakaba 1.1 },
4410 wakaba 1.72 ## NOTE: param*, transparent (Flow)
4411 wakaba 1.41 check_child_element => sub {
4412     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4413     $child_is_transparent, $element_state) = @_;
4414 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4415     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4416 wakaba 1.41 $self->{onerror}->(node => $child_el,
4417     type => 'element not allowed:minus',
4418 wakaba 1.104 level => $self->{level}->{must});
4419 wakaba 1.41 $element_state->{has_non_legend} = 1;
4420     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4421     #
4422     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
4423     if ($element_state->{has_non_param}) {
4424 wakaba 1.104 $self->{onerror}->(node => $child_el,
4425 wakaba 1.72 type => 'element not allowed:flow',
4426 wakaba 1.104 level => $self->{level}->{must});
4427 wakaba 1.39 }
4428 wakaba 1.41 } else {
4429 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4430 wakaba 1.41 $element_state->{has_non_param} = 1;
4431 wakaba 1.39 }
4432 wakaba 1.25 },
4433 wakaba 1.41 check_child_text => sub {
4434     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4435     if ($has_significant) {
4436     $element_state->{has_non_param} = 1;
4437     }
4438 wakaba 1.42 },
4439     check_end => sub {
4440     my ($self, $item, $element_state) = @_;
4441     if ($element_state->{has_significant}) {
4442 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
4443 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
4444     ## NOTE: Transparent.
4445     } else {
4446     $self->{onerror}->(node => $item->{node},
4447 wakaba 1.104 level => $self->{level}->{should},
4448 wakaba 1.42 type => 'no significant content');
4449     }
4450     },
4451 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
4452 wakaba 1.1 };
4453 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4454     ## What about |<section><object data><style scoped></style>x</object></section>|?
4455     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4456 wakaba 1.1
4457     $Element->{$HTML_NS}->{param} = {
4458 wakaba 1.40 %HTMLEmptyChecker,
4459 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4460 wakaba 1.40 check_attrs => sub {
4461     my ($self, $item, $element_state) = @_;
4462 wakaba 1.1 $GetHTMLAttrsChecker->({
4463     name => sub { },
4464 wakaba 1.70 type => $HTMLIMTAttrChecker,
4465 wakaba 1.1 value => sub { },
4466 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4467     data => 1, ref => 1, object => 1,
4468     }),
4469 wakaba 1.49 }, {
4470     %HTMLAttrStatus,
4471 wakaba 1.154 href => FEATURE_RDFA_REC,
4472 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4473     name => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4474 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4475 wakaba 1.153 value => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4476 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4477 wakaba 1.66 })->(@_);
4478 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4479     $self->{onerror}->(node => $item->{node},
4480 wakaba 1.104 type => 'attribute missing',
4481     text => 'name',
4482     level => $self->{level}->{must});
4483 wakaba 1.1 }
4484 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4485     $self->{onerror}->(node => $item->{node},
4486 wakaba 1.104 type => 'attribute missing',
4487     text => 'value',
4488     level => $self->{level}->{must});
4489 wakaba 1.1 }
4490     },
4491     };
4492    
4493     $Element->{$HTML_NS}->{video} = {
4494 wakaba 1.40 %HTMLTransparentChecker,
4495 wakaba 1.48 status => FEATURE_HTML5_LC,
4496 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4497 wakaba 1.1 src => $HTMLURIAttrChecker,
4498     ## TODO: start, loopstart, loopend, end
4499     ## ISSUE: they MUST be "value time offset"s. Value?
4500 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4501 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4502     controls => $GetHTMLBooleanAttrChecker->('controls'),
4503 wakaba 1.59 poster => $HTMLURIAttrChecker,
4504 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4505     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4506 wakaba 1.50 }, {
4507     %HTMLAttrStatus,
4508     autoplay => FEATURE_HTML5_LC,
4509     controls => FEATURE_HTML5_LC,
4510 wakaba 1.153 end => FEATURE_HTML5_AT_RISK,
4511 wakaba 1.50 height => FEATURE_HTML5_LC,
4512 wakaba 1.153 loopend => FEATURE_HTML5_AT_RISK,
4513     loopstart => FEATURE_HTML5_AT_RISK,
4514     playcount => FEATURE_HTML5_AT_RISK,
4515 wakaba 1.50 poster => FEATURE_HTML5_LC,
4516     src => FEATURE_HTML5_LC,
4517 wakaba 1.153 start => FEATURE_HTML5_AT_RISK,
4518 wakaba 1.50 width => FEATURE_HTML5_LC,
4519 wakaba 1.1 }),
4520 wakaba 1.42 check_start => sub {
4521     my ($self, $item, $element_state) = @_;
4522     $element_state->{allow_source}
4523     = not $item->{node}->has_attribute_ns (undef, 'src');
4524     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4525     ## NOTE: It might be set true by |check_element|.
4526 wakaba 1.66
4527     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4528     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4529 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4530     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4531 wakaba 1.42 },
4532     check_child_element => sub {
4533     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4534     $child_is_transparent, $element_state) = @_;
4535 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4536     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4537 wakaba 1.42 $self->{onerror}->(node => $child_el,
4538     type => 'element not allowed:minus',
4539 wakaba 1.104 level => $self->{level}->{must});
4540 wakaba 1.42 delete $element_state->{allow_source};
4541     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4542     #
4543     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4544 wakaba 1.45 unless ($element_state->{allow_source}) {
4545 wakaba 1.104 $self->{onerror}->(node => $child_el,
4546 wakaba 1.72 type => 'element not allowed:flow',
4547 wakaba 1.104 level => $self->{level}->{must});
4548 wakaba 1.42 }
4549 wakaba 1.45 $element_state->{has_source} = 1;
4550 wakaba 1.1 } else {
4551 wakaba 1.42 delete $element_state->{allow_source};
4552 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4553 wakaba 1.42 }
4554     },
4555     check_child_text => sub {
4556     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4557     if ($has_significant) {
4558     delete $element_state->{allow_source};
4559     }
4560 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4561 wakaba 1.42 },
4562     check_end => sub {
4563     my ($self, $item, $element_state) = @_;
4564     if ($element_state->{has_source} == -1) {
4565     $self->{onerror}->(node => $item->{node},
4566 wakaba 1.104 type => 'child element missing',
4567     text => 'source',
4568     level => $self->{level}->{must});
4569 wakaba 1.1 }
4570 wakaba 1.42
4571     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4572 wakaba 1.1 },
4573     };
4574    
4575     $Element->{$HTML_NS}->{audio} = {
4576 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4577 wakaba 1.48 status => FEATURE_HTML5_LC,
4578 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4579     src => $HTMLURIAttrChecker,
4580     ## TODO: start, loopstart, loopend, end
4581     ## ISSUE: they MUST be "value time offset"s. Value?
4582     ## ISSUE: playcount has no conformance creteria
4583     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4584     controls => $GetHTMLBooleanAttrChecker->('controls'),
4585 wakaba 1.50 }, {
4586     %HTMLAttrStatus,
4587     autoplay => FEATURE_HTML5_LC,
4588     controls => FEATURE_HTML5_LC,
4589 wakaba 1.153 end => FEATURE_HTML5_AT_RISK,
4590     loopend => FEATURE_HTML5_AT_RISK,
4591     loopstart => FEATURE_HTML5_AT_RISK,
4592     playcount => FEATURE_HTML5_AT_RISK,
4593 wakaba 1.50 src => FEATURE_HTML5_LC,
4594 wakaba 1.153 start => FEATURE_HTML5_AT_RISK,
4595 wakaba 1.42 }),
4596 wakaba 1.1 };
4597    
4598     $Element->{$HTML_NS}->{source} = {
4599 wakaba 1.40 %HTMLEmptyChecker,
4600 wakaba 1.153 status => FEATURE_HTML5_LC,
4601 wakaba 1.40 check_attrs => sub {
4602     my ($self, $item, $element_state) = @_;
4603 wakaba 1.1 $GetHTMLAttrsChecker->({
4604 wakaba 1.90 media => $HTMLMQAttrChecker,
4605     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4606     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4607 wakaba 1.1 type => $HTMLIMTAttrChecker,
4608 wakaba 1.50 }, {
4609     %HTMLAttrStatus,
4610 wakaba 1.153 media => FEATURE_HTML5_LC,
4611     pixelratio => FEATURE_HTML5_LC,
4612     src => FEATURE_HTML5_LC,
4613     type => FEATURE_HTML5_LC,
4614 wakaba 1.66 })->(@_);
4615 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4616     $self->{onerror}->(node => $item->{node},
4617 wakaba 1.104 type => 'attribute missing',
4618     text => 'src',
4619     level => $self->{level}->{must});
4620 wakaba 1.1 }
4621 wakaba 1.66
4622     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4623 wakaba 1.1 },
4624     };
4625    
4626     $Element->{$HTML_NS}->{canvas} = {
4627 wakaba 1.40 %HTMLTransparentChecker,
4628 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4629 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4630 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4631     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4632 wakaba 1.50 }, {
4633     %HTMLAttrStatus,
4634 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4635     width => FEATURE_HTML5_COMPLETE,
4636 wakaba 1.1 }),
4637 wakaba 1.178
4638     # Authors MUST provide alternative content (HTML5 revision 2868) -
4639     # This requirement cannot be checked, since the alternative content
4640     # might be placed outside of the element.
4641     }; # canvas
4642 wakaba 1.1
4643     $Element->{$HTML_NS}->{map} = {
4644 wakaba 1.72 %HTMLFlowContentChecker,
4645 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4646 wakaba 1.40 check_attrs => sub {
4647     my ($self, $item, $element_state) = @_;
4648 wakaba 1.100 my $has_name;
4649 wakaba 1.4 $GetHTMLAttrsChecker->({
4650 wakaba 1.100 name => sub {
4651     my ($self, $attr) = @_;
4652     my $value = $attr->value;
4653     if (length $value) {
4654     ## NOTE: Duplication is not non-conforming.
4655     ## NOTE: Space characters are not non-conforming.
4656     #
4657     } else {
4658     $self->{onerror}->(node => $attr,
4659     type => 'empty attribute value',
4660 wakaba 1.104 level => $self->{level}->{must});
4661 wakaba 1.100 }
4662 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4663 wakaba 1.100 $has_name = [$value, $attr];
4664 wakaba 1.4 },
4665 wakaba 1.49 }, {
4666     %HTMLAttrStatus,
4667 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4668     dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4669     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4670     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4671     #name => FEATURE_HTML5_LC | FEATURE_M12N10_REC_DEPRECATED,
4672     name => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4673 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4674     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4675     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4676     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4677     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4678     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4679     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4680     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4681     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4682     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4683 wakaba 1.153 title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4684 wakaba 1.66 })->(@_);
4685 wakaba 1.100
4686 wakaba 1.135 if ($has_name) {
4687 wakaba 1.145 my $id = $item->{node}->get_attribute_ns (undef, 'id');
4688 wakaba 1.135 if (defined $id and $has_name->[0] ne $id) {
4689 wakaba 1.155 $self->{onerror}
4690     ->(node => $item->{node}->get_attribute_node_ns (undef, 'id'),
4691     type => 'id ne name',
4692     level => $self->{level}->{must});
4693 wakaba 1.100 }
4694 wakaba 1.135 } else {
4695 wakaba 1.100 $self->{onerror}->(node => $item->{node},
4696 wakaba 1.104 type => 'attribute missing',
4697     text => 'name',
4698     level => $self->{level}->{must});
4699 wakaba 1.100 }
4700 wakaba 1.4 },
4701 wakaba 1.59 check_start => sub {
4702     my ($self, $item, $element_state) = @_;
4703     $element_state->{in_map_original} = $self->{flag}->{in_map};
4704 wakaba 1.137 $self->{flag}->{in_map} = [@{$self->{flag}->{in_map} or []}, {}];
4705     ## NOTE: |{in_map}| is a reference to the array which contains
4706     ## hash references. Hashes are corresponding to the opening
4707     ## |map| elements and each of them contains the key-value
4708     ## pairs corresponding to the absolute URLs for the processed
4709     ## |area| elements in the |map| element corresponding to the
4710     ## hash. The key represents the resource (## TODO: use
4711     ## absolute URL), while the value represents whether there is
4712     ## an |area| element whose |alt| attribute is specified to a
4713     ## non-empty value. If there IS such an |area| element for
4714     ## the resource specified by the key, then the value is set to
4715     ## zero (|0|). Otherwise, if there is no such an |area|
4716     ## element but there is any |area| element with the empty
4717     ## |alt=""| attribute, then the value contains an array
4718     ## reference that contains all of such |area| elements.
4719 wakaba 1.79
4720     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4721     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4722 wakaba 1.59 },
4723     check_end => sub {
4724     my ($self, $item, $element_state) = @_;
4725 wakaba 1.137
4726     for (keys %{$self->{flag}->{in_map}->[-1]}) {
4727     my $nodes = $self->{flag}->{in_map}->[-1]->{$_};
4728     next unless $nodes;
4729     for (@$nodes) {
4730     $self->{onerror}->(type => 'empty area alt',
4731     node => $_,
4732     level => $self->{level}->{html5_no_may});
4733     }
4734     }
4735    
4736     $self->{flag}->{in_map} = $element_state->{in_map_original};
4737    
4738 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4739 wakaba 1.59 },
4740 wakaba 1.1 };
4741    
4742     $Element->{$HTML_NS}->{area} = {
4743 wakaba 1.40 %HTMLEmptyChecker,
4744 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4745 wakaba 1.40 check_attrs => sub {
4746     my ($self, $item, $element_state) = @_;
4747 wakaba 1.1 my %attr;
4748     my $coords;
4749 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4750 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4751     $attr_ns = '' unless defined $attr_ns;
4752     my $attr_ln = $attr->manakai_local_name;
4753     my $checker;
4754 wakaba 1.73 my $status;
4755 wakaba 1.1 if ($attr_ns eq '') {
4756 wakaba 1.73 $status = {
4757     %HTMLAttrStatus,
4758     %HTMLM12NCommonAttrStatus,
4759 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
4760 wakaba 1.153 alt => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4761     coords => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4762 wakaba 1.154 href => FEATURE_HTML5_WD | FEATURE_RDFA_REC | FEATURE_M12N10_REC,
4763 wakaba 1.153 hreflang => FEATURE_HTML5_WD,
4764     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4765 wakaba 1.154 media => FEATURE_HTML5_WD,
4766 wakaba 1.73 nohref => FEATURE_M12N10_REC,
4767     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4768     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4769 wakaba 1.153 ping => FEATURE_HTML5_WD,
4770 wakaba 1.154 rel => FEATURE_HTML5_WD | FEATURE_RDFA_REC,
4771 wakaba 1.153 shape => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4772 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4773 wakaba 1.153 target => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4774     type => FEATURE_HTML5_WD,
4775 wakaba 1.73 }->{$attr_ln};
4776    
4777 wakaba 1.1 $checker = {
4778 wakaba 1.153 alt => sub {
4779     ## NOTE: Checked later.
4780     },
4781 wakaba 1.1 shape => $GetHTMLEnumeratedAttrChecker->({
4782     circ => -1, circle => 1,
4783     default => 1,
4784     poly => 1, polygon => -1,
4785     rect => 1, rectangle => -1,
4786     }),
4787     coords => sub {
4788     my ($self, $attr) = @_;
4789     my $value = $attr->value;
4790     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4791     $coords = [split /,/, $value];
4792     } else {
4793     $self->{onerror}->(node => $attr,
4794 wakaba 1.104 type => 'coords:syntax error',
4795     level => $self->{level}->{must});
4796 wakaba 1.1 }
4797     },
4798 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4799     target => $HTMLTargetAttrChecker,
4800 wakaba 1.1 href => $HTMLURIAttrChecker,
4801     ping => $HTMLSpaceURIsAttrChecker,
4802 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4803 wakaba 1.1 media => $HTMLMQAttrChecker,
4804     hreflang => $HTMLLanguageTagAttrChecker,
4805     type => $HTMLIMTAttrChecker,
4806     }->{$attr_ln};
4807     if ($checker) {
4808     $attr{$attr_ln} = $attr;
4809 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4810     $attr_ln !~ /[A-Z]/) {
4811 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4812     $status = $HTMLDatasetAttrStatus;
4813 wakaba 1.1 } else {
4814     $checker = $HTMLAttrChecker->{$attr_ln};
4815     }
4816     }
4817     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4818 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4819     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4820     || $AttrStatus->{$attr_ns}->{''};
4821     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4822 wakaba 1.62
4823 wakaba 1.1 if ($checker) {
4824 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4825 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4826 wakaba 1.54 #
4827 wakaba 1.1 } else {
4828 wakaba 1.104 $self->{onerror}->(node => $attr,
4829     type => 'unknown attribute',
4830     level => $self->{level}->{uncertain});
4831 wakaba 1.1 ## ISSUE: No comformance createria for unknown attributes in the spec
4832     }
4833 wakaba 1.49
4834 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4835 wakaba 1.1 }
4836    
4837     if (defined $attr{href}) {
4838 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4839 wakaba 1.137 if (defined $attr{alt}) {
4840     my $url = $attr{href}->value; ## TODO: resolve
4841     if (length $attr{alt}->value) {
4842     for (@{$self->{flag}->{in_map} or []}) {
4843     $_->{$url} = 0;
4844     }
4845     } else {
4846     ## NOTE: Empty |alt=""|. If there is another |area| element
4847     ## with the same |href=""| and that |area| elemnet's
4848     ## |alt=""| attribute is not an empty string, then this
4849     ## is conforming.
4850     for (@{$self->{flag}->{in_map} or []}) {
4851     push @{$_->{$url} ||= []}, $attr{alt}
4852     unless exists $_->{$url} and not $_->{$url};
4853     }
4854     }
4855     } else {
4856 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4857 wakaba 1.104 type => 'attribute missing',
4858     text => 'alt',
4859     level => $self->{level}->{must});
4860 wakaba 1.1 }
4861     } else {
4862     for (qw/target ping rel media hreflang type alt/) {
4863     if (defined $attr{$_}) {
4864     $self->{onerror}->(node => $attr{$_},
4865 wakaba 1.104 type => 'attribute not allowed',
4866     level => $self->{level}->{must});
4867 wakaba 1.1 }
4868     }
4869     }
4870    
4871     my $shape = 'rectangle';
4872     if (defined $attr{shape}) {
4873     $shape = {
4874     circ => 'circle', circle => 'circle',
4875     default => 'default',
4876     poly => 'polygon', polygon => 'polygon',
4877     rect => 'rectangle', rectangle => 'rectangle',
4878     }->{lc $attr{shape}->value} || 'rectangle';
4879     ## TODO: ASCII lowercase?
4880     }
4881    
4882     if ($shape eq 'circle') {
4883     if (defined $attr{coords}) {
4884     if (defined $coords) {
4885     if (@$coords == 3) {
4886     if ($coords->[2] < 0) {
4887     $self->{onerror}->(node => $attr{coords},
4888 wakaba 1.104 type => 'coords:out of range',
4889     index => 2,
4890     value => $coords->[2],
4891     level => $self->{level}->{must});
4892 wakaba 1.1 }
4893     } else {
4894     $self->{onerror}->(node => $attr{coords},
4895 wakaba 1.104 type => 'coords:number not 3',
4896     text => 0+@$coords,
4897     level => $self->{level}->{must});
4898 wakaba 1.1 }
4899     } else {
4900     ## NOTE: A syntax error has been reported.
4901     }
4902     } else {
4903 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4904 wakaba 1.104 type => 'attribute missing',
4905     text => 'coords',
4906     level => $self->{level}->{must});
4907 wakaba 1.1 }
4908     } elsif ($shape eq 'default') {
4909     if (defined $attr{coords}) {
4910     $self->{onerror}->(node => $attr{coords},
4911 wakaba 1.104 type => 'attribute not allowed',
4912     level => $self->{level}->{must});
4913 wakaba 1.1 }
4914     } elsif ($shape eq 'polygon') {
4915     if (defined $attr{coords}) {
4916     if (defined $coords) {
4917     if (@$coords >= 6) {
4918     unless (@$coords % 2 == 0) {
4919     $self->{onerror}->(node => $attr{coords},
4920 wakaba 1.104 type => 'coords:number not even',
4921     text => 0+@$coords,
4922     level => $self->{level}->{must});
4923 wakaba 1.1 }
4924     } else {
4925     $self->{onerror}->(node => $attr{coords},
4926 wakaba 1.104 type => 'coords:number lt 6',
4927     text => 0+@$coords,
4928     level => $self->{level}->{must});
4929 wakaba 1.1 }
4930     } else {
4931     ## NOTE: A syntax error has been reported.
4932     }
4933     } else {
4934 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4935 wakaba 1.104 type => 'attribute missing',
4936     text => 'coords',
4937     level => $self->{level}->{must});
4938 wakaba 1.1 }
4939     } elsif ($shape eq 'rectangle') {
4940     if (defined $attr{coords}) {
4941     if (defined $coords) {
4942     if (@$coords == 4) {
4943     unless ($coords->[0] < $coords->[2]) {
4944     $self->{onerror}->(node => $attr{coords},
4945 wakaba 1.104 type => 'coords:out of range',
4946     index => 0,
4947     value => $coords->[0],
4948     level => $self->{level}->{must});
4949 wakaba 1.1 }
4950     unless ($coords->[1] < $coords->[3]) {
4951     $self->{onerror}->(node => $attr{coords},
4952 wakaba 1.104 type => 'coords:out of range',
4953     index => 1,
4954     value => $coords->[1],
4955     level => $self->{level}->{must});
4956 wakaba 1.1 }
4957     } else {
4958     $self->{onerror}->(node => $attr{coords},
4959 wakaba 1.104 type => 'coords:number not 4',
4960     text => 0+@$coords,
4961     level => $self->{level}->{must});
4962 wakaba 1.1 }
4963     } else {
4964     ## NOTE: A syntax error has been reported.
4965     }
4966     } else {
4967 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4968 wakaba 1.104 type => 'attribute missing',
4969     text => 'coords',
4970     level => $self->{level}->{must});
4971 wakaba 1.1 }
4972     }
4973 wakaba 1.66
4974     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4975 wakaba 1.1 },
4976 wakaba 1.59 check_start => sub {
4977     my ($self, $item, $element_state) = @_;
4978     unless ($self->{flag}->{in_map} or
4979     not $item->{node}->manakai_parent_element) {
4980     $self->{onerror}->(node => $item->{node},
4981     type => 'element not allowed:area',
4982 wakaba 1.104 level => $self->{level}->{must});
4983 wakaba 1.59 }
4984 wakaba 1.79
4985     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4986     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4987 wakaba 1.59 },
4988 wakaba 1.1 };
4989    
4990     $Element->{$HTML_NS}->{table} = {
4991 wakaba 1.40 %HTMLChecker,
4992 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4993 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4994 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4995     cellspacing => $HTMLLengthAttrChecker,
4996 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4997     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4998     lhs => 1, rhs => 1, box => 1, border => 1,
4999     }),
5000     rules => $GetHTMLEnumeratedAttrChecker->({
5001     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
5002     }),
5003     summary => sub {}, ## NOTE: %Text; in HTML4.
5004     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
5005     }, {
5006 wakaba 1.49 %HTMLAttrStatus,
5007 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5008 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
5009     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5010     border => FEATURE_M12N10_REC,
5011     cellpadding => FEATURE_M12N10_REC,
5012     cellspacing => FEATURE_M12N10_REC,
5013 wakaba 1.61 cols => FEATURE_RFC1942,
5014 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
5015     dataformatas => FEATURE_HTML4_REC_RESERVED,
5016     datapagesize => FEATURE_M12N10_REC,
5017     datasrc => FEATURE_HTML4_REC_RESERVED,
5018     frame => FEATURE_M12N10_REC,
5019 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5020 wakaba 1.49 rules => FEATURE_M12N10_REC,
5021     summary => FEATURE_M12N10_REC,
5022     width => FEATURE_M12N10_REC,
5023     }),
5024 wakaba 1.40 check_start => sub {
5025     my ($self, $item, $element_state) = @_;
5026     $element_state->{phase} = 'before caption';
5027 wakaba 1.66
5028     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5029 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5030     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5031 wakaba 1.40 },
5032     check_child_element => sub {
5033     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5034     $child_is_transparent, $element_state) = @_;
5035 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5036     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5037 wakaba 1.40 $self->{onerror}->(node => $child_el,
5038     type => 'element not allowed:minus',
5039 wakaba 1.104 level => $self->{level}->{must});
5040 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5041     #
5042     } elsif ($element_state->{phase} eq 'in tbodys') {
5043     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5044     #$element_state->{phase} = 'in tbodys';
5045     } elsif (not $element_state->{has_tfoot} and
5046     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5047     $element_state->{phase} = 'after tfoot';
5048     $element_state->{has_tfoot} = 1;
5049     } else {
5050 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5051     level => $self->{level}->{must});
5052 wakaba 1.40 }
5053     } elsif ($element_state->{phase} eq 'in trs') {
5054     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5055     #$element_state->{phase} = 'in trs';
5056     } elsif (not $element_state->{has_tfoot} and
5057     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5058     $element_state->{phase} = 'after tfoot';
5059     $element_state->{has_tfoot} = 1;
5060     } else {
5061 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5062     level => $self->{level}->{must});
5063 wakaba 1.40 }
5064     } elsif ($element_state->{phase} eq 'after thead') {
5065     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5066     $element_state->{phase} = 'in tbodys';
5067     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5068     $element_state->{phase} = 'in trs';
5069     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5070     $element_state->{phase} = 'in tbodys';
5071     $element_state->{has_tfoot} = 1;
5072     } else {
5073 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5074     level => $self->{level}->{must});
5075 wakaba 1.40 }
5076     } elsif ($element_state->{phase} eq 'in colgroup') {
5077     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
5078     $element_state->{phase} = 'in colgroup';
5079     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
5080     $element_state->{phase} = 'after thead';
5081     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5082     $element_state->{phase} = 'in tbodys';
5083     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5084     $element_state->{phase} = 'in trs';
5085     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5086     $element_state->{phase} = 'in tbodys';
5087     $element_state->{has_tfoot} = 1;
5088     } else {
5089 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5090     level => $self->{level}->{must});
5091 wakaba 1.40 }
5092     } elsif ($element_state->{phase} eq 'before caption') {
5093     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
5094     $element_state->{phase} = 'in colgroup';
5095     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
5096     $element_state->{phase} = 'in colgroup';
5097     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
5098     $element_state->{phase} = 'after thead';
5099     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5100     $element_state->{phase} = 'in tbodys';
5101     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5102     $element_state->{phase} = 'in trs';
5103     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5104     $element_state->{phase} = 'in tbodys';
5105     $element_state->{has_tfoot} = 1;
5106     } else {
5107 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5108     level => $self->{level}->{must});
5109 wakaba 1.40 }
5110     } elsif ($element_state->{phase} eq 'after tfoot') {
5111 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5112     level => $self->{level}->{must});
5113 wakaba 1.40 } else {
5114     die "check_child_element: Bad |table| phase: $element_state->{phase}";
5115     }
5116     },
5117     check_child_text => sub {
5118     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5119     if ($has_significant) {
5120 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5121     level => $self->{level}->{must});
5122 wakaba 1.1 }
5123 wakaba 1.40 },
5124     check_end => sub {
5125     my ($self, $item, $element_state) = @_;
5126 wakaba 1.1
5127     ## Table model errors
5128     require Whatpm::HTMLTable;
5129 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
5130 wakaba 1.104 $self->{onerror}->(@_);
5131     }, $self->{level});
5132 wakaba 1.87 Whatpm::HTMLTable->assign_header
5133 wakaba 1.104 ($table, $self->{onerror}, $self->{level});
5134 wakaba 1.87 push @{$self->{return}->{table}}, $table;
5135 wakaba 1.1
5136 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5137 wakaba 1.1 },
5138     };
5139    
5140     $Element->{$HTML_NS}->{caption} = {
5141 wakaba 1.169 %HTMLFlowContentChecker,
5142 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5143 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5144     align => $GetHTMLEnumeratedAttrChecker->({
5145     top => 1, bottom => 1, left => 1, right => 1,
5146     }),
5147     }, {
5148 wakaba 1.49 %HTMLAttrStatus,
5149 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5150 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
5151 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5152 wakaba 1.49 }),
5153 wakaba 1.169 check_start => sub {
5154     my ($self, $item, $element_state) = @_;
5155     $self->_add_minus_elements ($element_state, {$HTML_NS => {table => 1}});
5156    
5157     $HTMLFlowContentChecker{check_start}->(@_);
5158     },
5159     check_end => sub {
5160     my ($self, $item, $element_state) = @_;
5161     $self->_remove_minus_elements ($element_state);
5162    
5163     $HTMLFlowContentChecker{check_end}->(@_);
5164     },
5165     }; # caption
5166 wakaba 1.1
5167 wakaba 1.69 my %cellalign = (
5168     ## HTML4 %cellhalign;
5169 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
5170     left => 1, center => 1, right => 1, justify => 1, char => 1,
5171     }),
5172     char => sub {
5173     my ($self, $attr) = @_;
5174 wakaba 1.69
5175 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
5176    
5177     my $value = $attr->value;
5178     if (length $value != 1) {
5179     $self->{onerror}->(node => $attr, type => 'char:syntax error',
5180 wakaba 1.105 level => $self->{level}->{html4_fact});
5181 wakaba 1.70 }
5182     },
5183 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
5184    
5185 wakaba 1.69 ## HTML4 %cellvalign;
5186 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
5187     top => 1, middle => 1, bottom => 1, baseline => 1,
5188     }),
5189 wakaba 1.69 );
5190    
5191 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
5192 wakaba 1.40 %HTMLEmptyChecker,
5193 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5194 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5195 wakaba 1.69 %cellalign,
5196 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5197     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
5198     ## TODO: "attribute not supported" if |col|.
5199     ## ISSUE: MUST NOT if any |col|?
5200     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
5201 wakaba 1.49 }, {
5202     %HTMLAttrStatus,
5203 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5204 wakaba 1.49 align => FEATURE_M12N10_REC,
5205     char => FEATURE_M12N10_REC,
5206     charoff => FEATURE_M12N10_REC,
5207 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5208     span => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5209 wakaba 1.49 valign => FEATURE_M12N10_REC,
5210     width => FEATURE_M12N10_REC,
5211 wakaba 1.1 }),
5212 wakaba 1.40 check_child_element => sub {
5213     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5214     $child_is_transparent, $element_state) = @_;
5215 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5216     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5217 wakaba 1.40 $self->{onerror}->(node => $child_el,
5218     type => 'element not allowed:minus',
5219 wakaba 1.104 level => $self->{level}->{must});
5220 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5221     #
5222     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
5223     #
5224     } else {
5225 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5226     level => $self->{level}->{must});
5227 wakaba 1.40 }
5228     },
5229     check_child_text => sub {
5230     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5231     if ($has_significant) {
5232 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5233     level => $self->{level}->{must});
5234 wakaba 1.1 }
5235     },
5236     };
5237    
5238     $Element->{$HTML_NS}->{col} = {
5239 wakaba 1.40 %HTMLEmptyChecker,
5240 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5241 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5242 wakaba 1.69 %cellalign,
5243 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5244 wakaba 1.49 }, {
5245     %HTMLAttrStatus,
5246 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5247 wakaba 1.49 align => FEATURE_M12N10_REC,
5248     char => FEATURE_M12N10_REC,
5249     charoff => FEATURE_M12N10_REC,
5250 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5251     span => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5252 wakaba 1.49 valign => FEATURE_M12N10_REC,
5253     width => FEATURE_M12N10_REC,
5254 wakaba 1.1 }),
5255     };
5256    
5257     $Element->{$HTML_NS}->{tbody} = {
5258 wakaba 1.40 %HTMLChecker,
5259 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5260 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5261     %cellalign,
5262     }, {
5263 wakaba 1.49 %HTMLAttrStatus,
5264 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5265 wakaba 1.49 align => FEATURE_M12N10_REC,
5266     char => FEATURE_M12N10_REC,
5267     charoff => FEATURE_M12N10_REC,
5268 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5269 wakaba 1.49 valign => FEATURE_M12N10_REC,
5270     }),
5271 wakaba 1.40 check_child_element => sub {
5272     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5273     $child_is_transparent, $element_state) = @_;
5274 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5275     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5276 wakaba 1.40 $self->{onerror}->(node => $child_el,
5277     type => 'element not allowed:minus',
5278 wakaba 1.104 level => $self->{level}->{must});
5279 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5280     #
5281     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5282 wakaba 1.84 #
5283 wakaba 1.40 } else {
5284 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5285     level => $self->{level}->{must});
5286 wakaba 1.40 }
5287     },
5288     check_child_text => sub {
5289     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5290     if ($has_significant) {
5291 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5292     level => $self->{level}->{must});
5293 wakaba 1.1 }
5294 wakaba 1.40 },
5295 wakaba 1.1 };
5296    
5297     $Element->{$HTML_NS}->{thead} = {
5298 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5299 wakaba 1.1 };
5300    
5301     $Element->{$HTML_NS}->{tfoot} = {
5302 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5303 wakaba 1.1 };
5304    
5305     $Element->{$HTML_NS}->{tr} = {
5306 wakaba 1.40 %HTMLChecker,
5307 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5308 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5309     %cellalign,
5310     bgcolor => $HTMLColorAttrChecker,
5311     }, {
5312 wakaba 1.49 %HTMLAttrStatus,
5313 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5314 wakaba 1.49 align => FEATURE_M12N10_REC,
5315     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5316     char => FEATURE_M12N10_REC,
5317     charoff => FEATURE_M12N10_REC,
5318 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5319 wakaba 1.49 valign => FEATURE_M12N10_REC,
5320     }),
5321 wakaba 1.40 check_child_element => sub {
5322     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5323     $child_is_transparent, $element_state) = @_;
5324 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5325     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5326 wakaba 1.40 $self->{onerror}->(node => $child_el,
5327     type => 'element not allowed:minus',
5328 wakaba 1.104 level => $self->{level}->{must});
5329 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5330     #
5331     } elsif ($child_nsuri eq $HTML_NS and
5332     ($child_ln eq 'td' or $child_ln eq 'th')) {
5333 wakaba 1.84 #
5334 wakaba 1.40 } else {
5335 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5336     level => $self->{level}->{must});
5337 wakaba 1.40 }
5338     },
5339     check_child_text => sub {
5340     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5341     if ($has_significant) {
5342 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5343     level => $self->{level}->{must});
5344 wakaba 1.1 }
5345     },
5346     };
5347    
5348     $Element->{$HTML_NS}->{td} = {
5349 wakaba 1.72 %HTMLFlowContentChecker,
5350 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5351 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5352 wakaba 1.69 %cellalign,
5353     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5354     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5355     bgcolor => $HTMLColorAttrChecker,
5356 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5357 wakaba 1.87 headers => sub {
5358     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
5359     ## Though that method does not check the |headers| attribute of a
5360     ## |td| element if the element does not form a table, in that case
5361     ## the |td| element is non-conforming anyway.
5362     },
5363 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5364 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5365 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
5366     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5367 wakaba 1.49 }, {
5368     %HTMLAttrStatus,
5369 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5370     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5371 wakaba 1.49 align => FEATURE_M12N10_REC,
5372 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5373 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5374     char => FEATURE_M12N10_REC,
5375     charoff => FEATURE_M12N10_REC,
5376 wakaba 1.153 colspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5377     headers => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5378 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5379 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5380 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5381 wakaba 1.153 rowspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5382 wakaba 1.82 scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5383 wakaba 1.49 valign => FEATURE_M12N10_REC,
5384     width => FEATURE_M12N10_REC_DEPRECATED,
5385 wakaba 1.1 }),
5386     };
5387    
5388     $Element->{$HTML_NS}->{th} = {
5389 wakaba 1.40 %HTMLPhrasingContentChecker,
5390 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5391 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5392 wakaba 1.69 %cellalign,
5393     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5394     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5395     bgcolor => $HTMLColorAttrChecker,
5396 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5397 wakaba 1.87 ## TODO: HTML4(?) |headers|
5398 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5399 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5400     scope => $GetHTMLEnumeratedAttrChecker
5401     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5402 wakaba 1.49 }, {
5403     %HTMLAttrStatus,
5404 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5405     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5406 wakaba 1.49 align => FEATURE_M12N10_REC,
5407 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5408 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5409     char => FEATURE_M12N10_REC,
5410     charoff => FEATURE_M12N10_REC,
5411 wakaba 1.153 colspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5412 wakaba 1.82 headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5413 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5414 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5415 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5416 wakaba 1.153 rowspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5417     scope => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5418 wakaba 1.49 valign => FEATURE_M12N10_REC,
5419     width => FEATURE_M12N10_REC_DEPRECATED,
5420 wakaba 1.1 }),
5421     };
5422    
5423 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
5424 wakaba 1.121 %HTMLFlowContentChecker,
5425 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5426 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5427 wakaba 1.161 accept => $AcceptAttrChecker,
5428 wakaba 1.129 'accept-charset' => $HTMLCharsetsAttrChecker,
5429 wakaba 1.166 action => $HTMLURIAttrChecker, ## TODO: Warn if submission is not defined for the scheme
5430 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5431 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
5432     'application/x-www-form-urlencoded' => 1,
5433     'multipart/form-data' => 1,
5434     'text/plain' => 1,
5435     }),
5436 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
5437     get => 1, post => 1, put => 1, delete => 1,
5438     }),
5439 wakaba 1.133 name => sub {
5440     my ($self, $attr) = @_;
5441    
5442     my $value = $attr->value;
5443     if ($value eq '') {
5444     $self->{onerror}->(type => 'empty form name',
5445     node => $attr,
5446     level => $self->{level}->{must});
5447     } else {
5448     if ($self->{form}->{$value}) {
5449     $self->{onerror}->(type => 'duplicate form name',
5450     node => $attr,
5451     value => $value,
5452     level => $self->{level}->{must});
5453     } else {
5454     $self->{form}->{$value} = 1;
5455     }
5456     }
5457     },
5458 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
5459     ## TODO: Tests for following attrs:
5460 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
5461     onforminput => $HTMLEventHandlerAttrChecker,
5462 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
5463     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5464 wakaba 1.52 target => $HTMLTargetAttrChecker,
5465     }, {
5466     %HTMLAttrStatus,
5467     %HTMLM12NCommonAttrStatus,
5468 wakaba 1.161 accept => FEATURE_HTML5_DROPPED | FEATURE_WF2X | FEATURE_M12N10_REC,
5469 wakaba 1.119 'accept-charset' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5470     action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5471 wakaba 1.56 data => FEATURE_WF2,
5472 wakaba 1.119 enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5473 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5474 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5475     #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
5476     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5477 wakaba 1.166 novalidate => FEATURE_HTML5_DEFAULT,
5478 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
5479     onforminput => FEATURE_WF2_INFORMATIVE,
5480 wakaba 1.56 onreceived => FEATURE_WF2,
5481 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5482     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5483 wakaba 1.56 replace => FEATURE_WF2,
5484 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5485     sdasuff => FEATURE_HTML20_RFC,
5486 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5487 wakaba 1.52 }),
5488 wakaba 1.66 check_start => sub {
5489     my ($self, $item, $element_state) = @_;
5490 wakaba 1.121 $self->_add_minus_elements ($element_state, {$HTML_NS => {form => 1}});
5491 wakaba 1.66
5492     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5493     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5494 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5495     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5496 wakaba 1.136 $element_state->{id_type} = 'form';
5497 wakaba 1.66 },
5498 wakaba 1.121 check_end => sub {
5499     my ($self, $item, $element_state) = @_;
5500     $self->_remove_minus_elements ($element_state);
5501    
5502     $HTMLFlowContentChecker{check_end}->(@_);
5503     },
5504 wakaba 1.52 };
5505    
5506     $Element->{$HTML_NS}->{fieldset} = {
5507 wakaba 1.134 %HTMLFlowContentChecker,
5508 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5509 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5510     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5511 wakaba 1.136 form => $HTMLFormAttrChecker,
5512 wakaba 1.165 name => $FormControlNameAttrChecker,
5513 wakaba 1.56 }, {
5514 wakaba 1.52 %HTMLAttrStatus,
5515     %HTMLM12NCommonAttrStatus,
5516 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5517     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5518 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5519 wakaba 1.125 name => FEATURE_HTML5_DEFAULT,
5520 wakaba 1.52 }),
5521 wakaba 1.134 ## NOTE: legend, Flow
5522     check_child_element => sub {
5523     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5524     $child_is_transparent, $element_state) = @_;
5525     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5526     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5527     $self->{onerror}->(node => $child_el,
5528     type => 'element not allowed:minus',
5529     level => $self->{level}->{must});
5530     $element_state->{has_non_legend} = 1;
5531     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5532     #
5533     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5534     if ($element_state->{has_non_legend}) {
5535     $self->{onerror}->(node => $child_el,
5536     type => 'element not allowed:details legend',
5537     level => $self->{level}->{must});
5538     }
5539     $element_state->{has_legend} = 1;
5540     $element_state->{has_non_legend} = 1;
5541     } else {
5542     $HTMLFlowContentChecker{check_child_element}->(@_);
5543     $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5544     ## TODO:
5545 wakaba 1.167 ## |<fieldset><object><legend>xx</legend></object>..</fieldset>|
5546 wakaba 1.134 ## should be an error, since |object| is allowed as flow,
5547     ## therefore |details| part of the content model does not match.
5548     }
5549     },
5550     check_child_text => sub {
5551     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5552     if ($has_significant) {
5553     $element_state->{has_non_legend} = 1;
5554     }
5555     },
5556     check_end => sub {
5557     my ($self, $item, $element_state) = @_;
5558    
5559     unless ($element_state->{has_legend}) {
5560     $self->{onerror}->(node => $item->{node},
5561     type => 'child element missing',
5562     text => 'legend',
5563     level => $self->{level}->{must});
5564     }
5565    
5566     $HTMLFlowContentChecker{check_end}->(@_);
5567 wakaba 1.167 ## ISSUE: |<fieldset><legend>aa</legend></fieldset>| error?
5568 wakaba 1.134 },
5569     ## NOTE: This definition is partially reused by |details| element's
5570     ## checker.
5571 wakaba 1.52 };
5572    
5573     $Element->{$HTML_NS}->{input} = {
5574 wakaba 1.119 %HTMLEmptyChecker,
5575     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5576 wakaba 1.140 check_attrs => sub {
5577     my ($self, $item, $element_state) = @_;
5578 wakaba 1.142
5579 wakaba 1.145 my $state = $item->{node}->get_attribute_ns (undef, 'type');
5580 wakaba 1.142 $state = 'text' unless defined $state;
5581     $state =~ tr/A-Z/a-z/; ## ASCII case-insensitive
5582    
5583 wakaba 1.140 for my $attr (@{$item->{node}->attributes}) {
5584     my $attr_ns = $attr->namespace_uri;
5585     $attr_ns = '' unless defined $attr_ns;
5586     my $attr_ln = $attr->manakai_local_name;
5587     my $checker;
5588     my $status;
5589     if ($attr_ns eq '') {
5590     $status =
5591     {
5592     %HTMLAttrStatus,
5593     %HTMLM12NCommonAttrStatus,
5594     accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5595     'accept-charset' => FEATURE_HTML2X_RFC,
5596 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
5597 wakaba 1.140 action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5598     align => FEATURE_M12N10_REC_DEPRECATED,
5599     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5600     autocomplete => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5601     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5602     checked => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5603     datafld => FEATURE_HTML4_REC_RESERVED,
5604     dataformatas => FEATURE_HTML4_REC_RESERVED,
5605     datasrc => FEATURE_HTML4_REC_RESERVED,
5606     disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5607     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5608     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5609 wakaba 1.178 height => FEATURE_HTML5_LC,
5610 wakaba 1.150 inputmode => FEATURE_HTML5_DROPPED | FEATURE_WF2X |
5611     FEATURE_XHTMLBASIC11_CR,
5612 wakaba 1.140 ismap => FEATURE_M12N10_REC,
5613 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5614 wakaba 1.140 list => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5615     max => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5616 wakaba 1.150 maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X |
5617     FEATURE_M12N10_REC,
5618 wakaba 1.140 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5619     min => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5620 wakaba 1.156 multiple => FEATURE_HTML5_DEFAULT,
5621 wakaba 1.140 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5622 wakaba 1.161 novalidate => FEATURE_HTML5_DEFAULT,
5623 wakaba 1.140 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5624     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5625     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5626     onformchange => FEATURE_WF2_INFORMATIVE,
5627     onforminput => FEATURE_WF2_INFORMATIVE,
5628     oninput => FEATURE_WF2,
5629     oninvalid => FEATURE_WF2,
5630     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5631     pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5632 wakaba 1.156 placeholder => FEATURE_HTML5_DEFAULT,
5633 wakaba 1.140 readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5634     replace => FEATURE_WF2,
5635     required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5636     sdapref => FEATURE_HTML20_RFC,
5637 wakaba 1.154 size => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5638 wakaba 1.140 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5639     step => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5640     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5641     target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5642 wakaba 1.161 template => FEATURE_HTML5_AT_RISK | FEATURE_WF2, ## TODO:dropped
5643 wakaba 1.140 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5644     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5645     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5646 wakaba 1.178 width => FEATURE_HTML5_LC,
5647 wakaba 1.140 }->{$attr_ln};
5648    
5649     $checker =
5650     {
5651 wakaba 1.141 ## NOTE: Value of an empty string means that the attribute is only
5652     ## applicable for a specific set of states.
5653 wakaba 1.142 accept => '',
5654 wakaba 1.149 'accept-charset' => $HTMLCharsetsAttrChecker,
5655     ## NOTE: To which states it applies is not defined in RFC 2070.
5656 wakaba 1.142 action => '',
5657 wakaba 1.150 align => '',
5658 wakaba 1.141 alt => '',
5659 wakaba 1.142 autocomplete => '',
5660 wakaba 1.165 autofocus => $AutofocusAttrChecker,
5661     ## NOTE: <input type=hidden disabled> is not disallowed.
5662 wakaba 1.142 checked => '',
5663     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5664 wakaba 1.165 ## NOTE: <input type=hidden disabled> is not disallowed.
5665 wakaba 1.142 enctype => '',
5666     form => $HTMLFormAttrChecker,
5667 wakaba 1.178 height => '',
5668 wakaba 1.150 inputmode => '',
5669     ismap => '', ## NOTE: "MUST" be type=image [HTML4]
5670 wakaba 1.142 list => '',
5671     max => '',
5672     maxlength => '',
5673     method => '',
5674     min => '',
5675 wakaba 1.156 multiple => '',
5676 wakaba 1.165 name => $FormControlNameAttrChecker,
5677 wakaba 1.166 novalidate => '',
5678 wakaba 1.149 onformchange => $HTMLEventHandlerAttrChecker, # [WF2]
5679     onforminput => $HTMLEventHandlerAttrChecker, # [WF2]
5680     oninput => $HTMLEventHandlerAttrChecker, # [WF2]
5681     oninvalid => $HTMLEventHandlerAttrChecker, # [WF2]
5682     ## TODO: tests for four attributes above
5683 wakaba 1.142 pattern => '',
5684 wakaba 1.156 placeholder => '',
5685 wakaba 1.142 readonly => '',
5686 wakaba 1.150 replace => '',
5687 wakaba 1.142 required => '',
5688     size => '',
5689     src => '',
5690     step => '',
5691     target => '',
5692 wakaba 1.140 type => $GetHTMLEnumeratedAttrChecker->({
5693 wakaba 1.156 hidden => 1, text => 1, search => 1, url => 1,
5694     email => 1, password => 1,
5695 wakaba 1.141 datetime => 1, date => 1, month => 1, week => 1, time => 1,
5696 wakaba 1.157 'datetime-local' => 1, number => 1, range => 1, color => 1,
5697     checkbox => 1,
5698 wakaba 1.141 radio => 1, file => 1, submit => 1, image => 1, reset => 1,
5699     button => 1,
5700 wakaba 1.140 }),
5701 wakaba 1.151 usemap => '',
5702 wakaba 1.142 value => '',
5703 wakaba 1.178 width => '',
5704 wakaba 1.140 }->{$attr_ln};
5705 wakaba 1.141
5706     ## State-dependent checkers
5707     unless ($checker) {
5708     if ($state eq 'hidden') {
5709     $checker =
5710     {
5711 wakaba 1.142 value => sub {
5712     my ($self, $attr, $item, $element_state) = @_;
5713 wakaba 1.145 my $name = $item->{node}->get_attribute_ns (undef, 'name');
5714 wakaba 1.142 if (defined $name and $name eq '_charset_') { ## case-sensitive
5715     $self->{onerror}->(node => $attr,
5716     type => '_charset_ value',
5717     level => $self->{level}->{must});
5718     }
5719     },
5720 wakaba 1.141 }->{$attr_ln} || $checker;
5721 wakaba 1.142 ## TODO: Warn if no name attribute?
5722     ## TODO: Warn if name!=_charset_ and no value attribute?
5723 wakaba 1.168 } elsif ({
5724     datetime => 1, date => 1, month => 1, time => 1,
5725     week => 1, 'datetime-local' => 1,
5726     }->{$state}) {
5727     my $v = {
5728     datetime => ['global_date_and_time_string'],
5729     date => ['date_string'],
5730     month => ['month_string'],
5731     week => ['week_string'],
5732     time => ['time_string'],
5733     'datetime-local' => ['local_date_and_time_string'],
5734     }->{$state};
5735 wakaba 1.144 $checker =
5736     {
5737 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5738     on => 1, off => 1,
5739     }),
5740 wakaba 1.158 list => $ListAttrChecker,
5741 wakaba 1.168 min => $GetDateTimeAttrChecker->($v->[0]),
5742     max => $GetDateTimeAttrChecker->($v->[0]),
5743 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5744 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5745 wakaba 1.148 step => $StepAttrChecker,
5746 wakaba 1.168 value => $GetDateTimeAttrChecker->($v->[0]),
5747 wakaba 1.144 }->{$attr_ln} || $checker;
5748     } elsif ($state eq 'number') {
5749     $checker =
5750     {
5751 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5752     on => 1, off => 1,
5753     }),
5754 wakaba 1.158 list => $ListAttrChecker,
5755 wakaba 1.144 max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5756     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5757 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5758 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5759 wakaba 1.148 step => $StepAttrChecker,
5760 wakaba 1.168 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5761 wakaba 1.144 }->{$attr_ln} || $checker;
5762     } elsif ($state eq 'range') {
5763     $checker =
5764     {
5765 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5766     on => 1, off => 1,
5767     }),
5768 wakaba 1.158 list => $ListAttrChecker,
5769 wakaba 1.144 max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5770     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5771 wakaba 1.148 step => $StepAttrChecker,
5772 wakaba 1.168 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5773 wakaba 1.144 }->{$attr_ln} || $checker;
5774 wakaba 1.157 } elsif ($state eq 'color') {
5775     $checker =
5776     {
5777     autocomplete => $GetHTMLEnumeratedAttrChecker->({
5778     on => 1, off => 1,
5779     }),
5780 wakaba 1.158 list => $ListAttrChecker,
5781 wakaba 1.157 value => sub {
5782     my ($self, $attr) = @_;
5783     unless ($attr->value =~ /\A#[0-9A-Fa-f]{6}\z/) {
5784     $self->{onerror}->(node => $attr,
5785     type => 'scolor:syntax error', ## TODOC: type
5786     level => $self->{level}->{must});
5787     }
5788     },
5789     }->{$attr_ln} || $checker;
5790 wakaba 1.144 } elsif ($state eq 'checkbox' or $state eq 'radio') {
5791     $checker =
5792     {
5793 wakaba 1.149 checked => $GetHTMLBooleanAttrChecker->('checked'),
5794     ## TODO: tests
5795 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5796 wakaba 1.144 value => sub { }, ## NOTE: No restriction.
5797     }->{$attr_ln} || $checker;
5798     ## TODO: There MUST be another input type=radio with same
5799     ## name (Radio state).
5800     ## ISSUE: There should be exactly one type=radio with checked?
5801     } elsif ($state eq 'file') {
5802     $checker =
5803     {
5804 wakaba 1.161 accept => $AcceptAttrChecker,
5805 wakaba 1.168 ## max (default 1) & min (default 0) [WF2]: Dropped by HTML5.
5806 wakaba 1.159 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5807 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5808 wakaba 1.144 }->{$attr_ln} || $checker;
5809     } elsif ($state eq 'submit') {
5810     $checker =
5811     {
5812 wakaba 1.149 action => $HTMLURIAttrChecker,
5813 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
5814     'application/x-www-form-urlencoded' => 1,
5815     'multipart/form-data' => 1,
5816     'text/plain' => 1,
5817     }),
5818 wakaba 1.149 method => $GetHTMLEnumeratedAttrChecker->({
5819     get => 1, post => 1, put => 1, delete => 1,
5820     }),
5821 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
5822 wakaba 1.149 replace => $GetHTMLEnumeratedAttrChecker->({
5823     document => 1, values => 1,
5824     }),
5825     target => $HTMLTargetAttrChecker,
5826 wakaba 1.144 value => sub { }, ## NOTE: No restriction.
5827     }->{$attr_ln} || $checker;
5828     } elsif ($state eq 'image') {
5829     $checker =
5830     {
5831 wakaba 1.149 action => $HTMLURIAttrChecker,
5832     align => $GetHTMLEnumeratedAttrChecker->({
5833     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
5834     }),
5835 wakaba 1.144 alt => sub {
5836     my ($self, $attr) = @_;
5837     my $value = $attr->value;
5838     unless (length $value) {
5839     $self->{onerror}->(node => $attr,
5840     type => 'empty anchor image alt',
5841     level => $self->{level}->{must});
5842     }
5843     },
5844 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
5845     'application/x-www-form-urlencoded' => 1,
5846     'multipart/form-data' => 1,
5847     'text/plain' => 1,
5848     }),
5849 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5850 wakaba 1.149 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
5851     method => $GetHTMLEnumeratedAttrChecker->({
5852     get => 1, post => 1, put => 1, delete => 1,
5853     }),
5854 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
5855 wakaba 1.149 replace => $GetHTMLEnumeratedAttrChecker->({
5856     document => 1, values => 1,
5857     }),
5858 wakaba 1.144 src => $HTMLURIAttrChecker,
5859     ## TODO: There is requirements on the referenced resource.
5860 wakaba 1.149 target => $HTMLTargetAttrChecker,
5861     usemap => $HTMLUsemapAttrChecker,
5862 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5863 wakaba 1.144 }->{$attr_ln} || $checker;
5864     ## TODO: alt & src are required.
5865     } elsif ({
5866     reset => 1, button => 1,
5867     ## NOTE: From Web Forms 2.0:
5868     remove => 1, 'move-up' => 1, 'move-down' => 1,
5869     add => 1,
5870     }->{$state}) {
5871     $checker =
5872     {
5873     ## NOTE: According to Web Forms 2.0, |input| attribute
5874     ## has |template| attribute to support the |add| button
5875     ## type (as part of the repetition template feature). It
5876     ## conflicts with the |template| global attribute
5877     ## introduced as part of the data template feature.
5878     ## NOTE: |template| attribute as defined in Web Forms 2.0
5879     ## has no author requirement.
5880     value => sub { }, ## NOTE: No restriction.
5881     }->{$attr_ln} || $checker;
5882 wakaba 1.156 } else { # Text, Search, E-mail, URL, Password
5883 wakaba 1.141 $checker =
5884     {
5885 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5886     on => 1, off => 1,
5887     }),
5888 wakaba 1.149 ## TODO: inputmode [WF2]
5889 wakaba 1.158 list => $ListAttrChecker,
5890 wakaba 1.147 maxlength => sub {
5891     my ($self, $attr, $item, $element_state) = @_;
5892    
5893     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
5894    
5895 wakaba 1.165 if ($attr->value =~ /^[\x09\x0A\x0C\x0D\x20]*([0-9]+)/) {
5896 wakaba 1.147 ## NOTE: Applying the rules for parsing non-negative
5897     ## integers results in a number.
5898     my $max_allowed_value_length = 0+$1;
5899    
5900     my $value = $item->{node}->get_attribute_ns (undef, 'value');
5901     if (defined $value) {
5902     my $codepoint_length = length $value;
5903 wakaba 1.162
5904 wakaba 1.147 if ($codepoint_length > $max_allowed_value_length) {
5905     $self->{onerror}
5906     ->(node => $item->{node}
5907     ->get_attribute_node_ns (undef, 'value'),
5908     type => 'value too long',
5909     level => $self->{level}->{must});
5910     }
5911     }
5912     }
5913     },
5914 wakaba 1.160 pattern => $PatternAttrChecker,
5915 wakaba 1.179 placeholder => $PlaceholderAttrChecker,
5916 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5917 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5918 wakaba 1.147 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub {shift > 0}),
5919 wakaba 1.143 value => sub {
5920 wakaba 1.156 my ($self, $attr, $item, $element_state) = @_;
5921     if ($state eq 'url') {
5922     $HTMLURIAttrChecker->(@_);
5923     } elsif ($state eq 'email') {
5924     if ($item->{node}->has_attribute_ns (undef, 'multiple')) {
5925     my @addr = split /,/, $attr->value, -1;
5926     @addr = ('') unless @addr;
5927     for (@addr) {
5928 wakaba 1.165 s/\A[\x09\x0A\x0C\x0D\x20]+//;
5929     s/[\x09\x0A\x0C\x0D\x20]\z//;
5930 wakaba 1.156 unless (/\A$ValidEmailAddress\z/) {
5931     $self->{onerror}->(node => $attr,
5932     type => 'email:syntax error', ## TODO: type
5933     value => $_,
5934     level => $self->{level}->{must});
5935     }
5936     }
5937     } else {
5938     unless ($attr->value =~ /\A$ValidEmailAddress\z/) {
5939     $self->{onerror}->(node => $attr,
5940     type => 'email:syntax error', ## TODO: type
5941     level => $self->{level}->{must});
5942     }
5943     }
5944     } else {
5945     if ($attr->value =~ /[\x0D\x0A]/) {
5946     $self->{onerror}->(node => $attr,
5947     type => 'newline in value', ## TODO: type
5948     level => $self->{level}->{must});
5949     }
5950     }
5951 wakaba 1.143 },
5952 wakaba 1.141 }->{$attr_ln} || $checker;
5953 wakaba 1.147 $checker = '' if $state eq 'password' and $attr_ln eq 'list';
5954 wakaba 1.156 $checker = $GetHTMLBooleanAttrChecker->('multiple')
5955     if $state eq 'email' and $attr_ln eq 'multiple';
5956 wakaba 1.161
5957     if ($item->{node}->has_attribute_ns (undef, 'pattern') and
5958     not $item->{node}->has_attribute_ns (undef, 'title')) {
5959     $self->{onerror}->(node => $item->{node},
5960     type => 'attribute missing',
5961     text => 'title',
5962     level => $self->{level}->{should});
5963     }
5964 wakaba 1.141 }
5965     }
5966    
5967     if (defined $checker) {
5968     if ($checker eq '') {
5969     $checker = sub {
5970     my ($self, $attr) = @_;
5971     $self->{onerror}->(node => $attr,
5972     type => 'input attr not applicable',
5973     text => $state,
5974     level => $self->{level}->{must});
5975     };
5976     }
5977 wakaba 1.140 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
5978     $attr_ln !~ /[A-Z]/) {
5979     $checker = $HTMLDatasetAttrChecker;
5980     $status = $HTMLDatasetAttrStatus;
5981     } else {
5982     $checker = $HTMLAttrChecker->{$attr_ln};
5983     }
5984     }
5985     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
5986     || $AttrChecker->{$attr_ns}->{''};
5987     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
5988     || $AttrStatus->{$attr_ns}->{''};
5989     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
5990 wakaba 1.157
5991 wakaba 1.140 if ($checker) {
5992     $checker->($self, $attr, $item, $element_state) if ref $checker;
5993     } elsif ($attr_ns eq '' and not $status) {
5994     #
5995     } else {
5996     $self->{onerror}->(node => $attr,
5997     type => 'unknown attribute',
5998     level => $self->{level}->{uncertain});
5999     ## ISSUE: No comformance createria for unknown attributes in the spec
6000     }
6001    
6002     $self->_attr_status_info ($attr, $status);
6003     }
6004 wakaba 1.168
6005     ## ISSUE: -0/+0
6006    
6007     if ($state eq 'range') {
6008     $element_state->{number_value}->{min} ||= 0;
6009     $element_state->{number_value}->{max} = 100
6010     unless defined $element_state->{number_value}->{max};
6011     }
6012    
6013     if (defined $element_state->{date_value}->{min} or
6014     defined $element_state->{date_value}->{max}) {
6015     my $min_value = $element_state->{date_value}->{min};
6016     my $max_value = $element_state->{date_value}->{max};
6017     my $value_value = $element_state->{date_value}->{value};
6018    
6019     if (defined $min_value and $min_value eq '' and
6020     (defined $max_value or defined $value_value)) {
6021     my $min = $item->{node}->get_attribute_node_ns (undef, 'min');
6022     $self->{onerror}->(node => $min,
6023     type => 'date value not supported', ## TODOC: type
6024     value => $min->value,
6025     level => $self->{level}->{unsupported});
6026     undef $min_value;
6027     }
6028     if (defined $max_value and $max_value eq '' and
6029     (defined $max_value or defined $value_value)) {
6030     my $max = $item->{node}->get_attribute_node_ns (undef, 'max');
6031     $self->{onerror}->(node => $max,
6032     type => 'date value not supported', ## TODOC: type
6033     value => $max->value,
6034     level => $self->{level}->{unsupported});
6035     undef $max_value;
6036     }
6037     if (defined $value_value and $value_value eq '' and
6038     (defined $max_value or defined $min_value)) {
6039     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6040     $self->{onerror}->(node => $value,
6041     type => 'date value not supported', ## TODOC: type
6042     value => $value->value,
6043     level => $self->{level}->{unsupported});
6044     undef $value_value;
6045     }
6046    
6047     if (defined $min_value and defined $max_value) {
6048     if ($min_value->to_html5_number > $max_value->to_html5_number) {
6049     my $max = $item->{node}->get_attribute_node_ns (undef, 'max');
6050     $self->{onerror}->(node => $max,
6051     type => 'max lt min', ## TODOC: type
6052     level => $self->{level}->{must});
6053     }
6054     }
6055    
6056     if (defined $min_value and defined $value_value) {
6057     if ($min_value->to_html5_number > $value_value->to_html5_number) {
6058     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6059     $self->{onerror}->(node => $value,
6060     type => 'value lt min', ## TODOC: type
6061     level => $self->{level}->{warn});
6062     ## NOTE: Not an error.
6063     }
6064     }
6065    
6066     if (defined $max_value and defined $value_value) {
6067     if ($max_value->to_html5_number < $value_value->to_html5_number) {
6068     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6069     $self->{onerror}->(node => $value,
6070     type => 'value gt max', ## TODOC: type
6071     level => $self->{level}->{warn});
6072     ## NOTE: Not an error.
6073     }
6074     }
6075     } elsif (defined $element_state->{number_value}->{min} or
6076     defined $element_state->{number_value}->{max}) {
6077     my $min_value = $element_state->{number_value}->{min};
6078     my $max_value = $element_state->{number_value}->{max};
6079     my $value_value = $element_state->{number_value}->{value};
6080    
6081     if (defined $min_value and defined $max_value) {
6082     if ($min_value > $max_value) {
6083     my $max = $item->{node}->get_attribute_node_ns (undef, 'max');
6084     $self->{onerror}->(node => $max,
6085     type => 'max lt min', ## TODOC: type
6086     level => $self->{level}->{must});
6087     }
6088     }
6089    
6090     if (defined $min_value and defined $value_value) {
6091     if ($min_value > $value_value) {
6092     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6093     $self->{onerror}->(node => $value,
6094     type => 'value lt min', ## TODOC: type
6095     level => $self->{level}->{warn});
6096     ## NOTE: Not an error.
6097     }
6098     }
6099    
6100     if (defined $max_value and defined $value_value) {
6101     if ($max_value < $value_value) {
6102     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6103     $self->{onerror}->(node => $value,
6104     type => 'value gt max', ## TODOC: type
6105     level => $self->{level}->{warn});
6106     ## NOTE: Not an error.
6107     }
6108     }
6109     }
6110 wakaba 1.150
6111 wakaba 1.168 ## TODO: Warn unless value = min * x where x is an integer.
6112    
6113 wakaba 1.150 $element_state->{uri_info}->{action}->{type}->{action} = 1;
6114     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6115     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
6116     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6117     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6118 wakaba 1.140 },
6119 wakaba 1.66 check_start => sub {
6120     my ($self, $item, $element_state) = @_;
6121 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6122     $self->{onerror}->(node => $item->{node},
6123     type => 'multiple labelable fae',
6124     level => $self->{level}->{must});
6125     } else {
6126     $self->{flag}->{has_labelable} = 2;
6127     }
6128 wakaba 1.138
6129     $element_state->{id_type} = 'labelable';
6130 wakaba 1.66 },
6131 wakaba 1.52 };
6132    
6133 wakaba 1.178 ## XXXresource: Dimension attributes have requirements on width and
6134     ## height of referenced resource.
6135 wakaba 1.80
6136 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
6137 wakaba 1.119 %HTMLPhrasingContentChecker, ## ISSUE: -interactive?
6138     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6139 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6140 wakaba 1.165 ## ISSUE: In HTML5, no "MUST NOT" for using |action|, |method|,
6141     ## |enctype|, |target|, and |novalidate| with non-|submit|-|type|
6142     ## |button| elements.
6143 wakaba 1.56 action => $HTMLURIAttrChecker,
6144 wakaba 1.165 autofocus => $AutofocusAttrChecker,
6145 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6146 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
6147     'application/x-www-form-urlencoded' => 1,
6148     'multipart/form-data' => 1,
6149     'text/plain' => 1,
6150     }),
6151 wakaba 1.136 form => $HTMLFormAttrChecker,
6152 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
6153     get => 1, post => 1, put => 1, delete => 1,
6154     }),
6155 wakaba 1.165 name => $FormControlNameAttrChecker,
6156 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
6157 wakaba 1.162 onformchange => $HTMLEventHandlerAttrChecker, ## TODO: tests
6158     onforminput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6159 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
6160     target => $HTMLTargetAttrChecker,
6161 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
6162     ## attribute to support the |add| button type (as part of repetition
6163     ## template feature). It conflicts with the |template| global attribute
6164     ## introduced as part of the data template feature.
6165     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
6166     ## author requirement.
6167 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
6168     button => 1, submit => 1, reset => 1,
6169     }),
6170 wakaba 1.162 value => sub {}, ## NOTE: No restriction.
6171 wakaba 1.52 }, {
6172     %HTMLAttrStatus,
6173     %HTMLM12NCommonAttrStatus,
6174 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
6175 wakaba 1.119 action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6176     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6177 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6178     dataformatas => FEATURE_HTML4_REC_RESERVED,
6179     datasrc => FEATURE_HTML4_REC_RESERVED,
6180 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6181     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6182     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6183 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6184 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6185     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6186 wakaba 1.162 novalidate => FEATURE_HTML5_DEFAULT,
6187 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6188     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6189 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
6190     onforminput => FEATURE_WF2_INFORMATIVE,
6191 wakaba 1.56 replace => FEATURE_WF2,
6192 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6193 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6194 wakaba 1.162 template => FEATURE_HTML5_AT_RISK | FEATURE_WF2, ## TODO: dropped
6195 wakaba 1.119 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6196     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6197 wakaba 1.52 }),
6198 wakaba 1.66 check_start => sub {
6199     my ($self, $item, $element_state) = @_;
6200 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6201     $self->{onerror}->(node => $item->{node},
6202     type => 'multiple labelable fae',
6203     level => $self->{level}->{must});
6204     } else {
6205     $self->{flag}->{has_labelable} = 2;
6206     }
6207 wakaba 1.162
6208     ## ISSUE: "The value attribute must not be present unless the form
6209     ## [content] attribute is present.": Wrong?
6210 wakaba 1.139
6211 wakaba 1.66 $element_state->{uri_info}->{action}->{type}->{action} = 1;
6212     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6213 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6214     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6215 wakaba 1.138
6216     $element_state->{id_type} = 'labelable';
6217 wakaba 1.66 },
6218 wakaba 1.52 };
6219    
6220     $Element->{$HTML_NS}->{label} = {
6221 wakaba 1.139 %HTMLPhrasingContentChecker,
6222 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC
6223     | FEATURE_XHTML2_ED,
6224 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6225 wakaba 1.138 for => sub {
6226     my ($self, $attr) = @_;
6227    
6228     ## NOTE: MUST be an ID of a labelable element.
6229    
6230     push @{$self->{idref}}, ['labelable', $attr->value, $attr];
6231     },
6232 wakaba 1.136 form => $HTMLFormAttrChecker,
6233 wakaba 1.52 }, {
6234     %HTMLAttrStatus,
6235 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6236 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_WF2 | FEATURE_M12N10_REC,
6237 wakaba 1.119 for => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6238     form => FEATURE_HTML5_DEFAULT,
6239 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6240 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6241     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6242     }),
6243 wakaba 1.139 check_start => sub {
6244     my ($self, $item, $element_state) = @_;
6245     $self->_add_minus_elements ($element_state, {$HTML_NS => {label => 1}});
6246    
6247     $element_state->{has_label_original} = $self->{flag}->{has_label};
6248     $self->{flag}->{has_label} = 1;
6249     $element_state->{has_labelable_original} = $self->{flag}->{has_labelable};
6250 wakaba 1.155 $self->{flag}->{has_labelable}
6251     = $item->{node}->has_attribute_ns (undef, 'for') ? 1 : 0;
6252 wakaba 1.139
6253     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6254     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6255     },
6256     check_end => sub {
6257     my ($self, $item, $element_state) = @_;
6258     $self->_remove_minus_elements ($element_state);
6259    
6260     if ($self->{flag}->{has_labelable} == 1) { # has for="" but no labelable
6261     $self->{flag}->{has_labelable}
6262     = $element_state->{has_labelable_original};
6263     }
6264     delete $self->{flag}->{has_label}
6265     unless $element_state->{has_label_original};
6266     ## TODO: Warn if no labelable descendant? <input type=hidden>?
6267    
6268     ## NOTE: |<label for=a><input id=a></label>| is non-conforming.
6269    
6270     $HTMLPhrasingContentChecker{check_end}->(@_);
6271     },
6272 wakaba 1.52 ## TODO: Tests for <nest/> in <label>
6273     };
6274    
6275     $Element->{$HTML_NS}->{select} = {
6276 wakaba 1.121 %HTMLChecker,
6277 wakaba 1.163 ## ISSUE: HTML5 has no requirement like these:
6278 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
6279     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
6280 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6281 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
6282 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6283 wakaba 1.165 autofocus => $AutofocusAttrChecker,
6284 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6285 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
6286 wakaba 1.136 form => $HTMLFormAttrChecker,
6287 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
6288 wakaba 1.165 name => $FormControlNameAttrChecker,
6289 wakaba 1.163 ## TODO: tests for on*
6290 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
6291     onforminput => $HTMLEventHandlerAttrChecker,
6292     oninput => $HTMLEventHandlerAttrChecker,
6293 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
6294 wakaba 1.163 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
6295 wakaba 1.52 }, {
6296     %HTMLAttrStatus,
6297     %HTMLM12NCommonAttrStatus,
6298 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_WF2,
6299 wakaba 1.119 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6300 wakaba 1.56 data => FEATURE_WF2,
6301 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6302     dataformatas => FEATURE_HTML4_REC_RESERVED,
6303     datasrc => FEATURE_HTML4_REC_RESERVED,
6304 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6305     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6306 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6307 wakaba 1.119 multiple => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6308     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6309 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6310     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6311 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
6312     onforminput => FEATURE_WF2_INFORMATIVE,
6313 wakaba 1.52 onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6314 wakaba 1.126 oninput => FEATURE_WF2,
6315 wakaba 1.56 oninvalid => FEATURE_WF2,
6316 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6317     sdapref => FEATURE_HTML20_RFC,
6318 wakaba 1.119 size => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6319 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6320     }),
6321 wakaba 1.66 check_start => sub {
6322     my ($self, $item, $element_state) = @_;
6323 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6324     $self->{onerror}->(node => $item->{node},
6325     type => 'multiple labelable fae',
6326     level => $self->{level}->{must});
6327     } else {
6328     $self->{flag}->{has_labelable} = 2;
6329     }
6330 wakaba 1.66
6331     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6332     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6333 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6334     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6335 wakaba 1.138
6336     $element_state->{id_type} = 'labelable';
6337 wakaba 1.66 },
6338 wakaba 1.121 check_child_element => sub {
6339 wakaba 1.163 ## NOTE: (option | optgroup)*
6340    
6341 wakaba 1.121 my ($self, $item, $child_el, $child_nsuri, $child_ln,
6342     $child_is_transparent, $element_state) = @_;
6343 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6344     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6345 wakaba 1.121 $self->{onerror}->(node => $child_el,
6346     type => 'element not allowed:minus',
6347     level => $self->{level}->{must});
6348     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6349     #
6350     } elsif ($child_nsuri eq $HTML_NS and
6351     {
6352     option => 1, optgroup => 1,
6353     }->{$child_ln}) {
6354     #
6355     } else {
6356     $self->{onerror}->(node => $child_el, type => 'element not allowed',
6357     level => $self->{level}->{must});
6358     }
6359     },
6360     check_child_text => sub {
6361     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6362     if ($has_significant) {
6363     $self->{onerror}->(node => $child_node, type => 'character not allowed',
6364     level => $self->{level}->{must});
6365     }
6366     },
6367 wakaba 1.52 };
6368 wakaba 1.1
6369 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
6370 wakaba 1.121 %HTMLPhrasingContentChecker,
6371 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6372 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
6373     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
6374     }, {
6375 wakaba 1.52 %HTMLAttrStatus,
6376 wakaba 1.56 data => FEATURE_WF2,
6377 wakaba 1.52 }),
6378 wakaba 1.66 check_start => sub {
6379     my ($self, $item, $element_state) = @_;
6380    
6381 wakaba 1.121 $element_state->{phase} = 'any'; # any | phrasing | option
6382    
6383 wakaba 1.66 $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6384 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6385     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6386 wakaba 1.158
6387     $element_state->{id_type} = 'datalist';
6388 wakaba 1.66 },
6389 wakaba 1.121 ## NOTE: phrasing | option*
6390     check_child_element => sub {
6391     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6392     $child_is_transparent, $element_state) = @_;
6393 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6394     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6395 wakaba 1.121 $self->{onerror}->(node => $child_el,
6396     type => 'element not allowed:minus',
6397     level => $self->{level}->{must});
6398     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6399     #
6400     } elsif ($element_state->{phase} eq 'phrasing') {
6401     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6402     #
6403     } else {
6404     $self->{onerror}->(node => $child_el,
6405     type => 'element not allowed:phrasing',
6406     level => $self->{level}->{must});
6407     }
6408     } elsif ($element_state->{phase} eq 'option') {
6409     if ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6410     #
6411     } else {
6412     $self->{onerror}->(node => $child_el,
6413     type => 'element not allowed',
6414     level => $self->{level}->{must});
6415     }
6416     } elsif ($element_state->{phase} eq 'any') {
6417     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6418     $element_state->{phase} = 'phrasing';
6419     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6420     $element_state->{phase} = 'option';
6421     } else {
6422     $self->{onerror}->(node => $child_el,
6423     type => 'element not allowed',
6424     level => $self->{level}->{must});
6425     }
6426     } else {
6427     die "check_child_element: Bad |datalist| phase: $element_state->{phase}";
6428     }
6429     },
6430     check_child_text => sub {
6431     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6432     if ($has_significant) {
6433     if ($element_state->{phase} eq 'phrasing') {
6434     #
6435     } elsif ($element_state->{phase} eq 'any') {
6436     $element_state->{phase} = 'phrasing';
6437     } else {
6438     $self->{onerror}->(node => $child_node,
6439     type => 'character not allowed',
6440     level => $self->{level}->{must});
6441     }
6442     }
6443     },
6444     check_end => sub {
6445     my ($self, $item, $element_state) = @_;
6446     if ($element_state->{phase} eq 'phrasing') {
6447     if ($element_state->{has_significant}) {
6448     $item->{real_parent_state}->{has_significant} = 1;
6449     } elsif ($item->{transparent}) {
6450     #
6451     } else {
6452     $self->{onerror}->(node => $item->{node},
6453     type => 'no significant content',
6454     level => $self->{level}->{should});
6455     }
6456     } else {
6457     ## NOTE: Since the content model explicitly allows a |datalist| element
6458     ## being empty, we don't raise "no significant content" error for this
6459     ## element when there is no element. (We should raise an error for
6460     ## |<datalist><br></datalist>|, however.)
6461     ## NOTE: As a side-effect, when the |datalist| element only contains
6462     ## non-conforming content, then the |phase| flag has not changed from
6463     ## |any|, no "no significant content" error is raised neither.
6464     $HTMLChecker{check_end}->(@_);
6465     }
6466     },
6467 wakaba 1.52 };
6468 wakaba 1.49
6469 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
6470 wakaba 1.121 %HTMLChecker,
6471 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6472 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6473     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6474 wakaba 1.164 label => sub {},
6475 wakaba 1.52 }, {
6476     %HTMLAttrStatus,
6477     %HTMLM12NCommonAttrStatus,
6478 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6479     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6480 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6481 wakaba 1.52 }),
6482 wakaba 1.164 check_attrs2 => sub {
6483     my ($self, $item, $element_state) = @_;
6484    
6485     unless ($item->{node}->has_attribute_ns (undef, 'label')) {
6486     $self->{onerror}->(node => $item->{node},
6487     type => 'attribute missing',
6488     text => 'label',
6489     level => $self->{level}->{must});
6490     }
6491     },
6492 wakaba 1.121 check_child_element => sub {
6493     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6494     $child_is_transparent, $element_state) = @_;
6495 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6496     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6497 wakaba 1.121 $self->{onerror}->(node => $child_el,
6498     type => 'element not allowed:minus',
6499     level => $self->{level}->{must});
6500     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6501     #
6502     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6503     #
6504     } else {
6505     $self->{onerror}->(node => $child_el, type => 'element not allowed',
6506     level => $self->{level}->{must});
6507     }
6508     },
6509     check_child_text => sub {
6510     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6511     if ($has_significant) {
6512     $self->{onerror}->(node => $child_node, type => 'character not allowed',
6513     level => $self->{level}->{must});
6514     }
6515     },
6516 wakaba 1.52 };
6517    
6518     $Element->{$HTML_NS}->{option} = {
6519     %HTMLTextChecker,
6520 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6521 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6522     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6523 wakaba 1.164 label => sub {}, ## NOTE: No restriction.
6524     selected => $GetHTMLBooleanAttrChecker->('selected'), ## ISSUE: Not a "boolean attribute"
6525     value => sub {}, ## NOTE: No restriction.
6526 wakaba 1.52 }, {
6527     %HTMLAttrStatus,
6528     %HTMLM12NCommonAttrStatus,
6529 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6530     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6531 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6532 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6533     sdapref => FEATURE_HTML20_RFC,
6534 wakaba 1.119 selected => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6535     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6536 wakaba 1.52 }),
6537     };
6538 wakaba 1.49
6539 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
6540     %HTMLTextChecker,
6541 wakaba 1.121 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6542 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6543 wakaba 1.164 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type [WF2]
6544 wakaba 1.165 autofocus => $AutofocusAttrChecker,
6545 wakaba 1.164 cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
6546 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6547 wakaba 1.136 form => $HTMLFormAttrChecker,
6548 wakaba 1.56 ## TODO: inputmode [WF2]
6549 wakaba 1.164 maxlength => sub {
6550     my ($self, $attr, $item, $element_state) = @_;
6551    
6552     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
6553    
6554 wakaba 1.165 if ($attr->value =~ /^[\x09\x0A\x0C\x0D\x20]*([0-9]+)/) {
6555 wakaba 1.164 ## NOTE: Applying the rules for parsing non-negative integers
6556     ## results in a number.
6557     my $max_allowed_value_length = 0+$1;
6558    
6559     ## ISSUE: "The the purposes of this requirement," (typo)
6560    
6561     ## ISSUE: This constraint is applied w/o CRLF normalization to
6562     ## |value| attribute, but w/ CRLF normalization to
6563     ## concept-value.
6564     my $value = $item->{node}->text_content;
6565     if (defined $value) {
6566     my $codepoint_length = length $value;
6567    
6568     if ($codepoint_length > $max_allowed_value_length) {
6569     $self->{onerror}->(node => $item->{node},
6570     type => 'value too long',
6571     level => $self->{level}->{must});
6572     }
6573     }
6574     }
6575     },
6576 wakaba 1.165 name => $FormControlNameAttrChecker,
6577 wakaba 1.164 onformchange => $HTMLEventHandlerAttrChecker, ## TODO: tests
6578     onforminput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6579     oninput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6580 wakaba 1.161 pattern => $PatternAttrChecker,
6581 wakaba 1.179 placeholder => $PlaceholderAttrChecker,
6582 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
6583 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
6584 wakaba 1.164 rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
6585     oninput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6586     oninvalid => $HTMLEventHandlerAttrChecker, ## TODO: tests
6587 wakaba 1.161 ## NOTE: |title| had special semantics if |pattern| was specified [WF2].
6588 wakaba 1.56 wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
6589 wakaba 1.52 }, {
6590     %HTMLAttrStatus,
6591     %HTMLM12NCommonAttrStatus,
6592 wakaba 1.164 accept => FEATURE_HTML5_DROPPED | FEATURE_WF2X,
6593 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
6594 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
6595 wakaba 1.121 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6596     cols => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6597 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6598 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
6599     datasrc => FEATURE_HTML4_REC_RESERVED,
6600 wakaba 1.121 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6601     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6602 wakaba 1.164 inputmode => FEATURE_HTML5_DROPPED | FEATURE_WF2X | FEATURE_XHTMLBASIC11_CR,
6603 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6604 wakaba 1.121 maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6605     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6606 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6607     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6608     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6609 wakaba 1.164 onformchange => FEATURE_WF2_INFORMATIVE, ## TODO: tests
6610     onforminput => FEATURE_WF2_INFORMATIVE, ## TODO: tests
6611     oninput => FEATURE_WF2, ## TODO: tests
6612     oninvalid => FEATURE_WF2, ## TODO: tests
6613 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6614 wakaba 1.161 pattern => FEATURE_HTML5_DROPPED | FEATURE_WF2X,
6615 wakaba 1.179 placeholder => FEATURE_HTML5_LC,
6616 wakaba 1.121 readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6617     required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6618     rows => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6619 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6620     sdapref => FEATURE_HTML20_RFC,
6621 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6622 wakaba 1.121 wrap => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6623 wakaba 1.52 }),
6624 wakaba 1.66 check_start => sub {
6625     my ($self, $item, $element_state) = @_;
6626 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6627     $self->{onerror}->(node => $item->{node},
6628     type => 'multiple labelable fae',
6629     level => $self->{level}->{must});
6630     } else {
6631     $self->{flag}->{has_labelable} = 2;
6632     }
6633 wakaba 1.164
6634     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6635     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6636     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6637    
6638     $element_state->{id_type} = 'labelable';
6639     },
6640     check_attrs2 => sub {
6641     my ($self, $item, $element_state) = @_;
6642 wakaba 1.66
6643 wakaba 1.161 if ($item->{node}->has_attribute_ns (undef, 'pattern') and
6644     not $item->{node}->has_attribute_ns (undef, 'title')) {
6645     ## NOTE: WF2 (dropped by HTML5)
6646     $self->{onerror}->(node => $item->{node},
6647     type => 'attribute missing',
6648     text => 'title',
6649     level => $self->{level}->{should});
6650     }
6651    
6652 wakaba 1.164 unless ($item->{node}->has_attribute_ns (undef, 'cols')) {
6653     my $wrap = $item->{node}->get_attribute_ns (undef, 'wrap');
6654     if (defined $wrap) {
6655     $wrap =~ tr/A-Z/a-z/; ## ASCII case-insensitive
6656     if ($wrap eq 'hard') {
6657     $self->{onerror}->(node => $item->{node},
6658     type => 'attribute missing',
6659     text => 'cols',
6660     level => $self->{level}->{must});
6661     }
6662     }
6663     }
6664 wakaba 1.66 },
6665 wakaba 1.52 };
6666 wakaba 1.49
6667 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
6668 wakaba 1.121 %HTMLPhrasingContentChecker,
6669     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6670 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
6671 wakaba 1.165 for => sub {
6672     my ($self, $attr) = @_;
6673    
6674     ## NOTE: "Unordered set of unique space-separated tokens".
6675    
6676     my %word;
6677     for my $word (grep {length $_}
6678     split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
6679     unless ($word{$word}) {
6680     $word{$word} = 1;
6681     push @{$self->{idref}}, ['any', $word, $attr];
6682     } else {
6683     $self->{onerror}->(node => $attr, type => 'duplicate token',
6684     value => $word,
6685     level => $self->{level}->{must});
6686     }
6687     }
6688     },
6689 wakaba 1.136 form => $HTMLFormAttrChecker,
6690 wakaba 1.165 name => $FormControlNameAttrChecker,
6691     onformchange => $HTMLEventHandlerAttrChecker, ## TODO: tests
6692     onforminput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6693 wakaba 1.56 }, {
6694 wakaba 1.52 %HTMLAttrStatus,
6695 wakaba 1.121 for => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6696     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6697     name => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6698 wakaba 1.56 onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
6699     onformchange => FEATURE_WF2,
6700     onforminput => FEATURE_WF2,
6701 wakaba 1.52 }),
6702     };
6703    
6704     $Element->{$HTML_NS}->{isindex} = {
6705     %HTMLEmptyChecker,
6706 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
6707     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
6708 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6709     prompt => sub {}, ## NOTE: Text [M12N]
6710     }, {
6711     %HTMLAttrStatus,
6712 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6713     dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6714     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6715     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6716 wakaba 1.52 prompt => FEATURE_M12N10_REC_DEPRECATED,
6717 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
6718 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6719     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6720 wakaba 1.52 }),
6721     ## TODO: Tests
6722     ## TODO: Tests for <nest/> in <isindex>
6723 wakaba 1.66 check_start => sub {
6724     my ($self, $item, $element_state) = @_;
6725    
6726     $element_state->{uri_info}->{action}->{type}->{action} = 1;
6727 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6728     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6729 wakaba 1.66 },
6730 wakaba 1.52 };
6731 wakaba 1.49
6732 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
6733 wakaba 1.40 %HTMLChecker,
6734 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6735 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6736 wakaba 1.91 charset => sub {
6737     my ($self, $attr) = @_;
6738    
6739     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
6740     $self->{onerror}->(type => 'attribute not allowed',
6741     node => $attr,
6742 wakaba 1.104 level => $self->{level}->{must});
6743 wakaba 1.91 }
6744    
6745     $HTMLCharsetChecker->($attr->value, @_);
6746     },
6747 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
6748 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
6749 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
6750     async => $GetHTMLBooleanAttrChecker->('async'),
6751 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
6752 wakaba 1.49 }, {
6753     %HTMLAttrStatus,
6754 wakaba 1.153 async => FEATURE_HTML5_WD,
6755     charset => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6756     defer => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6757 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
6758     for => FEATURE_HTML4_REC_RESERVED,
6759 wakaba 1.154 href => FEATURE_RDFA_REC,
6760 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6761 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
6762 wakaba 1.153 src => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6763     type => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6764 wakaba 1.9 }),
6765 wakaba 1.40 check_start => sub {
6766     my ($self, $item, $element_state) = @_;
6767 wakaba 1.1
6768 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
6769     $element_state->{must_be_empty} = 1;
6770 wakaba 1.1 } else {
6771     ## NOTE: No content model conformance in HTML5 spec.
6772 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
6773     my $language = $item->{node}->get_attribute_ns (undef, 'language');
6774 wakaba 1.1 if ((defined $type and $type eq '') or
6775     (defined $language and $language eq '')) {
6776     $type = 'text/javascript';
6777     } elsif (defined $type) {
6778     #
6779     } elsif (defined $language) {
6780     $type = 'text/' . $language;
6781     } else {
6782     $type = 'text/javascript';
6783     }
6784 wakaba 1.93
6785     if ($type =~ m[\A(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*/(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*(?>;|\z)]) {
6786     $type = "$1/$2";
6787     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
6788     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
6789     }
6790     $element_state->{script_type} = $type;
6791 wakaba 1.40 }
6792 wakaba 1.66
6793     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6794 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6795     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6796 wakaba 1.107
6797     $element_state->{text} = '';
6798 wakaba 1.40 },
6799     check_child_element => sub {
6800     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6801     $child_is_transparent, $element_state) = @_;
6802 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6803     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6804 wakaba 1.40 $self->{onerror}->(node => $child_el,
6805     type => 'element not allowed:minus',
6806 wakaba 1.104 level => $self->{level}->{must});
6807 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6808     #
6809     } else {
6810     if ($element_state->{must_be_empty}) {
6811     $self->{onerror}->(node => $child_el,
6812 wakaba 1.104 type => 'element not allowed:empty',
6813     level => $self->{level}->{must});
6814 wakaba 1.40 }
6815     }
6816     },
6817     check_child_text => sub {
6818     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6819     if ($has_significant and
6820     $element_state->{must_be_empty}) {
6821     $self->{onerror}->(node => $child_node,
6822 wakaba 1.104 type => 'character not allowed:empty',
6823     level => $self->{level}->{must});
6824 wakaba 1.40 }
6825 wakaba 1.115 $element_state->{text} .= $child_node->data;
6826 wakaba 1.40 },
6827     check_end => sub {
6828     my ($self, $item, $element_state) = @_;
6829     unless ($element_state->{must_be_empty}) {
6830 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
6831     ## NOTE: XML content should be checked by THIS instance of checker
6832     ## as part of normal tree validation.
6833 wakaba 1.104 $self->{onerror}->(node => $item->{node},
6834     type => 'XML script lang',
6835     text => $element_state->{script_type},
6836     level => $self->{level}->{uncertain});
6837     ## ISSUE: Should we raise some kind of error for
6838     ## <script type="text/xml">aaaaa</script>?
6839     ## NOTE: ^^^ This is why we throw an "uncertain" error.
6840 wakaba 1.93 } else {
6841     $self->{onsubdoc}->({s => $element_state->{text},
6842     container_node => $item->{node},
6843     media_type => $element_state->{script_type},
6844     is_char_string => 1});
6845     }
6846 wakaba 1.40
6847     $HTMLChecker{check_end}->(@_);
6848 wakaba 1.1 }
6849     },
6850 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
6851     ## NOTE: "When used to include script data, the script data must be embedded
6852     ## inline, the format of the data must be given using the type attribute,
6853     ## and the src attribute must not be specified." - not testable.
6854     ## TODO: It would be possible to err <script type=text/plain src=...>
6855 wakaba 1.1 };
6856 wakaba 1.25 ## ISSUE: Significant check and text child node
6857 wakaba 1.1
6858     ## NOTE: When script is disabled.
6859     $Element->{$HTML_NS}->{noscript} = {
6860 wakaba 1.40 %HTMLTransparentChecker,
6861 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
6862 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
6863     %HTMLAttrStatus,
6864     %HTMLM12NCommonAttrStatus,
6865 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6866 wakaba 1.49 }),
6867 wakaba 1.40 check_start => sub {
6868     my ($self, $item, $element_state) = @_;
6869 wakaba 1.3
6870 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
6871 wakaba 1.104 $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript',
6872     level => $self->{level}->{must});
6873 wakaba 1.3 }
6874    
6875 wakaba 1.40 unless ($self->{flag}->{in_head}) {
6876     $self->_add_minus_elements ($element_state,
6877     {$HTML_NS => {noscript => 1}});
6878     }
6879 wakaba 1.79
6880     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6881     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6882 wakaba 1.3 },
6883 wakaba 1.40 check_child_element => sub {
6884     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6885     $child_is_transparent, $element_state) = @_;
6886     if ($self->{flag}->{in_head}) {
6887 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6888     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6889 wakaba 1.40 $self->{onerror}->(node => $child_el,
6890     type => 'element not allowed:minus',
6891 wakaba 1.104 level => $self->{level}->{must});
6892 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6893     #
6894     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
6895     #
6896     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
6897     if ($child_el->has_attribute_ns (undef, 'scoped')) {
6898     $self->{onerror}->(node => $child_el,
6899     type => 'element not allowed:head noscript',
6900 wakaba 1.104 level => $self->{level}->{must});
6901 wakaba 1.40 }
6902     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
6903 wakaba 1.47 my $http_equiv_attr
6904     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
6905     if ($http_equiv_attr) {
6906     ## TODO: case
6907     if (lc $http_equiv_attr->value eq 'content-type') {
6908 wakaba 1.40 $self->{onerror}->(node => $child_el,
6909 wakaba 1.34 type => 'element not allowed:head noscript',
6910 wakaba 1.104 level => $self->{level}->{must});
6911 wakaba 1.47 } else {
6912     #
6913 wakaba 1.3 }
6914 wakaba 1.47 } else {
6915     $self->{onerror}->(node => $child_el,
6916     type => 'element not allowed:head noscript',
6917 wakaba 1.104 level => $self->{level}->{must});
6918 wakaba 1.3 }
6919 wakaba 1.40 } else {
6920     $self->{onerror}->(node => $child_el,
6921     type => 'element not allowed:head noscript',
6922 wakaba 1.104 level => $self->{level}->{must});
6923 wakaba 1.40 }
6924     } else {
6925     $HTMLTransparentChecker{check_child_element}->(@_);
6926     }
6927     },
6928     check_child_text => sub {
6929     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6930     if ($self->{flag}->{in_head}) {
6931     if ($has_significant) {
6932     $self->{onerror}->(node => $child_node,
6933 wakaba 1.104 type => 'character not allowed',
6934     level => $self->{level}->{must});
6935 wakaba 1.3 }
6936     } else {
6937 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
6938     }
6939     },
6940     check_end => sub {
6941     my ($self, $item, $element_state) = @_;
6942     $self->_remove_minus_elements ($element_state);
6943     if ($self->{flag}->{in_head}) {
6944     $HTMLChecker{check_end}->(@_);
6945     } else {
6946     $HTMLPhrasingContentChecker{check_end}->(@_);
6947 wakaba 1.3 }
6948 wakaba 1.1 },
6949     };
6950 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
6951 wakaba 1.1
6952     $Element->{$HTML_NS}->{'event-source'} = {
6953 wakaba 1.40 %HTMLEmptyChecker,
6954 wakaba 1.118 status => FEATURE_HTML5_LC_DROPPED,
6955     check_attrs => $GetHTMLAttrsChecker->({
6956     src => $HTMLURIAttrChecker,
6957     }, {
6958     %HTMLAttrStatus,
6959     src => FEATURE_HTML5_LC_DROPPED,
6960     }),
6961     check_start => sub {
6962     my ($self, $item, $element_state) = @_;
6963    
6964     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6965     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6966     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6967     },
6968     };
6969    
6970     $Element->{$HTML_NS}->{eventsource} = {
6971     %HTMLEmptyChecker,
6972 wakaba 1.180 status => FEATURE_HTML5_DROPPED,
6973 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6974 wakaba 1.1 src => $HTMLURIAttrChecker,
6975 wakaba 1.50 }, {
6976     %HTMLAttrStatus,
6977 wakaba 1.180 src => FEATURE_HTML5_DROPPED,
6978 wakaba 1.1 }),
6979 wakaba 1.66 check_start => sub {
6980     my ($self, $item, $element_state) = @_;
6981    
6982     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6983 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6984     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6985 wakaba 1.66 },
6986 wakaba 1.1 };
6987    
6988     $Element->{$HTML_NS}->{details} = {
6989 wakaba 1.134 %{$Element->{$HTML_NS}->{fieldset}},
6990 wakaba 1.153 status => FEATURE_HTML5_LC,
6991 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6992 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
6993 wakaba 1.50 }, {
6994     %HTMLAttrStatus,
6995 wakaba 1.153 open => FEATURE_HTML5_LC,
6996 wakaba 1.1 }),
6997     };
6998    
6999     $Element->{$HTML_NS}->{datagrid} = {
7000 wakaba 1.72 %HTMLFlowContentChecker,
7001 wakaba 1.48 status => FEATURE_HTML5_WD,
7002 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7003 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
7004     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
7005 wakaba 1.50 }, {
7006     %HTMLAttrStatus,
7007     disabled => FEATURE_HTML5_WD,
7008     multiple => FEATURE_HTML5_WD,
7009 wakaba 1.1 }),
7010 wakaba 1.40 check_start => sub {
7011     my ($self, $item, $element_state) = @_;
7012 wakaba 1.1
7013 wakaba 1.40 $self->_add_minus_elements ($element_state,
7014     {$HTML_NS => {a => 1, datagrid => 1}});
7015 wakaba 1.172
7016 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7017     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7018 wakaba 1.40 },
7019     check_end => sub {
7020     my ($self, $item, $element_state) = @_;
7021     $self->_remove_minus_elements ($element_state);
7022 wakaba 1.1
7023 wakaba 1.172 $HTMLFlowContentChecker{check_end}->(@_);
7024 wakaba 1.40 },
7025 wakaba 1.1 };
7026    
7027     $Element->{$HTML_NS}->{command} = {
7028 wakaba 1.40 %HTMLEmptyChecker,
7029 wakaba 1.48 status => FEATURE_HTML5_WD,
7030 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7031 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
7032     default => $GetHTMLBooleanAttrChecker->('default'),
7033     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
7034     icon => $HTMLURIAttrChecker,
7035     label => sub { }, ## NOTE: No conformance creteria
7036     radiogroup => sub { }, ## NOTE: No conformance creteria
7037     type => sub {
7038     my ($self, $attr) = @_;
7039     my $value = $attr->value;
7040     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
7041 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
7042     level => $self->{level}->{must});
7043 wakaba 1.1 }
7044     },
7045 wakaba 1.50 }, {
7046     %HTMLAttrStatus,
7047     checked => FEATURE_HTML5_WD,
7048 wakaba 1.175 default => FEATURE_HTML5_DROPPED, # HTML5 revision 3067
7049 wakaba 1.50 disabled => FEATURE_HTML5_WD,
7050     icon => FEATURE_HTML5_WD,
7051     label => FEATURE_HTML5_WD,
7052     radiogroup => FEATURE_HTML5_WD,
7053     type => FEATURE_HTML5_WD,
7054 wakaba 1.1 }),
7055 wakaba 1.66 check_start => sub {
7056     my ($self, $item, $element_state) = @_;
7057    
7058     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
7059 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7060     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7061 wakaba 1.66 },
7062 wakaba 1.115 };
7063    
7064     $Element->{$HTML_NS}->{bb} = {
7065     %HTMLPhrasingContentChecker,
7066 wakaba 1.153 status => FEATURE_HTML5_WD,
7067 wakaba 1.115 check_attrs => $GetHTMLAttrsChecker->({
7068     type => $GetHTMLEnumeratedAttrChecker->({makeapp => 1}),
7069     }, {
7070     %HTMLAttrStatus,
7071 wakaba 1.153 type => FEATURE_HTML5_WD,
7072 wakaba 1.115 }),
7073 wakaba 1.130 check_start => sub {
7074     my ($self, $item, $element_state) = @_;
7075     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
7076    
7077     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7078     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7079     },
7080     check_end => sub {
7081     my ($self, $item, $element_state) = @_;
7082     $self->_remove_minus_elements ($element_state);
7083    
7084     $HTMLTransparentChecker{check_end}->(@_);
7085     },
7086 wakaba 1.1 };
7087    
7088     $Element->{$HTML_NS}->{menu} = {
7089 wakaba 1.40 %HTMLPhrasingContentChecker,
7090 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
7091     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
7092     ## NOTE: We don't want any |menu| element warned as deprecated.
7093 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7094 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
7095 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
7096 wakaba 1.135 ## ISSUE: <menu id=""><p contextmenu=""> match? (In the current
7097     ## implementation, it does not match.)
7098 wakaba 1.1 label => sub { }, ## NOTE: No conformance creteria
7099     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
7100 wakaba 1.49 }, {
7101     %HTMLAttrStatus,
7102     %HTMLM12NCommonAttrStatus,
7103 wakaba 1.61 align => FEATURE_HTML2X_RFC,
7104 wakaba 1.113 autosubmit => FEATURE_HTML5_DROPPED,
7105 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
7106 wakaba 1.50 label => FEATURE_HTML5_WD,
7107 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7108 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
7109     sdapref => FEATURE_HTML20_RFC,
7110 wakaba 1.50 type => FEATURE_HTML5_WD,
7111 wakaba 1.1 }),
7112 wakaba 1.40 check_start => sub {
7113     my ($self, $item, $element_state) = @_;
7114     $element_state->{phase} = 'li or phrasing';
7115     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
7116     $self->{flag}->{in_menu} = 1;
7117 wakaba 1.79
7118     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7119     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7120 wakaba 1.135 $element_state->{id_type} = 'menu';
7121 wakaba 1.40 },
7122     check_child_element => sub {
7123     my ($self, $item, $child_el, $child_nsuri, $child_ln,
7124     $child_is_transparent, $element_state) = @_;
7125 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
7126     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
7127 wakaba 1.40 $self->{onerror}->(node => $child_el,
7128     type => 'element not allowed:minus',
7129 wakaba 1.104 level => $self->{level}->{must});
7130 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
7131     #
7132     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
7133     if ($element_state->{phase} eq 'li') {
7134     #
7135     } elsif ($element_state->{phase} eq 'li or phrasing') {
7136     $element_state->{phase} = 'li';
7137     } else {
7138 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
7139     level => $self->{level}->{must});
7140 wakaba 1.40 }
7141     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
7142     if ($element_state->{phase} eq 'phrasing') {
7143     #
7144     } elsif ($element_state->{phase} eq 'li or phrasing') {
7145     $element_state->{phase} = 'phrasing';
7146     } else {
7147 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
7148     level => $self->{level}->{must});
7149 wakaba 1.40 }
7150     } else {
7151 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
7152     level => $self->{level}->{must});
7153 wakaba 1.40 }
7154     },
7155     check_child_text => sub {
7156     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
7157     if ($has_significant) {
7158     if ($element_state->{phase} eq 'phrasing') {
7159     #
7160     } elsif ($element_state->{phase} eq 'li or phrasing') {
7161     $element_state->{phase} = 'phrasing';
7162     } else {
7163     $self->{onerror}->(node => $child_node,
7164 wakaba 1.104 type => 'character not allowed',
7165     level => $self->{level}->{must});
7166 wakaba 1.1 }
7167     }
7168 wakaba 1.40 },
7169     check_end => sub {
7170     my ($self, $item, $element_state) = @_;
7171     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
7172    
7173     if ($element_state->{phase} eq 'li') {
7174     $HTMLChecker{check_end}->(@_);
7175     } else { # 'phrasing' or 'li or phrasing'
7176     $HTMLPhrasingContentChecker{check_end}->(@_);
7177 wakaba 1.1 }
7178     },
7179 wakaba 1.8 };
7180    
7181     $Element->{$HTML_NS}->{datatemplate} = {
7182 wakaba 1.40 %HTMLChecker,
7183 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7184 wakaba 1.40 check_child_element => sub {
7185     my ($self, $item, $child_el, $child_nsuri, $child_ln,
7186     $child_is_transparent, $element_state) = @_;
7187 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
7188     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
7189 wakaba 1.40 $self->{onerror}->(node => $child_el,
7190     type => 'element not allowed:minus',
7191 wakaba 1.104 level => $self->{level}->{must});
7192 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
7193     #
7194     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
7195     #
7196     } else {
7197     $self->{onerror}->(node => $child_el,
7198 wakaba 1.104 type => 'element not allowed:datatemplate',
7199     level => $self->{level}->{must});
7200 wakaba 1.40 }
7201     },
7202     check_child_text => sub {
7203     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
7204     if ($has_significant) {
7205 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
7206     level => $self->{level}->{must});
7207 wakaba 1.8 }
7208     },
7209     is_xml_root => 1,
7210     };
7211    
7212     $Element->{$HTML_NS}->{rule} = {
7213 wakaba 1.40 %HTMLChecker,
7214 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7215 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7216 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
7217 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
7218 wakaba 1.50 }, {
7219     %HTMLAttrStatus,
7220     condition => FEATURE_HTML5_AT_RISK,
7221     mode => FEATURE_HTML5_AT_RISK,
7222 wakaba 1.8 }),
7223 wakaba 1.40 check_start => sub {
7224     my ($self, $item, $element_state) = @_;
7225 wakaba 1.79
7226 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
7227 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
7228     $self->{flag}->{in_rule} = 1;
7229    
7230     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7231     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7232 wakaba 1.40 },
7233     check_child_element => sub { },
7234     check_child_text => sub { },
7235     check_end => sub {
7236     my ($self, $item, $element_state) = @_;
7237 wakaba 1.79
7238 wakaba 1.40 $self->_remove_plus_elements ($element_state);
7239 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
7240    
7241 wakaba 1.40 $HTMLChecker{check_end}->(@_);
7242 wakaba 1.8 },
7243     ## NOTE: "MAY be anything that, when the parent |datatemplate|
7244     ## is applied to some conforming data, results in a conforming DOM tree.":
7245     ## We don't check against this.
7246     };
7247    
7248     $Element->{$HTML_NS}->{nest} = {
7249 wakaba 1.40 %HTMLEmptyChecker,
7250 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7251 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7252 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
7253     mode => sub {
7254     my ($self, $attr) = @_;
7255     my $value = $attr->value;
7256 wakaba 1.132 if ($value !~ /\A[^\x09\x0A\x0C\x0D\x20]+\z/) {
7257 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'mode:syntax error',
7258     level => $self->{level}->{must});
7259 wakaba 1.23 }
7260     },
7261 wakaba 1.50 }, {
7262     %HTMLAttrStatus,
7263     filter => FEATURE_HTML5_AT_RISK,
7264     mode => FEATURE_HTML5_AT_RISK,
7265 wakaba 1.8 }),
7266 wakaba 1.1 };
7267    
7268     $Element->{$HTML_NS}->{legend} = {
7269 wakaba 1.40 %HTMLPhrasingContentChecker,
7270 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
7271 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
7272     # align => $GetHTMLEnumeratedAttrChecker->({
7273     # top => 1, bottom => 1, left => 1, right => 1,
7274     # }),
7275 wakaba 1.167 form => $HTMLFormAttrChecker,
7276 wakaba 1.52 }, {
7277 wakaba 1.49 %HTMLAttrStatus,
7278     %HTMLM12NCommonAttrStatus,
7279 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
7280 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
7281 wakaba 1.167 form => FEATURE_HTML5_DROPPED,
7282 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7283 wakaba 1.49 }),
7284 wakaba 1.170 check_child_element => sub {
7285     my ($self, $item, $child_el, $child_nsuri, $child_ln,
7286     $child_is_transparent, $element_state) = @_;
7287     if ($item->{parent_state}->{in_figure}) {
7288     $HTMLFlowContentChecker{check_child_element}->(@_);
7289     } else {
7290     $HTMLPhrasingContentChecker{check_child_element}->(@_);
7291     }
7292     },
7293     check_child_text => sub {
7294     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
7295     if ($item->{parent_state}->{in_figure}) {
7296     $HTMLFlowContentChecker{check_child_text}->(@_);
7297     } else {
7298     $HTMLPhrasingContentChecker{check_child_text}->(@_);
7299     }
7300     },
7301     check_start => sub {
7302     my ($self, $item, $element_state) = @_;
7303     $self->_add_minus_elements ($element_state, {$HTML_NS => {figure => 1}});
7304    
7305     $HTMLFlowContentChecker{check_start}->(@_);
7306     },
7307     check_end => sub {
7308     my ($self, $item, $element_state) = @_;
7309     $self->_remove_minus_elements ($element_state);
7310    
7311     $HTMLFlowContentChecker{check_end}->(@_);
7312     },
7313     }; # legend
7314 wakaba 1.1
7315     $Element->{$HTML_NS}->{div} = {
7316 wakaba 1.72 %HTMLFlowContentChecker,
7317 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
7318 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
7319     align => $GetHTMLEnumeratedAttrChecker->({
7320     left => 1, center => 1, right => 1, justify => 1,
7321     }),
7322     }, {
7323 wakaba 1.49 %HTMLAttrStatus,
7324 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
7325 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
7326     datafld => FEATURE_HTML4_REC_RESERVED,
7327     dataformatas => FEATURE_HTML4_REC_RESERVED,
7328     datasrc => FEATURE_HTML4_REC_RESERVED,
7329 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7330 wakaba 1.49 }),
7331 wakaba 1.66 check_start => sub {
7332     my ($self, $item, $element_state) = @_;
7333    
7334     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
7335 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7336     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7337 wakaba 1.66 },
7338 wakaba 1.1 };
7339    
7340 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
7341 wakaba 1.72 %HTMLFlowContentChecker,
7342 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
7343     check_attrs => $GetHTMLAttrsChecker->({}, {
7344     %HTMLAttrStatus,
7345     %HTMLM12NCommonAttrStatus,
7346 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7347 wakaba 1.64 }),
7348     };
7349    
7350 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
7351 wakaba 1.40 %HTMLTransparentChecker,
7352 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
7353 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
7354     ## TODO: HTML4 |size|, |color|, |face|
7355 wakaba 1.49 }, {
7356     %HTMLAttrStatus,
7357 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7358 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
7359 wakaba 1.153 dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7360 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
7361 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7362     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7363 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
7364 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7365     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7366 wakaba 1.49 }),
7367 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
7368     ## it is allowed only in a document with the WYSIWYG signature. The
7369     ## checker does not check whether there is the signature, since the
7370     ## signature is dropped, too, and has never been implemented. (In addition,
7371     ## for any |font| element an "element not defined" error is raised anyway,
7372     ## such that we don't have to raise an additional error.)
7373 wakaba 1.1 };
7374 wakaba 1.49
7375 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
7376     %HTMLEmptyChecker,
7377     status => FEATURE_M12N10_REC_DEPRECATED,
7378     check_attrs => $GetHTMLAttrsChecker->({
7379     ## TODO: color, face, size
7380     }, {
7381     %HTMLAttrStatus,
7382     color => FEATURE_M12N10_REC_DEPRECATED,
7383     face => FEATURE_M12N10_REC_DEPRECATED,
7384 wakaba 1.153 #id => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
7385     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7386 wakaba 1.64 size => FEATURE_M12N10_REC_DEPRECATED,
7387     }),
7388     };
7389    
7390 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
7391     ## class title id cols rows onload onunload style(x10)
7392     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
7393     ## noframes Common, lang(xhtml10)
7394    
7395 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
7396 wakaba 1.56
7397 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
7398     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
7399     ## xmp, listing sdapref[HTML2,0]
7400    
7401 wakaba 1.56 =pod
7402    
7403 wakaba 1.61 HTML 2.0 nextid @n
7404    
7405     RFC 2659: CERTS CRYPTOPTS
7406    
7407     ISO-HTML: pre-html, divN
7408 wakaba 1.82
7409     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
7410     di (Common), nl (Common), handler (Common, type), standby (Common),
7411     summary (Common)
7412    
7413 wakaba 1.97 Access & XHTML2: access (LC)
7414 wakaba 1.82
7415     XML Events & XForms (for XHTML2 support; very, very low priority)
7416 wakaba 1.61
7417 wakaba 1.56 =cut
7418 wakaba 1.61
7419     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
7420     ## We added them only to |a|. |link| and |form| might also allow them
7421     ## in theory.
7422 wakaba 1.1
7423     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
7424    
7425     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24