/[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.156 - (hide annotations) (download)
Sat Dec 6 11:31:34 2008 UTC (16 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.155: +55 -14 lines
++ whatpm/t/dom-conformance/ChangeLog	6 Dec 2008 11:31:29 -0000
2008-12-06  Wakaba  <wakaba@suika.fam.cx>

	* html-form-input-1.dat: Added test data for <input type=search>
	and <input type=text|password|url|email value>.

	* html-interactive-1.dat: Fixed a typo error.

++ whatpm/Whatpm/ContentChecker/ChangeLog	6 Dec 2008 11:29:26 -0000
	* HTML.pm: Implemented <input multiple>, <input type=search>.
	Implemented <input value> validation for |type|s |text|, |search|,
	|email|, |url|, and |password|.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24