/[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.108 - (hide annotations) (download)
Sun Aug 17 05:09:12 2008 UTC (16 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.107: +6 -6 lines
++ whatpm/Whatpm/ChangeLog	17 Aug 2008 05:06:46 -0000
2008-08-17  Wakaba  <wakaba@suika.fam.cx>

	* H2H.pm (_shift_token): Support for unquoted HTML attribute
	values.

++ whatpm/Whatpm/ContentChecker/ChangeLog	17 Aug 2008 05:08:51 -0000
2008-08-17  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm (%XHTML2CommonAttrStatus): HTML5 status was missing.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24