/[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.179 - (hide annotations) (download)
Sat Jul 25 03:45:24 2009 UTC (15 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.178: +12 -8 lines
++ whatpm/t/dom-conformance/ChangeLog	25 Jul 2009 03:44:33 -0000
	* html-form-textarea.dat: Added tests on |textarea|'s
	|placeholder| attribute (cf. HTML5 revision 2921).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	25 Jul 2009 03:44:58 -0000
	* HTML.pm: Added support for |textarea| element's |placeholder|
	attribute (HTML5 revision 2921).

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     onkeyup onload onmessage onmousedown onmousemove onmouseout
1509     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     /) {
1519     $HTMLAttrChecker->{$_} = $HTMLEventHandlerAttrChecker;
1520     $HTMLAttrStatus{$_} = FEATURE_HTML5_DROPPED;
1521     }
1522    
1523 wakaba 1.82 ## NOTE: Non-standard global attributes in the HTML namespace.
1524     $AttrChecker->{$HTML_NS}->{''} = sub {}; # no syntactical checks
1525     $AttrStatus->{$HTML_NS}->{''} = 0; # disallowed and not part of any standard
1526    
1527     $AttrStatus->{$HTML_NS}->{active} = FEATURE_HTML5_DROPPED;
1528     for (qw/repeat repeat-max repeat-min repeat-start repeat-template/) {
1529     $AttrChecker->{$HTML_NS}->{$_} = $HTMLAttrChecker->{$_};
1530     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_WF2;
1531     }
1532    
1533 wakaba 1.120 for (qw/about content datatype property rel resource rev/) {
1534 wakaba 1.154 $AttrStatus->{$HTML_NS}->{$_} = FEATURE_RDFA_REC | FEATURE_XHTML2_ED;
1535 wakaba 1.82 }
1536 wakaba 1.154 $AttrStatus->{$HTML_NS}->{instanceof} = FEATURE_RDFA_LC_DROPPED | FEATURE_XHTML2_ED;
1537     $AttrStatus->{$HTML_NS}->{typeof} = FEATURE_RDFA_REC;
1538 wakaba 1.82 $AttrStatus->{$HTML_NS}->{role} = FEATURE_ROLE_LC;
1539     for (qw/cite coords datetime edit encoding href hreflang hrefmedia hreftype
1540     ismap layout media nextfocus prevfocus shape src srctype style
1541     target usemap/) {
1542     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_XHTML2_ED;
1543     }
1544     for (qw/class dir id title/) {
1545     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC | FEATURE_XHTML2_ED;
1546     }
1547     for (qw/onclick ondblclick onmousedown onmouseup onmouseover onmousemove
1548     onmouseout onkeypress onkeydown onkeyup/) {
1549     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC;
1550     }
1551    
1552 wakaba 1.73 my $HTMLDatasetAttrChecker = sub {
1553     ## NOTE: "Authors should ... when the attributes are ignored and
1554     ## any associated CSS dropped, the page is still usable." (semantic
1555     ## constraint.)
1556     }; # $HTMLDatasetAttrChecker
1557    
1558 wakaba 1.153 my $HTMLDatasetAttrStatus = FEATURE_HTML5_WD;
1559 wakaba 1.73
1560 wakaba 1.1 my $GetHTMLAttrsChecker = sub {
1561     my $element_specific_checker = shift;
1562 wakaba 1.49 my $element_specific_status = shift;
1563 wakaba 1.1 return sub {
1564 wakaba 1.40 my ($self, $item, $element_state) = @_;
1565     for my $attr (@{$item->{node}->attributes}) {
1566 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
1567     $attr_ns = '' unless defined $attr_ns;
1568     my $attr_ln = $attr->manakai_local_name;
1569     my $checker;
1570 wakaba 1.73 my $status;
1571 wakaba 1.1 if ($attr_ns eq '') {
1572 wakaba 1.122 if ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
1573     $attr_ln !~ /[A-Z]/) {
1574 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
1575     $status = $HTMLDatasetAttrStatus;
1576     } else {
1577     $checker = $element_specific_checker->{$attr_ln}
1578     || $HTMLAttrChecker->{$attr_ln};
1579     $status = $element_specific_status->{$attr_ln};
1580     }
1581 wakaba 1.1 }
1582     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
1583 wakaba 1.40 || $AttrChecker->{$attr_ns}->{''};
1584 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
1585     || $AttrStatus->{$attr_ns}->{''};
1586     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
1587 wakaba 1.1 if ($checker) {
1588 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
1589 wakaba 1.62 } elsif ($attr_ns eq '' and not $element_specific_status->{$attr_ln}) {
1590 wakaba 1.54 #
1591 wakaba 1.1 } else {
1592 wakaba 1.104 $self->{onerror}->(node => $attr,
1593     type => 'unknown attribute',
1594     level => $self->{level}->{uncertain});
1595 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
1596     }
1597 wakaba 1.82 $self->_attr_status_info ($attr, $status);
1598 wakaba 1.1 }
1599     };
1600     }; # $GetHTMLAttrsChecker
1601    
1602 wakaba 1.40 my %HTMLChecker = (
1603     %Whatpm::ContentChecker::AnyChecker,
1604 wakaba 1.79 check_start => sub {
1605     my ($self, $item, $element_state) = @_;
1606    
1607     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1608     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1609     },
1610 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, \%HTMLAttrStatus),
1611 wakaba 1.40 );
1612    
1613     my %HTMLEmptyChecker = (
1614     %HTMLChecker,
1615     check_child_element => sub {
1616     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1617     $child_is_transparent, $element_state) = @_;
1618 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1619     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1620 wakaba 1.40 $self->{onerror}->(node => $child_el,
1621     type => 'element not allowed:minus',
1622 wakaba 1.104 level => $self->{level}->{must});
1623 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1624     #
1625     } else {
1626     $self->{onerror}->(node => $child_el,
1627     type => 'element not allowed:empty',
1628 wakaba 1.104 level => $self->{level}->{must});
1629 wakaba 1.40 }
1630     },
1631     check_child_text => sub {
1632     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1633     if ($has_significant) {
1634     $self->{onerror}->(node => $child_node,
1635     type => 'character not allowed:empty',
1636 wakaba 1.104 level => $self->{level}->{must});
1637 wakaba 1.40 }
1638     },
1639     );
1640    
1641     my %HTMLTextChecker = (
1642     %HTMLChecker,
1643     check_child_element => sub {
1644     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1645     $child_is_transparent, $element_state) = @_;
1646 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1647     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1648 wakaba 1.40 $self->{onerror}->(node => $child_el,
1649     type => 'element not allowed:minus',
1650 wakaba 1.104 level => $self->{level}->{must});
1651 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1652     #
1653     } else {
1654 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed:text',
1655     level => $self->{level}->{must});
1656 wakaba 1.40 }
1657     },
1658     );
1659    
1660 wakaba 1.72 my %HTMLFlowContentChecker = (
1661 wakaba 1.40 %HTMLChecker,
1662     check_child_element => sub {
1663     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1664     $child_is_transparent, $element_state) = @_;
1665 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1666     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1667 wakaba 1.40 $self->{onerror}->(node => $child_el,
1668     type => 'element not allowed:minus',
1669 wakaba 1.104 level => $self->{level}->{must});
1670 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1671     #
1672     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1673     if ($element_state->{has_non_style} or
1674     not $child_el->has_attribute_ns (undef, 'scoped')) {
1675 wakaba 1.104 $self->{onerror}->(node => $child_el,
1676 wakaba 1.72 type => 'element not allowed:flow style',
1677 wakaba 1.104 level => $self->{level}->{must});
1678 wakaba 1.40 }
1679 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
1680 wakaba 1.43 $element_state->{has_non_style} = 1 unless $child_is_transparent;
1681 wakaba 1.40 } else {
1682     $element_state->{has_non_style} = 1;
1683 wakaba 1.104 $self->{onerror}->(node => $child_el,
1684 wakaba 1.72 type => 'element not allowed:flow',
1685 wakaba 1.104 level => $self->{level}->{must})
1686 wakaba 1.40 }
1687     },
1688     check_child_text => sub {
1689     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1690     if ($has_significant) {
1691     $element_state->{has_non_style} = 1;
1692     }
1693     },
1694     check_end => sub {
1695     my ($self, $item, $element_state) = @_;
1696 wakaba 1.95 ## NOTE: A modified copy of the code below is in |datagrid| checker.
1697 wakaba 1.40 if ($element_state->{has_significant}) {
1698 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
1699 wakaba 1.40 } elsif ($item->{transparent}) {
1700     #
1701     } else {
1702     $self->{onerror}->(node => $item->{node},
1703 wakaba 1.104 level => $self->{level}->{should},
1704 wakaba 1.40 type => 'no significant content');
1705     }
1706     },
1707     );
1708    
1709     my %HTMLPhrasingContentChecker = (
1710     %HTMLChecker,
1711     check_child_element => sub {
1712     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1713     $child_is_transparent, $element_state) = @_;
1714 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1715     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1716 wakaba 1.40 $self->{onerror}->(node => $child_el,
1717     type => 'element not allowed:minus',
1718 wakaba 1.104 level => $self->{level}->{must});
1719 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1720     #
1721     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
1722     #
1723     } else {
1724     $self->{onerror}->(node => $child_el,
1725     type => 'element not allowed:phrasing',
1726 wakaba 1.104 level => $self->{level}->{must});
1727 wakaba 1.40 }
1728     },
1729 wakaba 1.72 check_end => $HTMLFlowContentChecker{check_end},
1730 wakaba 1.40 ## NOTE: The definition for |li| assumes that the only differences
1731 wakaba 1.72 ## between flow and phrasing content checkers are |check_child_element|
1732 wakaba 1.40 ## and |check_child_text|.
1733     );
1734    
1735 wakaba 1.72 my %HTMLTransparentChecker = %HTMLFlowContentChecker;
1736 wakaba 1.40 ## ISSUE: Significant content rule should be applied to transparent element
1737 wakaba 1.46 ## with parent?
1738 wakaba 1.40
1739 wakaba 1.1 our $Element;
1740     our $ElementDefault;
1741    
1742     $Element->{$HTML_NS}->{''} = {
1743 wakaba 1.40 %HTMLChecker,
1744 wakaba 1.1 };
1745    
1746     $Element->{$HTML_NS}->{html} = {
1747 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1748 wakaba 1.1 is_root => 1,
1749 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
1750 wakaba 1.16 manifest => $HTMLURIAttrChecker,
1751 wakaba 1.67 version => sub {
1752     ## NOTE: According to HTML4 prose, this is a "cdata" attribute.
1753     ## Though DTDs of various versions of HTML define the attribute
1754     ## as |#FIXED|, this conformance checker does no check for
1755     ## the attribute value, since what kind of check should be done
1756     ## is unknown.
1757     },
1758 wakaba 1.49 }, {
1759     %HTMLAttrStatus,
1760 wakaba 1.82 %XHTML2CommonAttrStatus,
1761 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1762     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1763     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1764     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
1765     manifest => FEATURE_HTML5_WD,
1766 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1767 wakaba 1.82 version => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1768 wakaba 1.1 }),
1769 wakaba 1.40 check_start => sub {
1770     my ($self, $item, $element_state) = @_;
1771     $element_state->{phase} = 'before head';
1772 wakaba 1.79
1773 wakaba 1.66 $element_state->{uri_info}->{manifest}->{type}->{resource} = 1;
1774 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1775     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1776 wakaba 1.40 },
1777     check_child_element => sub {
1778     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1779     $child_is_transparent, $element_state) = @_;
1780 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1781     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1782 wakaba 1.40 $self->{onerror}->(node => $child_el,
1783     type => 'element not allowed:minus',
1784 wakaba 1.104 level => $self->{level}->{must});
1785 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1786     #
1787     } elsif ($element_state->{phase} eq 'before head') {
1788     if ($child_nsuri eq $HTML_NS and $child_ln eq 'head') {
1789     $element_state->{phase} = 'after head';
1790     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1791     $self->{onerror}->(node => $child_el,
1792 wakaba 1.104 type => 'ps element missing',
1793     text => 'head',
1794     level => $self->{level}->{must});
1795 wakaba 1.40 $element_state->{phase} = 'after body';
1796     } else {
1797     $self->{onerror}->(node => $child_el,
1798 wakaba 1.104 type => 'element not allowed',
1799     level => $self->{level}->{must});
1800 wakaba 1.40 }
1801     } elsif ($element_state->{phase} eq 'after head') {
1802     if ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1803     $element_state->{phase} = 'after body';
1804     } else {
1805     $self->{onerror}->(node => $child_el,
1806 wakaba 1.104 type => 'element not allowed',
1807     level => $self->{level}->{must});
1808 wakaba 1.40 }
1809     } elsif ($element_state->{phase} eq 'after body') {
1810     $self->{onerror}->(node => $child_el,
1811 wakaba 1.104 type => 'element not allowed',
1812     level => $self->{level}->{must});
1813 wakaba 1.40 } else {
1814     die "check_child_element: Bad |html| phase: $element_state->{phase}";
1815     }
1816     },
1817     check_child_text => sub {
1818     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1819     if ($has_significant) {
1820     $self->{onerror}->(node => $child_node,
1821 wakaba 1.104 type => 'character not allowed',
1822     level => $self->{level}->{must});
1823 wakaba 1.40 }
1824     },
1825     check_end => sub {
1826     my ($self, $item, $element_state) = @_;
1827     if ($element_state->{phase} eq 'after body') {
1828     #
1829     } elsif ($element_state->{phase} eq 'before head') {
1830     $self->{onerror}->(node => $item->{node},
1831 wakaba 1.104 type => 'child element missing',
1832     text => 'head',
1833     level => $self->{level}->{must});
1834 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1835 wakaba 1.104 type => 'child element missing',
1836     text => 'body',
1837     level => $self->{level}->{must});
1838 wakaba 1.40 } elsif ($element_state->{phase} eq 'after head') {
1839     $self->{onerror}->(node => $item->{node},
1840 wakaba 1.104 type => 'child element missing',
1841     text => 'body',
1842     level => $self->{level}->{must});
1843 wakaba 1.40 } else {
1844     die "check_end: Bad |html| phase: $element_state->{phase}";
1845     }
1846 wakaba 1.1
1847 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1848     },
1849     };
1850 wakaba 1.25
1851 wakaba 1.40 $Element->{$HTML_NS}->{head} = {
1852 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1853 wakaba 1.67 check_attrs => $GetHTMLAttrsChecker->({
1854     profile => $HTMLSpaceURIsAttrChecker, ## NOTE: MUST be profile URIs.
1855     }, {
1856 wakaba 1.49 %HTMLAttrStatus,
1857 wakaba 1.82 %XHTML2CommonAttrStatus,
1858 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1859     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1860     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1861     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
1862 wakaba 1.49 profile => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1863     }),
1864 wakaba 1.40 check_child_element => sub {
1865     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1866     $child_is_transparent, $element_state) = @_;
1867 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
1868     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
1869 wakaba 1.40 $self->{onerror}->(node => $child_el,
1870     type => 'element not allowed:minus',
1871 wakaba 1.104 level => $self->{level}->{must});
1872 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1873     #
1874     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'title') {
1875     unless ($element_state->{has_title}) {
1876     $element_state->{has_title} = 1;
1877     } else {
1878     $self->{onerror}->(node => $child_el,
1879     type => 'element not allowed:head title',
1880 wakaba 1.104 level => $self->{level}->{must});
1881 wakaba 1.40 }
1882     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1883     if ($child_el->has_attribute_ns (undef, 'scoped')) {
1884     $self->{onerror}->(node => $child_el,
1885     type => 'element not allowed:head style',
1886 wakaba 1.104 level => $self->{level}->{must});
1887 wakaba 1.1 }
1888 wakaba 1.40 } elsif ($HTMLMetadataContent->{$child_nsuri}->{$child_ln}) {
1889     #
1890    
1891     ## NOTE: |meta| is a metadata content. However, strictly speaking,
1892     ## a |meta| element with none of |charset|, |name|,
1893     ## or |http-equiv| attribute is not allowed. It is non-conforming
1894     ## anyway.
1895 wakaba 1.56
1896     ## TODO: |form| MUST be empty and in XML [WF2].
1897 wakaba 1.40 } else {
1898     $self->{onerror}->(node => $child_el,
1899     type => 'element not allowed:metadata',
1900 wakaba 1.104 level => $self->{level}->{must});
1901 wakaba 1.40 }
1902     $element_state->{in_head_original} = $self->{flag}->{in_head};
1903     $self->{flag}->{in_head} = 1;
1904     },
1905     check_child_text => sub {
1906     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1907     if ($has_significant) {
1908 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
1909     level => $self->{level}->{must});
1910 wakaba 1.1 }
1911 wakaba 1.40 },
1912     check_end => sub {
1913     my ($self, $item, $element_state) = @_;
1914     unless ($element_state->{has_title}) {
1915     $self->{onerror}->(node => $item->{node},
1916 wakaba 1.104 type => 'child element missing',
1917     text => 'title',
1918 wakaba 1.105 level => $self->{level}->{must});
1919 wakaba 1.1 }
1920 wakaba 1.40 $self->{flag}->{in_head} = $element_state->{in_head_original};
1921 wakaba 1.1
1922 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1923 wakaba 1.1 },
1924     };
1925    
1926 wakaba 1.40 $Element->{$HTML_NS}->{title} = {
1927     %HTMLTextChecker,
1928 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1929 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
1930     %HTMLAttrStatus,
1931 wakaba 1.82 %XHTML2CommonAttrStatus,
1932 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1933     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1934     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1935     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
1936 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1937 wakaba 1.49 }),
1938 wakaba 1.40 };
1939 wakaba 1.1
1940 wakaba 1.40 $Element->{$HTML_NS}->{base} = {
1941 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
1942 wakaba 1.40 %HTMLEmptyChecker,
1943     check_attrs => sub {
1944     my ($self, $item, $element_state) = @_;
1945 wakaba 1.1
1946 wakaba 1.40 if ($self->{has_base}) {
1947     $self->{onerror}->(node => $item->{node},
1948 wakaba 1.104 type => 'element not allowed:base',
1949     level => $self->{level}->{must});
1950 wakaba 1.40 } else {
1951     $self->{has_base} = 1;
1952 wakaba 1.29 }
1953    
1954 wakaba 1.40 my $has_href = $item->{node}->has_attribute_ns (undef, 'href');
1955     my $has_target = $item->{node}->has_attribute_ns (undef, 'target');
1956 wakaba 1.14
1957     if ($self->{has_uri_attr} and $has_href) {
1958 wakaba 1.4 ## ISSUE: Are these examples conforming?
1959     ## <head profile="a b c"><base href> (except for |profile|'s
1960     ## non-conformance)
1961     ## <title xml:base="relative"/><base href/> (maybe it should be)
1962     ## <unknown xmlns="relative"/><base href/> (assuming that
1963     ## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though))
1964     ## <style>@import 'relative';</style><base href>
1965     ## <script>location.href = 'relative';</script><base href>
1966 wakaba 1.14 ## NOTE: <html manifest=".."><head><base href=""/> is conforming as
1967     ## an exception.
1968 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1969 wakaba 1.104 type => 'basehref after URL attribute',
1970     level => $self->{level}->{must});
1971 wakaba 1.4 }
1972 wakaba 1.14 if ($self->{has_hyperlink_element} and $has_target) {
1973 wakaba 1.4 ## ISSUE: Are these examples conforming?
1974     ## <head><title xlink:href=""/><base target="name"/></head>
1975     ## <xbl:xbl>...<svg:a href=""/>...</xbl:xbl><base target="name"/>
1976     ## (assuming that |xbl:xbl| is allowed before |base|)
1977     ## NOTE: These are non-conformant anyway because of |head|'s content model:
1978     ## <link href=""/><base target="name"/>
1979     ## <link rel=unknown href=""><base target=name>
1980 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1981 wakaba 1.104 type => 'basetarget after hyperlink',
1982     level => $self->{level}->{must});
1983 wakaba 1.4 }
1984    
1985 wakaba 1.14 if (not $has_href and not $has_target) {
1986 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1987 wakaba 1.104 type => 'attribute missing:href|target',
1988     level => $self->{level}->{must});
1989 wakaba 1.14 }
1990    
1991 wakaba 1.66 $element_state->{uri_info}->{href}->{type}->{base} = 1;
1992    
1993 wakaba 1.4 return $GetHTMLAttrsChecker->({
1994     href => $HTMLURIAttrChecker,
1995     target => $HTMLTargetAttrChecker,
1996 wakaba 1.49 }, {
1997     %HTMLAttrStatus,
1998 wakaba 1.153 href => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
1999     id => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2000     target => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2001 wakaba 1.40 })->($self, $item, $element_state);
2002 wakaba 1.4 },
2003 wakaba 1.1 };
2004    
2005     $Element->{$HTML_NS}->{link} = {
2006 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2007 wakaba 1.40 %HTMLEmptyChecker,
2008     check_attrs => sub {
2009     my ($self, $item, $element_state) = @_;
2010 wakaba 1.96 my $sizes_attr;
2011 wakaba 1.1 $GetHTMLAttrsChecker->({
2012 wakaba 1.91 charset => sub {
2013     my ($self, $attr) = @_;
2014     $HTMLCharsetChecker->($attr->value, @_);
2015     },
2016 wakaba 1.1 href => $HTMLURIAttrChecker,
2017 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(0, $item, @_) },
2018 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
2019 wakaba 1.1 media => $HTMLMQAttrChecker,
2020     hreflang => $HTMLLanguageTagAttrChecker,
2021 wakaba 1.96 sizes => sub {
2022     my ($self, $attr) = @_;
2023     $sizes_attr = $attr;
2024     my %word;
2025     for my $word (grep {length $_}
2026 wakaba 1.132 split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
2027 wakaba 1.96 unless ($word{$word}) {
2028     $word{$word} = 1;
2029     if ($word eq 'any' or $word =~ /\A[1-9][0-9]*x[1-9][0-9]*\z/) {
2030     #
2031     } else {
2032     $self->{onerror}->(node => $attr,
2033 wakaba 1.104 type => 'sizes:syntax error',
2034 wakaba 1.96 value => $word,
2035 wakaba 1.104 level => $self->{level}->{must});
2036 wakaba 1.96 }
2037     } else {
2038     $self->{onerror}->(node => $attr, type => 'duplicate token',
2039     value => $word,
2040 wakaba 1.104 level => $self->{level}->{must});
2041 wakaba 1.96 }
2042     }
2043     },
2044 wakaba 1.70 target => $HTMLTargetAttrChecker,
2045 wakaba 1.1 type => $HTMLIMTAttrChecker,
2046     ## NOTE: Though |title| has special semantics,
2047     ## syntactically same as the |title| as global attribute.
2048 wakaba 1.49 }, {
2049     %HTMLAttrStatus,
2050 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2051 wakaba 1.91 charset => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
2052     ## NOTE: |charset| attribute had been part of HTML5 spec though
2053     ## it had been commented out.
2054 wakaba 1.154 href => FEATURE_HTML5_LC | FEATURE_RDFA_REC | FEATURE_XHTML2_ED |
2055 wakaba 1.82 FEATURE_M12N10_REC,
2056 wakaba 1.153 hreflang => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2057     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2058     media => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2059 wakaba 1.61 methods => FEATURE_HTML20_RFC,
2060 wakaba 1.154 rel => FEATURE_HTML5_LC | FEATURE_RDFA_REC | FEATURE_XHTML2_ED |
2061 wakaba 1.153 FEATURE_M12N10_REC,
2062 wakaba 1.154 rev => FEATURE_RDFA_REC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2063 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2064 wakaba 1.153 sizes => FEATURE_HTML5_LC,
2065 wakaba 1.82 target => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2066 wakaba 1.153 # title: HTML5_WD | HTML5_LC | ...
2067     type => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
2068 wakaba 1.61 urn => FEATURE_HTML20_RFC,
2069 wakaba 1.40 })->($self, $item, $element_state);
2070 wakaba 1.96
2071 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'href')) {
2072     $self->{has_hyperlink_element} = 1 if $item->{has_hyperlink_link_type};
2073 wakaba 1.4 } else {
2074 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2075 wakaba 1.104 type => 'attribute missing',
2076     text => 'href',
2077     level => $self->{level}->{must});
2078 wakaba 1.1 }
2079 wakaba 1.96
2080 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'rel')) {
2081     $self->{onerror}->(node => $item->{node},
2082 wakaba 1.104 type => 'attribute missing',
2083     text => 'rel',
2084     level => $self->{level}->{must});
2085 wakaba 1.96 }
2086    
2087     if ($sizes_attr and not $element_state->{link_rel}->{icon}) {
2088     $self->{onerror}->(node => $sizes_attr,
2089     type => 'attribute not allowed',
2090 wakaba 1.104 level => $self->{level}->{must});
2091 wakaba 1.1 }
2092 wakaba 1.116
2093     if ($element_state->{link_rel}->{alternate} and
2094     $element_state->{link_rel}->{stylesheet}) {
2095     my $title_attr = $item->{node}->get_attribute_node_ns (undef, 'title');
2096     unless ($title_attr) {
2097     $self->{onerror}->(node => $item->{node},
2098     type => 'attribute missing',
2099     text => 'title',
2100     level => $self->{level}->{must});
2101     } elsif ($title_attr->value eq '') {
2102     $self->{onerror}->(node => $title_attr,
2103     type => 'empty style sheet title',
2104     level => $self->{level}->{must});
2105     }
2106     }
2107 wakaba 1.1 },
2108     };
2109    
2110     $Element->{$HTML_NS}->{meta} = {
2111 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2112 wakaba 1.40 %HTMLEmptyChecker,
2113     check_attrs => sub {
2114     my ($self, $item, $element_state) = @_;
2115 wakaba 1.1 my $name_attr;
2116     my $http_equiv_attr;
2117     my $charset_attr;
2118     my $content_attr;
2119 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2120 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2121     $attr_ns = '' unless defined $attr_ns;
2122     my $attr_ln = $attr->manakai_local_name;
2123     my $checker;
2124 wakaba 1.73 my $status;
2125 wakaba 1.1 if ($attr_ns eq '') {
2126 wakaba 1.73 $status = {
2127     %HTMLAttrStatus,
2128 wakaba 1.82 %XHTML2CommonAttrStatus,
2129 wakaba 1.153 charset => FEATURE_HTML5_WD,
2130     content => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2131     dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2132     'http-equiv' => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2133     id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
2134     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2135     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2136 wakaba 1.73 scheme => FEATURE_M12N10_REC,
2137     }->{$attr_ln};
2138    
2139 wakaba 1.1 if ($attr_ln eq 'content') {
2140     $content_attr = $attr;
2141     $checker = 1;
2142     } elsif ($attr_ln eq 'name') {
2143     $name_attr = $attr;
2144     $checker = 1;
2145     } elsif ($attr_ln eq 'http-equiv') {
2146     $http_equiv_attr = $attr;
2147     $checker = 1;
2148     } elsif ($attr_ln eq 'charset') {
2149     $charset_attr = $attr;
2150     $checker = 1;
2151 wakaba 1.67 } elsif ($attr_ln eq 'scheme') {
2152 wakaba 1.71 ## NOTE: <http://suika.fam.cx/2007/html/standards#html-meta-scheme>
2153 wakaba 1.67 $checker = sub {};
2154 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
2155     $attr_ln !~ /[A-Z]/) {
2156 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
2157     $status = $HTMLDatasetAttrStatus;
2158 wakaba 1.1 } else {
2159     $checker = $HTMLAttrChecker->{$attr_ln}
2160 wakaba 1.67 || $AttrChecker->{$attr_ns}->{$attr_ln}
2161 wakaba 1.1 || $AttrChecker->{$attr_ns}->{''};
2162     }
2163     } else {
2164     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2165 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
2166     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
2167     || $AttrStatus->{$attr_ns}->{''};
2168     $status = FEATURE_ALLOWED if not defined $status;
2169 wakaba 1.1 }
2170 wakaba 1.62
2171 wakaba 1.1 if ($checker) {
2172 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2173 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2174 wakaba 1.54 #
2175 wakaba 1.1 } else {
2176 wakaba 1.104 $self->{onerror}->(node => $attr,
2177     type => 'unknown attribute',
2178     level => $self->{level}->{uncertain});
2179 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
2180     }
2181    
2182 wakaba 1.82 $self->_attr_status_info ($attr, $status);
2183 wakaba 1.1 }
2184    
2185     if (defined $name_attr) {
2186     if (defined $http_equiv_attr) {
2187     $self->{onerror}->(node => $http_equiv_attr,
2188 wakaba 1.104 type => 'attribute not allowed',
2189     level => $self->{level}->{must});
2190 wakaba 1.1 } elsif (defined $charset_attr) {
2191     $self->{onerror}->(node => $charset_attr,
2192 wakaba 1.104 type => 'attribute not allowed',
2193     level => $self->{level}->{must});
2194 wakaba 1.1 }
2195     my $metadata_name = $name_attr->value;
2196     my $metadata_value;
2197     if (defined $content_attr) {
2198     $metadata_value = $content_attr->value;
2199     } else {
2200 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2201 wakaba 1.104 type => 'attribute missing',
2202     text => 'content',
2203     level => $self->{level}->{must});
2204 wakaba 1.1 $metadata_value = '';
2205     }
2206     } elsif (defined $http_equiv_attr) {
2207     if (defined $charset_attr) {
2208     $self->{onerror}->(node => $charset_attr,
2209 wakaba 1.104 type => 'attribute not allowed',
2210     level => $self->{level}->{must});
2211 wakaba 1.1 }
2212     unless (defined $content_attr) {
2213 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2214 wakaba 1.104 type => 'attribute missing',
2215     text => 'content',
2216     level => $self->{level}->{must});
2217 wakaba 1.1 }
2218     } elsif (defined $charset_attr) {
2219     if (defined $content_attr) {
2220     $self->{onerror}->(node => $content_attr,
2221 wakaba 1.104 type => 'attribute not allowed',
2222     level => $self->{level}->{must});
2223 wakaba 1.1 }
2224     } else {
2225     if (defined $content_attr) {
2226     $self->{onerror}->(node => $content_attr,
2227 wakaba 1.104 type => 'attribute not allowed',
2228     level => $self->{level}->{must});
2229 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2230 wakaba 1.104 type => 'attribute missing:name|http-equiv',
2231     level => $self->{level}->{must});
2232 wakaba 1.1 } else {
2233 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2234 wakaba 1.104 type => 'attribute missing:name|http-equiv|charset',
2235     level => $self->{level}->{must});
2236 wakaba 1.1 }
2237     }
2238    
2239 wakaba 1.32 my $check_charset_decl = sub () {
2240 wakaba 1.40 my $parent = $item->{node}->manakai_parent_element;
2241 wakaba 1.29 if ($parent and $parent eq $parent->owner_document->manakai_head) {
2242     for my $el (@{$parent->child_nodes}) {
2243     next unless $el->node_type == 1; # ELEMENT_NODE
2244 wakaba 1.40 unless ($el eq $item->{node}) {
2245 wakaba 1.29 ## NOTE: Not the first child element.
2246 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2247 wakaba 1.32 type => 'element not allowed:meta charset',
2248 wakaba 1.104 level => $self->{level}->{must});
2249 wakaba 1.29 }
2250     last;
2251     ## NOTE: Entity references are not supported.
2252     }
2253     } else {
2254 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2255 wakaba 1.32 type => 'element not allowed:meta charset',
2256 wakaba 1.104 level => $self->{level}->{must});
2257 wakaba 1.29 }
2258    
2259 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
2260     $self->{onerror}->(node => $item->{node},
2261 wakaba 1.32 type => 'in XML:charset',
2262 wakaba 1.104 level => $self->{level}->{must});
2263 wakaba 1.1 }
2264 wakaba 1.32 }; # $check_charset_decl
2265 wakaba 1.21
2266 wakaba 1.32 my $check_charset = sub ($$) {
2267     my ($attr, $charset_value) = @_;
2268 wakaba 1.21
2269 wakaba 1.91 my $charset;
2270     ($charset, $charset_value)
2271     = $HTMLCharsetChecker->($charset_value, $self, $attr);
2272    
2273 wakaba 1.40 my $ic = $item->{node}->owner_document->input_encoding;
2274 wakaba 1.21 if (defined $ic) {
2275     ## TODO: Test for this case
2276     my $ic_charset = $Message::Charset::Info::IANACharset->{$ic};
2277     if ($charset ne $ic_charset) {
2278 wakaba 1.32 $self->{onerror}->(node => $attr,
2279 wakaba 1.104 type => 'mismatched charset name',
2280 wakaba 1.106 text => $ic,
2281 wakaba 1.104 value => $charset_value,
2282     level => $self->{level}->{must});
2283 wakaba 1.21 }
2284     } else {
2285     ## NOTE: MUST, but not checkable, since the document is not originally
2286     ## in serialized form (or the parser does not preserve the input
2287     ## encoding information).
2288 wakaba 1.32 $self->{onerror}->(node => $attr,
2289 wakaba 1.104 type => 'mismatched charset name not checked',
2290     value => $charset_value,
2291     level => $self->{level}->{uncertain});
2292 wakaba 1.21 }
2293    
2294 wakaba 1.32 if ($attr->get_user_data ('manakai_has_reference')) {
2295     $self->{onerror}->(node => $attr,
2296 wakaba 1.104 type => 'charref in charset',
2297     level => $self->{level}->{must},
2298     layer => 'syntax');
2299 wakaba 1.22 }
2300 wakaba 1.32 }; # $check_charset
2301    
2302     ## TODO: metadata conformance
2303    
2304     ## TODO: pragma conformance
2305     if (defined $http_equiv_attr) { ## An enumerated attribute
2306     my $keyword = lc $http_equiv_attr->value; ## TODO: ascii case?
2307 wakaba 1.33
2308 wakaba 1.85 if ($self->{has_http_equiv}->{$keyword}) {
2309     $self->{onerror}->(type => 'duplicate http-equiv', value => $keyword,
2310     node => $http_equiv_attr,
2311 wakaba 1.104 level => $self->{level}->{must});
2312 wakaba 1.85 } else {
2313     $self->{has_http_equiv}->{$keyword} = 1;
2314     }
2315    
2316     if ($keyword eq 'content-type') {
2317 wakaba 1.58 ## TODO: refs in "text/html; charset=" are not disallowed since rev.1275.
2318 wakaba 1.33
2319 wakaba 1.32 $check_charset_decl->();
2320     if ($content_attr) {
2321     my $content = $content_attr->value;
2322 wakaba 1.58 if ($content =~ m!^[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll];
2323 wakaba 1.132 [\x09\x0A\x0C\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
2324 wakaba 1.58 =(.+)\z!sx) {
2325 wakaba 1.32 $check_charset->($content_attr, $1);
2326     } else {
2327     $self->{onerror}->(node => $content_attr,
2328     type => 'meta content-type syntax error',
2329 wakaba 1.104 level => $self->{level}->{must});
2330 wakaba 1.85 }
2331     }
2332     } elsif ($keyword eq 'default-style') {
2333     ## ISSUE: Not defined yet in the spec.
2334     } elsif ($keyword eq 'refresh') {
2335     if ($content_attr) {
2336     my $content = $content_attr->value;
2337     if ($content =~ /\A[0-9]+\z/) {
2338     ## NOTE: Valid non-negative integer.
2339     #
2340 wakaba 1.132 } elsif ($content =~ s/\A[0-9]+;[\x09\x0A\x0C\x0D\x20]+[Uu][Rr][Ll]=//) {
2341 wakaba 1.85 ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.)
2342     Whatpm::URIChecker->check_iri_reference ($content, sub {
2343 wakaba 1.104 $self->{onerror}->(value => $content, @_, node => $content_attr);
2344 wakaba 1.106 }, $self->{level});
2345 wakaba 1.85 $self->{has_uri_attr} = 1; ## NOTE: One of "attributes with URIs".
2346    
2347     $element_state->{uri_info}->{content}->{node} = $content_attr;
2348     $element_state->{uri_info}->{content}->{type}->{hyperlink} = 1;
2349     ## TODO: absolute
2350     push @{$self->{return}->{uri}->{$content} ||= []},
2351     $element_state->{uri_info}->{content};
2352     } else {
2353     $self->{onerror}->(node => $content_attr,
2354     type => 'refresh:syntax error',
2355 wakaba 1.104 level => $self->{level}->{must});
2356 wakaba 1.32 }
2357     }
2358     } else {
2359     $self->{onerror}->(node => $http_equiv_attr,
2360 wakaba 1.104 type => 'enumerated:invalid',
2361     level => $self->{level}->{must});
2362 wakaba 1.32 }
2363     }
2364    
2365     if (defined $charset_attr) {
2366     $check_charset_decl->();
2367     $check_charset->($charset_attr, $charset_attr->value);
2368 wakaba 1.1 }
2369     },
2370     };
2371    
2372     $Element->{$HTML_NS}->{style} = {
2373 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2374 wakaba 1.40 %HTMLChecker,
2375     check_attrs => $GetHTMLAttrsChecker->({
2376 wakaba 1.1 type => $HTMLIMTAttrChecker, ## TODO: MUST be a styling language
2377     media => $HTMLMQAttrChecker,
2378     scoped => $GetHTMLBooleanAttrChecker->('scoped'),
2379     ## NOTE: |title| has special semantics for |style|s, but is syntactically
2380     ## not different
2381 wakaba 1.49 }, {
2382     %HTMLAttrStatus,
2383 wakaba 1.82 %XHTML2CommonAttrStatus,
2384 wakaba 1.153 dir => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2385 wakaba 1.82 disabled => FEATURE_XHTML2_ED,
2386 wakaba 1.154 href => FEATURE_RDFA_REC | FEATURE_XHTML2_ED,
2387 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
2388     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2389     media => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2390     scoped => FEATURE_HTML5_FD,
2391     title => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2392     type => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2393 wakaba 1.1 }),
2394 wakaba 1.40 check_start => sub {
2395     my ($self, $item, $element_state) = @_;
2396    
2397 wakaba 1.27 ## NOTE: |html:style| itself has no conformance creteria on content model.
2398 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
2399 wakaba 1.93 $type = 'text/css' unless defined $type;
2400     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]) {
2401     $type = "$1/$2";
2402     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
2403     } else {
2404     ## NOTE: We don't know how parameters are handled by UAs. According to
2405     ## HTML5 specification, <style> with unknown parameters in |type=""|
2406     ## must be ignored.
2407     undef $type;
2408     }
2409     if (not defined $type) {
2410     $element_state->{allow_element} = 1; # invalid type=""
2411     } elsif ($type eq 'text/css') {
2412 wakaba 1.40 $element_state->{allow_element} = 0;
2413 wakaba 1.93 #} elsif ($type =~ m![/+][Xx][Mm][Ll]\z!) {
2414     # ## NOTE: There is no definition for "XML-based styling language" in HTML5
2415     # $element_state->{allow_element} = 1;
2416 wakaba 1.40 } else {
2417     $element_state->{allow_element} = 1; # unknown
2418     }
2419 wakaba 1.93 $element_state->{style_type} = $type;
2420 wakaba 1.79
2421     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2422     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2423 wakaba 1.107
2424     $element_state->{text} = '';
2425 wakaba 1.40 },
2426     check_child_element => sub {
2427     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2428     $child_is_transparent, $element_state) = @_;
2429 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
2430     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
2431 wakaba 1.40 $self->{onerror}->(node => $child_el,
2432     type => 'element not allowed:minus',
2433 wakaba 1.104 level => $self->{level}->{must});
2434 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2435     #
2436     } elsif ($element_state->{allow_element}) {
2437     #
2438     } else {
2439 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2440     level => $self->{level}->{must});
2441 wakaba 1.40 }
2442     },
2443     check_child_text => sub {
2444     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2445 wakaba 1.115 $element_state->{text} .= $child_node->data;
2446 wakaba 1.40 },
2447     check_end => sub {
2448     my ($self, $item, $element_state) = @_;
2449 wakaba 1.93 if (not defined $element_state->{style_type}) {
2450     ## NOTE: Invalid type=""
2451     #
2452     } elsif ($element_state->{style_type} eq 'text/css') {
2453 wakaba 1.40 $self->{onsubdoc}->({s => $element_state->{text},
2454     container_node => $item->{node},
2455 wakaba 1.28 media_type => 'text/css', is_char_string => 1});
2456 wakaba 1.93 } elsif ($element_state->{style_type} =~ m![+/][Xx][Mm][Ll]\z!) {
2457     ## NOTE: XML content should be checked by THIS instance of checker
2458     ## as part of normal tree validation. However, we don't know of any
2459     ## XML-based styling language that can be used in HTML <style> element,
2460     ## such that we throw a "style language not supported" error.
2461 wakaba 1.104 $self->{onerror}->(node => $item->{node},
2462     type => 'XML style lang',
2463     text => $element_state->{style_type},
2464     level => $self->{level}->{uncertain});
2465 wakaba 1.93 } else {
2466     ## NOTE: Should we raise some kind of error for,
2467     ## say, <style type="text/plaion">?
2468     $self->{onsubdoc}->({s => $element_state->{text},
2469     container_node => $item->{node},
2470     media_type => $element_state->{style_type},
2471     is_char_string => 1});
2472 wakaba 1.27 }
2473 wakaba 1.40
2474     $HTMLChecker{check_end}->(@_);
2475 wakaba 1.1 },
2476     };
2477 wakaba 1.25 ## ISSUE: Relationship to significant content check?
2478 wakaba 1.1
2479     $Element->{$HTML_NS}->{body} = {
2480 wakaba 1.72 %HTMLFlowContentChecker,
2481 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2482 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2483     alink => $HTMLColorAttrChecker,
2484     background => $HTMLURIAttrChecker,
2485     bgcolor => $HTMLColorAttrChecker,
2486     link => $HTMLColorAttrChecker,
2487     text => $HTMLColorAttrChecker,
2488     vlink => $HTMLColorAttrChecker,
2489     }, {
2490 wakaba 1.49 %HTMLAttrStatus,
2491 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2492 wakaba 1.49 alink => FEATURE_M12N10_REC_DEPRECATED,
2493     background => FEATURE_M12N10_REC_DEPRECATED,
2494     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
2495 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2496 wakaba 1.49 link => FEATURE_M12N10_REC_DEPRECATED,
2497 wakaba 1.50 onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2498     onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2499 wakaba 1.49 text => FEATURE_M12N10_REC_DEPRECATED,
2500     vlink => FEATURE_M12N10_REC_DEPRECATED,
2501     }),
2502 wakaba 1.68 check_start => sub {
2503     my ($self, $item, $element_state) = @_;
2504    
2505     $element_state->{uri_info}->{background}->{type}->{embedded} = 1;
2506 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2507     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2508 wakaba 1.68 },
2509 wakaba 1.1 };
2510    
2511     $Element->{$HTML_NS}->{section} = {
2512 wakaba 1.72 %HTMLFlowContentChecker,
2513 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED,
2514 wakaba 1.82 check_attrs => $GetHTMLAttrsChecker->({
2515     }, {
2516     %HTMLAttrStatus,
2517     %XHTML2CommonAttrStatus,
2518     }),
2519 wakaba 1.1 };
2520    
2521     $Element->{$HTML_NS}->{nav} = {
2522 wakaba 1.153 status => FEATURE_HTML5_LC,
2523 wakaba 1.72 %HTMLFlowContentChecker,
2524 wakaba 1.1 };
2525    
2526     $Element->{$HTML_NS}->{article} = {
2527 wakaba 1.174 %HTMLFlowContentChecker,
2528 wakaba 1.153 status => FEATURE_HTML5_LC,
2529 wakaba 1.174 check_attrs => $GetHTMLAttrsChecker->({
2530     pubdate => $GetDateTimeAttrChecker->('global_date_and_time_string'),
2531     }, {
2532     %HTMLAttrStatus,
2533     # XXX cite
2534     pubdate => FEATURE_HTML5_LC,
2535     }),
2536     }; # article
2537 wakaba 1.1
2538     $Element->{$HTML_NS}->{blockquote} = {
2539 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2540 wakaba 1.72 %HTMLFlowContentChecker,
2541 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2542 wakaba 1.1 cite => $HTMLURIAttrChecker,
2543 wakaba 1.49 }, {
2544     %HTMLAttrStatus,
2545 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2546 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2547 wakaba 1.154 cite => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2548 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2549 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2550 wakaba 1.1 }),
2551 wakaba 1.66 check_start => sub {
2552     my ($self, $item, $element_state) = @_;
2553    
2554     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2555 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2556     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2557 wakaba 1.66 },
2558 wakaba 1.1 };
2559    
2560     $Element->{$HTML_NS}->{aside} = {
2561 wakaba 1.153 status => FEATURE_HTML5_LC,
2562 wakaba 1.72 %HTMLFlowContentChecker,
2563 wakaba 1.1 };
2564    
2565     $Element->{$HTML_NS}->{h1} = {
2566 wakaba 1.40 %HTMLPhrasingContentChecker,
2567 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2568 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2569     align => $GetHTMLEnumeratedAttrChecker->({
2570     left => 1, center => 1, right => 1, justify => 1,
2571     }),
2572     }, {
2573 wakaba 1.49 %HTMLAttrStatus,
2574 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2575 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2576 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2577 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2578 wakaba 1.49 }),
2579 wakaba 1.40 check_start => sub {
2580     my ($self, $item, $element_state) = @_;
2581     $self->{flag}->{has_hn} = 1;
2582 wakaba 1.79
2583     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2584     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2585 wakaba 1.1 },
2586     };
2587    
2588 wakaba 1.40 $Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}};
2589 wakaba 1.1
2590 wakaba 1.40 $Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}};
2591 wakaba 1.1
2592 wakaba 1.40 $Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}};
2593 wakaba 1.1
2594 wakaba 1.40 $Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}};
2595 wakaba 1.1
2596 wakaba 1.40 $Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}};
2597 wakaba 1.1
2598 wakaba 1.29 ## TODO: Explicit sectioning is "encouraged".
2599 wakaba 1.174
2600     # XXX footer in header is disallowed (HTML5 revision 3050)
2601 wakaba 1.29
2602 wakaba 1.1 $Element->{$HTML_NS}->{header} = {
2603 wakaba 1.153 status => FEATURE_HTML5_LC,
2604 wakaba 1.72 %HTMLFlowContentChecker,
2605 wakaba 1.40 check_start => sub {
2606     my ($self, $item, $element_state) = @_;
2607     $self->_add_minus_elements ($element_state,
2608     {$HTML_NS => {qw/header 1 footer 1/}},
2609 wakaba 1.58 $HTMLSectioningContent);
2610 wakaba 1.40 $element_state->{has_hn_original} = $self->{flag}->{has_hn};
2611     $self->{flag}->{has_hn} = 0;
2612 wakaba 1.79
2613     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2614     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2615 wakaba 1.40 },
2616     check_end => sub {
2617     my ($self, $item, $element_state) = @_;
2618     $self->_remove_minus_elements ($element_state);
2619     unless ($self->{flag}->{has_hn}) {
2620     $self->{onerror}->(node => $item->{node},
2621 wakaba 1.104 type => 'element missing:hn',
2622     level => $self->{level}->{must});
2623 wakaba 1.40 }
2624     $self->{flag}->{has_hn} ||= $element_state->{has_hn_original};
2625 wakaba 1.1
2626 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2627 wakaba 1.1 },
2628 wakaba 1.40 ## ISSUE: <header><del><h1>...</h1></del></header> is conforming?
2629 wakaba 1.1 };
2630    
2631     $Element->{$HTML_NS}->{footer} = {
2632 wakaba 1.153 status => FEATURE_HTML5_LC,
2633 wakaba 1.72 %HTMLFlowContentChecker,
2634 wakaba 1.40 check_start => sub {
2635     my ($self, $item, $element_state) = @_;
2636     $self->_add_minus_elements ($element_state,
2637 wakaba 1.177 {$HTML_NS => {header => 1, footer => 1}},
2638 wakaba 1.58 $HTMLSectioningContent,
2639 wakaba 1.57 $HTMLHeadingContent);
2640 wakaba 1.79
2641     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2642     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2643 wakaba 1.40 },
2644     check_end => sub {
2645     my ($self, $item, $element_state) = @_;
2646     $self->_remove_minus_elements ($element_state);
2647 wakaba 1.1
2648 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2649 wakaba 1.1 },
2650     };
2651    
2652     $Element->{$HTML_NS}->{address} = {
2653 wakaba 1.72 %HTMLFlowContentChecker,
2654 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2655 wakaba 1.110 check_attrs => $GetHTMLAttrsChecker->({
2656     ## TODO: add test
2657     #align => $GetHTMLEnumeratedAttrChecker->({
2658     # left => 1, center => 1, right => 1, justify => 1,
2659     #}),
2660     }, {
2661 wakaba 1.49 %HTMLAttrStatus,
2662 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2663 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2664 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2665 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2666     sdapref => FEATURE_HTML20_RFC,
2667 wakaba 1.49 }),
2668 wakaba 1.40 check_start => sub {
2669     my ($self, $item, $element_state) = @_;
2670 wakaba 1.177 $self->_add_minus_elements
2671     ($element_state,
2672     {$HTML_NS => {header => 1, footer => 1, address => 1}},
2673     $HTMLSectioningContent, $HTMLHeadingContent);
2674 wakaba 1.79
2675     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2676     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2677 wakaba 1.40 },
2678     check_end => sub {
2679     my ($self, $item, $element_state) = @_;
2680     $self->_remove_minus_elements ($element_state);
2681 wakaba 1.29
2682 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2683 wakaba 1.29 },
2684 wakaba 1.1 };
2685    
2686     $Element->{$HTML_NS}->{p} = {
2687 wakaba 1.40 %HTMLPhrasingContentChecker,
2688 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2689 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2690     align => $GetHTMLEnumeratedAttrChecker->({
2691     left => 1, center => 1, right => 1, justify => 1,
2692     }),
2693     }, {
2694 wakaba 1.49 %HTMLAttrStatus,
2695 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2696 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2697 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2698 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2699 wakaba 1.49 }),
2700 wakaba 1.1 };
2701    
2702     $Element->{$HTML_NS}->{hr} = {
2703 wakaba 1.40 %HTMLEmptyChecker,
2704 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
2705 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
2706     ## TODO: HTML4 |align|, |noshade|, |size|, |width|
2707     }, {
2708 wakaba 1.49 %HTMLAttrStatus,
2709     %HTMLM12NCommonAttrStatus,
2710     align => FEATURE_M12N10_REC_DEPRECATED,
2711 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2712 wakaba 1.49 noshade => FEATURE_M12N10_REC_DEPRECATED,
2713 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2714 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
2715     width => FEATURE_M12N10_REC_DEPRECATED,
2716     }),
2717 wakaba 1.1 };
2718    
2719     $Element->{$HTML_NS}->{br} = {
2720 wakaba 1.40 %HTMLEmptyChecker,
2721 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
2722 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2723     clear => $GetHTMLEnumeratedAttrChecker->({
2724     left => 1, all => 1, right => 1, none => 1,
2725     }),
2726     }, {
2727 wakaba 1.49 %HTMLAttrStatus,
2728 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2729 wakaba 1.49 clear => FEATURE_M12N10_REC_DEPRECATED,
2730 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2731 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2732 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2733     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2734 wakaba 1.49 }),
2735 wakaba 1.29 ## NOTE: Blank line MUST NOT be used for presentation purpose.
2736     ## (This requirement is semantic so that we cannot check.)
2737 wakaba 1.1 };
2738    
2739     $Element->{$HTML_NS}->{dialog} = {
2740 wakaba 1.153 status => FEATURE_HTML5_WD,
2741 wakaba 1.40 %HTMLChecker,
2742     check_start => sub {
2743     my ($self, $item, $element_state) = @_;
2744     $element_state->{phase} = 'before dt';
2745 wakaba 1.79
2746     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2747     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2748 wakaba 1.40 },
2749     check_child_element => sub {
2750     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2751     $child_is_transparent, $element_state) = @_;
2752 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
2753     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
2754 wakaba 1.40 $self->{onerror}->(node => $child_el,
2755     type => 'element not allowed:minus',
2756 wakaba 1.104 level => $self->{level}->{must});
2757 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2758     #
2759     } elsif ($element_state->{phase} eq 'before dt') {
2760     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2761     $element_state->{phase} = 'before dd';
2762     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2763     $self->{onerror}
2764 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2765     text => 'dt',
2766     level => $self->{level}->{must});
2767 wakaba 1.40 $element_state->{phase} = 'before dt';
2768     } else {
2769 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2770     level => $self->{level}->{must});
2771 wakaba 1.40 }
2772     } elsif ($element_state->{phase} eq 'before dd') {
2773     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2774     $element_state->{phase} = 'before dt';
2775     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2776     $self->{onerror}
2777 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2778     text => 'dd',
2779     level => $self->{level}->{must});
2780 wakaba 1.40 $element_state->{phase} = 'before dd';
2781     } else {
2782 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2783     level => $self->{level}->{must});
2784 wakaba 1.1 }
2785 wakaba 1.40 } else {
2786     die "check_child_element: Bad |dialog| phase: $element_state->{phase}";
2787     }
2788     },
2789     check_child_text => sub {
2790     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2791     if ($has_significant) {
2792 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2793     level => $self->{level}->{must});
2794 wakaba 1.1 }
2795 wakaba 1.40 },
2796     check_end => sub {
2797     my ($self, $item, $element_state) = @_;
2798     if ($element_state->{phase} eq 'before dd') {
2799     $self->{onerror}->(node => $item->{node},
2800 wakaba 1.104 type => 'child element missing',
2801     text => 'dd',
2802     level => $self->{level}->{must});
2803 wakaba 1.1 }
2804 wakaba 1.40
2805     $HTMLChecker{check_end}->(@_);
2806 wakaba 1.1 },
2807     };
2808    
2809     $Element->{$HTML_NS}->{pre} = {
2810 wakaba 1.40 %HTMLPhrasingContentChecker,
2811 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2812 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2813     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2814     }, {
2815 wakaba 1.49 %HTMLAttrStatus,
2816 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2817 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2818 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2819 wakaba 1.49 width => FEATURE_M12N10_REC_DEPRECATED,
2820     }),
2821 wakaba 1.101 check_end => sub {
2822     my ($self, $item, $element_state) = @_;
2823    
2824     ## TODO: Flag to enable/disable IDL checking?
2825 wakaba 1.145 my $class = $item->{node}->get_attribute_ns (undef, 'class');
2826 wakaba 1.102 if ($class =~ /\bidl(?>-code)?\b/) { ## TODO: use classList.has
2827     ## NOTE: pre.idl: WHATWG, XHR, Selectors API, CSSOM specs
2828     ## NOTE: pre.code > code.idl-code: WebIDL spec
2829     ## NOTE: pre.idl-code: DOM1 spec
2830     ## NOTE: div.idl-code > pre: DOM, ProgressEvent specs
2831     ## NOTE: pre.schema: ReSpec-generated specs
2832 wakaba 1.101 $self->{onsubdoc}->({s => $item->{node}->text_content,
2833     container_node => $item->{node},
2834     media_type => 'text/x-webidl',
2835     is_char_string => 1});
2836     }
2837    
2838 wakaba 1.110 $HTMLPhrasingContentChecker{check_end}->(@_);
2839 wakaba 1.101 },
2840 wakaba 1.1 };
2841    
2842     $Element->{$HTML_NS}->{ol} = {
2843 wakaba 1.40 %HTMLChecker,
2844 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2845 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2846 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
2847 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
2848 wakaba 1.1 start => $HTMLIntegerAttrChecker,
2849 wakaba 1.69 ## TODO: HTML4 |type|
2850 wakaba 1.49 }, {
2851     %HTMLAttrStatus,
2852 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2853 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2854 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2855 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2856     reversed => FEATURE_HTML5_WD,
2857 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2858 wakaba 1.153 #start => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
2859     start => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
2860 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2861 wakaba 1.1 }),
2862 wakaba 1.40 check_child_element => sub {
2863     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2864     $child_is_transparent, $element_state) = @_;
2865 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
2866     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
2867 wakaba 1.40 $self->{onerror}->(node => $child_el,
2868     type => 'element not allowed:minus',
2869 wakaba 1.104 level => $self->{level}->{must});
2870 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2871     #
2872     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
2873     #
2874     } else {
2875 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2876     level => $self->{level}->{must});
2877 wakaba 1.1 }
2878 wakaba 1.40 },
2879     check_child_text => sub {
2880     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2881     if ($has_significant) {
2882 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2883     level => $self->{level}->{must});
2884 wakaba 1.1 }
2885     },
2886     };
2887    
2888     $Element->{$HTML_NS}->{ul} = {
2889 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
2890 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2891 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2892     compact => $GetHTMLBooleanAttrChecker->('compact'),
2893 wakaba 1.69 ## TODO: HTML4 |type|
2894     ## TODO: sdaform, align
2895 wakaba 1.68 }, {
2896 wakaba 1.49 %HTMLAttrStatus,
2897 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2898 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2899 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2900 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2901 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2902 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2903     }),
2904 wakaba 1.1 };
2905    
2906 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
2907     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
2908     %{$Element->{$HTML_NS}->{ul}},
2909     status => FEATURE_M12N10_REC_DEPRECATED,
2910 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2911     compact => $GetHTMLBooleanAttrChecker->('compact'),
2912     }, {
2913 wakaba 1.64 %HTMLAttrStatus,
2914     %HTMLM12NCommonAttrStatus,
2915     align => FEATURE_HTML2X_RFC,
2916     compact => FEATURE_M12N10_REC_DEPRECATED,
2917 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2918 wakaba 1.64 sdaform => FEATURE_HTML20_RFC,
2919     sdapref => FEATURE_HTML20_RFC,
2920     }),
2921     };
2922    
2923 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
2924 wakaba 1.72 %HTMLFlowContentChecker,
2925 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2926 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2927 wakaba 1.69 ## TODO: HTML4 |type|
2928 wakaba 1.49 value => sub {
2929 wakaba 1.1 my ($self, $attr) = @_;
2930 wakaba 1.152
2931     my $parent_is_ol;
2932 wakaba 1.1 my $parent = $attr->owner_element->manakai_parent_element;
2933     if (defined $parent) {
2934     my $parent_ns = $parent->namespace_uri;
2935     $parent_ns = '' unless defined $parent_ns;
2936     my $parent_ln = $parent->manakai_local_name;
2937 wakaba 1.152 $parent_is_ol = ($parent_ns eq $HTML_NS and $parent_ln eq 'ol');
2938     }
2939    
2940     unless ($parent_is_ol) {
2941     ## ISSUE: No "MUST" in the spec.
2942     $self->{onerror}->(node => $attr,
2943     type => 'non-ol li value',
2944     level => $self->{level}->{html5_fact});
2945 wakaba 1.1 }
2946 wakaba 1.152
2947 wakaba 1.1 $HTMLIntegerAttrChecker->($self, $attr);
2948 wakaba 1.131 },
2949 wakaba 1.49 }, {
2950     %HTMLAttrStatus,
2951 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2952 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2953 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2954 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2955 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2956 wakaba 1.154 #value => FEATURE_HTML5_LC | FEATURE_XHTMLBASIC11_CR |
2957 wakaba 1.55 # FEATURE_M12N10_REC_DEPRECATED,
2958 wakaba 1.154 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED |
2959 wakaba 1.82 FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2960 wakaba 1.1 }),
2961 wakaba 1.40 check_child_element => sub {
2962     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2963     $child_is_transparent, $element_state) = @_;
2964     if ($self->{flag}->{in_menu}) {
2965 wakaba 1.152 ## TODO: In <dir> element, then ...
2966 wakaba 1.40 $HTMLPhrasingContentChecker{check_child_element}->(@_);
2967     } else {
2968 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2969 wakaba 1.40 }
2970     },
2971     check_child_text => sub {
2972     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2973     if ($self->{flag}->{in_menu}) {
2974 wakaba 1.152 ## TODO: In <dir> element, then ...
2975 wakaba 1.40 $HTMLPhrasingContentChecker{check_child_text}->(@_);
2976 wakaba 1.1 } else {
2977 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2978 wakaba 1.1 }
2979     },
2980     };
2981    
2982     $Element->{$HTML_NS}->{dl} = {
2983 wakaba 1.40 %HTMLChecker,
2984 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2985 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2986     compact => $GetHTMLBooleanAttrChecker->('compact'),
2987     }, {
2988 wakaba 1.49 %HTMLAttrStatus,
2989 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2990 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2991 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
2992 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2993     sdapref => FEATURE_HTML20_RFC,
2994 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2995     }),
2996 wakaba 1.40 check_start => sub {
2997     my ($self, $item, $element_state) = @_;
2998     $element_state->{phase} = 'before dt';
2999 wakaba 1.79
3000     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3001     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3002 wakaba 1.40 },
3003     check_child_element => sub {
3004     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3005     $child_is_transparent, $element_state) = @_;
3006 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
3007     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
3008 wakaba 1.40 $self->{onerror}->(node => $child_el,
3009     type => 'element not allowed:minus',
3010 wakaba 1.104 level => $self->{level}->{must});
3011 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3012     #
3013     } elsif ($element_state->{phase} eq 'in dds') {
3014     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
3015     #$element_state->{phase} = 'in dds';
3016     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
3017     $element_state->{phase} = 'in dts';
3018     } else {
3019 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
3020     level => $self->{level}->{must});
3021 wakaba 1.40 }
3022     } elsif ($element_state->{phase} eq 'in dts') {
3023     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
3024     #$element_state->{phase} = 'in dts';
3025     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
3026     $element_state->{phase} = 'in dds';
3027     } else {
3028 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
3029     level => $self->{level}->{must});
3030 wakaba 1.40 }
3031     } elsif ($element_state->{phase} eq 'before dt') {
3032     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
3033     $element_state->{phase} = 'in dts';
3034     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
3035     $self->{onerror}
3036 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
3037     text => 'dt',
3038     level => $self->{level}->{must});
3039 wakaba 1.40 $element_state->{phase} = 'in dds';
3040     } else {
3041 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
3042     level => $self->{level}->{must});
3043 wakaba 1.1 }
3044 wakaba 1.40 } else {
3045     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
3046 wakaba 1.1 }
3047 wakaba 1.40 },
3048     check_child_text => sub {
3049     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3050     if ($has_significant) {
3051 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
3052     level => $self->{level}->{must});
3053 wakaba 1.40 }
3054     },
3055     check_end => sub {
3056     my ($self, $item, $element_state) = @_;
3057     if ($element_state->{phase} eq 'in dts') {
3058     $self->{onerror}->(node => $item->{node},
3059 wakaba 1.104 type => 'child element missing',
3060     text => 'dd',
3061     level => $self->{level}->{must});
3062 wakaba 1.1 }
3063    
3064 wakaba 1.40 $HTMLChecker{check_end}->(@_);
3065 wakaba 1.1 },
3066     };
3067    
3068     $Element->{$HTML_NS}->{dt} = {
3069 wakaba 1.40 %HTMLPhrasingContentChecker,
3070 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3071 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3072     %HTMLAttrStatus,
3073 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3074 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3075 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3076 wakaba 1.49 }),
3077 wakaba 1.1 };
3078    
3079     $Element->{$HTML_NS}->{dd} = {
3080 wakaba 1.72 %HTMLFlowContentChecker,
3081 wakaba 1.154 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3082 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3083     %HTMLAttrStatus,
3084 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3085 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3086 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3087 wakaba 1.49 }),
3088 wakaba 1.1 };
3089    
3090     $Element->{$HTML_NS}->{a} = {
3091 wakaba 1.123 %HTMLTransparentChecker,
3092 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3093 wakaba 1.40 check_attrs => sub {
3094     my ($self, $item, $element_state) = @_;
3095 wakaba 1.1 my %attr;
3096 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3097 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3098     $attr_ns = '' unless defined $attr_ns;
3099     my $attr_ln = $attr->manakai_local_name;
3100     my $checker;
3101 wakaba 1.73 my $status;
3102 wakaba 1.1 if ($attr_ns eq '') {
3103 wakaba 1.73 $status = {
3104     %HTMLAttrStatus,
3105 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3106 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
3107 wakaba 1.73 charset => FEATURE_M12N10_REC,
3108 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3109 wakaba 1.73 cryptopts => FEATURE_RFC2659,
3110     dn => FEATURE_RFC2659,
3111 wakaba 1.154 href => FEATURE_HTML5_WD | FEATURE_RDFA_REC | FEATURE_XHTML2_ED |
3112 wakaba 1.153 FEATURE_M12N10_REC,
3113     hreflang => FEATURE_HTML5_WD | FEATURE_XHTML2_ED |
3114     FEATURE_M12N10_REC,
3115     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3116     media => FEATURE_HTML5_WD | FEATURE_XHTML2_ED,
3117 wakaba 1.73 methods => FEATURE_HTML20_RFC,
3118     name => FEATURE_M12N10_REC_DEPRECATED,
3119     nonce => FEATURE_RFC2659,
3120     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3121     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3122 wakaba 1.153 ping => FEATURE_HTML5_WD,
3123 wakaba 1.154 rel => FEATURE_HTML5_WD | FEATURE_RDFA_REC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3124     rev => FEATURE_RDFA_REC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3125 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
3126 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3127 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3128 wakaba 1.153 target => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3129     type => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3130 wakaba 1.73 urn => FEATURE_HTML20_RFC,
3131     }->{$attr_ln};
3132    
3133 wakaba 1.1 $checker = {
3134 wakaba 1.91 charset => sub {
3135     my ($self, $attr) = @_;
3136     $HTMLCharsetChecker->($attr->value, @_);
3137     },
3138 wakaba 1.70 ## TODO: HTML4 |coords|
3139 wakaba 1.1 target => $HTMLTargetAttrChecker,
3140     href => $HTMLURIAttrChecker,
3141     ping => $HTMLSpaceURIsAttrChecker,
3142 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
3143 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
3144 wakaba 1.70 ## TODO: HTML4 |shape|
3145 wakaba 1.1 media => $HTMLMQAttrChecker,
3146 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
3147 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
3148     type => $HTMLIMTAttrChecker,
3149     }->{$attr_ln};
3150     if ($checker) {
3151     $attr{$attr_ln} = $attr;
3152 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
3153     $attr_ln !~ /[A-Z]/) {
3154 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
3155     $status = $HTMLDatasetAttrStatus;
3156 wakaba 1.1 } else {
3157     $checker = $HTMLAttrChecker->{$attr_ln};
3158     }
3159     }
3160     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3161     || $AttrChecker->{$attr_ns}->{''};
3162 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3163     || $AttrStatus->{$attr_ns}->{''};
3164     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3165 wakaba 1.62
3166 wakaba 1.1 if ($checker) {
3167 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
3168 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3169 wakaba 1.54 #
3170 wakaba 1.1 } else {
3171 wakaba 1.104 $self->{onerror}->(node => $attr,
3172     type => 'unknown attribute',
3173     level => $self->{level}->{uncertain});
3174 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
3175 wakaba 1.1 }
3176 wakaba 1.49
3177 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3178 wakaba 1.1 }
3179    
3180 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
3181 wakaba 1.4 if (defined $attr{href}) {
3182     $self->{has_hyperlink_element} = 1;
3183 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
3184 wakaba 1.4 } else {
3185 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
3186     if (defined $attr{$_}) {
3187     $self->{onerror}->(node => $attr{$_},
3188 wakaba 1.104 type => 'attribute not allowed',
3189     level => $self->{level}->{must});
3190 wakaba 1.1 }
3191     }
3192     }
3193 wakaba 1.66
3194     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
3195 wakaba 1.1 },
3196 wakaba 1.40 check_start => sub {
3197     my ($self, $item, $element_state) = @_;
3198     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
3199 wakaba 1.79
3200     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3201     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3202 wakaba 1.40 },
3203     check_end => sub {
3204     my ($self, $item, $element_state) = @_;
3205     $self->_remove_minus_elements ($element_state);
3206 wakaba 1.59 delete $self->{flag}->{in_a_href}
3207     unless $element_state->{in_a_href_original};
3208 wakaba 1.1
3209 wakaba 1.123 $HTMLTransparentChecker{check_end}->(@_);
3210 wakaba 1.1 },
3211     };
3212    
3213     $Element->{$HTML_NS}->{q} = {
3214 wakaba 1.153 status => FEATURE_HTML5_AT_RISK | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3215 wakaba 1.40 %HTMLPhrasingContentChecker,
3216     check_attrs => $GetHTMLAttrsChecker->({
3217 wakaba 1.50 cite => $HTMLURIAttrChecker,
3218     }, {
3219 wakaba 1.49 %HTMLAttrStatus,
3220 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3221 wakaba 1.153 cite => FEATURE_HTML5_AT_RISK | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3222     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3223 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3224     sdasuff => FEATURE_HTML2X_RFC,
3225 wakaba 1.1 }),
3226 wakaba 1.66 check_start => sub {
3227     my ($self, $item, $element_state) = @_;
3228    
3229     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3230 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3231     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3232 wakaba 1.66 },
3233 wakaba 1.1 };
3234 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
3235     ## placed inside the <code>q</code> element." Though we cannot test the
3236     ## element against this requirement since it incluides a semantic bit,
3237     ## it might be possible to inform of the existence of quotation marks OUTSIDE
3238     ## the |q| element.
3239 wakaba 1.1
3240     $Element->{$HTML_NS}->{cite} = {
3241 wakaba 1.40 %HTMLPhrasingContentChecker,
3242 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3243 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3244     %HTMLAttrStatus,
3245 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3246 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3247 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3248 wakaba 1.49 }),
3249 wakaba 1.1 };
3250    
3251     $Element->{$HTML_NS}->{em} = {
3252 wakaba 1.40 %HTMLPhrasingContentChecker,
3253 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3254 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3255     %HTMLAttrStatus,
3256 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3257 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3258 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3259 wakaba 1.49 }),
3260 wakaba 1.1 };
3261    
3262     $Element->{$HTML_NS}->{strong} = {
3263 wakaba 1.40 %HTMLPhrasingContentChecker,
3264 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3265 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3266     %HTMLAttrStatus,
3267 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3268 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3269 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3270 wakaba 1.49 }),
3271 wakaba 1.1 };
3272    
3273     $Element->{$HTML_NS}->{small} = {
3274 wakaba 1.40 %HTMLPhrasingContentChecker,
3275 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
3276 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3277     %HTMLAttrStatus,
3278     %HTMLM12NCommonAttrStatus,
3279 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3280 wakaba 1.49 }),
3281 wakaba 1.1 };
3282    
3283 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
3284     %HTMLPhrasingContentChecker,
3285     status => FEATURE_M12N10_REC,
3286     check_attrs => $GetHTMLAttrsChecker->({}, {
3287     %HTMLAttrStatus,
3288     %HTMLM12NCommonAttrStatus,
3289 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3290 wakaba 1.51 }),
3291     };
3292    
3293 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
3294 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3295 wakaba 1.40 %HTMLPhrasingContentChecker,
3296 wakaba 1.1 };
3297    
3298     $Element->{$HTML_NS}->{dfn} = {
3299 wakaba 1.40 %HTMLPhrasingContentChecker,
3300 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3301 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3302     %HTMLAttrStatus,
3303 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3304 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3305 wakaba 1.49 }),
3306 wakaba 1.40 check_start => sub {
3307     my ($self, $item, $element_state) = @_;
3308     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
3309 wakaba 1.1
3310 wakaba 1.40 my $node = $item->{node};
3311 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
3312     unless (defined $term) {
3313     for my $child (@{$node->child_nodes}) {
3314     if ($child->node_type == 1) { # ELEMENT_NODE
3315     if (defined $term) {
3316     undef $term;
3317     last;
3318     } elsif ($child->manakai_local_name eq 'abbr') {
3319     my $nsuri = $child->namespace_uri;
3320     if (defined $nsuri and $nsuri eq $HTML_NS) {
3321     my $attr = $child->get_attribute_node_ns (undef, 'title');
3322     if ($attr) {
3323     $term = $attr->value;
3324     }
3325     }
3326     }
3327     } elsif ($child->node_type == 3 or $child->node_type == 4) {
3328     ## TEXT_NODE or CDATA_SECTION_NODE
3329 wakaba 1.132 if ($child->data =~ /\A[\x09\x0A\x0C\x0D\x20]+\z/) { # Inter-element whitespace
3330 wakaba 1.1 next;
3331     }
3332     undef $term;
3333     last;
3334     }
3335     }
3336     unless (defined $term) {
3337     $term = $node->text_content;
3338     }
3339     }
3340     if ($self->{term}->{$term}) {
3341     push @{$self->{term}->{$term}}, $node;
3342     } else {
3343     $self->{term}->{$term} = [$node];
3344     }
3345 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
3346     ## |ruby| unless |dfn| has |title|.
3347 wakaba 1.79
3348     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3349     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3350 wakaba 1.40 },
3351     check_end => sub {
3352     my ($self, $item, $element_state) = @_;
3353     $self->_remove_minus_elements ($element_state);
3354 wakaba 1.1
3355 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3356 wakaba 1.1 },
3357     };
3358    
3359     $Element->{$HTML_NS}->{abbr} = {
3360 wakaba 1.40 %HTMLPhrasingContentChecker,
3361 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3362 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3363     %HTMLAttrStatus,
3364 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3365     full => FEATURE_XHTML2_ED,
3366 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3367 wakaba 1.49 }),
3368 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
3369     ## number (plural vs singular) must match the grammatical number of the
3370     ## contents of the element." Though this can be checked by machine,
3371     ## it requires language-specific knowledge and dictionary, such that
3372     ## we don't support the check of the requirement.
3373     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
3374 wakaba 1.49 };
3375    
3376     $Element->{$HTML_NS}->{acronym} = {
3377     %HTMLPhrasingContentChecker,
3378     status => FEATURE_M12N10_REC,
3379     check_attrs => $GetHTMLAttrsChecker->({}, {
3380     %HTMLAttrStatus,
3381     %HTMLM12NCommonAttrStatus,
3382 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3383 wakaba 1.49 }),
3384 wakaba 1.1 };
3385    
3386     $Element->{$HTML_NS}->{time} = {
3387 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3388 wakaba 1.40 %HTMLPhrasingContentChecker,
3389     check_attrs => $GetHTMLAttrsChecker->({
3390 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
3391 wakaba 1.49 }, {
3392     %HTMLAttrStatus,
3393     %HTMLM12NCommonAttrStatus,
3394 wakaba 1.72 datetime => FEATURE_HTML5_FD,
3395 wakaba 1.1 }),
3396 wakaba 1.168 ## TODO: Update definition
3397 wakaba 1.1 ## TODO: Write tests
3398 wakaba 1.40 check_end => sub {
3399     my ($self, $item, $element_state) = @_;
3400 wakaba 1.1
3401 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
3402 wakaba 1.1 my $input;
3403     my $reg_sp;
3404     my $input_node;
3405     if ($attr) {
3406     $input = $attr->value;
3407 wakaba 1.132 $reg_sp = qr/[\x09\x0A\x0C\x0D\x20]*/;
3408 wakaba 1.1 $input_node = $attr;
3409     } else {
3410 wakaba 1.40 $input = $item->{node}->text_content;
3411 wakaba 1.112 $reg_sp = qr/\p{WhiteSpace}*/;
3412 wakaba 1.40 $input_node = $item->{node};
3413 wakaba 1.1
3414     ## ISSUE: What is the definition for "successfully extracts a date
3415     ## or time"? If the algorithm says the string is invalid but
3416     ## return some date or time, is it "successfully"?
3417     }
3418    
3419     my $hour;
3420     my $minute;
3421     my $second;
3422     if ($input =~ /
3423     \A
3424 wakaba 1.112 $reg_sp
3425 wakaba 1.1 ([0-9]+) # 1
3426     (?>
3427     -([0-9]+) # 2
3428 wakaba 1.112 -((?>[0-9]+)) # 3 # Use (?>) such that yyyy-mm-ddhh:mm does not match
3429     $reg_sp
3430 wakaba 1.1 (?>
3431     T
3432 wakaba 1.112 $reg_sp
3433 wakaba 1.1 )?
3434     ([0-9]+) # 4
3435     :([0-9]+) # 5
3436     (?>
3437     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
3438     )?
3439 wakaba 1.112 $reg_sp
3440 wakaba 1.1 (?>
3441     Z
3442 wakaba 1.112 $reg_sp
3443 wakaba 1.1 |
3444     [+-]([0-9]+):([0-9]+) # 7, 8
3445 wakaba 1.112 $reg_sp
3446 wakaba 1.1 )?
3447     \z
3448     |
3449     :([0-9]+) # 9
3450     (?>
3451     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
3452     )?
3453 wakaba 1.112 $reg_sp
3454     \z
3455 wakaba 1.1 )
3456     /x) {
3457     if (defined $2) { ## YYYY-MM-DD T? hh:mm
3458     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
3459     length $4 != 2 or length $5 != 2) {
3460     $self->{onerror}->(node => $input_node,
3461 wakaba 1.104 type => 'dateortime:syntax error',
3462     level => $self->{level}->{must});
3463 wakaba 1.1 }
3464    
3465     if (1 <= $2 and $2 <= 12) {
3466 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3467     level => $self->{level}->{must})
3468 wakaba 1.1 if $3 < 1 or
3469     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
3470 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3471     level => $self->{level}->{must})
3472 wakaba 1.1 if $2 == 2 and $3 == 29 and
3473     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
3474     } else {
3475     $self->{onerror}->(node => $input_node,
3476 wakaba 1.104 type => 'datetime:bad month',
3477     level => $self->{level}->{must});
3478 wakaba 1.1 }
3479    
3480     ($hour, $minute, $second) = ($4, $5, $6);
3481    
3482     if (defined $7) { ## [+-]hh:mm
3483     if (length $7 != 2 or length $8 != 2) {
3484     $self->{onerror}->(node => $input_node,
3485 wakaba 1.104 type => 'dateortime:syntax error',
3486     level => $self->{level}->{must});
3487 wakaba 1.1 }
3488    
3489     $self->{onerror}->(node => $input_node,
3490 wakaba 1.104 type => 'datetime:bad timezone hour',
3491     level => $self->{level}->{must})
3492 wakaba 1.1 if $7 > 23;
3493     $self->{onerror}->(node => $input_node,
3494 wakaba 1.104 type => 'datetime:bad timezone minute',
3495     level => $self->{level}->{must})
3496 wakaba 1.1 if $8 > 59;
3497     }
3498     } else { ## hh:mm
3499     if (length $1 != 2 or length $9 != 2) {
3500     $self->{onerror}->(node => $input_node,
3501 wakaba 1.104 type => qq'dateortime:syntax error',
3502     level => $self->{level}->{must});
3503 wakaba 1.1 }
3504    
3505     ($hour, $minute, $second) = ($1, $9, $10);
3506     }
3507    
3508 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad hour',
3509     level => $self->{level}->{must}) if $hour > 23;
3510     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute',
3511     level => $self->{level}->{must}) if $minute > 59;
3512 wakaba 1.1
3513     if (defined $second) { ## s
3514     ## NOTE: Integer part of second don't have to have length of two.
3515    
3516     if (substr ($second, 0, 1) eq '.') {
3517     $self->{onerror}->(node => $input_node,
3518 wakaba 1.104 type => 'dateortime:syntax error',
3519     level => $self->{level}->{must});
3520 wakaba 1.1 }
3521    
3522 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad second',
3523     level => $self->{level}->{must}) if $second >= 60;
3524 wakaba 1.1 }
3525     } else {
3526     $self->{onerror}->(node => $input_node,
3527 wakaba 1.104 type => 'dateortime:syntax error',
3528     level => $self->{level}->{must});
3529 wakaba 1.1 }
3530    
3531 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3532 wakaba 1.1 },
3533     };
3534    
3535     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
3536 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
3537 wakaba 1.113 ## TODO: content checking
3538     ## TODO: content or value must contain number (rev 2053)
3539 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3540 wakaba 1.40 %HTMLPhrasingContentChecker,
3541     check_attrs => $GetHTMLAttrsChecker->({
3542 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3543     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3544     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3545     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3546     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3547     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3548 wakaba 1.50 }, {
3549     %HTMLAttrStatus,
3550     high => FEATURE_HTML5_DEFAULT,
3551     low => FEATURE_HTML5_DEFAULT,
3552     max => FEATURE_HTML5_DEFAULT,
3553     min => FEATURE_HTML5_DEFAULT,
3554     optimum => FEATURE_HTML5_DEFAULT,
3555     value => FEATURE_HTML5_DEFAULT,
3556 wakaba 1.1 }),
3557     };
3558    
3559     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3560 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3561 wakaba 1.40 %HTMLPhrasingContentChecker,
3562     check_attrs => $GetHTMLAttrsChecker->({
3563 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3564     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3565 wakaba 1.50 }, {
3566     %HTMLAttrStatus,
3567     max => FEATURE_HTML5_DEFAULT,
3568     value => FEATURE_HTML5_DEFAULT,
3569 wakaba 1.1 }),
3570     };
3571    
3572     $Element->{$HTML_NS}->{code} = {
3573 wakaba 1.40 %HTMLPhrasingContentChecker,
3574 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3575 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3576     %HTMLAttrStatus,
3577 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3578 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3579 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3580 wakaba 1.49 }),
3581 wakaba 1.1 };
3582    
3583     $Element->{$HTML_NS}->{var} = {
3584 wakaba 1.40 %HTMLPhrasingContentChecker,
3585 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3586 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3587     %HTMLAttrStatus,
3588 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3589 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3590 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3591 wakaba 1.49 }),
3592 wakaba 1.1 };
3593    
3594     $Element->{$HTML_NS}->{samp} = {
3595 wakaba 1.40 %HTMLPhrasingContentChecker,
3596 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3597 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3598     %HTMLAttrStatus,
3599 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3600 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3601 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3602 wakaba 1.49 }),
3603 wakaba 1.1 };
3604    
3605     $Element->{$HTML_NS}->{kbd} = {
3606 wakaba 1.40 %HTMLPhrasingContentChecker,
3607 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3608 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3609     %HTMLAttrStatus,
3610 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3611 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3612 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3613 wakaba 1.49 }),
3614 wakaba 1.1 };
3615    
3616     $Element->{$HTML_NS}->{sub} = {
3617 wakaba 1.40 %HTMLPhrasingContentChecker,
3618 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3619 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3620     %HTMLAttrStatus,
3621 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3622 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3623 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3624 wakaba 1.49 }),
3625 wakaba 1.1 };
3626    
3627 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3628 wakaba 1.1
3629     $Element->{$HTML_NS}->{span} = {
3630 wakaba 1.40 %HTMLPhrasingContentChecker,
3631 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3632 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3633     %HTMLAttrStatus,
3634 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3635 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3636     dataformatas => FEATURE_HTML4_REC_RESERVED,
3637     datasrc => FEATURE_HTML4_REC_RESERVED,
3638 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3639 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3640 wakaba 1.49 }),
3641 wakaba 1.1 };
3642    
3643     $Element->{$HTML_NS}->{i} = {
3644 wakaba 1.40 %HTMLPhrasingContentChecker,
3645 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3646     check_attrs => $GetHTMLAttrsChecker->({}, {
3647     %HTMLAttrStatus,
3648     %HTMLM12NCommonAttrStatus,
3649 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3650 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3651 wakaba 1.49 }),
3652 wakaba 1.1 };
3653    
3654 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3655    
3656 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3657     %HTMLPhrasingContentChecker,
3658     status => FEATURE_M12N10_REC,
3659     check_attrs => $GetHTMLAttrsChecker->({}, {
3660     %HTMLAttrStatus,
3661     %HTMLM12NCommonAttrStatus,
3662 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3663 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3664     }),
3665     };
3666 wakaba 1.51
3667     $Element->{$HTML_NS}->{s} = {
3668 wakaba 1.40 %HTMLPhrasingContentChecker,
3669 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3670 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3671     %HTMLAttrStatus,
3672     %HTMLM12NCommonAttrStatus,
3673 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3674 wakaba 1.49 }),
3675 wakaba 1.1 };
3676    
3677 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3678    
3679     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3680    
3681 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3682 wakaba 1.40 %HTMLPhrasingContentChecker,
3683 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3684 wakaba 1.40 check_attrs => sub {
3685     my ($self, $item, $element_state) = @_;
3686 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3687     %HTMLAttrStatus,
3688 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3689     dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3690     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3691     style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3692     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3693     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
3694 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3695     sdasuff => FEATURE_HTML2X_RFC,
3696 wakaba 1.49 })->($self, $item, $element_state);
3697 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3698     $self->{onerror}->(node => $item->{node},
3699 wakaba 1.104 type => 'attribute missing',
3700     text => 'dir',
3701     level => $self->{level}->{must});
3702 wakaba 1.1 }
3703     },
3704     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3705     };
3706    
3707 wakaba 1.99 $Element->{$HTML_NS}->{ruby} = {
3708     %HTMLPhrasingContentChecker,
3709     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3710     check_attrs => $GetHTMLAttrsChecker->({}, {
3711     %HTMLAttrStatus,
3712     %HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0
3713 wakaba 1.153 lang => FEATURE_HTML5_WD,
3714 wakaba 1.99 }),
3715     check_start => sub {
3716     my ($self, $item, $element_state) = @_;
3717    
3718     $element_state->{phase} = 'before-rb';
3719     #$element_state->{has_sig}
3720 wakaba 1.100
3721     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3722     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3723 wakaba 1.99 },
3724     ## NOTE: (phrasing, (rt | (rp, rt, rp)))+
3725     check_child_element => sub {
3726     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3727     $child_is_transparent, $element_state) = @_;
3728 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
3729     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
3730 wakaba 1.99 $self->{onerror}->(node => $child_el,
3731     type => 'element not allowed:minus',
3732 wakaba 1.104 level => $self->{level}->{must});
3733 wakaba 1.99 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3734     #
3735     } elsif ($element_state->{phase} eq 'before-rb') {
3736     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3737     $element_state->{phase} = 'in-rb';
3738     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3739     $self->{onerror}->(node => $child_el,
3740 wakaba 1.104 level => $self->{level}->{should},
3741     type => 'no significant content before');
3742 wakaba 1.99 $element_state->{phase} = 'after-rt';
3743     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3744     $self->{onerror}->(node => $child_el,
3745 wakaba 1.104 level => $self->{level}->{should},
3746     type => 'no significant content before');
3747 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3748     } else {
3749     $self->{onerror}->(node => $child_el,
3750 wakaba 1.104 type => 'element not allowed:ruby base',
3751     level => $self->{level}->{must});
3752 wakaba 1.99 $element_state->{phase} = 'in-rb';
3753     }
3754     } elsif ($element_state->{phase} eq 'in-rb') {
3755     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3756     #$element_state->{phase} = 'in-rb';
3757     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3758     unless ($element_state->{has_significant}) {
3759     $self->{onerror}->(node => $child_el,
3760 wakaba 1.104 level => $self->{level}->{should},
3761     type => 'no significant content before');
3762 wakaba 1.99 }
3763     $element_state->{phase} = 'after-rt';
3764     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3765     unless ($element_state->{has_significant}) {
3766     $self->{onerror}->(node => $child_el,
3767 wakaba 1.104 level => $self->{level}->{should},
3768     type => 'no significant content before');
3769 wakaba 1.99 }
3770     $element_state->{phase} = 'after-rp1';
3771     } else {
3772     $self->{onerror}->(node => $child_el,
3773 wakaba 1.104 type => 'element not allowed:ruby base',
3774     level => $self->{level}->{must});
3775 wakaba 1.99 #$element_state->{phase} = 'in-rb';
3776     }
3777     } elsif ($element_state->{phase} eq 'after-rt') {
3778     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3779     if ($element_state->{has_significant}) {
3780     $element_state->{has_sig} = 1;
3781     delete $element_state->{has_significant};
3782     }
3783     $element_state->{phase} = 'in-rb';
3784     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3785     $self->{onerror}->(node => $child_el,
3786 wakaba 1.104 level => $self->{level}->{should},
3787     type => 'no significant content before');
3788 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3789     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3790     $self->{onerror}->(node => $child_el,
3791 wakaba 1.104 level => $self->{level}->{should},
3792     type => 'no significant content before');
3793 wakaba 1.99 #$element_state->{phase} = 'after-rt';
3794     } else {
3795     $self->{onerror}->(node => $child_el,
3796 wakaba 1.104 type => 'element not allowed:ruby base',
3797     level => $self->{level}->{must});
3798 wakaba 1.99 if ($element_state->{has_significant}) {
3799     $element_state->{has_sig} = 1;
3800     delete $element_state->{has_significant};
3801     }
3802     $element_state->{phase} = 'in-rb';
3803     }
3804     } elsif ($element_state->{phase} eq 'after-rp1') {
3805     if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3806     $element_state->{phase} = 'after-rp-rt';
3807     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3808     $self->{onerror}->(node => $child_el,
3809 wakaba 1.104 type => 'ps element missing',
3810     text => 'rt',
3811     level => $self->{level}->{must});
3812 wakaba 1.99 $element_state->{phase} = 'after-rp2';
3813     } else {
3814     $self->{onerror}->(node => $child_el,
3815 wakaba 1.104 type => 'ps element missing',
3816     text => 'rt',
3817     level => $self->{level}->{must});
3818 wakaba 1.99 $self->{onerror}->(node => $child_el,
3819 wakaba 1.104 type => 'ps element missing',
3820     text => 'rp',
3821     level => $self->{level}->{must});
3822 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3823     $self->{onerror}->(node => $child_el,
3824 wakaba 1.104 type => 'element not allowed:ruby base',
3825     level => $self->{level}->{must});
3826 wakaba 1.99 }
3827     if ($element_state->{has_significant}) {
3828     $element_state->{has_sig} = 1;
3829     delete $element_state->{has_significant};
3830     }
3831     $element_state->{phase} = 'in-rb';
3832     }
3833     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3834     if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3835     $element_state->{phase} = 'after-rp2';
3836     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3837     $self->{onerror}->(node => $child_el,
3838 wakaba 1.104 type => 'ps element missing',
3839     text => 'rp',
3840     level => $self->{level}->{must});
3841 wakaba 1.99 $self->{onerror}->(node => $child_el,
3842 wakaba 1.104 level => $self->{level}->{should},
3843     type => 'no significant content before');
3844 wakaba 1.99 $element_state->{phase} = 'after-rt';
3845     } else {
3846     $self->{onerror}->(node => $child_el,
3847 wakaba 1.104 type => 'ps element missing',
3848     text => 'rp',
3849     level => $self->{level}->{must});
3850 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3851     $self->{onerror}->(node => $child_el,
3852 wakaba 1.104 type => 'element not allowed:ruby base',
3853     level => $self->{level}->{must});
3854 wakaba 1.99 }
3855     if ($element_state->{has_significant}) {
3856     $element_state->{has_sig} = 1;
3857     delete $element_state->{has_significant};
3858     }
3859     $element_state->{phase} = 'in-rb';
3860     }
3861     } elsif ($element_state->{phase} eq 'after-rp2') {
3862     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3863     if ($element_state->{has_significant}) {
3864     $element_state->{has_sig} = 1;
3865     delete $element_state->{has_significant};
3866     }
3867     $element_state->{phase} = 'in-rb';
3868     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3869     $self->{onerror}->(node => $child_el,
3870 wakaba 1.104 level => $self->{level}->{should},
3871     type => 'no significant content before');
3872 wakaba 1.99 $element_state->{phase} = 'after-rt';
3873     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3874     $self->{onerror}->(node => $child_el,
3875 wakaba 1.104 level => $self->{level}->{should},
3876     type => 'no significant content before');
3877 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3878     } else {
3879     $self->{onerror}->(node => $child_el,
3880 wakaba 1.104 type => 'element not allowed:ruby base',
3881     level => $self->{level}->{must});
3882 wakaba 1.99 if ($element_state->{has_significant}) {
3883     $element_state->{has_sig} = 1;
3884     delete $element_state->{has_significant};
3885     }
3886     $element_state->{phase} = 'in-rb';
3887     }
3888     } else {
3889     die "check_child_element: Bad |ruby| phase: $element_state->{phase}";
3890     }
3891     },
3892     check_child_text => sub {
3893     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3894     if ($has_significant) {
3895     if ($element_state->{phase} eq 'before-rb') {
3896     $element_state->{phase} = 'in-rb';
3897     } elsif ($element_state->{phase} eq 'in-rb') {
3898     #
3899     } elsif ($element_state->{phase} eq 'after-rt' or
3900     $element_state->{phase} eq 'after-rp2') {
3901     $element_state->{phase} = 'in-rb';
3902     } elsif ($element_state->{phase} eq 'after-rp1') {
3903     $self->{onerror}->(node => $child_node,
3904 wakaba 1.104 type => 'ps element missing',
3905     text => 'rt',
3906     level => $self->{level}->{must});
3907 wakaba 1.99 $self->{onerror}->(node => $child_node,
3908 wakaba 1.104 type => 'ps element missing',
3909     text => 'rp',
3910     level => $self->{level}->{must});
3911 wakaba 1.99 $element_state->{phase} = 'in-rb';
3912     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3913     $self->{onerror}->(node => $child_node,
3914 wakaba 1.104 type => 'ps element missing',
3915     text => 'rp',
3916     level => $self->{level}->{must});
3917 wakaba 1.99 $element_state->{phase} = 'in-rb';
3918     } else {
3919     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3920     }
3921     }
3922     },
3923     check_end => sub {
3924     my ($self, $item, $element_state) = @_;
3925     $self->_remove_minus_elements ($element_state);
3926    
3927     if ($element_state->{phase} eq 'before-rb') {
3928     $self->{onerror}->(node => $item->{node},
3929 wakaba 1.104 level => $self->{level}->{should},
3930 wakaba 1.99 type => 'no significant content');
3931     $self->{onerror}->(node => $item->{node},
3932 wakaba 1.104 type => 'element missing',
3933     text => 'rt',
3934     level => $self->{level}->{must});
3935 wakaba 1.99 } elsif ($element_state->{phase} eq 'in-rb') {
3936     unless ($element_state->{has_significant}) {
3937     $self->{onerror}->(node => $item->{node},
3938 wakaba 1.104 level => $self->{level}->{should},
3939     type => 'no significant content at the end');
3940 wakaba 1.99 }
3941     $self->{onerror}->(node => $item->{node},
3942 wakaba 1.104 type => 'element missing',
3943     text => 'rt',
3944     level => $self->{level}->{must});
3945 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rt' or
3946     $element_state->{phase} eq 'after-rp2') {
3947     #
3948     } elsif ($element_state->{phase} eq 'after-rp1') {
3949     $self->{onerror}->(node => $item->{node},
3950 wakaba 1.104 type => 'element missing',
3951     text => 'rt',
3952     level => $self->{level}->{must});
3953 wakaba 1.99 $self->{onerror}->(node => $item->{node},
3954 wakaba 1.104 type => 'element missing',
3955     text => 'rp',
3956     level => $self->{level}->{must});
3957 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rp-rt') {
3958     $self->{onerror}->(node => $item->{node},
3959 wakaba 1.104 type => 'element missing',
3960     text => 'rp',
3961     level => $self->{level}->{must});
3962 wakaba 1.99 } else {
3963     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3964     }
3965    
3966     ## NOTE: A modified version of |check_end| of %AnyChecker.
3967     if ($element_state->{has_significant} or $element_state->{has_sig}) {
3968     $item->{real_parent_state}->{has_significant} = 1;
3969     }
3970     },
3971     };
3972    
3973     $Element->{$HTML_NS}->{rt} = {
3974     %HTMLPhrasingContentChecker,
3975     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3976     check_attrs => $GetHTMLAttrsChecker->({}, {
3977     %HTMLAttrStatus,
3978     %HTMLM12NXHTML2CommonAttrStatus,
3979 wakaba 1.153 lang => FEATURE_HTML5_WD,
3980 wakaba 1.99 }),
3981     };
3982    
3983     $Element->{$HTML_NS}->{rp} = {
3984 wakaba 1.171 %HTMLPhrasingContentChecker,
3985 wakaba 1.99 status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3986     check_attrs => $GetHTMLAttrsChecker->({}, {
3987     %HTMLAttrStatus,
3988     %HTMLM12NXHTML2CommonAttrStatus,
3989 wakaba 1.153 lang => FEATURE_HTML5_WD,
3990 wakaba 1.99 }),
3991 wakaba 1.171 }; # rp
3992 wakaba 1.99
3993 wakaba 1.29 =pod
3994    
3995     ## TODO:
3996    
3997     +
3998     + <p>Partly because of the confusion described above, authors are
3999     + strongly recommended to always mark up all paragraphs with the
4000     + <code>p</code> element, and to not have any <code>ins</code> or
4001     + <code>del</code> elements that cross across any <span
4002     + title="paragraph">implied paragraphs</span>.</p>
4003     +
4004     (An informative note)
4005    
4006     <p><code>ins</code> elements should not cross <span
4007     + title="paragraph">implied paragraph</span> boundaries.</p>
4008     (normative)
4009    
4010     + <p><code>del</code> elements should not cross <span
4011     + title="paragraph">implied paragraph</span> boundaries.</p>
4012     (normative)
4013    
4014     =cut
4015    
4016 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
4017 wakaba 1.40 %HTMLTransparentChecker,
4018 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4019 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4020 wakaba 1.1 cite => $HTMLURIAttrChecker,
4021 wakaba 1.168 datetime => $GetDateTimeAttrChecker->('global_date_and_time_string'),
4022 wakaba 1.49 }, {
4023     %HTMLAttrStatus,
4024     %HTMLM12NCommonAttrStatus,
4025 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4026 wakaba 1.153 datetime => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4027     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4028 wakaba 1.1 }),
4029 wakaba 1.66 check_start => sub {
4030     my ($self, $item, $element_state) = @_;
4031    
4032     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
4033 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4034     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4035 wakaba 1.66 },
4036 wakaba 1.1 };
4037    
4038     $Element->{$HTML_NS}->{del} = {
4039 wakaba 1.40 %HTMLTransparentChecker,
4040 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4041 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4042 wakaba 1.1 cite => $HTMLURIAttrChecker,
4043 wakaba 1.168 datetime => $GetDateTimeAttrChecker->('global_date_and_time_string'),
4044 wakaba 1.49 }, {
4045     %HTMLAttrStatus,
4046     %HTMLM12NCommonAttrStatus,
4047 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4048 wakaba 1.153 datetime => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4049     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4050 wakaba 1.1 }),
4051 wakaba 1.40 check_end => sub {
4052     my ($self, $item, $element_state) = @_;
4053     if ($element_state->{has_significant}) {
4054     ## NOTE: Significantness flag does not propagate.
4055     } elsif ($item->{transparent}) {
4056     #
4057     } else {
4058     $self->{onerror}->(node => $item->{node},
4059 wakaba 1.104 level => $self->{level}->{should},
4060 wakaba 1.40 type => 'no significant content');
4061     }
4062 wakaba 1.1 },
4063 wakaba 1.66 check_start => sub {
4064     my ($self, $item, $element_state) = @_;
4065    
4066     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
4067 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4068     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4069 wakaba 1.66 },
4070 wakaba 1.1 };
4071    
4072 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
4073 wakaba 1.72 %HTMLFlowContentChecker,
4074 wakaba 1.153 status => FEATURE_HTML5_WD,
4075 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
4076 wakaba 1.41 check_child_element => sub {
4077     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4078     $child_is_transparent, $element_state) = @_;
4079 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4080     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4081 wakaba 1.41 $self->{onerror}->(node => $child_el,
4082     type => 'element not allowed:minus',
4083 wakaba 1.104 level => $self->{level}->{must});
4084 wakaba 1.41 $element_state->{has_non_legend} = 1;
4085     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4086     #
4087     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
4088     if ($element_state->{has_legend_at_first}) {
4089     $self->{onerror}->(node => $child_el,
4090     type => 'element not allowed:figure legend',
4091 wakaba 1.104 level => $self->{level}->{must});
4092 wakaba 1.41 } elsif ($element_state->{has_legend}) {
4093     $self->{onerror}->(node => $element_state->{has_legend},
4094     type => 'element not allowed:figure legend',
4095 wakaba 1.104 level => $self->{level}->{must});
4096 wakaba 1.41 $element_state->{has_legend} = $child_el;
4097     } elsif ($element_state->{has_non_legend}) {
4098     $element_state->{has_legend} = $child_el;
4099     } else {
4100     $element_state->{has_legend_at_first} = 1;
4101 wakaba 1.35 }
4102 wakaba 1.41 delete $element_state->{has_non_legend};
4103     } else {
4104 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4105 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
4106 wakaba 1.41 }
4107     },
4108     check_child_text => sub {
4109     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4110     if ($has_significant) {
4111     $element_state->{has_non_legend} = 1;
4112 wakaba 1.35 }
4113 wakaba 1.170
4114     $element_state->{in_figure} = 1;
4115 wakaba 1.41 },
4116     check_end => sub {
4117     my ($self, $item, $element_state) = @_;
4118 wakaba 1.35
4119 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
4120     #
4121     } elsif ($element_state->{has_legend}) {
4122     if ($element_state->{has_non_legend}) {
4123     $self->{onerror}->(node => $element_state->{has_legend},
4124 wakaba 1.35 type => 'element not allowed:figure legend',
4125 wakaba 1.104 level => $self->{level}->{must});
4126 wakaba 1.35 }
4127     }
4128 wakaba 1.41
4129 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4130 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
4131 wakaba 1.35 },
4132     };
4133 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
4134 wakaba 1.1
4135 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
4136     my ($self, $attr) = @_;
4137 wakaba 1.104 $self->{onerror}->(node => $attr,
4138     type => 'unknown attribute',
4139     level => $self->{level}->{uncertain});
4140 wakaba 1.92 };
4141    
4142 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
4143 wakaba 1.40 %HTMLEmptyChecker,
4144 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4145 wakaba 1.40 check_attrs => sub {
4146     my ($self, $item, $element_state) = @_;
4147 wakaba 1.1 $GetHTMLAttrsChecker->({
4148 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4149     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4150     }),
4151 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
4152 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4153 wakaba 1.1 src => $HTMLURIAttrChecker,
4154     usemap => $HTMLUsemapAttrChecker,
4155 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4156 wakaba 1.1 ismap => sub {
4157 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
4158     if (not $self->{flag}->{in_a_href}) {
4159 wakaba 1.15 $self->{onerror}->(node => $attr,
4160 wakaba 1.59 type => 'attribute not allowed:ismap',
4161 wakaba 1.104 level => $self->{level}->{must});
4162 wakaba 1.1 }
4163 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
4164 wakaba 1.1 },
4165 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
4166     ## TODO: HTML4 |name|
4167 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4168 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4169 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4170 wakaba 1.49 }, {
4171     %HTMLAttrStatus,
4172 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4173 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4174 wakaba 1.153 alt => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4175 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
4176 wakaba 1.153 height => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4177 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
4178 wakaba 1.153 ismap => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4179     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4180 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
4181     name => FEATURE_M12N10_REC_DEPRECATED,
4182 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4183 wakaba 1.153 src => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4184     usemap => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4185 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
4186 wakaba 1.153 width => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4187 wakaba 1.66 })->($self, $item, $element_state);
4188 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
4189     $self->{onerror}->(node => $item->{node},
4190 wakaba 1.104 type => 'attribute missing',
4191     text => 'alt',
4192     level => $self->{level}->{should});
4193 wakaba 1.114 ## TODO: ...
4194 wakaba 1.1 }
4195 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4196     $self->{onerror}->(node => $item->{node},
4197 wakaba 1.104 type => 'attribute missing',
4198     text => 'src',
4199     level => $self->{level}->{must});
4200 wakaba 1.1 }
4201 wakaba 1.66
4202 wakaba 1.114 ## TODO: external resource check
4203    
4204 wakaba 1.66 $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4205     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
4206     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
4207     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
4208 wakaba 1.1 },
4209     };
4210    
4211     $Element->{$HTML_NS}->{iframe} = {
4212 wakaba 1.40 %HTMLTextChecker,
4213 wakaba 1.114 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4214 wakaba 1.49 ## NOTE: Not part of M12N10 Strict
4215 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4216 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4217 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4218 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
4219     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
4220     }),
4221     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
4222 wakaba 1.1 src => $HTMLURIAttrChecker,
4223 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4224 wakaba 1.49 }, {
4225     %HTMLAttrStatus,
4226     %HTMLM12NCommonAttrStatus,
4227     align => FEATURE_XHTML10_REC,
4228 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4229 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
4230 wakaba 1.153 height => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4231     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4232 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
4233     marginheight => FEATURE_M12N10_REC,
4234     marginwidth => FEATURE_M12N10_REC,
4235 wakaba 1.114 #name => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
4236     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4237     sandbox => FEATURE_HTML5_WD,
4238 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
4239 wakaba 1.114 seemless => FEATURE_HTML5_WD,
4240     src => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4241 wakaba 1.153 title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4242     width => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4243 wakaba 1.1 }),
4244 wakaba 1.66 check_start => sub {
4245     my ($self, $item, $element_state) = @_;
4246    
4247     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4248 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4249     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4250 wakaba 1.66 },
4251 wakaba 1.40 };
4252    
4253 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
4254 wakaba 1.40 %HTMLEmptyChecker,
4255 wakaba 1.98 status => FEATURE_HTML5_WD,
4256 wakaba 1.40 check_attrs => sub {
4257     my ($self, $item, $element_state) = @_;
4258 wakaba 1.1 my $has_src;
4259 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4260 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4261     $attr_ns = '' unless defined $attr_ns;
4262     my $attr_ln = $attr->manakai_local_name;
4263     my $checker;
4264 wakaba 1.73
4265     my $status = {
4266     %HTMLAttrStatus,
4267 wakaba 1.153 height => FEATURE_HTML5_LC,
4268 wakaba 1.98 src => FEATURE_HTML5_WD,
4269     type => FEATURE_HTML5_WD,
4270 wakaba 1.153 width => FEATURE_HTML5_LC,
4271 wakaba 1.73 }->{$attr_ln};
4272    
4273 wakaba 1.1 if ($attr_ns eq '') {
4274     if ($attr_ln eq 'src') {
4275     $checker = $HTMLURIAttrChecker;
4276     $has_src = 1;
4277     } elsif ($attr_ln eq 'type') {
4278     $checker = $HTMLIMTAttrChecker;
4279 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
4280 wakaba 1.178 $checker = $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 });
4281 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4282     $attr_ln !~ /[A-Z]/) {
4283 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4284     $status = $HTMLDatasetAttrStatus;
4285 wakaba 1.117 } elsif ($attr_ln !~ /^[Xx][Mm][Ll]/ and
4286 wakaba 1.118 $attr_ln !~ /[A-Z]/ and
4287 wakaba 1.117 $attr_ln =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
4288 wakaba 1.1 $checker = $HTMLAttrChecker->{$attr_ln}
4289     || sub { }; ## NOTE: Any local attribute is ok.
4290 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
4291 wakaba 1.117 } else {
4292     $checker = $HTMLAttrChecker->{$attr_ln};
4293 wakaba 1.1 }
4294     }
4295     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4296 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4297     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4298     || $AttrStatus->{$attr_ns}->{''};
4299     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4300 wakaba 1.62
4301 wakaba 1.1 if ($checker) {
4302 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
4303 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4304 wakaba 1.54 #
4305 wakaba 1.1 } else {
4306 wakaba 1.104 $self->{onerror}->(node => $attr,
4307     type => 'unknown attribute',
4308     level => $self->{level}->{uncertain});
4309 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
4310     }
4311    
4312 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4313 wakaba 1.1 }
4314    
4315     unless ($has_src) {
4316 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4317 wakaba 1.104 type => 'attribute missing',
4318     text => 'src',
4319 wakaba 1.114 level => $self->{level}->{info});
4320     ## NOTE: <embed> without src="" is allowed since revision 1929.
4321     ## We issues an informational message since <embed> w/o src=""
4322     ## is likely an authoring error.
4323 wakaba 1.1 }
4324 wakaba 1.114
4325     ## TODO: external resource check
4326 wakaba 1.66
4327     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4328 wakaba 1.1 },
4329     };
4330    
4331 wakaba 1.49 ## TODO:
4332     ## {applet} FEATURE_M12N10_REC_DEPRECATED
4333     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
4334    
4335 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
4336 wakaba 1.40 %HTMLTransparentChecker,
4337 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4338 wakaba 1.40 check_attrs => sub {
4339     my ($self, $item, $element_state) = @_;
4340 wakaba 1.1 $GetHTMLAttrsChecker->({
4341 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4342     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4343     }),
4344     archive => $HTMLSpaceURIsAttrChecker,
4345     ## TODO: Relative to @codebase
4346     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4347     classid => $HTMLURIAttrChecker,
4348     codebase => $HTMLURIAttrChecker,
4349     codetype => $HTMLIMTAttrChecker,
4350     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
4351 wakaba 1.1 data => $HTMLURIAttrChecker,
4352 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
4353     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
4354     ## [HTML4] but we don't know how to test this.
4355 wakaba 1.167 form => $HTMLFormAttrChecker,
4356 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4357 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4358 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4359 wakaba 1.167 ## NOTE: |name| attribute of the |object| element defines
4360     ## the name of the browsing context created by the element,
4361     ## if any, but is also used as the form control name of the
4362     ## form control provided by the plugin, if any.
4363 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
4364 wakaba 1.1 type => $HTMLIMTAttrChecker,
4365     usemap => $HTMLUsemapAttrChecker,
4366 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4367 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4368 wakaba 1.49 }, {
4369     %HTMLAttrStatus,
4370 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4371 wakaba 1.49 align => FEATURE_XHTML10_REC,
4372 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4373 wakaba 1.49 border => FEATURE_XHTML10_REC,
4374     classid => FEATURE_M12N10_REC,
4375     codebase => FEATURE_M12N10_REC,
4376     codetype => FEATURE_M12N10_REC,
4377 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
4378 wakaba 1.153 data => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4379 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4380     dataformatas => FEATURE_HTML4_REC_RESERVED,
4381     datasrc => FEATURE_HTML4_REC_RESERVED,
4382 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4383 wakaba 1.167 form => FEATURE_HTML5_DEFAULT,
4384 wakaba 1.153 height => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4385 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
4386 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4387     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4388 wakaba 1.49 standby => FEATURE_M12N10_REC,
4389 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4390 wakaba 1.153 type => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4391     usemap => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4392 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
4393 wakaba 1.153 width => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4394 wakaba 1.66 })->($self, $item, $element_state);
4395 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
4396     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
4397     $self->{onerror}->(node => $item->{node},
4398 wakaba 1.104 type => 'attribute missing:data|type',
4399     level => $self->{level}->{must});
4400 wakaba 1.1 }
4401     }
4402 wakaba 1.66
4403     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
4404     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
4405     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
4406     ## TODO: archive
4407     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4408 wakaba 1.1 },
4409 wakaba 1.72 ## NOTE: param*, transparent (Flow)
4410 wakaba 1.41 check_child_element => sub {
4411     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4412     $child_is_transparent, $element_state) = @_;
4413 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4414     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4415 wakaba 1.41 $self->{onerror}->(node => $child_el,
4416     type => 'element not allowed:minus',
4417 wakaba 1.104 level => $self->{level}->{must});
4418 wakaba 1.41 $element_state->{has_non_legend} = 1;
4419     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4420     #
4421     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
4422     if ($element_state->{has_non_param}) {
4423 wakaba 1.104 $self->{onerror}->(node => $child_el,
4424 wakaba 1.72 type => 'element not allowed:flow',
4425 wakaba 1.104 level => $self->{level}->{must});
4426 wakaba 1.39 }
4427 wakaba 1.41 } else {
4428 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4429 wakaba 1.41 $element_state->{has_non_param} = 1;
4430 wakaba 1.39 }
4431 wakaba 1.25 },
4432 wakaba 1.41 check_child_text => sub {
4433     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4434     if ($has_significant) {
4435     $element_state->{has_non_param} = 1;
4436     }
4437 wakaba 1.42 },
4438     check_end => sub {
4439     my ($self, $item, $element_state) = @_;
4440     if ($element_state->{has_significant}) {
4441 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
4442 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
4443     ## NOTE: Transparent.
4444     } else {
4445     $self->{onerror}->(node => $item->{node},
4446 wakaba 1.104 level => $self->{level}->{should},
4447 wakaba 1.42 type => 'no significant content');
4448     }
4449     },
4450 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
4451 wakaba 1.1 };
4452 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4453     ## What about |<section><object data><style scoped></style>x</object></section>|?
4454     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4455 wakaba 1.1
4456     $Element->{$HTML_NS}->{param} = {
4457 wakaba 1.40 %HTMLEmptyChecker,
4458 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4459 wakaba 1.40 check_attrs => sub {
4460     my ($self, $item, $element_state) = @_;
4461 wakaba 1.1 $GetHTMLAttrsChecker->({
4462     name => sub { },
4463 wakaba 1.70 type => $HTMLIMTAttrChecker,
4464 wakaba 1.1 value => sub { },
4465 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4466     data => 1, ref => 1, object => 1,
4467     }),
4468 wakaba 1.49 }, {
4469     %HTMLAttrStatus,
4470 wakaba 1.154 href => FEATURE_RDFA_REC,
4471 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4472     name => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4473 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4474 wakaba 1.153 value => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4475 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4476 wakaba 1.66 })->(@_);
4477 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4478     $self->{onerror}->(node => $item->{node},
4479 wakaba 1.104 type => 'attribute missing',
4480     text => 'name',
4481     level => $self->{level}->{must});
4482 wakaba 1.1 }
4483 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4484     $self->{onerror}->(node => $item->{node},
4485 wakaba 1.104 type => 'attribute missing',
4486     text => 'value',
4487     level => $self->{level}->{must});
4488 wakaba 1.1 }
4489     },
4490     };
4491    
4492     $Element->{$HTML_NS}->{video} = {
4493 wakaba 1.40 %HTMLTransparentChecker,
4494 wakaba 1.48 status => FEATURE_HTML5_LC,
4495 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4496 wakaba 1.1 src => $HTMLURIAttrChecker,
4497     ## TODO: start, loopstart, loopend, end
4498     ## ISSUE: they MUST be "value time offset"s. Value?
4499 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4500 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4501     controls => $GetHTMLBooleanAttrChecker->('controls'),
4502 wakaba 1.59 poster => $HTMLURIAttrChecker,
4503 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4504     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4505 wakaba 1.50 }, {
4506     %HTMLAttrStatus,
4507     autoplay => FEATURE_HTML5_LC,
4508     controls => FEATURE_HTML5_LC,
4509 wakaba 1.153 end => FEATURE_HTML5_AT_RISK,
4510 wakaba 1.50 height => FEATURE_HTML5_LC,
4511 wakaba 1.153 loopend => FEATURE_HTML5_AT_RISK,
4512     loopstart => FEATURE_HTML5_AT_RISK,
4513     playcount => FEATURE_HTML5_AT_RISK,
4514 wakaba 1.50 poster => FEATURE_HTML5_LC,
4515     src => FEATURE_HTML5_LC,
4516 wakaba 1.153 start => FEATURE_HTML5_AT_RISK,
4517 wakaba 1.50 width => FEATURE_HTML5_LC,
4518 wakaba 1.1 }),
4519 wakaba 1.42 check_start => sub {
4520     my ($self, $item, $element_state) = @_;
4521     $element_state->{allow_source}
4522     = not $item->{node}->has_attribute_ns (undef, 'src');
4523     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4524     ## NOTE: It might be set true by |check_element|.
4525 wakaba 1.66
4526     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4527     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4528 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4529     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4530 wakaba 1.42 },
4531     check_child_element => sub {
4532     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4533     $child_is_transparent, $element_state) = @_;
4534 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
4535     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
4536 wakaba 1.42 $self->{onerror}->(node => $child_el,
4537     type => 'element not allowed:minus',
4538 wakaba 1.104 level => $self->{level}->{must});
4539 wakaba 1.42 delete $element_state->{allow_source};
4540     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4541     #
4542     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4543 wakaba 1.45 unless ($element_state->{allow_source}) {
4544 wakaba 1.104 $self->{onerror}->(node => $child_el,
4545 wakaba 1.72 type => 'element not allowed:flow',
4546 wakaba 1.104 level => $self->{level}->{must});
4547 wakaba 1.42 }
4548 wakaba 1.45 $element_state->{has_source} = 1;
4549 wakaba 1.1 } else {
4550 wakaba 1.42 delete $element_state->{allow_source};
4551 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4552 wakaba 1.42 }
4553     },
4554     check_child_text => sub {
4555     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4556     if ($has_significant) {
4557     delete $element_state->{allow_source};
4558     }
4559 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4560 wakaba 1.42 },
4561     check_end => sub {
4562     my ($self, $item, $element_state) = @_;
4563     if ($element_state->{has_source} == -1) {
4564     $self->{onerror}->(node => $item->{node},
4565 wakaba 1.104 type => 'child element missing',
4566     text => 'source',
4567     level => $self->{level}->{must});
4568 wakaba 1.1 }
4569 wakaba 1.42
4570     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4571 wakaba 1.1 },
4572     };
4573    
4574     $Element->{$HTML_NS}->{audio} = {
4575 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4576 wakaba 1.48 status => FEATURE_HTML5_LC,
4577 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4578     src => $HTMLURIAttrChecker,
4579     ## TODO: start, loopstart, loopend, end
4580     ## ISSUE: they MUST be "value time offset"s. Value?
4581     ## ISSUE: playcount has no conformance creteria
4582     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4583     controls => $GetHTMLBooleanAttrChecker->('controls'),
4584 wakaba 1.50 }, {
4585     %HTMLAttrStatus,
4586     autoplay => FEATURE_HTML5_LC,
4587     controls => FEATURE_HTML5_LC,
4588 wakaba 1.153 end => FEATURE_HTML5_AT_RISK,
4589     loopend => FEATURE_HTML5_AT_RISK,
4590     loopstart => FEATURE_HTML5_AT_RISK,
4591     playcount => FEATURE_HTML5_AT_RISK,
4592 wakaba 1.50 src => FEATURE_HTML5_LC,
4593 wakaba 1.153 start => FEATURE_HTML5_AT_RISK,
4594 wakaba 1.42 }),
4595 wakaba 1.1 };
4596    
4597     $Element->{$HTML_NS}->{source} = {
4598 wakaba 1.40 %HTMLEmptyChecker,
4599 wakaba 1.153 status => FEATURE_HTML5_LC,
4600 wakaba 1.40 check_attrs => sub {
4601     my ($self, $item, $element_state) = @_;
4602 wakaba 1.1 $GetHTMLAttrsChecker->({
4603 wakaba 1.90 media => $HTMLMQAttrChecker,
4604     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4605     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4606 wakaba 1.1 type => $HTMLIMTAttrChecker,
4607 wakaba 1.50 }, {
4608     %HTMLAttrStatus,
4609 wakaba 1.153 media => FEATURE_HTML5_LC,
4610     pixelratio => FEATURE_HTML5_LC,
4611     src => FEATURE_HTML5_LC,
4612     type => FEATURE_HTML5_LC,
4613 wakaba 1.66 })->(@_);
4614 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4615     $self->{onerror}->(node => $item->{node},
4616 wakaba 1.104 type => 'attribute missing',
4617     text => 'src',
4618     level => $self->{level}->{must});
4619 wakaba 1.1 }
4620 wakaba 1.66
4621     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4622 wakaba 1.1 },
4623     };
4624    
4625     $Element->{$HTML_NS}->{canvas} = {
4626 wakaba 1.40 %HTMLTransparentChecker,
4627 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4628 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4629 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4630     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4631 wakaba 1.50 }, {
4632     %HTMLAttrStatus,
4633 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4634     width => FEATURE_HTML5_COMPLETE,
4635 wakaba 1.1 }),
4636 wakaba 1.178
4637     # Authors MUST provide alternative content (HTML5 revision 2868) -
4638     # This requirement cannot be checked, since the alternative content
4639     # might be placed outside of the element.
4640     }; # canvas
4641 wakaba 1.1
4642     $Element->{$HTML_NS}->{map} = {
4643 wakaba 1.72 %HTMLFlowContentChecker,
4644 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4645 wakaba 1.40 check_attrs => sub {
4646     my ($self, $item, $element_state) = @_;
4647 wakaba 1.100 my $has_name;
4648 wakaba 1.4 $GetHTMLAttrsChecker->({
4649 wakaba 1.100 name => sub {
4650     my ($self, $attr) = @_;
4651     my $value = $attr->value;
4652     if (length $value) {
4653     ## NOTE: Duplication is not non-conforming.
4654     ## NOTE: Space characters are not non-conforming.
4655     #
4656     } else {
4657     $self->{onerror}->(node => $attr,
4658     type => 'empty attribute value',
4659 wakaba 1.104 level => $self->{level}->{must});
4660 wakaba 1.100 }
4661 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4662 wakaba 1.100 $has_name = [$value, $attr];
4663 wakaba 1.4 },
4664 wakaba 1.49 }, {
4665     %HTMLAttrStatus,
4666 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4667     dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4668     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4669     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4670     #name => FEATURE_HTML5_LC | FEATURE_M12N10_REC_DEPRECATED,
4671     name => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4672 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4673     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4674     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4675     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4676     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4677     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4678     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4679     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4680     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4681     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4682 wakaba 1.153 title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4683 wakaba 1.66 })->(@_);
4684 wakaba 1.100
4685 wakaba 1.135 if ($has_name) {
4686 wakaba 1.145 my $id = $item->{node}->get_attribute_ns (undef, 'id');
4687 wakaba 1.135 if (defined $id and $has_name->[0] ne $id) {
4688 wakaba 1.155 $self->{onerror}
4689     ->(node => $item->{node}->get_attribute_node_ns (undef, 'id'),
4690     type => 'id ne name',
4691     level => $self->{level}->{must});
4692 wakaba 1.100 }
4693 wakaba 1.135 } else {
4694 wakaba 1.100 $self->{onerror}->(node => $item->{node},
4695 wakaba 1.104 type => 'attribute missing',
4696     text => 'name',
4697     level => $self->{level}->{must});
4698 wakaba 1.100 }
4699 wakaba 1.4 },
4700 wakaba 1.59 check_start => sub {
4701     my ($self, $item, $element_state) = @_;
4702     $element_state->{in_map_original} = $self->{flag}->{in_map};
4703 wakaba 1.137 $self->{flag}->{in_map} = [@{$self->{flag}->{in_map} or []}, {}];
4704     ## NOTE: |{in_map}| is a reference to the array which contains
4705     ## hash references. Hashes are corresponding to the opening
4706     ## |map| elements and each of them contains the key-value
4707     ## pairs corresponding to the absolute URLs for the processed
4708     ## |area| elements in the |map| element corresponding to the
4709     ## hash. The key represents the resource (## TODO: use
4710     ## absolute URL), while the value represents whether there is
4711     ## an |area| element whose |alt| attribute is specified to a
4712     ## non-empty value. If there IS such an |area| element for
4713     ## the resource specified by the key, then the value is set to
4714     ## zero (|0|). Otherwise, if there is no such an |area|
4715     ## element but there is any |area| element with the empty
4716     ## |alt=""| attribute, then the value contains an array
4717     ## reference that contains all of such |area| elements.
4718 wakaba 1.79
4719     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4720     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4721 wakaba 1.59 },
4722     check_end => sub {
4723     my ($self, $item, $element_state) = @_;
4724 wakaba 1.137
4725     for (keys %{$self->{flag}->{in_map}->[-1]}) {
4726     my $nodes = $self->{flag}->{in_map}->[-1]->{$_};
4727     next unless $nodes;
4728     for (@$nodes) {
4729     $self->{onerror}->(type => 'empty area alt',
4730     node => $_,
4731     level => $self->{level}->{html5_no_may});
4732     }
4733     }
4734    
4735     $self->{flag}->{in_map} = $element_state->{in_map_original};
4736    
4737 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4738 wakaba 1.59 },
4739 wakaba 1.1 };
4740    
4741     $Element->{$HTML_NS}->{area} = {
4742 wakaba 1.40 %HTMLEmptyChecker,
4743 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4744 wakaba 1.40 check_attrs => sub {
4745     my ($self, $item, $element_state) = @_;
4746 wakaba 1.1 my %attr;
4747     my $coords;
4748 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4749 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4750     $attr_ns = '' unless defined $attr_ns;
4751     my $attr_ln = $attr->manakai_local_name;
4752     my $checker;
4753 wakaba 1.73 my $status;
4754 wakaba 1.1 if ($attr_ns eq '') {
4755 wakaba 1.73 $status = {
4756     %HTMLAttrStatus,
4757     %HTMLM12NCommonAttrStatus,
4758 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
4759 wakaba 1.153 alt => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4760     coords => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4761 wakaba 1.154 href => FEATURE_HTML5_WD | FEATURE_RDFA_REC | FEATURE_M12N10_REC,
4762 wakaba 1.153 hreflang => FEATURE_HTML5_WD,
4763     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
4764 wakaba 1.154 media => FEATURE_HTML5_WD,
4765 wakaba 1.73 nohref => FEATURE_M12N10_REC,
4766     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4767     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4768 wakaba 1.153 ping => FEATURE_HTML5_WD,
4769 wakaba 1.154 rel => FEATURE_HTML5_WD | FEATURE_RDFA_REC,
4770 wakaba 1.153 shape => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
4771 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4772 wakaba 1.153 target => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4773     type => FEATURE_HTML5_WD,
4774 wakaba 1.73 }->{$attr_ln};
4775    
4776 wakaba 1.1 $checker = {
4777 wakaba 1.153 alt => sub {
4778     ## NOTE: Checked later.
4779     },
4780 wakaba 1.1 shape => $GetHTMLEnumeratedAttrChecker->({
4781     circ => -1, circle => 1,
4782     default => 1,
4783     poly => 1, polygon => -1,
4784     rect => 1, rectangle => -1,
4785     }),
4786     coords => sub {
4787     my ($self, $attr) = @_;
4788     my $value = $attr->value;
4789     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4790     $coords = [split /,/, $value];
4791     } else {
4792     $self->{onerror}->(node => $attr,
4793 wakaba 1.104 type => 'coords:syntax error',
4794     level => $self->{level}->{must});
4795 wakaba 1.1 }
4796     },
4797 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4798     target => $HTMLTargetAttrChecker,
4799 wakaba 1.1 href => $HTMLURIAttrChecker,
4800     ping => $HTMLSpaceURIsAttrChecker,
4801 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4802 wakaba 1.1 media => $HTMLMQAttrChecker,
4803     hreflang => $HTMLLanguageTagAttrChecker,
4804     type => $HTMLIMTAttrChecker,
4805     }->{$attr_ln};
4806     if ($checker) {
4807     $attr{$attr_ln} = $attr;
4808 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4809     $attr_ln !~ /[A-Z]/) {
4810 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4811     $status = $HTMLDatasetAttrStatus;
4812 wakaba 1.1 } else {
4813     $checker = $HTMLAttrChecker->{$attr_ln};
4814     }
4815     }
4816     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4817 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4818     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4819     || $AttrStatus->{$attr_ns}->{''};
4820     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4821 wakaba 1.62
4822 wakaba 1.1 if ($checker) {
4823 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4824 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4825 wakaba 1.54 #
4826 wakaba 1.1 } else {
4827 wakaba 1.104 $self->{onerror}->(node => $attr,
4828     type => 'unknown attribute',
4829     level => $self->{level}->{uncertain});
4830 wakaba 1.1 ## ISSUE: No comformance createria for unknown attributes in the spec
4831     }
4832 wakaba 1.49
4833 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4834 wakaba 1.1 }
4835    
4836     if (defined $attr{href}) {
4837 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4838 wakaba 1.137 if (defined $attr{alt}) {
4839     my $url = $attr{href}->value; ## TODO: resolve
4840     if (length $attr{alt}->value) {
4841     for (@{$self->{flag}->{in_map} or []}) {
4842     $_->{$url} = 0;
4843     }
4844     } else {
4845     ## NOTE: Empty |alt=""|. If there is another |area| element
4846     ## with the same |href=""| and that |area| elemnet's
4847     ## |alt=""| attribute is not an empty string, then this
4848     ## is conforming.
4849     for (@{$self->{flag}->{in_map} or []}) {
4850     push @{$_->{$url} ||= []}, $attr{alt}
4851     unless exists $_->{$url} and not $_->{$url};
4852     }
4853     }
4854     } else {
4855 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4856 wakaba 1.104 type => 'attribute missing',
4857     text => 'alt',
4858     level => $self->{level}->{must});
4859 wakaba 1.1 }
4860     } else {
4861     for (qw/target ping rel media hreflang type alt/) {
4862     if (defined $attr{$_}) {
4863     $self->{onerror}->(node => $attr{$_},
4864 wakaba 1.104 type => 'attribute not allowed',
4865     level => $self->{level}->{must});
4866 wakaba 1.1 }
4867     }
4868     }
4869    
4870     my $shape = 'rectangle';
4871     if (defined $attr{shape}) {
4872     $shape = {
4873     circ => 'circle', circle => 'circle',
4874     default => 'default',
4875     poly => 'polygon', polygon => 'polygon',
4876     rect => 'rectangle', rectangle => 'rectangle',
4877     }->{lc $attr{shape}->value} || 'rectangle';
4878     ## TODO: ASCII lowercase?
4879     }
4880    
4881     if ($shape eq 'circle') {
4882     if (defined $attr{coords}) {
4883     if (defined $coords) {
4884     if (@$coords == 3) {
4885     if ($coords->[2] < 0) {
4886     $self->{onerror}->(node => $attr{coords},
4887 wakaba 1.104 type => 'coords:out of range',
4888     index => 2,
4889     value => $coords->[2],
4890     level => $self->{level}->{must});
4891 wakaba 1.1 }
4892     } else {
4893     $self->{onerror}->(node => $attr{coords},
4894 wakaba 1.104 type => 'coords:number not 3',
4895     text => 0+@$coords,
4896     level => $self->{level}->{must});
4897 wakaba 1.1 }
4898     } else {
4899     ## NOTE: A syntax error has been reported.
4900     }
4901     } else {
4902 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4903 wakaba 1.104 type => 'attribute missing',
4904     text => 'coords',
4905     level => $self->{level}->{must});
4906 wakaba 1.1 }
4907     } elsif ($shape eq 'default') {
4908     if (defined $attr{coords}) {
4909     $self->{onerror}->(node => $attr{coords},
4910 wakaba 1.104 type => 'attribute not allowed',
4911     level => $self->{level}->{must});
4912 wakaba 1.1 }
4913     } elsif ($shape eq 'polygon') {
4914     if (defined $attr{coords}) {
4915     if (defined $coords) {
4916     if (@$coords >= 6) {
4917     unless (@$coords % 2 == 0) {
4918     $self->{onerror}->(node => $attr{coords},
4919 wakaba 1.104 type => 'coords:number not even',
4920     text => 0+@$coords,
4921     level => $self->{level}->{must});
4922 wakaba 1.1 }
4923     } else {
4924     $self->{onerror}->(node => $attr{coords},
4925 wakaba 1.104 type => 'coords:number lt 6',
4926     text => 0+@$coords,
4927     level => $self->{level}->{must});
4928 wakaba 1.1 }
4929     } else {
4930     ## NOTE: A syntax error has been reported.
4931     }
4932     } else {
4933 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4934 wakaba 1.104 type => 'attribute missing',
4935     text => 'coords',
4936     level => $self->{level}->{must});
4937 wakaba 1.1 }
4938     } elsif ($shape eq 'rectangle') {
4939     if (defined $attr{coords}) {
4940     if (defined $coords) {
4941     if (@$coords == 4) {
4942     unless ($coords->[0] < $coords->[2]) {
4943     $self->{onerror}->(node => $attr{coords},
4944 wakaba 1.104 type => 'coords:out of range',
4945     index => 0,
4946     value => $coords->[0],
4947     level => $self->{level}->{must});
4948 wakaba 1.1 }
4949     unless ($coords->[1] < $coords->[3]) {
4950     $self->{onerror}->(node => $attr{coords},
4951 wakaba 1.104 type => 'coords:out of range',
4952     index => 1,
4953     value => $coords->[1],
4954     level => $self->{level}->{must});
4955 wakaba 1.1 }
4956     } else {
4957     $self->{onerror}->(node => $attr{coords},
4958 wakaba 1.104 type => 'coords:number not 4',
4959     text => 0+@$coords,
4960     level => $self->{level}->{must});
4961 wakaba 1.1 }
4962     } else {
4963     ## NOTE: A syntax error has been reported.
4964     }
4965     } else {
4966 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4967 wakaba 1.104 type => 'attribute missing',
4968     text => 'coords',
4969     level => $self->{level}->{must});
4970 wakaba 1.1 }
4971     }
4972 wakaba 1.66
4973     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4974 wakaba 1.1 },
4975 wakaba 1.59 check_start => sub {
4976     my ($self, $item, $element_state) = @_;
4977     unless ($self->{flag}->{in_map} or
4978     not $item->{node}->manakai_parent_element) {
4979     $self->{onerror}->(node => $item->{node},
4980     type => 'element not allowed:area',
4981 wakaba 1.104 level => $self->{level}->{must});
4982 wakaba 1.59 }
4983 wakaba 1.79
4984     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4985     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4986 wakaba 1.59 },
4987 wakaba 1.1 };
4988    
4989     $Element->{$HTML_NS}->{table} = {
4990 wakaba 1.40 %HTMLChecker,
4991 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4992 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4993 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4994     cellspacing => $HTMLLengthAttrChecker,
4995 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4996     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4997     lhs => 1, rhs => 1, box => 1, border => 1,
4998     }),
4999     rules => $GetHTMLEnumeratedAttrChecker->({
5000     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
5001     }),
5002     summary => sub {}, ## NOTE: %Text; in HTML4.
5003     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
5004     }, {
5005 wakaba 1.49 %HTMLAttrStatus,
5006 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5007 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
5008     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5009     border => FEATURE_M12N10_REC,
5010     cellpadding => FEATURE_M12N10_REC,
5011     cellspacing => FEATURE_M12N10_REC,
5012 wakaba 1.61 cols => FEATURE_RFC1942,
5013 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
5014     dataformatas => FEATURE_HTML4_REC_RESERVED,
5015     datapagesize => FEATURE_M12N10_REC,
5016     datasrc => FEATURE_HTML4_REC_RESERVED,
5017     frame => FEATURE_M12N10_REC,
5018 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5019 wakaba 1.49 rules => FEATURE_M12N10_REC,
5020     summary => FEATURE_M12N10_REC,
5021     width => FEATURE_M12N10_REC,
5022     }),
5023 wakaba 1.40 check_start => sub {
5024     my ($self, $item, $element_state) = @_;
5025     $element_state->{phase} = 'before caption';
5026 wakaba 1.66
5027     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5028 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5029     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5030 wakaba 1.40 },
5031     check_child_element => sub {
5032     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5033     $child_is_transparent, $element_state) = @_;
5034 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5035     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5036 wakaba 1.40 $self->{onerror}->(node => $child_el,
5037     type => 'element not allowed:minus',
5038 wakaba 1.104 level => $self->{level}->{must});
5039 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5040     #
5041     } elsif ($element_state->{phase} eq 'in tbodys') {
5042     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5043     #$element_state->{phase} = 'in tbodys';
5044     } elsif (not $element_state->{has_tfoot} and
5045     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5046     $element_state->{phase} = 'after tfoot';
5047     $element_state->{has_tfoot} = 1;
5048     } else {
5049 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5050     level => $self->{level}->{must});
5051 wakaba 1.40 }
5052     } elsif ($element_state->{phase} eq 'in trs') {
5053     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5054     #$element_state->{phase} = 'in trs';
5055     } elsif (not $element_state->{has_tfoot} and
5056     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5057     $element_state->{phase} = 'after tfoot';
5058     $element_state->{has_tfoot} = 1;
5059     } else {
5060 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5061     level => $self->{level}->{must});
5062 wakaba 1.40 }
5063     } elsif ($element_state->{phase} eq 'after thead') {
5064     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5065     $element_state->{phase} = 'in tbodys';
5066     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5067     $element_state->{phase} = 'in trs';
5068     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5069     $element_state->{phase} = 'in tbodys';
5070     $element_state->{has_tfoot} = 1;
5071     } else {
5072 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5073     level => $self->{level}->{must});
5074 wakaba 1.40 }
5075     } elsif ($element_state->{phase} eq 'in colgroup') {
5076     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
5077     $element_state->{phase} = 'in colgroup';
5078     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
5079     $element_state->{phase} = 'after thead';
5080     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5081     $element_state->{phase} = 'in tbodys';
5082     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5083     $element_state->{phase} = 'in trs';
5084     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5085     $element_state->{phase} = 'in tbodys';
5086     $element_state->{has_tfoot} = 1;
5087     } else {
5088 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5089     level => $self->{level}->{must});
5090 wakaba 1.40 }
5091     } elsif ($element_state->{phase} eq 'before caption') {
5092     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
5093     $element_state->{phase} = 'in colgroup';
5094     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
5095     $element_state->{phase} = 'in colgroup';
5096     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
5097     $element_state->{phase} = 'after thead';
5098     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
5099     $element_state->{phase} = 'in tbodys';
5100     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5101     $element_state->{phase} = 'in trs';
5102     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
5103     $element_state->{phase} = 'in tbodys';
5104     $element_state->{has_tfoot} = 1;
5105     } else {
5106 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5107     level => $self->{level}->{must});
5108 wakaba 1.40 }
5109     } elsif ($element_state->{phase} eq 'after tfoot') {
5110 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5111     level => $self->{level}->{must});
5112 wakaba 1.40 } else {
5113     die "check_child_element: Bad |table| phase: $element_state->{phase}";
5114     }
5115     },
5116     check_child_text => sub {
5117     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5118     if ($has_significant) {
5119 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5120     level => $self->{level}->{must});
5121 wakaba 1.1 }
5122 wakaba 1.40 },
5123     check_end => sub {
5124     my ($self, $item, $element_state) = @_;
5125 wakaba 1.1
5126     ## Table model errors
5127     require Whatpm::HTMLTable;
5128 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
5129 wakaba 1.104 $self->{onerror}->(@_);
5130     }, $self->{level});
5131 wakaba 1.87 Whatpm::HTMLTable->assign_header
5132 wakaba 1.104 ($table, $self->{onerror}, $self->{level});
5133 wakaba 1.87 push @{$self->{return}->{table}}, $table;
5134 wakaba 1.1
5135 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5136 wakaba 1.1 },
5137     };
5138    
5139     $Element->{$HTML_NS}->{caption} = {
5140 wakaba 1.169 %HTMLFlowContentChecker,
5141 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5142 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5143     align => $GetHTMLEnumeratedAttrChecker->({
5144     top => 1, bottom => 1, left => 1, right => 1,
5145     }),
5146     }, {
5147 wakaba 1.49 %HTMLAttrStatus,
5148 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5149 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
5150 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5151 wakaba 1.49 }),
5152 wakaba 1.169 check_start => sub {
5153     my ($self, $item, $element_state) = @_;
5154     $self->_add_minus_elements ($element_state, {$HTML_NS => {table => 1}});
5155    
5156     $HTMLFlowContentChecker{check_start}->(@_);
5157     },
5158     check_end => sub {
5159     my ($self, $item, $element_state) = @_;
5160     $self->_remove_minus_elements ($element_state);
5161    
5162     $HTMLFlowContentChecker{check_end}->(@_);
5163     },
5164     }; # caption
5165 wakaba 1.1
5166 wakaba 1.69 my %cellalign = (
5167     ## HTML4 %cellhalign;
5168 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
5169     left => 1, center => 1, right => 1, justify => 1, char => 1,
5170     }),
5171     char => sub {
5172     my ($self, $attr) = @_;
5173 wakaba 1.69
5174 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
5175    
5176     my $value = $attr->value;
5177     if (length $value != 1) {
5178     $self->{onerror}->(node => $attr, type => 'char:syntax error',
5179 wakaba 1.105 level => $self->{level}->{html4_fact});
5180 wakaba 1.70 }
5181     },
5182 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
5183    
5184 wakaba 1.69 ## HTML4 %cellvalign;
5185 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
5186     top => 1, middle => 1, bottom => 1, baseline => 1,
5187     }),
5188 wakaba 1.69 );
5189    
5190 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
5191 wakaba 1.40 %HTMLEmptyChecker,
5192 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5193 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5194 wakaba 1.69 %cellalign,
5195 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5196     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
5197     ## TODO: "attribute not supported" if |col|.
5198     ## ISSUE: MUST NOT if any |col|?
5199     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
5200 wakaba 1.49 }, {
5201     %HTMLAttrStatus,
5202 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5203 wakaba 1.49 align => FEATURE_M12N10_REC,
5204     char => FEATURE_M12N10_REC,
5205     charoff => FEATURE_M12N10_REC,
5206 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5207     span => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5208 wakaba 1.49 valign => FEATURE_M12N10_REC,
5209     width => FEATURE_M12N10_REC,
5210 wakaba 1.1 }),
5211 wakaba 1.40 check_child_element => sub {
5212     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5213     $child_is_transparent, $element_state) = @_;
5214 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5215     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5216 wakaba 1.40 $self->{onerror}->(node => $child_el,
5217     type => 'element not allowed:minus',
5218 wakaba 1.104 level => $self->{level}->{must});
5219 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5220     #
5221     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
5222     #
5223     } else {
5224 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5225     level => $self->{level}->{must});
5226 wakaba 1.40 }
5227     },
5228     check_child_text => sub {
5229     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5230     if ($has_significant) {
5231 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5232     level => $self->{level}->{must});
5233 wakaba 1.1 }
5234     },
5235     };
5236    
5237     $Element->{$HTML_NS}->{col} = {
5238 wakaba 1.40 %HTMLEmptyChecker,
5239 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5240 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5241 wakaba 1.69 %cellalign,
5242 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5243 wakaba 1.49 }, {
5244     %HTMLAttrStatus,
5245 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5246 wakaba 1.49 align => FEATURE_M12N10_REC,
5247     char => FEATURE_M12N10_REC,
5248     charoff => FEATURE_M12N10_REC,
5249 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5250     span => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5251 wakaba 1.49 valign => FEATURE_M12N10_REC,
5252     width => FEATURE_M12N10_REC,
5253 wakaba 1.1 }),
5254     };
5255    
5256     $Element->{$HTML_NS}->{tbody} = {
5257 wakaba 1.40 %HTMLChecker,
5258 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5259 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5260     %cellalign,
5261     }, {
5262 wakaba 1.49 %HTMLAttrStatus,
5263 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5264 wakaba 1.49 align => FEATURE_M12N10_REC,
5265     char => FEATURE_M12N10_REC,
5266     charoff => FEATURE_M12N10_REC,
5267 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5268 wakaba 1.49 valign => FEATURE_M12N10_REC,
5269     }),
5270 wakaba 1.40 check_child_element => sub {
5271     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5272     $child_is_transparent, $element_state) = @_;
5273 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5274     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5275 wakaba 1.40 $self->{onerror}->(node => $child_el,
5276     type => 'element not allowed:minus',
5277 wakaba 1.104 level => $self->{level}->{must});
5278 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5279     #
5280     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
5281 wakaba 1.84 #
5282 wakaba 1.40 } else {
5283 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5284     level => $self->{level}->{must});
5285 wakaba 1.40 }
5286     },
5287     check_child_text => sub {
5288     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5289     if ($has_significant) {
5290 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5291     level => $self->{level}->{must});
5292 wakaba 1.1 }
5293 wakaba 1.40 },
5294 wakaba 1.1 };
5295    
5296     $Element->{$HTML_NS}->{thead} = {
5297 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5298 wakaba 1.1 };
5299    
5300     $Element->{$HTML_NS}->{tfoot} = {
5301 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5302 wakaba 1.1 };
5303    
5304     $Element->{$HTML_NS}->{tr} = {
5305 wakaba 1.40 %HTMLChecker,
5306 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5307 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5308     %cellalign,
5309     bgcolor => $HTMLColorAttrChecker,
5310     }, {
5311 wakaba 1.49 %HTMLAttrStatus,
5312 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5313 wakaba 1.49 align => FEATURE_M12N10_REC,
5314     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5315     char => FEATURE_M12N10_REC,
5316     charoff => FEATURE_M12N10_REC,
5317 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5318 wakaba 1.49 valign => FEATURE_M12N10_REC,
5319     }),
5320 wakaba 1.40 check_child_element => sub {
5321     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5322     $child_is_transparent, $element_state) = @_;
5323 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5324     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5325 wakaba 1.40 $self->{onerror}->(node => $child_el,
5326     type => 'element not allowed:minus',
5327 wakaba 1.104 level => $self->{level}->{must});
5328 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5329     #
5330     } elsif ($child_nsuri eq $HTML_NS and
5331     ($child_ln eq 'td' or $child_ln eq 'th')) {
5332 wakaba 1.84 #
5333 wakaba 1.40 } else {
5334 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5335     level => $self->{level}->{must});
5336 wakaba 1.40 }
5337     },
5338     check_child_text => sub {
5339     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5340     if ($has_significant) {
5341 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5342     level => $self->{level}->{must});
5343 wakaba 1.1 }
5344     },
5345     };
5346    
5347     $Element->{$HTML_NS}->{td} = {
5348 wakaba 1.72 %HTMLFlowContentChecker,
5349 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5350 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5351 wakaba 1.69 %cellalign,
5352     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5353     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5354     bgcolor => $HTMLColorAttrChecker,
5355 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5356 wakaba 1.87 headers => sub {
5357     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
5358     ## Though that method does not check the |headers| attribute of a
5359     ## |td| element if the element does not form a table, in that case
5360     ## the |td| element is non-conforming anyway.
5361     },
5362 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5363 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5364 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
5365     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5366 wakaba 1.49 }, {
5367     %HTMLAttrStatus,
5368 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5369     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5370 wakaba 1.49 align => FEATURE_M12N10_REC,
5371 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5372 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5373     char => FEATURE_M12N10_REC,
5374     charoff => FEATURE_M12N10_REC,
5375 wakaba 1.153 colspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5376     headers => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5377 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5378 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5379 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5380 wakaba 1.153 rowspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5381 wakaba 1.82 scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5382 wakaba 1.49 valign => FEATURE_M12N10_REC,
5383     width => FEATURE_M12N10_REC_DEPRECATED,
5384 wakaba 1.1 }),
5385     };
5386    
5387     $Element->{$HTML_NS}->{th} = {
5388 wakaba 1.40 %HTMLPhrasingContentChecker,
5389 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5390 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5391 wakaba 1.69 %cellalign,
5392     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5393     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5394     bgcolor => $HTMLColorAttrChecker,
5395 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5396 wakaba 1.87 ## TODO: HTML4(?) |headers|
5397 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5398 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5399     scope => $GetHTMLEnumeratedAttrChecker
5400     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5401 wakaba 1.49 }, {
5402     %HTMLAttrStatus,
5403 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5404     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5405 wakaba 1.49 align => FEATURE_M12N10_REC,
5406 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5407 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5408     char => FEATURE_M12N10_REC,
5409     charoff => FEATURE_M12N10_REC,
5410 wakaba 1.153 colspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5411 wakaba 1.82 headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5412 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5413 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5414 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5415 wakaba 1.153 rowspan => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5416     scope => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5417 wakaba 1.49 valign => FEATURE_M12N10_REC,
5418     width => FEATURE_M12N10_REC_DEPRECATED,
5419 wakaba 1.1 }),
5420     };
5421    
5422 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
5423 wakaba 1.121 %HTMLFlowContentChecker,
5424 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5425 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5426 wakaba 1.161 accept => $AcceptAttrChecker,
5427 wakaba 1.129 'accept-charset' => $HTMLCharsetsAttrChecker,
5428 wakaba 1.166 action => $HTMLURIAttrChecker, ## TODO: Warn if submission is not defined for the scheme
5429 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5430 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
5431     'application/x-www-form-urlencoded' => 1,
5432     'multipart/form-data' => 1,
5433     'text/plain' => 1,
5434     }),
5435 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
5436     get => 1, post => 1, put => 1, delete => 1,
5437     }),
5438 wakaba 1.133 name => sub {
5439     my ($self, $attr) = @_;
5440    
5441     my $value = $attr->value;
5442     if ($value eq '') {
5443     $self->{onerror}->(type => 'empty form name',
5444     node => $attr,
5445     level => $self->{level}->{must});
5446     } else {
5447     if ($self->{form}->{$value}) {
5448     $self->{onerror}->(type => 'duplicate form name',
5449     node => $attr,
5450     value => $value,
5451     level => $self->{level}->{must});
5452     } else {
5453     $self->{form}->{$value} = 1;
5454     }
5455     }
5456     },
5457 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
5458     ## TODO: Tests for following attrs:
5459 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
5460     onforminput => $HTMLEventHandlerAttrChecker,
5461 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
5462     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5463 wakaba 1.52 target => $HTMLTargetAttrChecker,
5464     }, {
5465     %HTMLAttrStatus,
5466     %HTMLM12NCommonAttrStatus,
5467 wakaba 1.161 accept => FEATURE_HTML5_DROPPED | FEATURE_WF2X | FEATURE_M12N10_REC,
5468 wakaba 1.119 'accept-charset' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5469     action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5470 wakaba 1.56 data => FEATURE_WF2,
5471 wakaba 1.119 enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5472 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5473 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5474     #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
5475     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5476 wakaba 1.166 novalidate => FEATURE_HTML5_DEFAULT,
5477 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
5478     onforminput => FEATURE_WF2_INFORMATIVE,
5479 wakaba 1.56 onreceived => FEATURE_WF2,
5480 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5481     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5482 wakaba 1.56 replace => FEATURE_WF2,
5483 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5484     sdasuff => FEATURE_HTML20_RFC,
5485 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5486 wakaba 1.52 }),
5487 wakaba 1.66 check_start => sub {
5488     my ($self, $item, $element_state) = @_;
5489 wakaba 1.121 $self->_add_minus_elements ($element_state, {$HTML_NS => {form => 1}});
5490 wakaba 1.66
5491     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5492     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5493 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5494     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5495 wakaba 1.136 $element_state->{id_type} = 'form';
5496 wakaba 1.66 },
5497 wakaba 1.121 check_end => sub {
5498     my ($self, $item, $element_state) = @_;
5499     $self->_remove_minus_elements ($element_state);
5500    
5501     $HTMLFlowContentChecker{check_end}->(@_);
5502     },
5503 wakaba 1.52 };
5504    
5505     $Element->{$HTML_NS}->{fieldset} = {
5506 wakaba 1.134 %HTMLFlowContentChecker,
5507 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5508 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5509     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5510 wakaba 1.136 form => $HTMLFormAttrChecker,
5511 wakaba 1.165 name => $FormControlNameAttrChecker,
5512 wakaba 1.56 }, {
5513 wakaba 1.52 %HTMLAttrStatus,
5514     %HTMLM12NCommonAttrStatus,
5515 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5516     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5517 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5518 wakaba 1.125 name => FEATURE_HTML5_DEFAULT,
5519 wakaba 1.52 }),
5520 wakaba 1.134 ## NOTE: legend, Flow
5521     check_child_element => sub {
5522     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5523     $child_is_transparent, $element_state) = @_;
5524     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
5525     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
5526     $self->{onerror}->(node => $child_el,
5527     type => 'element not allowed:minus',
5528     level => $self->{level}->{must});
5529     $element_state->{has_non_legend} = 1;
5530     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5531     #
5532     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5533     if ($element_state->{has_non_legend}) {
5534     $self->{onerror}->(node => $child_el,
5535     type => 'element not allowed:details legend',
5536     level => $self->{level}->{must});
5537     }
5538     $element_state->{has_legend} = 1;
5539     $element_state->{has_non_legend} = 1;
5540     } else {
5541     $HTMLFlowContentChecker{check_child_element}->(@_);
5542     $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5543     ## TODO:
5544 wakaba 1.167 ## |<fieldset><object><legend>xx</legend></object>..</fieldset>|
5545 wakaba 1.134 ## should be an error, since |object| is allowed as flow,
5546     ## therefore |details| part of the content model does not match.
5547     }
5548     },
5549     check_child_text => sub {
5550     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5551     if ($has_significant) {
5552     $element_state->{has_non_legend} = 1;
5553     }
5554     },
5555     check_end => sub {
5556     my ($self, $item, $element_state) = @_;
5557    
5558     unless ($element_state->{has_legend}) {
5559     $self->{onerror}->(node => $item->{node},
5560     type => 'child element missing',
5561     text => 'legend',
5562     level => $self->{level}->{must});
5563     }
5564    
5565     $HTMLFlowContentChecker{check_end}->(@_);
5566 wakaba 1.167 ## ISSUE: |<fieldset><legend>aa</legend></fieldset>| error?
5567 wakaba 1.134 },
5568     ## NOTE: This definition is partially reused by |details| element's
5569     ## checker.
5570 wakaba 1.52 };
5571    
5572     $Element->{$HTML_NS}->{input} = {
5573 wakaba 1.119 %HTMLEmptyChecker,
5574     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5575 wakaba 1.140 check_attrs => sub {
5576     my ($self, $item, $element_state) = @_;
5577 wakaba 1.142
5578 wakaba 1.145 my $state = $item->{node}->get_attribute_ns (undef, 'type');
5579 wakaba 1.142 $state = 'text' unless defined $state;
5580     $state =~ tr/A-Z/a-z/; ## ASCII case-insensitive
5581    
5582 wakaba 1.140 for my $attr (@{$item->{node}->attributes}) {
5583     my $attr_ns = $attr->namespace_uri;
5584     $attr_ns = '' unless defined $attr_ns;
5585     my $attr_ln = $attr->manakai_local_name;
5586     my $checker;
5587     my $status;
5588     if ($attr_ns eq '') {
5589     $status =
5590     {
5591     %HTMLAttrStatus,
5592     %HTMLM12NCommonAttrStatus,
5593     accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5594     'accept-charset' => FEATURE_HTML2X_RFC,
5595 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
5596 wakaba 1.140 action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5597     align => FEATURE_M12N10_REC_DEPRECATED,
5598     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5599     autocomplete => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5600     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5601     checked => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5602     datafld => FEATURE_HTML4_REC_RESERVED,
5603     dataformatas => FEATURE_HTML4_REC_RESERVED,
5604     datasrc => FEATURE_HTML4_REC_RESERVED,
5605     disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5606     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5607     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5608 wakaba 1.178 height => FEATURE_HTML5_LC,
5609 wakaba 1.150 inputmode => FEATURE_HTML5_DROPPED | FEATURE_WF2X |
5610     FEATURE_XHTMLBASIC11_CR,
5611 wakaba 1.140 ismap => FEATURE_M12N10_REC,
5612 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
5613 wakaba 1.140 list => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5614     max => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5615 wakaba 1.150 maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X |
5616     FEATURE_M12N10_REC,
5617 wakaba 1.140 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5618     min => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5619 wakaba 1.156 multiple => FEATURE_HTML5_DEFAULT,
5620 wakaba 1.140 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5621 wakaba 1.161 novalidate => FEATURE_HTML5_DEFAULT,
5622 wakaba 1.140 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5623     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5624     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5625     onformchange => FEATURE_WF2_INFORMATIVE,
5626     onforminput => FEATURE_WF2_INFORMATIVE,
5627     oninput => FEATURE_WF2,
5628     oninvalid => FEATURE_WF2,
5629     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5630     pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5631 wakaba 1.156 placeholder => FEATURE_HTML5_DEFAULT,
5632 wakaba 1.140 readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5633     replace => FEATURE_WF2,
5634     required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5635     sdapref => FEATURE_HTML20_RFC,
5636 wakaba 1.154 size => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5637 wakaba 1.140 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5638     step => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5639     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5640     target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5641 wakaba 1.161 template => FEATURE_HTML5_AT_RISK | FEATURE_WF2, ## TODO:dropped
5642 wakaba 1.140 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5643     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5644     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5645 wakaba 1.178 width => FEATURE_HTML5_LC,
5646 wakaba 1.140 }->{$attr_ln};
5647    
5648     $checker =
5649     {
5650 wakaba 1.141 ## NOTE: Value of an empty string means that the attribute is only
5651     ## applicable for a specific set of states.
5652 wakaba 1.142 accept => '',
5653 wakaba 1.149 'accept-charset' => $HTMLCharsetsAttrChecker,
5654     ## NOTE: To which states it applies is not defined in RFC 2070.
5655 wakaba 1.142 action => '',
5656 wakaba 1.150 align => '',
5657 wakaba 1.141 alt => '',
5658 wakaba 1.142 autocomplete => '',
5659 wakaba 1.165 autofocus => $AutofocusAttrChecker,
5660     ## NOTE: <input type=hidden disabled> is not disallowed.
5661 wakaba 1.142 checked => '',
5662     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5663 wakaba 1.165 ## NOTE: <input type=hidden disabled> is not disallowed.
5664 wakaba 1.142 enctype => '',
5665     form => $HTMLFormAttrChecker,
5666 wakaba 1.178 height => '',
5667 wakaba 1.150 inputmode => '',
5668     ismap => '', ## NOTE: "MUST" be type=image [HTML4]
5669 wakaba 1.142 list => '',
5670     max => '',
5671     maxlength => '',
5672     method => '',
5673     min => '',
5674 wakaba 1.156 multiple => '',
5675 wakaba 1.165 name => $FormControlNameAttrChecker,
5676 wakaba 1.166 novalidate => '',
5677 wakaba 1.149 onformchange => $HTMLEventHandlerAttrChecker, # [WF2]
5678     onforminput => $HTMLEventHandlerAttrChecker, # [WF2]
5679     oninput => $HTMLEventHandlerAttrChecker, # [WF2]
5680     oninvalid => $HTMLEventHandlerAttrChecker, # [WF2]
5681     ## TODO: tests for four attributes above
5682 wakaba 1.142 pattern => '',
5683 wakaba 1.156 placeholder => '',
5684 wakaba 1.142 readonly => '',
5685 wakaba 1.150 replace => '',
5686 wakaba 1.142 required => '',
5687     size => '',
5688     src => '',
5689     step => '',
5690     target => '',
5691 wakaba 1.140 type => $GetHTMLEnumeratedAttrChecker->({
5692 wakaba 1.156 hidden => 1, text => 1, search => 1, url => 1,
5693     email => 1, password => 1,
5694 wakaba 1.141 datetime => 1, date => 1, month => 1, week => 1, time => 1,
5695 wakaba 1.157 'datetime-local' => 1, number => 1, range => 1, color => 1,
5696     checkbox => 1,
5697 wakaba 1.141 radio => 1, file => 1, submit => 1, image => 1, reset => 1,
5698     button => 1,
5699 wakaba 1.140 }),
5700 wakaba 1.151 usemap => '',
5701 wakaba 1.142 value => '',
5702 wakaba 1.178 width => '',
5703 wakaba 1.140 }->{$attr_ln};
5704 wakaba 1.141
5705     ## State-dependent checkers
5706     unless ($checker) {
5707     if ($state eq 'hidden') {
5708     $checker =
5709     {
5710 wakaba 1.142 value => sub {
5711     my ($self, $attr, $item, $element_state) = @_;
5712 wakaba 1.145 my $name = $item->{node}->get_attribute_ns (undef, 'name');
5713 wakaba 1.142 if (defined $name and $name eq '_charset_') { ## case-sensitive
5714     $self->{onerror}->(node => $attr,
5715     type => '_charset_ value',
5716     level => $self->{level}->{must});
5717     }
5718     },
5719 wakaba 1.141 }->{$attr_ln} || $checker;
5720 wakaba 1.142 ## TODO: Warn if no name attribute?
5721     ## TODO: Warn if name!=_charset_ and no value attribute?
5722 wakaba 1.168 } elsif ({
5723     datetime => 1, date => 1, month => 1, time => 1,
5724     week => 1, 'datetime-local' => 1,
5725     }->{$state}) {
5726     my $v = {
5727     datetime => ['global_date_and_time_string'],
5728     date => ['date_string'],
5729     month => ['month_string'],
5730     week => ['week_string'],
5731     time => ['time_string'],
5732     'datetime-local' => ['local_date_and_time_string'],
5733     }->{$state};
5734 wakaba 1.144 $checker =
5735     {
5736 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5737     on => 1, off => 1,
5738     }),
5739 wakaba 1.158 list => $ListAttrChecker,
5740 wakaba 1.168 min => $GetDateTimeAttrChecker->($v->[0]),
5741     max => $GetDateTimeAttrChecker->($v->[0]),
5742 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5743 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5744 wakaba 1.148 step => $StepAttrChecker,
5745 wakaba 1.168 value => $GetDateTimeAttrChecker->($v->[0]),
5746 wakaba 1.144 }->{$attr_ln} || $checker;
5747     } elsif ($state eq 'number') {
5748     $checker =
5749     {
5750 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5751     on => 1, off => 1,
5752     }),
5753 wakaba 1.158 list => $ListAttrChecker,
5754 wakaba 1.144 max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5755     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5756 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5757 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5758 wakaba 1.148 step => $StepAttrChecker,
5759 wakaba 1.168 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5760 wakaba 1.144 }->{$attr_ln} || $checker;
5761     } elsif ($state eq 'range') {
5762     $checker =
5763     {
5764 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5765     on => 1, off => 1,
5766     }),
5767 wakaba 1.158 list => $ListAttrChecker,
5768 wakaba 1.144 max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5769     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5770 wakaba 1.148 step => $StepAttrChecker,
5771 wakaba 1.168 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
5772 wakaba 1.144 }->{$attr_ln} || $checker;
5773 wakaba 1.157 } elsif ($state eq 'color') {
5774     $checker =
5775     {
5776     autocomplete => $GetHTMLEnumeratedAttrChecker->({
5777     on => 1, off => 1,
5778     }),
5779 wakaba 1.158 list => $ListAttrChecker,
5780 wakaba 1.157 value => sub {
5781     my ($self, $attr) = @_;
5782     unless ($attr->value =~ /\A#[0-9A-Fa-f]{6}\z/) {
5783     $self->{onerror}->(node => $attr,
5784     type => 'scolor:syntax error', ## TODOC: type
5785     level => $self->{level}->{must});
5786     }
5787     },
5788     }->{$attr_ln} || $checker;
5789 wakaba 1.144 } elsif ($state eq 'checkbox' or $state eq 'radio') {
5790     $checker =
5791     {
5792 wakaba 1.149 checked => $GetHTMLBooleanAttrChecker->('checked'),
5793     ## TODO: tests
5794 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5795 wakaba 1.144 value => sub { }, ## NOTE: No restriction.
5796     }->{$attr_ln} || $checker;
5797     ## TODO: There MUST be another input type=radio with same
5798     ## name (Radio state).
5799     ## ISSUE: There should be exactly one type=radio with checked?
5800     } elsif ($state eq 'file') {
5801     $checker =
5802     {
5803 wakaba 1.161 accept => $AcceptAttrChecker,
5804 wakaba 1.168 ## max (default 1) & min (default 0) [WF2]: Dropped by HTML5.
5805 wakaba 1.159 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5806 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5807 wakaba 1.144 }->{$attr_ln} || $checker;
5808     } elsif ($state eq 'submit') {
5809     $checker =
5810     {
5811 wakaba 1.149 action => $HTMLURIAttrChecker,
5812 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
5813     'application/x-www-form-urlencoded' => 1,
5814     'multipart/form-data' => 1,
5815     'text/plain' => 1,
5816     }),
5817 wakaba 1.149 method => $GetHTMLEnumeratedAttrChecker->({
5818     get => 1, post => 1, put => 1, delete => 1,
5819     }),
5820 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
5821 wakaba 1.149 replace => $GetHTMLEnumeratedAttrChecker->({
5822     document => 1, values => 1,
5823     }),
5824     target => $HTMLTargetAttrChecker,
5825 wakaba 1.144 value => sub { }, ## NOTE: No restriction.
5826     }->{$attr_ln} || $checker;
5827     } elsif ($state eq 'image') {
5828     $checker =
5829     {
5830 wakaba 1.149 action => $HTMLURIAttrChecker,
5831     align => $GetHTMLEnumeratedAttrChecker->({
5832     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
5833     }),
5834 wakaba 1.144 alt => sub {
5835     my ($self, $attr) = @_;
5836     my $value = $attr->value;
5837     unless (length $value) {
5838     $self->{onerror}->(node => $attr,
5839     type => 'empty anchor image alt',
5840     level => $self->{level}->{must});
5841     }
5842     },
5843 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
5844     'application/x-www-form-urlencoded' => 1,
5845     'multipart/form-data' => 1,
5846     'text/plain' => 1,
5847     }),
5848 wakaba 1.178 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5849 wakaba 1.149 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
5850     method => $GetHTMLEnumeratedAttrChecker->({
5851     get => 1, post => 1, put => 1, delete => 1,
5852     }),
5853 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
5854 wakaba 1.149 replace => $GetHTMLEnumeratedAttrChecker->({
5855     document => 1, values => 1,
5856     }),
5857 wakaba 1.144 src => $HTMLURIAttrChecker,
5858     ## TODO: There is requirements on the referenced resource.
5859 wakaba 1.149 target => $HTMLTargetAttrChecker,
5860     usemap => $HTMLUsemapAttrChecker,
5861 wakaba 1.178 width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5862 wakaba 1.144 }->{$attr_ln} || $checker;
5863     ## TODO: alt & src are required.
5864     } elsif ({
5865     reset => 1, button => 1,
5866     ## NOTE: From Web Forms 2.0:
5867     remove => 1, 'move-up' => 1, 'move-down' => 1,
5868     add => 1,
5869     }->{$state}) {
5870     $checker =
5871     {
5872     ## NOTE: According to Web Forms 2.0, |input| attribute
5873     ## has |template| attribute to support the |add| button
5874     ## type (as part of the repetition template feature). It
5875     ## conflicts with the |template| global attribute
5876     ## introduced as part of the data template feature.
5877     ## NOTE: |template| attribute as defined in Web Forms 2.0
5878     ## has no author requirement.
5879     value => sub { }, ## NOTE: No restriction.
5880     }->{$attr_ln} || $checker;
5881 wakaba 1.156 } else { # Text, Search, E-mail, URL, Password
5882 wakaba 1.141 $checker =
5883     {
5884 wakaba 1.145 autocomplete => $GetHTMLEnumeratedAttrChecker->({
5885     on => 1, off => 1,
5886     }),
5887 wakaba 1.149 ## TODO: inputmode [WF2]
5888 wakaba 1.158 list => $ListAttrChecker,
5889 wakaba 1.147 maxlength => sub {
5890     my ($self, $attr, $item, $element_state) = @_;
5891    
5892     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
5893    
5894 wakaba 1.165 if ($attr->value =~ /^[\x09\x0A\x0C\x0D\x20]*([0-9]+)/) {
5895 wakaba 1.147 ## NOTE: Applying the rules for parsing non-negative
5896     ## integers results in a number.
5897     my $max_allowed_value_length = 0+$1;
5898    
5899     my $value = $item->{node}->get_attribute_ns (undef, 'value');
5900     if (defined $value) {
5901     my $codepoint_length = length $value;
5902 wakaba 1.162
5903 wakaba 1.147 if ($codepoint_length > $max_allowed_value_length) {
5904     $self->{onerror}
5905     ->(node => $item->{node}
5906     ->get_attribute_node_ns (undef, 'value'),
5907     type => 'value too long',
5908     level => $self->{level}->{must});
5909     }
5910     }
5911     }
5912     },
5913 wakaba 1.160 pattern => $PatternAttrChecker,
5914 wakaba 1.179 placeholder => $PlaceholderAttrChecker,
5915 wakaba 1.145 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5916 wakaba 1.146 required => $GetHTMLBooleanAttrChecker->('required'),
5917 wakaba 1.147 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub {shift > 0}),
5918 wakaba 1.143 value => sub {
5919 wakaba 1.156 my ($self, $attr, $item, $element_state) = @_;
5920     if ($state eq 'url') {
5921     $HTMLURIAttrChecker->(@_);
5922     } elsif ($state eq 'email') {
5923     if ($item->{node}->has_attribute_ns (undef, 'multiple')) {
5924     my @addr = split /,/, $attr->value, -1;
5925     @addr = ('') unless @addr;
5926     for (@addr) {
5927 wakaba 1.165 s/\A[\x09\x0A\x0C\x0D\x20]+//;
5928     s/[\x09\x0A\x0C\x0D\x20]\z//;
5929 wakaba 1.156 unless (/\A$ValidEmailAddress\z/) {
5930     $self->{onerror}->(node => $attr,
5931     type => 'email:syntax error', ## TODO: type
5932     value => $_,
5933     level => $self->{level}->{must});
5934     }
5935     }
5936     } else {
5937     unless ($attr->value =~ /\A$ValidEmailAddress\z/) {
5938     $self->{onerror}->(node => $attr,
5939     type => 'email:syntax error', ## TODO: type
5940     level => $self->{level}->{must});
5941     }
5942     }
5943     } else {
5944     if ($attr->value =~ /[\x0D\x0A]/) {
5945     $self->{onerror}->(node => $attr,
5946     type => 'newline in value', ## TODO: type
5947     level => $self->{level}->{must});
5948     }
5949     }
5950 wakaba 1.143 },
5951 wakaba 1.141 }->{$attr_ln} || $checker;
5952 wakaba 1.147 $checker = '' if $state eq 'password' and $attr_ln eq 'list';
5953 wakaba 1.156 $checker = $GetHTMLBooleanAttrChecker->('multiple')
5954     if $state eq 'email' and $attr_ln eq 'multiple';
5955 wakaba 1.161
5956     if ($item->{node}->has_attribute_ns (undef, 'pattern') and
5957     not $item->{node}->has_attribute_ns (undef, 'title')) {
5958     $self->{onerror}->(node => $item->{node},
5959     type => 'attribute missing',
5960     text => 'title',
5961     level => $self->{level}->{should});
5962     }
5963 wakaba 1.141 }
5964     }
5965    
5966     if (defined $checker) {
5967     if ($checker eq '') {
5968     $checker = sub {
5969     my ($self, $attr) = @_;
5970     $self->{onerror}->(node => $attr,
5971     type => 'input attr not applicable',
5972     text => $state,
5973     level => $self->{level}->{must});
5974     };
5975     }
5976 wakaba 1.140 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
5977     $attr_ln !~ /[A-Z]/) {
5978     $checker = $HTMLDatasetAttrChecker;
5979     $status = $HTMLDatasetAttrStatus;
5980     } else {
5981     $checker = $HTMLAttrChecker->{$attr_ln};
5982     }
5983     }
5984     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
5985     || $AttrChecker->{$attr_ns}->{''};
5986     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
5987     || $AttrStatus->{$attr_ns}->{''};
5988     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
5989 wakaba 1.157
5990 wakaba 1.140 if ($checker) {
5991     $checker->($self, $attr, $item, $element_state) if ref $checker;
5992     } elsif ($attr_ns eq '' and not $status) {
5993     #
5994     } else {
5995     $self->{onerror}->(node => $attr,
5996     type => 'unknown attribute',
5997     level => $self->{level}->{uncertain});
5998     ## ISSUE: No comformance createria for unknown attributes in the spec
5999     }
6000    
6001     $self->_attr_status_info ($attr, $status);
6002     }
6003 wakaba 1.168
6004     ## ISSUE: -0/+0
6005    
6006     if ($state eq 'range') {
6007     $element_state->{number_value}->{min} ||= 0;
6008     $element_state->{number_value}->{max} = 100
6009     unless defined $element_state->{number_value}->{max};
6010     }
6011    
6012     if (defined $element_state->{date_value}->{min} or
6013     defined $element_state->{date_value}->{max}) {
6014     my $min_value = $element_state->{date_value}->{min};
6015     my $max_value = $element_state->{date_value}->{max};
6016     my $value_value = $element_state->{date_value}->{value};
6017    
6018     if (defined $min_value and $min_value eq '' and
6019     (defined $max_value or defined $value_value)) {
6020     my $min = $item->{node}->get_attribute_node_ns (undef, 'min');
6021     $self->{onerror}->(node => $min,
6022     type => 'date value not supported', ## TODOC: type
6023     value => $min->value,
6024     level => $self->{level}->{unsupported});
6025     undef $min_value;
6026     }
6027     if (defined $max_value and $max_value eq '' and
6028     (defined $max_value or defined $value_value)) {
6029     my $max = $item->{node}->get_attribute_node_ns (undef, 'max');
6030     $self->{onerror}->(node => $max,
6031     type => 'date value not supported', ## TODOC: type
6032     value => $max->value,
6033     level => $self->{level}->{unsupported});
6034     undef $max_value;
6035     }
6036     if (defined $value_value and $value_value eq '' and
6037     (defined $max_value or defined $min_value)) {
6038     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6039     $self->{onerror}->(node => $value,
6040     type => 'date value not supported', ## TODOC: type
6041     value => $value->value,
6042     level => $self->{level}->{unsupported});
6043     undef $value_value;
6044     }
6045    
6046     if (defined $min_value and defined $max_value) {
6047     if ($min_value->to_html5_number > $max_value->to_html5_number) {
6048     my $max = $item->{node}->get_attribute_node_ns (undef, 'max');
6049     $self->{onerror}->(node => $max,
6050     type => 'max lt min', ## TODOC: type
6051     level => $self->{level}->{must});
6052     }
6053     }
6054    
6055     if (defined $min_value and defined $value_value) {
6056     if ($min_value->to_html5_number > $value_value->to_html5_number) {
6057     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6058     $self->{onerror}->(node => $value,
6059     type => 'value lt min', ## TODOC: type
6060     level => $self->{level}->{warn});
6061     ## NOTE: Not an error.
6062     }
6063     }
6064    
6065     if (defined $max_value and defined $value_value) {
6066     if ($max_value->to_html5_number < $value_value->to_html5_number) {
6067     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6068     $self->{onerror}->(node => $value,
6069     type => 'value gt max', ## TODOC: type
6070     level => $self->{level}->{warn});
6071     ## NOTE: Not an error.
6072     }
6073     }
6074     } elsif (defined $element_state->{number_value}->{min} or
6075     defined $element_state->{number_value}->{max}) {
6076     my $min_value = $element_state->{number_value}->{min};
6077     my $max_value = $element_state->{number_value}->{max};
6078     my $value_value = $element_state->{number_value}->{value};
6079    
6080     if (defined $min_value and defined $max_value) {
6081     if ($min_value > $max_value) {
6082     my $max = $item->{node}->get_attribute_node_ns (undef, 'max');
6083     $self->{onerror}->(node => $max,
6084     type => 'max lt min', ## TODOC: type
6085     level => $self->{level}->{must});
6086     }
6087     }
6088    
6089     if (defined $min_value and defined $value_value) {
6090     if ($min_value > $value_value) {
6091     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6092     $self->{onerror}->(node => $value,
6093     type => 'value lt min', ## TODOC: type
6094     level => $self->{level}->{warn});
6095     ## NOTE: Not an error.
6096     }
6097     }
6098    
6099     if (defined $max_value and defined $value_value) {
6100     if ($max_value < $value_value) {
6101     my $value = $item->{node}->get_attribute_node_ns (undef, 'value');
6102     $self->{onerror}->(node => $value,
6103     type => 'value gt max', ## TODOC: type
6104     level => $self->{level}->{warn});
6105     ## NOTE: Not an error.
6106     }
6107     }
6108     }
6109 wakaba 1.150
6110 wakaba 1.168 ## TODO: Warn unless value = min * x where x is an integer.
6111    
6112 wakaba 1.150 $element_state->{uri_info}->{action}->{type}->{action} = 1;
6113     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6114     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
6115     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6116     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6117 wakaba 1.140 },
6118 wakaba 1.66 check_start => sub {
6119     my ($self, $item, $element_state) = @_;
6120 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6121     $self->{onerror}->(node => $item->{node},
6122     type => 'multiple labelable fae',
6123     level => $self->{level}->{must});
6124     } else {
6125     $self->{flag}->{has_labelable} = 2;
6126     }
6127 wakaba 1.138
6128     $element_state->{id_type} = 'labelable';
6129 wakaba 1.66 },
6130 wakaba 1.52 };
6131    
6132 wakaba 1.178 ## XXXresource: Dimension attributes have requirements on width and
6133     ## height of referenced resource.
6134 wakaba 1.80
6135 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
6136 wakaba 1.119 %HTMLPhrasingContentChecker, ## ISSUE: -interactive?
6137     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6138 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6139 wakaba 1.165 ## ISSUE: In HTML5, no "MUST NOT" for using |action|, |method|,
6140     ## |enctype|, |target|, and |novalidate| with non-|submit|-|type|
6141     ## |button| elements.
6142 wakaba 1.56 action => $HTMLURIAttrChecker,
6143 wakaba 1.165 autofocus => $AutofocusAttrChecker,
6144 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6145 wakaba 1.166 enctype => $GetHTMLEnumeratedAttrChecker->({
6146     'application/x-www-form-urlencoded' => 1,
6147     'multipart/form-data' => 1,
6148     'text/plain' => 1,
6149     }),
6150 wakaba 1.136 form => $HTMLFormAttrChecker,
6151 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
6152     get => 1, post => 1, put => 1, delete => 1,
6153     }),
6154 wakaba 1.165 name => $FormControlNameAttrChecker,
6155 wakaba 1.166 novalidate => $GetHTMLBooleanAttrChecker->('novalidate'),
6156 wakaba 1.162 onformchange => $HTMLEventHandlerAttrChecker, ## TODO: tests
6157     onforminput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6158 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
6159     target => $HTMLTargetAttrChecker,
6160 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
6161     ## attribute to support the |add| button type (as part of repetition
6162     ## template feature). It conflicts with the |template| global attribute
6163     ## introduced as part of the data template feature.
6164     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
6165     ## author requirement.
6166 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
6167     button => 1, submit => 1, reset => 1,
6168     }),
6169 wakaba 1.162 value => sub {}, ## NOTE: No restriction.
6170 wakaba 1.52 }, {
6171     %HTMLAttrStatus,
6172     %HTMLM12NCommonAttrStatus,
6173 wakaba 1.176 accesskey => FEATURE_M12N10_REC | FEATURE_HTML5_FD,
6174 wakaba 1.119 action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6175     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6176 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6177     dataformatas => FEATURE_HTML4_REC_RESERVED,
6178     datasrc => FEATURE_HTML4_REC_RESERVED,
6179 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6180     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6181     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6182 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6183 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6184     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6185 wakaba 1.162 novalidate => FEATURE_HTML5_DEFAULT,
6186 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6187     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6188 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
6189     onforminput => FEATURE_WF2_INFORMATIVE,
6190 wakaba 1.56 replace => FEATURE_WF2,
6191 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6192 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6193 wakaba 1.162 template => FEATURE_HTML5_AT_RISK | FEATURE_WF2, ## TODO: dropped
6194 wakaba 1.119 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6195     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6196 wakaba 1.52 }),
6197 wakaba 1.66 check_start => sub {
6198     my ($self, $item, $element_state) = @_;
6199 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6200     $self->{onerror}->(node => $item->{node},
6201     type => 'multiple labelable fae',
6202     level => $self->{level}->{must});
6203     } else {
6204     $self->{flag}->{has_labelable} = 2;
6205     }
6206 wakaba 1.162
6207     ## ISSUE: "The value attribute must not be present unless the form
6208     ## [content] attribute is present.": Wrong?
6209 wakaba 1.139
6210 wakaba 1.66 $element_state->{uri_info}->{action}->{type}->{action} = 1;
6211     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6212 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6213     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6214 wakaba 1.138
6215     $element_state->{id_type} = 'labelable';
6216 wakaba 1.66 },
6217 wakaba 1.52 };
6218    
6219     $Element->{$HTML_NS}->{label} = {
6220 wakaba 1.139 %HTMLPhrasingContentChecker,
6221 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC
6222     | FEATURE_XHTML2_ED,
6223 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6224 wakaba 1.138 for => sub {
6225     my ($self, $attr) = @_;
6226    
6227     ## NOTE: MUST be an ID of a labelable element.
6228    
6229     push @{$self->{idref}}, ['labelable', $attr->value, $attr];
6230     },
6231 wakaba 1.136 form => $HTMLFormAttrChecker,
6232 wakaba 1.52 }, {
6233     %HTMLAttrStatus,
6234 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6235 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_WF2 | FEATURE_M12N10_REC,
6236 wakaba 1.119 for => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6237     form => FEATURE_HTML5_DEFAULT,
6238 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6239 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6240     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6241     }),
6242 wakaba 1.139 check_start => sub {
6243     my ($self, $item, $element_state) = @_;
6244     $self->_add_minus_elements ($element_state, {$HTML_NS => {label => 1}});
6245    
6246     $element_state->{has_label_original} = $self->{flag}->{has_label};
6247     $self->{flag}->{has_label} = 1;
6248     $element_state->{has_labelable_original} = $self->{flag}->{has_labelable};
6249 wakaba 1.155 $self->{flag}->{has_labelable}
6250     = $item->{node}->has_attribute_ns (undef, 'for') ? 1 : 0;
6251 wakaba 1.139
6252     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6253     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6254     },
6255     check_end => sub {
6256     my ($self, $item, $element_state) = @_;
6257     $self->_remove_minus_elements ($element_state);
6258    
6259     if ($self->{flag}->{has_labelable} == 1) { # has for="" but no labelable
6260     $self->{flag}->{has_labelable}
6261     = $element_state->{has_labelable_original};
6262     }
6263     delete $self->{flag}->{has_label}
6264     unless $element_state->{has_label_original};
6265     ## TODO: Warn if no labelable descendant? <input type=hidden>?
6266    
6267     ## NOTE: |<label for=a><input id=a></label>| is non-conforming.
6268    
6269     $HTMLPhrasingContentChecker{check_end}->(@_);
6270     },
6271 wakaba 1.52 ## TODO: Tests for <nest/> in <label>
6272     };
6273    
6274     $Element->{$HTML_NS}->{select} = {
6275 wakaba 1.121 %HTMLChecker,
6276 wakaba 1.163 ## ISSUE: HTML5 has no requirement like these:
6277 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
6278     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
6279 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6280 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
6281 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6282 wakaba 1.165 autofocus => $AutofocusAttrChecker,
6283 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6284 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
6285 wakaba 1.136 form => $HTMLFormAttrChecker,
6286 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
6287 wakaba 1.165 name => $FormControlNameAttrChecker,
6288 wakaba 1.163 ## TODO: tests for on*
6289 wakaba 1.126 onformchange => $HTMLEventHandlerAttrChecker,
6290     onforminput => $HTMLEventHandlerAttrChecker,
6291     oninput => $HTMLEventHandlerAttrChecker,
6292 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
6293 wakaba 1.163 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
6294 wakaba 1.52 }, {
6295     %HTMLAttrStatus,
6296     %HTMLM12NCommonAttrStatus,
6297 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_WF2,
6298 wakaba 1.119 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6299 wakaba 1.56 data => FEATURE_WF2,
6300 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6301     dataformatas => FEATURE_HTML4_REC_RESERVED,
6302     datasrc => FEATURE_HTML4_REC_RESERVED,
6303 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6304     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6305 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6306 wakaba 1.119 multiple => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6307     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6308 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6309     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6310 wakaba 1.126 onformchange => FEATURE_WF2_INFORMATIVE,
6311     onforminput => FEATURE_WF2_INFORMATIVE,
6312 wakaba 1.52 onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6313 wakaba 1.126 oninput => FEATURE_WF2,
6314 wakaba 1.56 oninvalid => FEATURE_WF2,
6315 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6316     sdapref => FEATURE_HTML20_RFC,
6317 wakaba 1.119 size => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6318 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6319     }),
6320 wakaba 1.66 check_start => sub {
6321     my ($self, $item, $element_state) = @_;
6322 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6323     $self->{onerror}->(node => $item->{node},
6324     type => 'multiple labelable fae',
6325     level => $self->{level}->{must});
6326     } else {
6327     $self->{flag}->{has_labelable} = 2;
6328     }
6329 wakaba 1.66
6330     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6331     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6332 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6333     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6334 wakaba 1.138
6335     $element_state->{id_type} = 'labelable';
6336 wakaba 1.66 },
6337 wakaba 1.121 check_child_element => sub {
6338 wakaba 1.163 ## NOTE: (option | optgroup)*
6339    
6340 wakaba 1.121 my ($self, $item, $child_el, $child_nsuri, $child_ln,
6341     $child_is_transparent, $element_state) = @_;
6342 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6343     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6344 wakaba 1.121 $self->{onerror}->(node => $child_el,
6345     type => 'element not allowed:minus',
6346     level => $self->{level}->{must});
6347     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6348     #
6349     } elsif ($child_nsuri eq $HTML_NS and
6350     {
6351     option => 1, optgroup => 1,
6352     }->{$child_ln}) {
6353     #
6354     } else {
6355     $self->{onerror}->(node => $child_el, type => 'element not allowed',
6356     level => $self->{level}->{must});
6357     }
6358     },
6359     check_child_text => sub {
6360     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6361     if ($has_significant) {
6362     $self->{onerror}->(node => $child_node, type => 'character not allowed',
6363     level => $self->{level}->{must});
6364     }
6365     },
6366 wakaba 1.52 };
6367 wakaba 1.1
6368 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
6369 wakaba 1.121 %HTMLPhrasingContentChecker,
6370 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6371 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
6372     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
6373     }, {
6374 wakaba 1.52 %HTMLAttrStatus,
6375 wakaba 1.56 data => FEATURE_WF2,
6376 wakaba 1.52 }),
6377 wakaba 1.66 check_start => sub {
6378     my ($self, $item, $element_state) = @_;
6379    
6380 wakaba 1.121 $element_state->{phase} = 'any'; # any | phrasing | option
6381    
6382 wakaba 1.66 $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6383 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6384     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6385 wakaba 1.158
6386     $element_state->{id_type} = 'datalist';
6387 wakaba 1.66 },
6388 wakaba 1.121 ## NOTE: phrasing | option*
6389     check_child_element => sub {
6390     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6391     $child_is_transparent, $element_state) = @_;
6392 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6393     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6394 wakaba 1.121 $self->{onerror}->(node => $child_el,
6395     type => 'element not allowed:minus',
6396     level => $self->{level}->{must});
6397     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6398     #
6399     } elsif ($element_state->{phase} eq 'phrasing') {
6400     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6401     #
6402     } else {
6403     $self->{onerror}->(node => $child_el,
6404     type => 'element not allowed:phrasing',
6405     level => $self->{level}->{must});
6406     }
6407     } elsif ($element_state->{phase} eq 'option') {
6408     if ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6409     #
6410     } else {
6411     $self->{onerror}->(node => $child_el,
6412     type => 'element not allowed',
6413     level => $self->{level}->{must});
6414     }
6415     } elsif ($element_state->{phase} eq 'any') {
6416     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6417     $element_state->{phase} = 'phrasing';
6418     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6419     $element_state->{phase} = 'option';
6420     } else {
6421     $self->{onerror}->(node => $child_el,
6422     type => 'element not allowed',
6423     level => $self->{level}->{must});
6424     }
6425     } else {
6426     die "check_child_element: Bad |datalist| phase: $element_state->{phase}";
6427     }
6428     },
6429     check_child_text => sub {
6430     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6431     if ($has_significant) {
6432     if ($element_state->{phase} eq 'phrasing') {
6433     #
6434     } elsif ($element_state->{phase} eq 'any') {
6435     $element_state->{phase} = 'phrasing';
6436     } else {
6437     $self->{onerror}->(node => $child_node,
6438     type => 'character not allowed',
6439     level => $self->{level}->{must});
6440     }
6441     }
6442     },
6443     check_end => sub {
6444     my ($self, $item, $element_state) = @_;
6445     if ($element_state->{phase} eq 'phrasing') {
6446     if ($element_state->{has_significant}) {
6447     $item->{real_parent_state}->{has_significant} = 1;
6448     } elsif ($item->{transparent}) {
6449     #
6450     } else {
6451     $self->{onerror}->(node => $item->{node},
6452     type => 'no significant content',
6453     level => $self->{level}->{should});
6454     }
6455     } else {
6456     ## NOTE: Since the content model explicitly allows a |datalist| element
6457     ## being empty, we don't raise "no significant content" error for this
6458     ## element when there is no element. (We should raise an error for
6459     ## |<datalist><br></datalist>|, however.)
6460     ## NOTE: As a side-effect, when the |datalist| element only contains
6461     ## non-conforming content, then the |phase| flag has not changed from
6462     ## |any|, no "no significant content" error is raised neither.
6463     $HTMLChecker{check_end}->(@_);
6464     }
6465     },
6466 wakaba 1.52 };
6467 wakaba 1.49
6468 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
6469 wakaba 1.121 %HTMLChecker,
6470 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6471 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6472     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6473 wakaba 1.164 label => sub {},
6474 wakaba 1.52 }, {
6475     %HTMLAttrStatus,
6476     %HTMLM12NCommonAttrStatus,
6477 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6478     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6479 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6480 wakaba 1.52 }),
6481 wakaba 1.164 check_attrs2 => sub {
6482     my ($self, $item, $element_state) = @_;
6483    
6484     unless ($item->{node}->has_attribute_ns (undef, 'label')) {
6485     $self->{onerror}->(node => $item->{node},
6486     type => 'attribute missing',
6487     text => 'label',
6488     level => $self->{level}->{must});
6489     }
6490     },
6491 wakaba 1.121 check_child_element => sub {
6492     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6493     $child_is_transparent, $element_state) = @_;
6494 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6495     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6496 wakaba 1.121 $self->{onerror}->(node => $child_el,
6497     type => 'element not allowed:minus',
6498     level => $self->{level}->{must});
6499     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6500     #
6501     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
6502     #
6503     } else {
6504     $self->{onerror}->(node => $child_el, type => 'element not allowed',
6505     level => $self->{level}->{must});
6506     }
6507     },
6508     check_child_text => sub {
6509     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6510     if ($has_significant) {
6511     $self->{onerror}->(node => $child_node, type => 'character not allowed',
6512     level => $self->{level}->{must});
6513     }
6514     },
6515 wakaba 1.52 };
6516    
6517     $Element->{$HTML_NS}->{option} = {
6518     %HTMLTextChecker,
6519 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6520 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6521     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6522 wakaba 1.164 label => sub {}, ## NOTE: No restriction.
6523     selected => $GetHTMLBooleanAttrChecker->('selected'), ## ISSUE: Not a "boolean attribute"
6524     value => sub {}, ## NOTE: No restriction.
6525 wakaba 1.52 }, {
6526     %HTMLAttrStatus,
6527     %HTMLM12NCommonAttrStatus,
6528 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6529     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6530 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6531 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6532     sdapref => FEATURE_HTML20_RFC,
6533 wakaba 1.119 selected => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6534     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6535 wakaba 1.52 }),
6536     };
6537 wakaba 1.49
6538 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
6539     %HTMLTextChecker,
6540 wakaba 1.121 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6541 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6542 wakaba 1.164 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type [WF2]
6543 wakaba 1.165 autofocus => $AutofocusAttrChecker,
6544 wakaba 1.164 cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
6545 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6546 wakaba 1.136 form => $HTMLFormAttrChecker,
6547 wakaba 1.56 ## TODO: inputmode [WF2]
6548 wakaba 1.164 maxlength => sub {
6549     my ($self, $attr, $item, $element_state) = @_;
6550    
6551     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
6552    
6553 wakaba 1.165 if ($attr->value =~ /^[\x09\x0A\x0C\x0D\x20]*([0-9]+)/) {
6554 wakaba 1.164 ## NOTE: Applying the rules for parsing non-negative integers
6555     ## results in a number.
6556     my $max_allowed_value_length = 0+$1;
6557    
6558     ## ISSUE: "The the purposes of this requirement," (typo)
6559    
6560     ## ISSUE: This constraint is applied w/o CRLF normalization to
6561     ## |value| attribute, but w/ CRLF normalization to
6562     ## concept-value.
6563     my $value = $item->{node}->text_content;
6564     if (defined $value) {
6565     my $codepoint_length = length $value;
6566    
6567     if ($codepoint_length > $max_allowed_value_length) {
6568     $self->{onerror}->(node => $item->{node},
6569     type => 'value too long',
6570     level => $self->{level}->{must});
6571     }
6572     }
6573     }
6574     },
6575 wakaba 1.165 name => $FormControlNameAttrChecker,
6576 wakaba 1.164 onformchange => $HTMLEventHandlerAttrChecker, ## TODO: tests
6577     onforminput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6578     oninput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6579 wakaba 1.161 pattern => $PatternAttrChecker,
6580 wakaba 1.179 placeholder => $PlaceholderAttrChecker,
6581 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
6582 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
6583 wakaba 1.164 rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
6584     oninput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6585     oninvalid => $HTMLEventHandlerAttrChecker, ## TODO: tests
6586 wakaba 1.161 ## NOTE: |title| had special semantics if |pattern| was specified [WF2].
6587 wakaba 1.56 wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
6588 wakaba 1.52 }, {
6589     %HTMLAttrStatus,
6590     %HTMLM12NCommonAttrStatus,
6591 wakaba 1.164 accept => FEATURE_HTML5_DROPPED | FEATURE_WF2X,
6592 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
6593 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
6594 wakaba 1.121 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6595     cols => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6596 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
6597 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
6598     datasrc => FEATURE_HTML4_REC_RESERVED,
6599 wakaba 1.121 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6600     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6601 wakaba 1.164 inputmode => FEATURE_HTML5_DROPPED | FEATURE_WF2X | FEATURE_XHTMLBASIC11_CR,
6602 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6603 wakaba 1.121 maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6604     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6605 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6606     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6607     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6608 wakaba 1.164 onformchange => FEATURE_WF2_INFORMATIVE, ## TODO: tests
6609     onforminput => FEATURE_WF2_INFORMATIVE, ## TODO: tests
6610     oninput => FEATURE_WF2, ## TODO: tests
6611     oninvalid => FEATURE_WF2, ## TODO: tests
6612 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6613 wakaba 1.161 pattern => FEATURE_HTML5_DROPPED | FEATURE_WF2X,
6614 wakaba 1.179 placeholder => FEATURE_HTML5_LC,
6615 wakaba 1.121 readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
6616     required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6617     rows => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6618 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6619     sdapref => FEATURE_HTML20_RFC,
6620 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6621 wakaba 1.121 wrap => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6622 wakaba 1.52 }),
6623 wakaba 1.66 check_start => sub {
6624     my ($self, $item, $element_state) = @_;
6625 wakaba 1.139 if ($self->{flag}->{has_label} and $self->{flag}->{has_labelable}) {
6626     $self->{onerror}->(node => $item->{node},
6627     type => 'multiple labelable fae',
6628     level => $self->{level}->{must});
6629     } else {
6630     $self->{flag}->{has_labelable} = 2;
6631     }
6632 wakaba 1.164
6633     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
6634     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6635     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6636    
6637     $element_state->{id_type} = 'labelable';
6638     },
6639     check_attrs2 => sub {
6640     my ($self, $item, $element_state) = @_;
6641 wakaba 1.66
6642 wakaba 1.161 if ($item->{node}->has_attribute_ns (undef, 'pattern') and
6643     not $item->{node}->has_attribute_ns (undef, 'title')) {
6644     ## NOTE: WF2 (dropped by HTML5)
6645     $self->{onerror}->(node => $item->{node},
6646     type => 'attribute missing',
6647     text => 'title',
6648     level => $self->{level}->{should});
6649     }
6650    
6651 wakaba 1.164 unless ($item->{node}->has_attribute_ns (undef, 'cols')) {
6652     my $wrap = $item->{node}->get_attribute_ns (undef, 'wrap');
6653     if (defined $wrap) {
6654     $wrap =~ tr/A-Z/a-z/; ## ASCII case-insensitive
6655     if ($wrap eq 'hard') {
6656     $self->{onerror}->(node => $item->{node},
6657     type => 'attribute missing',
6658     text => 'cols',
6659     level => $self->{level}->{must});
6660     }
6661     }
6662     }
6663 wakaba 1.66 },
6664 wakaba 1.52 };
6665 wakaba 1.49
6666 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
6667 wakaba 1.121 %HTMLPhrasingContentChecker,
6668     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6669 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
6670 wakaba 1.165 for => sub {
6671     my ($self, $attr) = @_;
6672    
6673     ## NOTE: "Unordered set of unique space-separated tokens".
6674    
6675     my %word;
6676     for my $word (grep {length $_}
6677     split /[\x09\x0A\x0C\x0D\x20]+/, $attr->value) {
6678     unless ($word{$word}) {
6679     $word{$word} = 1;
6680     push @{$self->{idref}}, ['any', $word, $attr];
6681     } else {
6682     $self->{onerror}->(node => $attr, type => 'duplicate token',
6683     value => $word,
6684     level => $self->{level}->{must});
6685     }
6686     }
6687     },
6688 wakaba 1.136 form => $HTMLFormAttrChecker,
6689 wakaba 1.165 name => $FormControlNameAttrChecker,
6690     onformchange => $HTMLEventHandlerAttrChecker, ## TODO: tests
6691     onforminput => $HTMLEventHandlerAttrChecker, ## TODO: tests
6692 wakaba 1.56 }, {
6693 wakaba 1.52 %HTMLAttrStatus,
6694 wakaba 1.121 for => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6695     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6696     name => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
6697 wakaba 1.56 onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
6698     onformchange => FEATURE_WF2,
6699     onforminput => FEATURE_WF2,
6700 wakaba 1.52 }),
6701     };
6702    
6703     $Element->{$HTML_NS}->{isindex} = {
6704     %HTMLEmptyChecker,
6705 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
6706     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
6707 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6708     prompt => sub {}, ## NOTE: Text [M12N]
6709     }, {
6710     %HTMLAttrStatus,
6711 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6712     dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6713     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6714     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6715 wakaba 1.52 prompt => FEATURE_M12N10_REC_DEPRECATED,
6716 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
6717 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6718     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6719 wakaba 1.52 }),
6720     ## TODO: Tests
6721     ## TODO: Tests for <nest/> in <isindex>
6722 wakaba 1.66 check_start => sub {
6723     my ($self, $item, $element_state) = @_;
6724    
6725     $element_state->{uri_info}->{action}->{type}->{action} = 1;
6726 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6727     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6728 wakaba 1.66 },
6729 wakaba 1.52 };
6730 wakaba 1.49
6731 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
6732 wakaba 1.40 %HTMLChecker,
6733 wakaba 1.153 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6734 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6735 wakaba 1.91 charset => sub {
6736     my ($self, $attr) = @_;
6737    
6738     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
6739     $self->{onerror}->(type => 'attribute not allowed',
6740     node => $attr,
6741 wakaba 1.104 level => $self->{level}->{must});
6742 wakaba 1.91 }
6743    
6744     $HTMLCharsetChecker->($attr->value, @_);
6745     },
6746 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
6747 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
6748 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
6749     async => $GetHTMLBooleanAttrChecker->('async'),
6750 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
6751 wakaba 1.49 }, {
6752     %HTMLAttrStatus,
6753 wakaba 1.153 async => FEATURE_HTML5_WD,
6754     charset => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6755     defer => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6756 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
6757     for => FEATURE_HTML4_REC_RESERVED,
6758 wakaba 1.154 href => FEATURE_RDFA_REC,
6759 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6760 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
6761 wakaba 1.153 src => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6762     type => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
6763 wakaba 1.9 }),
6764 wakaba 1.40 check_start => sub {
6765     my ($self, $item, $element_state) = @_;
6766 wakaba 1.1
6767 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
6768     $element_state->{must_be_empty} = 1;
6769 wakaba 1.1 } else {
6770     ## NOTE: No content model conformance in HTML5 spec.
6771 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
6772     my $language = $item->{node}->get_attribute_ns (undef, 'language');
6773 wakaba 1.1 if ((defined $type and $type eq '') or
6774     (defined $language and $language eq '')) {
6775     $type = 'text/javascript';
6776     } elsif (defined $type) {
6777     #
6778     } elsif (defined $language) {
6779     $type = 'text/' . $language;
6780     } else {
6781     $type = 'text/javascript';
6782     }
6783 wakaba 1.93
6784     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)]) {
6785     $type = "$1/$2";
6786     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
6787     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
6788     }
6789     $element_state->{script_type} = $type;
6790 wakaba 1.40 }
6791 wakaba 1.66
6792     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6793 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6794     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6795 wakaba 1.107
6796     $element_state->{text} = '';
6797 wakaba 1.40 },
6798     check_child_element => sub {
6799     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6800     $child_is_transparent, $element_state) = @_;
6801 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6802     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6803 wakaba 1.40 $self->{onerror}->(node => $child_el,
6804     type => 'element not allowed:minus',
6805 wakaba 1.104 level => $self->{level}->{must});
6806 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6807     #
6808     } else {
6809     if ($element_state->{must_be_empty}) {
6810     $self->{onerror}->(node => $child_el,
6811 wakaba 1.104 type => 'element not allowed:empty',
6812     level => $self->{level}->{must});
6813 wakaba 1.40 }
6814     }
6815     },
6816     check_child_text => sub {
6817     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6818     if ($has_significant and
6819     $element_state->{must_be_empty}) {
6820     $self->{onerror}->(node => $child_node,
6821 wakaba 1.104 type => 'character not allowed:empty',
6822     level => $self->{level}->{must});
6823 wakaba 1.40 }
6824 wakaba 1.115 $element_state->{text} .= $child_node->data;
6825 wakaba 1.40 },
6826     check_end => sub {
6827     my ($self, $item, $element_state) = @_;
6828     unless ($element_state->{must_be_empty}) {
6829 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
6830     ## NOTE: XML content should be checked by THIS instance of checker
6831     ## as part of normal tree validation.
6832 wakaba 1.104 $self->{onerror}->(node => $item->{node},
6833     type => 'XML script lang',
6834     text => $element_state->{script_type},
6835     level => $self->{level}->{uncertain});
6836     ## ISSUE: Should we raise some kind of error for
6837     ## <script type="text/xml">aaaaa</script>?
6838     ## NOTE: ^^^ This is why we throw an "uncertain" error.
6839 wakaba 1.93 } else {
6840     $self->{onsubdoc}->({s => $element_state->{text},
6841     container_node => $item->{node},
6842     media_type => $element_state->{script_type},
6843     is_char_string => 1});
6844     }
6845 wakaba 1.40
6846     $HTMLChecker{check_end}->(@_);
6847 wakaba 1.1 }
6848     },
6849 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
6850     ## NOTE: "When used to include script data, the script data must be embedded
6851     ## inline, the format of the data must be given using the type attribute,
6852     ## and the src attribute must not be specified." - not testable.
6853     ## TODO: It would be possible to err <script type=text/plain src=...>
6854 wakaba 1.1 };
6855 wakaba 1.25 ## ISSUE: Significant check and text child node
6856 wakaba 1.1
6857     ## NOTE: When script is disabled.
6858     $Element->{$HTML_NS}->{noscript} = {
6859 wakaba 1.40 %HTMLTransparentChecker,
6860 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
6861 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
6862     %HTMLAttrStatus,
6863     %HTMLM12NCommonAttrStatus,
6864 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
6865 wakaba 1.49 }),
6866 wakaba 1.40 check_start => sub {
6867     my ($self, $item, $element_state) = @_;
6868 wakaba 1.3
6869 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
6870 wakaba 1.104 $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript',
6871     level => $self->{level}->{must});
6872 wakaba 1.3 }
6873    
6874 wakaba 1.40 unless ($self->{flag}->{in_head}) {
6875     $self->_add_minus_elements ($element_state,
6876     {$HTML_NS => {noscript => 1}});
6877     }
6878 wakaba 1.79
6879     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6880     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6881 wakaba 1.3 },
6882 wakaba 1.40 check_child_element => sub {
6883     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6884     $child_is_transparent, $element_state) = @_;
6885     if ($self->{flag}->{in_head}) {
6886 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
6887     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
6888 wakaba 1.40 $self->{onerror}->(node => $child_el,
6889     type => 'element not allowed:minus',
6890 wakaba 1.104 level => $self->{level}->{must});
6891 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6892     #
6893     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
6894     #
6895     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
6896     if ($child_el->has_attribute_ns (undef, 'scoped')) {
6897     $self->{onerror}->(node => $child_el,
6898     type => 'element not allowed:head noscript',
6899 wakaba 1.104 level => $self->{level}->{must});
6900 wakaba 1.40 }
6901     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
6902 wakaba 1.47 my $http_equiv_attr
6903     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
6904     if ($http_equiv_attr) {
6905     ## TODO: case
6906     if (lc $http_equiv_attr->value eq 'content-type') {
6907 wakaba 1.40 $self->{onerror}->(node => $child_el,
6908 wakaba 1.34 type => 'element not allowed:head noscript',
6909 wakaba 1.104 level => $self->{level}->{must});
6910 wakaba 1.47 } else {
6911     #
6912 wakaba 1.3 }
6913 wakaba 1.47 } else {
6914     $self->{onerror}->(node => $child_el,
6915     type => 'element not allowed:head noscript',
6916 wakaba 1.104 level => $self->{level}->{must});
6917 wakaba 1.3 }
6918 wakaba 1.40 } else {
6919     $self->{onerror}->(node => $child_el,
6920     type => 'element not allowed:head noscript',
6921 wakaba 1.104 level => $self->{level}->{must});
6922 wakaba 1.40 }
6923     } else {
6924     $HTMLTransparentChecker{check_child_element}->(@_);
6925     }
6926     },
6927     check_child_text => sub {
6928     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6929     if ($self->{flag}->{in_head}) {
6930     if ($has_significant) {
6931     $self->{onerror}->(node => $child_node,
6932 wakaba 1.104 type => 'character not allowed',
6933     level => $self->{level}->{must});
6934 wakaba 1.3 }
6935     } else {
6936 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
6937     }
6938     },
6939     check_end => sub {
6940     my ($self, $item, $element_state) = @_;
6941     $self->_remove_minus_elements ($element_state);
6942     if ($self->{flag}->{in_head}) {
6943     $HTMLChecker{check_end}->(@_);
6944     } else {
6945     $HTMLPhrasingContentChecker{check_end}->(@_);
6946 wakaba 1.3 }
6947 wakaba 1.1 },
6948     };
6949 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
6950 wakaba 1.1
6951     $Element->{$HTML_NS}->{'event-source'} = {
6952 wakaba 1.40 %HTMLEmptyChecker,
6953 wakaba 1.118 status => FEATURE_HTML5_LC_DROPPED,
6954     check_attrs => $GetHTMLAttrsChecker->({
6955     src => $HTMLURIAttrChecker,
6956     }, {
6957     %HTMLAttrStatus,
6958     src => FEATURE_HTML5_LC_DROPPED,
6959     }),
6960     check_start => sub {
6961     my ($self, $item, $element_state) = @_;
6962    
6963     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6964     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6965     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6966     },
6967     };
6968    
6969     $Element->{$HTML_NS}->{eventsource} = {
6970     %HTMLEmptyChecker,
6971 wakaba 1.153 status => FEATURE_HTML5_WD,
6972 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6973 wakaba 1.1 src => $HTMLURIAttrChecker,
6974 wakaba 1.50 }, {
6975     %HTMLAttrStatus,
6976 wakaba 1.153 src => FEATURE_HTML5_WD,
6977 wakaba 1.1 }),
6978 wakaba 1.66 check_start => sub {
6979     my ($self, $item, $element_state) = @_;
6980    
6981     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6982 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6983     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6984 wakaba 1.66 },
6985 wakaba 1.1 };
6986    
6987     $Element->{$HTML_NS}->{details} = {
6988 wakaba 1.134 %{$Element->{$HTML_NS}->{fieldset}},
6989 wakaba 1.153 status => FEATURE_HTML5_LC,
6990 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6991 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
6992 wakaba 1.50 }, {
6993     %HTMLAttrStatus,
6994 wakaba 1.153 open => FEATURE_HTML5_LC,
6995 wakaba 1.1 }),
6996     };
6997    
6998     $Element->{$HTML_NS}->{datagrid} = {
6999 wakaba 1.72 %HTMLFlowContentChecker,
7000 wakaba 1.48 status => FEATURE_HTML5_WD,
7001 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7002 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
7003     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
7004 wakaba 1.50 }, {
7005     %HTMLAttrStatus,
7006     disabled => FEATURE_HTML5_WD,
7007     multiple => FEATURE_HTML5_WD,
7008 wakaba 1.1 }),
7009 wakaba 1.40 check_start => sub {
7010     my ($self, $item, $element_state) = @_;
7011 wakaba 1.1
7012 wakaba 1.40 $self->_add_minus_elements ($element_state,
7013     {$HTML_NS => {a => 1, datagrid => 1}});
7014 wakaba 1.172
7015 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7016     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7017 wakaba 1.40 },
7018     check_end => sub {
7019     my ($self, $item, $element_state) = @_;
7020     $self->_remove_minus_elements ($element_state);
7021 wakaba 1.1
7022 wakaba 1.172 $HTMLFlowContentChecker{check_end}->(@_);
7023 wakaba 1.40 },
7024 wakaba 1.1 };
7025    
7026     $Element->{$HTML_NS}->{command} = {
7027 wakaba 1.40 %HTMLEmptyChecker,
7028 wakaba 1.48 status => FEATURE_HTML5_WD,
7029 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7030 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
7031     default => $GetHTMLBooleanAttrChecker->('default'),
7032     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
7033     icon => $HTMLURIAttrChecker,
7034     label => sub { }, ## NOTE: No conformance creteria
7035     radiogroup => sub { }, ## NOTE: No conformance creteria
7036     type => sub {
7037     my ($self, $attr) = @_;
7038     my $value = $attr->value;
7039     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
7040 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
7041     level => $self->{level}->{must});
7042 wakaba 1.1 }
7043     },
7044 wakaba 1.50 }, {
7045     %HTMLAttrStatus,
7046     checked => FEATURE_HTML5_WD,
7047 wakaba 1.175 default => FEATURE_HTML5_DROPPED, # HTML5 revision 3067
7048 wakaba 1.50 disabled => FEATURE_HTML5_WD,
7049     icon => FEATURE_HTML5_WD,
7050     label => FEATURE_HTML5_WD,
7051     radiogroup => FEATURE_HTML5_WD,
7052     type => FEATURE_HTML5_WD,
7053 wakaba 1.1 }),
7054 wakaba 1.66 check_start => sub {
7055     my ($self, $item, $element_state) = @_;
7056    
7057     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
7058 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7059     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7060 wakaba 1.66 },
7061 wakaba 1.115 };
7062    
7063     $Element->{$HTML_NS}->{bb} = {
7064     %HTMLPhrasingContentChecker,
7065 wakaba 1.153 status => FEATURE_HTML5_WD,
7066 wakaba 1.115 check_attrs => $GetHTMLAttrsChecker->({
7067     type => $GetHTMLEnumeratedAttrChecker->({makeapp => 1}),
7068     }, {
7069     %HTMLAttrStatus,
7070 wakaba 1.153 type => FEATURE_HTML5_WD,
7071 wakaba 1.115 }),
7072 wakaba 1.130 check_start => sub {
7073     my ($self, $item, $element_state) = @_;
7074     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
7075    
7076     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7077     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7078     },
7079     check_end => sub {
7080     my ($self, $item, $element_state) = @_;
7081     $self->_remove_minus_elements ($element_state);
7082    
7083     $HTMLTransparentChecker{check_end}->(@_);
7084     },
7085 wakaba 1.1 };
7086    
7087     $Element->{$HTML_NS}->{menu} = {
7088 wakaba 1.40 %HTMLPhrasingContentChecker,
7089 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
7090     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
7091     ## NOTE: We don't want any |menu| element warned as deprecated.
7092 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7093 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
7094 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
7095 wakaba 1.135 ## ISSUE: <menu id=""><p contextmenu=""> match? (In the current
7096     ## implementation, it does not match.)
7097 wakaba 1.1 label => sub { }, ## NOTE: No conformance creteria
7098     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
7099 wakaba 1.49 }, {
7100     %HTMLAttrStatus,
7101     %HTMLM12NCommonAttrStatus,
7102 wakaba 1.61 align => FEATURE_HTML2X_RFC,
7103 wakaba 1.113 autosubmit => FEATURE_HTML5_DROPPED,
7104 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
7105 wakaba 1.50 label => FEATURE_HTML5_WD,
7106 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7107 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
7108     sdapref => FEATURE_HTML20_RFC,
7109 wakaba 1.50 type => FEATURE_HTML5_WD,
7110 wakaba 1.1 }),
7111 wakaba 1.40 check_start => sub {
7112     my ($self, $item, $element_state) = @_;
7113     $element_state->{phase} = 'li or phrasing';
7114     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
7115     $self->{flag}->{in_menu} = 1;
7116 wakaba 1.79
7117     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7118     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7119 wakaba 1.135 $element_state->{id_type} = 'menu';
7120 wakaba 1.40 },
7121     check_child_element => sub {
7122     my ($self, $item, $child_el, $child_nsuri, $child_ln,
7123     $child_is_transparent, $element_state) = @_;
7124 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
7125     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
7126 wakaba 1.40 $self->{onerror}->(node => $child_el,
7127     type => 'element not allowed:minus',
7128 wakaba 1.104 level => $self->{level}->{must});
7129 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
7130     #
7131     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
7132     if ($element_state->{phase} eq 'li') {
7133     #
7134     } elsif ($element_state->{phase} eq 'li or phrasing') {
7135     $element_state->{phase} = 'li';
7136     } else {
7137 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
7138     level => $self->{level}->{must});
7139 wakaba 1.40 }
7140     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
7141     if ($element_state->{phase} eq 'phrasing') {
7142     #
7143     } elsif ($element_state->{phase} eq 'li or phrasing') {
7144     $element_state->{phase} = 'phrasing';
7145     } else {
7146 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
7147     level => $self->{level}->{must});
7148 wakaba 1.40 }
7149     } else {
7150 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
7151     level => $self->{level}->{must});
7152 wakaba 1.40 }
7153     },
7154     check_child_text => sub {
7155     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
7156     if ($has_significant) {
7157     if ($element_state->{phase} eq 'phrasing') {
7158     #
7159     } elsif ($element_state->{phase} eq 'li or phrasing') {
7160     $element_state->{phase} = 'phrasing';
7161     } else {
7162     $self->{onerror}->(node => $child_node,
7163 wakaba 1.104 type => 'character not allowed',
7164     level => $self->{level}->{must});
7165 wakaba 1.1 }
7166     }
7167 wakaba 1.40 },
7168     check_end => sub {
7169     my ($self, $item, $element_state) = @_;
7170     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
7171    
7172     if ($element_state->{phase} eq 'li') {
7173     $HTMLChecker{check_end}->(@_);
7174     } else { # 'phrasing' or 'li or phrasing'
7175     $HTMLPhrasingContentChecker{check_end}->(@_);
7176 wakaba 1.1 }
7177     },
7178 wakaba 1.8 };
7179    
7180     $Element->{$HTML_NS}->{datatemplate} = {
7181 wakaba 1.40 %HTMLChecker,
7182 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7183 wakaba 1.40 check_child_element => sub {
7184     my ($self, $item, $child_el, $child_nsuri, $child_ln,
7185     $child_is_transparent, $element_state) = @_;
7186 wakaba 1.130 if ($self->{minus_elements}->{$child_nsuri}->{$child_ln} and
7187     $IsInHTMLInteractiveContent->($child_el, $child_nsuri, $child_ln)) {
7188 wakaba 1.40 $self->{onerror}->(node => $child_el,
7189     type => 'element not allowed:minus',
7190 wakaba 1.104 level => $self->{level}->{must});
7191 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
7192     #
7193     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
7194     #
7195     } else {
7196     $self->{onerror}->(node => $child_el,
7197 wakaba 1.104 type => 'element not allowed:datatemplate',
7198     level => $self->{level}->{must});
7199 wakaba 1.40 }
7200     },
7201     check_child_text => sub {
7202     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
7203     if ($has_significant) {
7204 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
7205     level => $self->{level}->{must});
7206 wakaba 1.8 }
7207     },
7208     is_xml_root => 1,
7209     };
7210    
7211     $Element->{$HTML_NS}->{rule} = {
7212 wakaba 1.40 %HTMLChecker,
7213 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7214 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7215 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
7216 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
7217 wakaba 1.50 }, {
7218     %HTMLAttrStatus,
7219     condition => FEATURE_HTML5_AT_RISK,
7220     mode => FEATURE_HTML5_AT_RISK,
7221 wakaba 1.8 }),
7222 wakaba 1.40 check_start => sub {
7223     my ($self, $item, $element_state) = @_;
7224 wakaba 1.79
7225 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
7226 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
7227     $self->{flag}->{in_rule} = 1;
7228    
7229     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7230     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7231 wakaba 1.40 },
7232     check_child_element => sub { },
7233     check_child_text => sub { },
7234     check_end => sub {
7235     my ($self, $item, $element_state) = @_;
7236 wakaba 1.79
7237 wakaba 1.40 $self->_remove_plus_elements ($element_state);
7238 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
7239    
7240 wakaba 1.40 $HTMLChecker{check_end}->(@_);
7241 wakaba 1.8 },
7242     ## NOTE: "MAY be anything that, when the parent |datatemplate|
7243     ## is applied to some conforming data, results in a conforming DOM tree.":
7244     ## We don't check against this.
7245     };
7246    
7247     $Element->{$HTML_NS}->{nest} = {
7248 wakaba 1.40 %HTMLEmptyChecker,
7249 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
7250 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
7251 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
7252     mode => sub {
7253     my ($self, $attr) = @_;
7254     my $value = $attr->value;
7255 wakaba 1.132 if ($value !~ /\A[^\x09\x0A\x0C\x0D\x20]+\z/) {
7256 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'mode:syntax error',
7257     level => $self->{level}->{must});
7258 wakaba 1.23 }
7259     },
7260 wakaba 1.50 }, {
7261     %HTMLAttrStatus,
7262     filter => FEATURE_HTML5_AT_RISK,
7263     mode => FEATURE_HTML5_AT_RISK,
7264 wakaba 1.8 }),
7265 wakaba 1.1 };
7266    
7267     $Element->{$HTML_NS}->{legend} = {
7268 wakaba 1.40 %HTMLPhrasingContentChecker,
7269 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_M12N10_REC,
7270 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
7271     # align => $GetHTMLEnumeratedAttrChecker->({
7272     # top => 1, bottom => 1, left => 1, right => 1,
7273     # }),
7274 wakaba 1.167 form => $HTMLFormAttrChecker,
7275 wakaba 1.52 }, {
7276 wakaba 1.49 %HTMLAttrStatus,
7277     %HTMLM12NCommonAttrStatus,
7278 wakaba 1.176 accesskey => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
7279 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
7280 wakaba 1.167 form => FEATURE_HTML5_DROPPED,
7281 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7282 wakaba 1.49 }),
7283 wakaba 1.170 check_child_element => sub {
7284     my ($self, $item, $child_el, $child_nsuri, $child_ln,
7285     $child_is_transparent, $element_state) = @_;
7286     if ($item->{parent_state}->{in_figure}) {
7287     $HTMLFlowContentChecker{check_child_element}->(@_);
7288     } else {
7289     $HTMLPhrasingContentChecker{check_child_element}->(@_);
7290     }
7291     },
7292     check_child_text => sub {
7293     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
7294     if ($item->{parent_state}->{in_figure}) {
7295     $HTMLFlowContentChecker{check_child_text}->(@_);
7296     } else {
7297     $HTMLPhrasingContentChecker{check_child_text}->(@_);
7298     }
7299     },
7300     check_start => sub {
7301     my ($self, $item, $element_state) = @_;
7302     $self->_add_minus_elements ($element_state, {$HTML_NS => {figure => 1}});
7303    
7304     $HTMLFlowContentChecker{check_start}->(@_);
7305     },
7306     check_end => sub {
7307     my ($self, $item, $element_state) = @_;
7308     $self->_remove_minus_elements ($element_state);
7309    
7310     $HTMLFlowContentChecker{check_end}->(@_);
7311     },
7312     }; # legend
7313 wakaba 1.1
7314     $Element->{$HTML_NS}->{div} = {
7315 wakaba 1.72 %HTMLFlowContentChecker,
7316 wakaba 1.153 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
7317 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
7318     align => $GetHTMLEnumeratedAttrChecker->({
7319     left => 1, center => 1, right => 1, justify => 1,
7320     }),
7321     }, {
7322 wakaba 1.49 %HTMLAttrStatus,
7323 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
7324 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
7325     datafld => FEATURE_HTML4_REC_RESERVED,
7326     dataformatas => FEATURE_HTML4_REC_RESERVED,
7327     datasrc => FEATURE_HTML4_REC_RESERVED,
7328 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7329 wakaba 1.49 }),
7330 wakaba 1.66 check_start => sub {
7331     my ($self, $item, $element_state) = @_;
7332    
7333     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
7334 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
7335     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
7336 wakaba 1.66 },
7337 wakaba 1.1 };
7338    
7339 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
7340 wakaba 1.72 %HTMLFlowContentChecker,
7341 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
7342     check_attrs => $GetHTMLAttrsChecker->({}, {
7343     %HTMLAttrStatus,
7344     %HTMLM12NCommonAttrStatus,
7345 wakaba 1.153 lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7346 wakaba 1.64 }),
7347     };
7348    
7349 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
7350 wakaba 1.40 %HTMLTransparentChecker,
7351 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
7352 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
7353     ## TODO: HTML4 |size|, |color|, |face|
7354 wakaba 1.49 }, {
7355     %HTMLAttrStatus,
7356 wakaba 1.153 class => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7357 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
7358 wakaba 1.153 dir => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7359 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
7360 wakaba 1.153 id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7361     lang => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7362 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
7363 wakaba 1.153 style => FEATURE_HTML5_WD | FEATURE_XHTML10_REC,
7364     title => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7365 wakaba 1.49 }),
7366 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
7367     ## it is allowed only in a document with the WYSIWYG signature. The
7368     ## checker does not check whether there is the signature, since the
7369     ## signature is dropped, too, and has never been implemented. (In addition,
7370     ## for any |font| element an "element not defined" error is raised anyway,
7371     ## such that we don't have to raise an additional error.)
7372 wakaba 1.1 };
7373 wakaba 1.49
7374 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
7375     %HTMLEmptyChecker,
7376     status => FEATURE_M12N10_REC_DEPRECATED,
7377     check_attrs => $GetHTMLAttrsChecker->({
7378     ## TODO: color, face, size
7379     }, {
7380     %HTMLAttrStatus,
7381     color => FEATURE_M12N10_REC_DEPRECATED,
7382     face => FEATURE_M12N10_REC_DEPRECATED,
7383 wakaba 1.153 #id => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
7384     id => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
7385 wakaba 1.64 size => FEATURE_M12N10_REC_DEPRECATED,
7386     }),
7387     };
7388    
7389 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
7390     ## class title id cols rows onload onunload style(x10)
7391     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
7392     ## noframes Common, lang(xhtml10)
7393    
7394 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
7395 wakaba 1.56
7396 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
7397     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
7398     ## xmp, listing sdapref[HTML2,0]
7399    
7400 wakaba 1.56 =pod
7401    
7402 wakaba 1.61 HTML 2.0 nextid @n
7403    
7404     RFC 2659: CERTS CRYPTOPTS
7405    
7406     ISO-HTML: pre-html, divN
7407 wakaba 1.82
7408     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
7409     di (Common), nl (Common), handler (Common, type), standby (Common),
7410     summary (Common)
7411    
7412 wakaba 1.97 Access & XHTML2: access (LC)
7413 wakaba 1.82
7414     XML Events & XForms (for XHTML2 support; very, very low priority)
7415 wakaba 1.61
7416 wakaba 1.56 =cut
7417 wakaba 1.61
7418     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
7419     ## We added them only to |a|. |link| and |form| might also allow them
7420     ## in theory.
7421 wakaba 1.1
7422     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
7423    
7424     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24