/[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.159 - (hide annotations) (download)
Sat Dec 6 12:33:58 2008 UTC (16 years, 7 months ago) by wakaba
Branch: MAIN
Changes since 1.158: +9 -0 lines
++ whatpm/t/dom-conformance/ChangeLog	6 Dec 2008 12:33:50 -0000
	* html-form-input-1.dat: Added test data for <input multiple> and
	<input placeholder>.

2008-12-06  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	6 Dec 2008 12:33:04 -0000
	* HTML.pm: Implemented <input type=file multiple> and <input
	placeholder>.

2008-12-06  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24