/[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.161 - (hide annotations) (download)
Fri Dec 12 05:05:20 2008 UTC (16 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.160: +75 -9 lines
++ whatpm/t/ChangeLog	12 Dec 2008 05:03:53 -0000
2008-12-12  Wakaba  <wakaba@suika.fam.cx>

	* ContentChecker.t: Added new test data file.

++ whatpm/t/dom-conformance/ChangeLog	12 Dec 2008 05:04:54 -0000
2008-12-12  Wakaba  <wakaba@suika.fam.cx>

	* html-form-input-1.dat, html-forms-1.dat: Added test data for
	@accept and @pattern.

	* html-form-textarea.dat: New test data file.

++ whatpm/Whatpm/ChangeLog	12 Dec 2008 05:00:26 -0000
2008-12-12  Wakaba  <wakaba@suika.fam.cx>

	* IMTChecker.pm: Added more definitions for subtypes.

++ whatpm/Whatpm/ContentChecker/ChangeLog	12 Dec 2008 05:03:26 -0000
2008-12-12  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Implemented <input accept> and <form accept>.  Raise a
	SHOULD-level error if @pattern but no @title.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24