/[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.106 - (hide annotations) (download)
Fri Aug 15 16:02:02 2008 UTC (16 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.105: +6 -6 lines
++ whatpm/Whatpm/ChangeLog	15 Aug 2008 16:01:09 -0000
2008-08-16  Wakaba  <wakaba@suika.fam.cx>

	* ContentChecker.pm, RDFXML.pm: Pass {level} object to language tag
	and URL checkers.  Support for more error levels for bogus
	langauge tag and URL "standards".

	* LangTag.pm, URIChecker.pm: Support for new style error
	level reporting.

++ whatpm/Whatpm/ContentChecker/ChangeLog	15 Aug 2008 16:01:45 -0000
2008-08-16  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm, Atom.pm: Pass {level} object to language tag
	and URL checkers.

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     class => FEATURE_XHTML2_ED,
1158     id => FEATURE_XHTML2_ED,
1159     #xml:id
1160     layout => FEATURE_XHTML2_ED,
1161     title => FEATURE_XHTML2_ED,
1162    
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     dir => FEATURE_XHTML2_ED,
1179    
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     role => FEATURE_XHTML2_ED,
1210    
1211     ## Style
1212     style => FEATURE_XHTML2_ED, # "strongly discouraged"
1213     );
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.40 },
2127     check_child_element => sub {
2128     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2129     $child_is_transparent, $element_state) = @_;
2130     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2131     $self->{onerror}->(node => $child_el,
2132     type => 'element not allowed:minus',
2133 wakaba 1.104 level => $self->{level}->{must});
2134 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2135     #
2136     } elsif ($element_state->{allow_element}) {
2137     #
2138     } else {
2139 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2140     level => $self->{level}->{must});
2141 wakaba 1.40 }
2142     },
2143     check_child_text => sub {
2144     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2145     $element_state->{text} .= $child_node->text_content;
2146     },
2147     check_end => sub {
2148     my ($self, $item, $element_state) = @_;
2149 wakaba 1.93 if (not defined $element_state->{style_type}) {
2150     ## NOTE: Invalid type=""
2151     #
2152     } elsif ($element_state->{style_type} eq 'text/css') {
2153 wakaba 1.40 $self->{onsubdoc}->({s => $element_state->{text},
2154     container_node => $item->{node},
2155 wakaba 1.28 media_type => 'text/css', is_char_string => 1});
2156 wakaba 1.93 } elsif ($element_state->{style_type} =~ m![+/][Xx][Mm][Ll]\z!) {
2157     ## NOTE: XML content should be checked by THIS instance of checker
2158     ## as part of normal tree validation. However, we don't know of any
2159     ## XML-based styling language that can be used in HTML <style> element,
2160     ## such that we throw a "style language not supported" error.
2161 wakaba 1.104 $self->{onerror}->(node => $item->{node},
2162     type => 'XML style lang',
2163     text => $element_state->{style_type},
2164     level => $self->{level}->{uncertain});
2165 wakaba 1.93 } else {
2166     ## NOTE: Should we raise some kind of error for,
2167     ## say, <style type="text/plaion">?
2168     $self->{onsubdoc}->({s => $element_state->{text},
2169     container_node => $item->{node},
2170     media_type => $element_state->{style_type},
2171     is_char_string => 1});
2172 wakaba 1.27 }
2173 wakaba 1.40
2174     $HTMLChecker{check_end}->(@_);
2175 wakaba 1.1 },
2176     };
2177 wakaba 1.25 ## ISSUE: Relationship to significant content check?
2178 wakaba 1.1
2179     $Element->{$HTML_NS}->{body} = {
2180 wakaba 1.72 %HTMLFlowContentChecker,
2181 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2182 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2183     alink => $HTMLColorAttrChecker,
2184     background => $HTMLURIAttrChecker,
2185     bgcolor => $HTMLColorAttrChecker,
2186     link => $HTMLColorAttrChecker,
2187     text => $HTMLColorAttrChecker,
2188     vlink => $HTMLColorAttrChecker,
2189     }, {
2190 wakaba 1.49 %HTMLAttrStatus,
2191 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2192 wakaba 1.49 alink => FEATURE_M12N10_REC_DEPRECATED,
2193     background => FEATURE_M12N10_REC_DEPRECATED,
2194     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
2195 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2196 wakaba 1.49 link => FEATURE_M12N10_REC_DEPRECATED,
2197 wakaba 1.50 onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2198     onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2199 wakaba 1.49 text => FEATURE_M12N10_REC_DEPRECATED,
2200     vlink => FEATURE_M12N10_REC_DEPRECATED,
2201     }),
2202 wakaba 1.68 check_start => sub {
2203     my ($self, $item, $element_state) = @_;
2204    
2205     $element_state->{uri_info}->{background}->{type}->{embedded} = 1;
2206 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2207     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2208 wakaba 1.68 },
2209 wakaba 1.1 };
2210    
2211     $Element->{$HTML_NS}->{section} = {
2212 wakaba 1.72 %HTMLFlowContentChecker,
2213 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2214     check_attrs => $GetHTMLAttrsChecker->({
2215     }, {
2216     %HTMLAttrStatus,
2217     %XHTML2CommonAttrStatus,
2218     }),
2219 wakaba 1.1 };
2220    
2221     $Element->{$HTML_NS}->{nav} = {
2222 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2223 wakaba 1.72 %HTMLFlowContentChecker,
2224 wakaba 1.1 };
2225    
2226     $Element->{$HTML_NS}->{article} = {
2227 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2228 wakaba 1.72 %HTMLFlowContentChecker,
2229 wakaba 1.1 };
2230    
2231     $Element->{$HTML_NS}->{blockquote} = {
2232 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2233 wakaba 1.72 %HTMLFlowContentChecker,
2234 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2235 wakaba 1.1 cite => $HTMLURIAttrChecker,
2236 wakaba 1.49 }, {
2237     %HTMLAttrStatus,
2238 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2239 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2240 wakaba 1.82 cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2241 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2242 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2243 wakaba 1.1 }),
2244 wakaba 1.66 check_start => sub {
2245     my ($self, $item, $element_state) = @_;
2246    
2247     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2248 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2249     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2250 wakaba 1.66 },
2251 wakaba 1.1 };
2252    
2253     $Element->{$HTML_NS}->{aside} = {
2254 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2255 wakaba 1.72 %HTMLFlowContentChecker,
2256 wakaba 1.1 };
2257    
2258     $Element->{$HTML_NS}->{h1} = {
2259 wakaba 1.40 %HTMLPhrasingContentChecker,
2260 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2261 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2262     align => $GetHTMLEnumeratedAttrChecker->({
2263     left => 1, center => 1, right => 1, justify => 1,
2264     }),
2265     }, {
2266 wakaba 1.49 %HTMLAttrStatus,
2267 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2268 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2269 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2270 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2271 wakaba 1.49 }),
2272 wakaba 1.40 check_start => sub {
2273     my ($self, $item, $element_state) = @_;
2274     $self->{flag}->{has_hn} = 1;
2275 wakaba 1.79
2276     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2277     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2278 wakaba 1.1 },
2279     };
2280    
2281 wakaba 1.40 $Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}};
2282 wakaba 1.1
2283 wakaba 1.40 $Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}};
2284 wakaba 1.1
2285 wakaba 1.40 $Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}};
2286 wakaba 1.1
2287 wakaba 1.40 $Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}};
2288 wakaba 1.1
2289 wakaba 1.40 $Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}};
2290 wakaba 1.1
2291 wakaba 1.29 ## TODO: Explicit sectioning is "encouraged".
2292    
2293 wakaba 1.1 $Element->{$HTML_NS}->{header} = {
2294 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2295 wakaba 1.72 %HTMLFlowContentChecker,
2296 wakaba 1.40 check_start => sub {
2297     my ($self, $item, $element_state) = @_;
2298     $self->_add_minus_elements ($element_state,
2299     {$HTML_NS => {qw/header 1 footer 1/}},
2300 wakaba 1.58 $HTMLSectioningContent);
2301 wakaba 1.40 $element_state->{has_hn_original} = $self->{flag}->{has_hn};
2302     $self->{flag}->{has_hn} = 0;
2303 wakaba 1.79
2304     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2305     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2306 wakaba 1.40 },
2307     check_end => sub {
2308     my ($self, $item, $element_state) = @_;
2309     $self->_remove_minus_elements ($element_state);
2310     unless ($self->{flag}->{has_hn}) {
2311     $self->{onerror}->(node => $item->{node},
2312 wakaba 1.104 type => 'element missing:hn',
2313     level => $self->{level}->{must});
2314 wakaba 1.40 }
2315     $self->{flag}->{has_hn} ||= $element_state->{has_hn_original};
2316 wakaba 1.1
2317 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2318 wakaba 1.1 },
2319 wakaba 1.40 ## ISSUE: <header><del><h1>...</h1></del></header> is conforming?
2320 wakaba 1.1 };
2321    
2322     $Element->{$HTML_NS}->{footer} = {
2323 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2324 wakaba 1.72 %HTMLFlowContentChecker,
2325 wakaba 1.40 check_start => sub {
2326     my ($self, $item, $element_state) = @_;
2327     $self->_add_minus_elements ($element_state,
2328     {$HTML_NS => {footer => 1}},
2329 wakaba 1.58 $HTMLSectioningContent,
2330 wakaba 1.57 $HTMLHeadingContent);
2331 wakaba 1.79
2332     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2333     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2334 wakaba 1.40 },
2335     check_end => sub {
2336     my ($self, $item, $element_state) = @_;
2337     $self->_remove_minus_elements ($element_state);
2338 wakaba 1.1
2339 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2340 wakaba 1.1 },
2341     };
2342    
2343     $Element->{$HTML_NS}->{address} = {
2344 wakaba 1.72 %HTMLFlowContentChecker,
2345 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2346 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2347     %HTMLAttrStatus,
2348 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2349 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2350 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2351 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2352     sdapref => FEATURE_HTML20_RFC,
2353 wakaba 1.49 }),
2354 wakaba 1.40 check_start => sub {
2355     my ($self, $item, $element_state) = @_;
2356     $self->_add_minus_elements ($element_state,
2357     {$HTML_NS => {footer => 1, address => 1}},
2358     $HTMLSectioningContent, $HTMLHeadingContent);
2359 wakaba 1.79
2360     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2361     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2362 wakaba 1.40 },
2363     check_end => sub {
2364     my ($self, $item, $element_state) = @_;
2365     $self->_remove_minus_elements ($element_state);
2366 wakaba 1.29
2367 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2368 wakaba 1.29 },
2369 wakaba 1.1 };
2370    
2371     $Element->{$HTML_NS}->{p} = {
2372 wakaba 1.40 %HTMLPhrasingContentChecker,
2373 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2374 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2375     align => $GetHTMLEnumeratedAttrChecker->({
2376     left => 1, center => 1, right => 1, justify => 1,
2377     }),
2378     }, {
2379 wakaba 1.49 %HTMLAttrStatus,
2380 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2381 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2382 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2383 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2384 wakaba 1.49 }),
2385 wakaba 1.1 };
2386    
2387     $Element->{$HTML_NS}->{hr} = {
2388 wakaba 1.40 %HTMLEmptyChecker,
2389 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2390 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
2391     ## TODO: HTML4 |align|, |noshade|, |size|, |width|
2392     }, {
2393 wakaba 1.49 %HTMLAttrStatus,
2394     %HTMLM12NCommonAttrStatus,
2395     align => FEATURE_M12N10_REC_DEPRECATED,
2396 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2397 wakaba 1.49 noshade => FEATURE_M12N10_REC_DEPRECATED,
2398 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2399 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
2400     width => FEATURE_M12N10_REC_DEPRECATED,
2401     }),
2402 wakaba 1.1 };
2403    
2404     $Element->{$HTML_NS}->{br} = {
2405 wakaba 1.40 %HTMLEmptyChecker,
2406 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2407 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2408     clear => $GetHTMLEnumeratedAttrChecker->({
2409     left => 1, all => 1, right => 1, none => 1,
2410     }),
2411     }, {
2412 wakaba 1.49 %HTMLAttrStatus,
2413 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2414 wakaba 1.49 clear => FEATURE_M12N10_REC_DEPRECATED,
2415 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2416 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2417 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2418 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2419 wakaba 1.49 }),
2420 wakaba 1.29 ## NOTE: Blank line MUST NOT be used for presentation purpose.
2421     ## (This requirement is semantic so that we cannot check.)
2422 wakaba 1.1 };
2423    
2424     $Element->{$HTML_NS}->{dialog} = {
2425 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2426 wakaba 1.40 %HTMLChecker,
2427     check_start => sub {
2428     my ($self, $item, $element_state) = @_;
2429     $element_state->{phase} = 'before dt';
2430 wakaba 1.79
2431     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2432     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2433 wakaba 1.40 },
2434     check_child_element => sub {
2435     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2436     $child_is_transparent, $element_state) = @_;
2437     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2438     $self->{onerror}->(node => $child_el,
2439     type => 'element not allowed:minus',
2440 wakaba 1.104 level => $self->{level}->{must});
2441 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2442     #
2443     } elsif ($element_state->{phase} eq 'before dt') {
2444     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2445     $element_state->{phase} = 'before dd';
2446     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2447     $self->{onerror}
2448 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2449     text => 'dt',
2450     level => $self->{level}->{must});
2451 wakaba 1.40 $element_state->{phase} = 'before dt';
2452     } else {
2453 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2454     level => $self->{level}->{must});
2455 wakaba 1.40 }
2456     } elsif ($element_state->{phase} eq 'before dd') {
2457     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2458     $element_state->{phase} = 'before dt';
2459     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2460     $self->{onerror}
2461 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2462     text => 'dd',
2463     level => $self->{level}->{must});
2464 wakaba 1.40 $element_state->{phase} = 'before dd';
2465     } else {
2466 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2467     level => $self->{level}->{must});
2468 wakaba 1.1 }
2469 wakaba 1.40 } else {
2470     die "check_child_element: Bad |dialog| phase: $element_state->{phase}";
2471     }
2472     },
2473     check_child_text => sub {
2474     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2475     if ($has_significant) {
2476 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2477     level => $self->{level}->{must});
2478 wakaba 1.1 }
2479 wakaba 1.40 },
2480     check_end => sub {
2481     my ($self, $item, $element_state) = @_;
2482     if ($element_state->{phase} eq 'before dd') {
2483     $self->{onerror}->(node => $item->{node},
2484 wakaba 1.104 type => 'child element missing',
2485     text => 'dd',
2486     level => $self->{level}->{must});
2487 wakaba 1.1 }
2488 wakaba 1.40
2489     $HTMLChecker{check_end}->(@_);
2490 wakaba 1.1 },
2491     };
2492    
2493     $Element->{$HTML_NS}->{pre} = {
2494 wakaba 1.40 %HTMLPhrasingContentChecker,
2495 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2496 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2497     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2498     }, {
2499 wakaba 1.49 %HTMLAttrStatus,
2500 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2501 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2502 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2503 wakaba 1.49 width => FEATURE_M12N10_REC_DEPRECATED,
2504     }),
2505 wakaba 1.101 check_end => sub {
2506     my ($self, $item, $element_state) = @_;
2507    
2508     ## TODO: Flag to enable/disable IDL checking?
2509     my $class = $item->{node}->get_attribute ('class');
2510 wakaba 1.102 if ($class =~ /\bidl(?>-code)?\b/) { ## TODO: use classList.has
2511     ## NOTE: pre.idl: WHATWG, XHR, Selectors API, CSSOM specs
2512     ## NOTE: pre.code > code.idl-code: WebIDL spec
2513     ## NOTE: pre.idl-code: DOM1 spec
2514     ## NOTE: div.idl-code > pre: DOM, ProgressEvent specs
2515     ## NOTE: pre.schema: ReSpec-generated specs
2516 wakaba 1.101 $self->{onsubdoc}->({s => $item->{node}->text_content,
2517     container_node => $item->{node},
2518     media_type => 'text/x-webidl',
2519     is_char_string => 1});
2520     }
2521    
2522     $HTMLChecker{check_end}->(@_);
2523     },
2524 wakaba 1.1 };
2525    
2526     $Element->{$HTML_NS}->{ol} = {
2527 wakaba 1.40 %HTMLChecker,
2528 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2529 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2530 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
2531 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
2532 wakaba 1.1 start => $HTMLIntegerAttrChecker,
2533 wakaba 1.69 ## TODO: HTML4 |type|
2534 wakaba 1.49 }, {
2535     %HTMLAttrStatus,
2536 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2537 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2538 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2539 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2540 wakaba 1.53 reversed => FEATURE_HTML5_DEFAULT,
2541 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2542 wakaba 1.54 #start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
2543     start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2544 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2545 wakaba 1.1 }),
2546 wakaba 1.40 check_child_element => sub {
2547     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2548     $child_is_transparent, $element_state) = @_;
2549     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2550     $self->{onerror}->(node => $child_el,
2551     type => 'element not allowed:minus',
2552 wakaba 1.104 level => $self->{level}->{must});
2553 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2554     #
2555     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
2556     #
2557     } else {
2558 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2559     level => $self->{level}->{must});
2560 wakaba 1.1 }
2561 wakaba 1.40 },
2562     check_child_text => sub {
2563     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2564     if ($has_significant) {
2565 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2566     level => $self->{level}->{must});
2567 wakaba 1.1 }
2568     },
2569     };
2570    
2571     $Element->{$HTML_NS}->{ul} = {
2572 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
2573 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2574 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2575     compact => $GetHTMLBooleanAttrChecker->('compact'),
2576 wakaba 1.69 ## TODO: HTML4 |type|
2577     ## TODO: sdaform, align
2578 wakaba 1.68 }, {
2579 wakaba 1.49 %HTMLAttrStatus,
2580 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2581 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2582 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2583 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2584 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2585 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2586     }),
2587 wakaba 1.1 };
2588    
2589 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
2590     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
2591     %{$Element->{$HTML_NS}->{ul}},
2592     status => FEATURE_M12N10_REC_DEPRECATED,
2593 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2594     compact => $GetHTMLBooleanAttrChecker->('compact'),
2595     }, {
2596 wakaba 1.64 %HTMLAttrStatus,
2597     %HTMLM12NCommonAttrStatus,
2598     align => FEATURE_HTML2X_RFC,
2599     compact => FEATURE_M12N10_REC_DEPRECATED,
2600     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2601     sdaform => FEATURE_HTML20_RFC,
2602     sdapref => FEATURE_HTML20_RFC,
2603     }),
2604     };
2605    
2606 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
2607 wakaba 1.72 %HTMLFlowContentChecker,
2608 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2609 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2610 wakaba 1.69 ## TODO: HTML4 |type|
2611 wakaba 1.49 value => sub {
2612 wakaba 1.1 my ($self, $attr) = @_;
2613     my $parent = $attr->owner_element->manakai_parent_element;
2614     if (defined $parent) {
2615     my $parent_ns = $parent->namespace_uri;
2616     $parent_ns = '' unless defined $parent_ns;
2617     my $parent_ln = $parent->manakai_local_name;
2618     unless ($parent_ns eq $HTML_NS and $parent_ln eq 'ol') {
2619 wakaba 1.104 $self->{onerror}->(node => $attr,
2620     type => 'unknown attribute',
2621     level => $self->{level}->{uncertain});
2622 wakaba 1.1 }
2623     }
2624     $HTMLIntegerAttrChecker->($self, $attr);
2625 wakaba 1.49 }, ## TODO: test
2626     }, {
2627     %HTMLAttrStatus,
2628 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2629 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2630 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2631 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2632 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2633 wakaba 1.55 #value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2634     # FEATURE_M12N10_REC_DEPRECATED,
2635 wakaba 1.82 value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
2636     FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2637 wakaba 1.1 }),
2638 wakaba 1.40 check_child_element => sub {
2639     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2640     $child_is_transparent, $element_state) = @_;
2641     if ($self->{flag}->{in_menu}) {
2642     $HTMLPhrasingContentChecker{check_child_element}->(@_);
2643     } else {
2644 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2645 wakaba 1.40 }
2646     },
2647     check_child_text => sub {
2648     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2649     if ($self->{flag}->{in_menu}) {
2650     $HTMLPhrasingContentChecker{check_child_text}->(@_);
2651 wakaba 1.1 } else {
2652 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2653 wakaba 1.1 }
2654     },
2655     };
2656    
2657     $Element->{$HTML_NS}->{dl} = {
2658 wakaba 1.40 %HTMLChecker,
2659 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2660 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2661     compact => $GetHTMLBooleanAttrChecker->('compact'),
2662     }, {
2663 wakaba 1.49 %HTMLAttrStatus,
2664 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2665 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2666 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2667 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2668     sdapref => FEATURE_HTML20_RFC,
2669 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2670     }),
2671 wakaba 1.40 check_start => sub {
2672     my ($self, $item, $element_state) = @_;
2673     $element_state->{phase} = 'before dt';
2674 wakaba 1.79
2675     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2676     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2677 wakaba 1.40 },
2678     check_child_element => sub {
2679     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2680     $child_is_transparent, $element_state) = @_;
2681     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2682     $self->{onerror}->(node => $child_el,
2683     type => 'element not allowed:minus',
2684 wakaba 1.104 level => $self->{level}->{must});
2685 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2686     #
2687     } elsif ($element_state->{phase} eq 'in dds') {
2688     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2689     #$element_state->{phase} = 'in dds';
2690     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2691     $element_state->{phase} = 'in dts';
2692     } else {
2693 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2694     level => $self->{level}->{must});
2695 wakaba 1.40 }
2696     } elsif ($element_state->{phase} eq 'in dts') {
2697     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2698     #$element_state->{phase} = 'in dts';
2699     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2700     $element_state->{phase} = 'in dds';
2701     } else {
2702 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2703     level => $self->{level}->{must});
2704 wakaba 1.40 }
2705     } elsif ($element_state->{phase} eq 'before dt') {
2706     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2707     $element_state->{phase} = 'in dts';
2708     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2709     $self->{onerror}
2710 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2711     text => 'dt',
2712     level => $self->{level}->{must});
2713 wakaba 1.40 $element_state->{phase} = 'in dds';
2714     } else {
2715 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2716     level => $self->{level}->{must});
2717 wakaba 1.1 }
2718 wakaba 1.40 } else {
2719     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
2720 wakaba 1.1 }
2721 wakaba 1.40 },
2722     check_child_text => sub {
2723     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2724     if ($has_significant) {
2725 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2726     level => $self->{level}->{must});
2727 wakaba 1.40 }
2728     },
2729     check_end => sub {
2730     my ($self, $item, $element_state) = @_;
2731     if ($element_state->{phase} eq 'in dts') {
2732     $self->{onerror}->(node => $item->{node},
2733 wakaba 1.104 type => 'child element missing',
2734     text => 'dd',
2735     level => $self->{level}->{must});
2736 wakaba 1.1 }
2737    
2738 wakaba 1.40 $HTMLChecker{check_end}->(@_);
2739 wakaba 1.1 },
2740     };
2741    
2742     $Element->{$HTML_NS}->{dt} = {
2743 wakaba 1.40 %HTMLPhrasingContentChecker,
2744 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2745 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2746     %HTMLAttrStatus,
2747 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2748 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2749 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2750 wakaba 1.49 }),
2751 wakaba 1.1 };
2752    
2753     $Element->{$HTML_NS}->{dd} = {
2754 wakaba 1.72 %HTMLFlowContentChecker,
2755 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2756 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2757     %HTMLAttrStatus,
2758 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2759 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2760 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2761 wakaba 1.49 }),
2762 wakaba 1.1 };
2763    
2764     $Element->{$HTML_NS}->{a} = {
2765 wakaba 1.40 %HTMLPhrasingContentChecker,
2766 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2767 wakaba 1.40 check_attrs => sub {
2768     my ($self, $item, $element_state) = @_;
2769 wakaba 1.1 my %attr;
2770 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2771 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2772     $attr_ns = '' unless defined $attr_ns;
2773     my $attr_ln = $attr->manakai_local_name;
2774     my $checker;
2775 wakaba 1.73 my $status;
2776 wakaba 1.1 if ($attr_ns eq '') {
2777 wakaba 1.73 $status = {
2778     %HTMLAttrStatus,
2779 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2780 wakaba 1.73 accesskey => FEATURE_M12N10_REC,
2781     charset => FEATURE_M12N10_REC,
2782 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2783 wakaba 1.73 cryptopts => FEATURE_RFC2659,
2784     dn => FEATURE_RFC2659,
2785 wakaba 1.99 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2786 wakaba 1.82 hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2787 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2788 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2789 wakaba 1.73 methods => FEATURE_HTML20_RFC,
2790     name => FEATURE_M12N10_REC_DEPRECATED,
2791     nonce => FEATURE_RFC2659,
2792     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2793     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2794     ping => FEATURE_HTML5_DEFAULT,
2795 wakaba 1.99 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2796     rev => FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2797 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
2798 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2799 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2800 wakaba 1.82 target => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2801 wakaba 1.73 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2802     urn => FEATURE_HTML20_RFC,
2803     }->{$attr_ln};
2804    
2805 wakaba 1.1 $checker = {
2806 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
2807 wakaba 1.91 charset => sub {
2808     my ($self, $attr) = @_;
2809     $HTMLCharsetChecker->($attr->value, @_);
2810     },
2811 wakaba 1.70 ## TODO: HTML4 |coords|
2812 wakaba 1.1 target => $HTMLTargetAttrChecker,
2813     href => $HTMLURIAttrChecker,
2814     ping => $HTMLSpaceURIsAttrChecker,
2815 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
2816 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
2817 wakaba 1.70 ## TODO: HTML4 |shape|
2818 wakaba 1.1 media => $HTMLMQAttrChecker,
2819 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
2820 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
2821     type => $HTMLIMTAttrChecker,
2822     }->{$attr_ln};
2823     if ($checker) {
2824     $attr{$attr_ln} = $attr;
2825 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
2826 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
2827     $status = $HTMLDatasetAttrStatus;
2828 wakaba 1.1 } else {
2829     $checker = $HTMLAttrChecker->{$attr_ln};
2830     }
2831     }
2832     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2833     || $AttrChecker->{$attr_ns}->{''};
2834 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
2835     || $AttrStatus->{$attr_ns}->{''};
2836     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
2837 wakaba 1.62
2838 wakaba 1.1 if ($checker) {
2839 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2840 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2841 wakaba 1.54 #
2842 wakaba 1.1 } else {
2843 wakaba 1.104 $self->{onerror}->(node => $attr,
2844     type => 'unknown attribute',
2845     level => $self->{level}->{uncertain});
2846 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
2847 wakaba 1.1 }
2848 wakaba 1.49
2849 wakaba 1.82 $self->_attr_status_info ($attr, $status);
2850 wakaba 1.1 }
2851    
2852 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
2853 wakaba 1.4 if (defined $attr{href}) {
2854     $self->{has_hyperlink_element} = 1;
2855 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
2856 wakaba 1.4 } else {
2857 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
2858     if (defined $attr{$_}) {
2859     $self->{onerror}->(node => $attr{$_},
2860 wakaba 1.104 type => 'attribute not allowed',
2861     level => $self->{level}->{must});
2862 wakaba 1.1 }
2863     }
2864     }
2865 wakaba 1.66
2866     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
2867 wakaba 1.1 },
2868 wakaba 1.40 check_start => sub {
2869     my ($self, $item, $element_state) = @_;
2870     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
2871 wakaba 1.79
2872     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2873     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2874 wakaba 1.40 },
2875     check_end => sub {
2876     my ($self, $item, $element_state) = @_;
2877     $self->_remove_minus_elements ($element_state);
2878 wakaba 1.59 delete $self->{flag}->{in_a_href}
2879     unless $element_state->{in_a_href_original};
2880 wakaba 1.1
2881 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2882 wakaba 1.1 },
2883     };
2884    
2885     $Element->{$HTML_NS}->{q} = {
2886 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2887 wakaba 1.40 %HTMLPhrasingContentChecker,
2888     check_attrs => $GetHTMLAttrsChecker->({
2889 wakaba 1.50 cite => $HTMLURIAttrChecker,
2890     }, {
2891 wakaba 1.49 %HTMLAttrStatus,
2892 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2893     cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2894 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2895 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2896     sdasuff => FEATURE_HTML2X_RFC,
2897 wakaba 1.1 }),
2898 wakaba 1.66 check_start => sub {
2899     my ($self, $item, $element_state) = @_;
2900    
2901     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2902 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2903     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2904 wakaba 1.66 },
2905 wakaba 1.1 };
2906 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
2907     ## placed inside the <code>q</code> element." Though we cannot test the
2908     ## element against this requirement since it incluides a semantic bit,
2909     ## it might be possible to inform of the existence of quotation marks OUTSIDE
2910     ## the |q| element.
2911 wakaba 1.1
2912     $Element->{$HTML_NS}->{cite} = {
2913 wakaba 1.40 %HTMLPhrasingContentChecker,
2914 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2915 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2916     %HTMLAttrStatus,
2917 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2918 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2919 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2920 wakaba 1.49 }),
2921 wakaba 1.1 };
2922    
2923     $Element->{$HTML_NS}->{em} = {
2924 wakaba 1.40 %HTMLPhrasingContentChecker,
2925 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2926 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2927     %HTMLAttrStatus,
2928 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2929 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2930 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2931 wakaba 1.49 }),
2932 wakaba 1.1 };
2933    
2934     $Element->{$HTML_NS}->{strong} = {
2935 wakaba 1.40 %HTMLPhrasingContentChecker,
2936 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2937 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2938     %HTMLAttrStatus,
2939 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2940 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2941 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2942 wakaba 1.49 }),
2943 wakaba 1.1 };
2944    
2945     $Element->{$HTML_NS}->{small} = {
2946 wakaba 1.40 %HTMLPhrasingContentChecker,
2947 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2948     check_attrs => $GetHTMLAttrsChecker->({}, {
2949     %HTMLAttrStatus,
2950     %HTMLM12NCommonAttrStatus,
2951 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2952 wakaba 1.49 }),
2953 wakaba 1.1 };
2954    
2955 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
2956     %HTMLPhrasingContentChecker,
2957     status => FEATURE_M12N10_REC,
2958     check_attrs => $GetHTMLAttrsChecker->({}, {
2959     %HTMLAttrStatus,
2960     %HTMLM12NCommonAttrStatus,
2961     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2962     }),
2963     };
2964    
2965 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
2966 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2967 wakaba 1.40 %HTMLPhrasingContentChecker,
2968 wakaba 1.1 };
2969    
2970     $Element->{$HTML_NS}->{dfn} = {
2971 wakaba 1.40 %HTMLPhrasingContentChecker,
2972 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2973 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2974     %HTMLAttrStatus,
2975 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2976 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2977 wakaba 1.49 }),
2978 wakaba 1.40 check_start => sub {
2979     my ($self, $item, $element_state) = @_;
2980     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
2981 wakaba 1.1
2982 wakaba 1.40 my $node = $item->{node};
2983 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
2984     unless (defined $term) {
2985     for my $child (@{$node->child_nodes}) {
2986     if ($child->node_type == 1) { # ELEMENT_NODE
2987     if (defined $term) {
2988     undef $term;
2989     last;
2990     } elsif ($child->manakai_local_name eq 'abbr') {
2991     my $nsuri = $child->namespace_uri;
2992     if (defined $nsuri and $nsuri eq $HTML_NS) {
2993     my $attr = $child->get_attribute_node_ns (undef, 'title');
2994     if ($attr) {
2995     $term = $attr->value;
2996     }
2997     }
2998     }
2999     } elsif ($child->node_type == 3 or $child->node_type == 4) {
3000     ## TEXT_NODE or CDATA_SECTION_NODE
3001     if ($child->data =~ /\A[\x09-\x0D\x20]+\z/) { # Inter-element whitespace
3002     next;
3003     }
3004     undef $term;
3005     last;
3006     }
3007     }
3008     unless (defined $term) {
3009     $term = $node->text_content;
3010     }
3011     }
3012     if ($self->{term}->{$term}) {
3013     push @{$self->{term}->{$term}}, $node;
3014     } else {
3015     $self->{term}->{$term} = [$node];
3016     }
3017 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
3018     ## |ruby| unless |dfn| has |title|.
3019 wakaba 1.79
3020     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3021     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3022 wakaba 1.40 },
3023     check_end => sub {
3024     my ($self, $item, $element_state) = @_;
3025     $self->_remove_minus_elements ($element_state);
3026 wakaba 1.1
3027 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3028 wakaba 1.1 },
3029     };
3030    
3031     $Element->{$HTML_NS}->{abbr} = {
3032 wakaba 1.40 %HTMLPhrasingContentChecker,
3033 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3034 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3035     %HTMLAttrStatus,
3036 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3037     full => FEATURE_XHTML2_ED,
3038 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3039 wakaba 1.49 }),
3040 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
3041     ## number (plural vs singular) must match the grammatical number of the
3042     ## contents of the element." Though this can be checked by machine,
3043     ## it requires language-specific knowledge and dictionary, such that
3044     ## we don't support the check of the requirement.
3045     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
3046 wakaba 1.49 };
3047    
3048     $Element->{$HTML_NS}->{acronym} = {
3049     %HTMLPhrasingContentChecker,
3050     status => FEATURE_M12N10_REC,
3051     check_attrs => $GetHTMLAttrsChecker->({}, {
3052     %HTMLAttrStatus,
3053     %HTMLM12NCommonAttrStatus,
3054 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3055 wakaba 1.49 }),
3056 wakaba 1.1 };
3057    
3058     $Element->{$HTML_NS}->{time} = {
3059 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3060 wakaba 1.40 %HTMLPhrasingContentChecker,
3061     check_attrs => $GetHTMLAttrsChecker->({
3062 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
3063 wakaba 1.49 }, {
3064     %HTMLAttrStatus,
3065     %HTMLM12NCommonAttrStatus,
3066 wakaba 1.72 datetime => FEATURE_HTML5_FD,
3067 wakaba 1.1 }),
3068     ## TODO: Write tests
3069 wakaba 1.40 check_end => sub {
3070     my ($self, $item, $element_state) = @_;
3071 wakaba 1.1
3072 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
3073 wakaba 1.1 my $input;
3074     my $reg_sp;
3075     my $input_node;
3076     if ($attr) {
3077     $input = $attr->value;
3078     $reg_sp = qr/[\x09-\x0D\x20]*/;
3079     $input_node = $attr;
3080     } else {
3081 wakaba 1.40 $input = $item->{node}->text_content;
3082 wakaba 1.1 $reg_sp = qr/\p{Zs}*/;
3083 wakaba 1.40 $input_node = $item->{node};
3084 wakaba 1.1
3085     ## ISSUE: What is the definition for "successfully extracts a date
3086     ## or time"? If the algorithm says the string is invalid but
3087     ## return some date or time, is it "successfully"?
3088     }
3089    
3090     my $hour;
3091     my $minute;
3092     my $second;
3093     if ($input =~ /
3094     \A
3095     [\x09-\x0D\x20]*
3096     ([0-9]+) # 1
3097     (?>
3098     -([0-9]+) # 2
3099     -([0-9]+) # 3
3100     [\x09-\x0D\x20]*
3101     (?>
3102     T
3103     [\x09-\x0D\x20]*
3104     )?
3105     ([0-9]+) # 4
3106     :([0-9]+) # 5
3107     (?>
3108     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
3109     )?
3110     [\x09-\x0D\x20]*
3111     (?>
3112     Z
3113     [\x09-\x0D\x20]*
3114     |
3115     [+-]([0-9]+):([0-9]+) # 7, 8
3116     [\x09-\x0D\x20]*
3117     )?
3118     \z
3119     |
3120     :([0-9]+) # 9
3121     (?>
3122     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
3123     )?
3124     [\x09-\x0D\x20]*\z
3125     )
3126     /x) {
3127     if (defined $2) { ## YYYY-MM-DD T? hh:mm
3128     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
3129     length $4 != 2 or length $5 != 2) {
3130     $self->{onerror}->(node => $input_node,
3131 wakaba 1.104 type => 'dateortime:syntax error',
3132     level => $self->{level}->{must});
3133 wakaba 1.1 }
3134    
3135     if (1 <= $2 and $2 <= 12) {
3136 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3137     level => $self->{level}->{must})
3138 wakaba 1.1 if $3 < 1 or
3139     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
3140 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3141     level => $self->{level}->{must})
3142 wakaba 1.1 if $2 == 2 and $3 == 29 and
3143     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
3144     } else {
3145     $self->{onerror}->(node => $input_node,
3146 wakaba 1.104 type => 'datetime:bad month',
3147     level => $self->{level}->{must});
3148 wakaba 1.1 }
3149    
3150     ($hour, $minute, $second) = ($4, $5, $6);
3151    
3152     if (defined $7) { ## [+-]hh:mm
3153     if (length $7 != 2 or length $8 != 2) {
3154     $self->{onerror}->(node => $input_node,
3155 wakaba 1.104 type => 'dateortime:syntax error',
3156     level => $self->{level}->{must});
3157 wakaba 1.1 }
3158    
3159     $self->{onerror}->(node => $input_node,
3160 wakaba 1.104 type => 'datetime:bad timezone hour',
3161     level => $self->{level}->{must})
3162 wakaba 1.1 if $7 > 23;
3163     $self->{onerror}->(node => $input_node,
3164 wakaba 1.104 type => 'datetime:bad timezone minute',
3165     level => $self->{level}->{must})
3166 wakaba 1.1 if $8 > 59;
3167     }
3168     } else { ## hh:mm
3169     if (length $1 != 2 or length $9 != 2) {
3170     $self->{onerror}->(node => $input_node,
3171 wakaba 1.104 type => qq'dateortime:syntax error',
3172     level => $self->{level}->{must});
3173 wakaba 1.1 }
3174    
3175     ($hour, $minute, $second) = ($1, $9, $10);
3176     }
3177    
3178 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad hour',
3179     level => $self->{level}->{must}) if $hour > 23;
3180     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute',
3181     level => $self->{level}->{must}) if $minute > 59;
3182 wakaba 1.1
3183     if (defined $second) { ## s
3184     ## NOTE: Integer part of second don't have to have length of two.
3185    
3186     if (substr ($second, 0, 1) eq '.') {
3187     $self->{onerror}->(node => $input_node,
3188 wakaba 1.104 type => 'dateortime:syntax error',
3189     level => $self->{level}->{must});
3190 wakaba 1.1 }
3191    
3192 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad second',
3193     level => $self->{level}->{must}) if $second >= 60;
3194 wakaba 1.1 }
3195     } else {
3196     $self->{onerror}->(node => $input_node,
3197 wakaba 1.104 type => 'dateortime:syntax error',
3198     level => $self->{level}->{must});
3199 wakaba 1.1 }
3200    
3201 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3202 wakaba 1.1 },
3203     };
3204    
3205     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
3206 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
3207 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3208 wakaba 1.40 %HTMLPhrasingContentChecker,
3209     check_attrs => $GetHTMLAttrsChecker->({
3210 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3211     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3212     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3213     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3214     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3215     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3216 wakaba 1.50 }, {
3217     %HTMLAttrStatus,
3218     high => FEATURE_HTML5_DEFAULT,
3219     low => FEATURE_HTML5_DEFAULT,
3220     max => FEATURE_HTML5_DEFAULT,
3221     min => FEATURE_HTML5_DEFAULT,
3222     optimum => FEATURE_HTML5_DEFAULT,
3223     value => FEATURE_HTML5_DEFAULT,
3224 wakaba 1.1 }),
3225     };
3226    
3227     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3228 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3229 wakaba 1.40 %HTMLPhrasingContentChecker,
3230     check_attrs => $GetHTMLAttrsChecker->({
3231 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3232     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3233 wakaba 1.50 }, {
3234     %HTMLAttrStatus,
3235     max => FEATURE_HTML5_DEFAULT,
3236     value => FEATURE_HTML5_DEFAULT,
3237 wakaba 1.1 }),
3238     };
3239    
3240     $Element->{$HTML_NS}->{code} = {
3241 wakaba 1.40 %HTMLPhrasingContentChecker,
3242 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3243 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3244     %HTMLAttrStatus,
3245 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3246 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3247 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3248 wakaba 1.49 }),
3249 wakaba 1.1 };
3250    
3251     $Element->{$HTML_NS}->{var} = {
3252 wakaba 1.40 %HTMLPhrasingContentChecker,
3253 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3254 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3255     %HTMLAttrStatus,
3256 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3257 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3258 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3259 wakaba 1.49 }),
3260 wakaba 1.1 };
3261    
3262     $Element->{$HTML_NS}->{samp} = {
3263 wakaba 1.40 %HTMLPhrasingContentChecker,
3264 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3265 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3266     %HTMLAttrStatus,
3267 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3268 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3269 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3270 wakaba 1.49 }),
3271 wakaba 1.1 };
3272    
3273     $Element->{$HTML_NS}->{kbd} = {
3274 wakaba 1.40 %HTMLPhrasingContentChecker,
3275 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3276 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3277     %HTMLAttrStatus,
3278 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3279 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3280 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3281 wakaba 1.49 }),
3282 wakaba 1.1 };
3283    
3284     $Element->{$HTML_NS}->{sub} = {
3285 wakaba 1.40 %HTMLPhrasingContentChecker,
3286 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3287 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3288     %HTMLAttrStatus,
3289 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3290 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3291 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3292 wakaba 1.49 }),
3293 wakaba 1.1 };
3294    
3295 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3296 wakaba 1.1
3297     $Element->{$HTML_NS}->{span} = {
3298 wakaba 1.40 %HTMLPhrasingContentChecker,
3299 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3300 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3301     %HTMLAttrStatus,
3302 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3303 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3304     dataformatas => FEATURE_HTML4_REC_RESERVED,
3305     datasrc => FEATURE_HTML4_REC_RESERVED,
3306 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3307 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3308 wakaba 1.49 }),
3309 wakaba 1.1 };
3310    
3311     $Element->{$HTML_NS}->{i} = {
3312 wakaba 1.40 %HTMLPhrasingContentChecker,
3313 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3314     check_attrs => $GetHTMLAttrsChecker->({}, {
3315     %HTMLAttrStatus,
3316     %HTMLM12NCommonAttrStatus,
3317 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3318 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3319 wakaba 1.49 }),
3320 wakaba 1.1 };
3321    
3322 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3323    
3324 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3325     %HTMLPhrasingContentChecker,
3326     status => FEATURE_M12N10_REC,
3327     check_attrs => $GetHTMLAttrsChecker->({}, {
3328     %HTMLAttrStatus,
3329     %HTMLM12NCommonAttrStatus,
3330     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3331     sdaform => FEATURE_HTML20_RFC,
3332     }),
3333     };
3334 wakaba 1.51
3335     $Element->{$HTML_NS}->{s} = {
3336 wakaba 1.40 %HTMLPhrasingContentChecker,
3337 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3338 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3339     %HTMLAttrStatus,
3340     %HTMLM12NCommonAttrStatus,
3341 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3342 wakaba 1.49 }),
3343 wakaba 1.1 };
3344    
3345 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3346    
3347     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3348    
3349 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3350 wakaba 1.40 %HTMLPhrasingContentChecker,
3351 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3352 wakaba 1.40 check_attrs => sub {
3353     my ($self, $item, $element_state) = @_;
3354 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3355     %HTMLAttrStatus,
3356 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3357     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3358     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3359 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3360 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3361     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3362 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3363     sdasuff => FEATURE_HTML2X_RFC,
3364 wakaba 1.49 })->($self, $item, $element_state);
3365 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3366     $self->{onerror}->(node => $item->{node},
3367 wakaba 1.104 type => 'attribute missing',
3368     text => 'dir',
3369     level => $self->{level}->{must});
3370 wakaba 1.1 }
3371     },
3372     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3373     };
3374    
3375 wakaba 1.99 $Element->{$HTML_NS}->{ruby} = {
3376     %HTMLPhrasingContentChecker,
3377     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3378     check_attrs => $GetHTMLAttrsChecker->({}, {
3379     %HTMLAttrStatus,
3380     %HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0
3381     lang => FEATURE_HTML5_DEFAULT,
3382     }),
3383     check_start => sub {
3384     my ($self, $item, $element_state) = @_;
3385    
3386     $element_state->{phase} = 'before-rb';
3387     #$element_state->{has_sig}
3388 wakaba 1.100
3389     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3390     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3391 wakaba 1.99 },
3392     ## NOTE: (phrasing, (rt | (rp, rt, rp)))+
3393     check_child_element => sub {
3394     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3395     $child_is_transparent, $element_state) = @_;
3396     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3397     $self->{onerror}->(node => $child_el,
3398     type => 'element not allowed:minus',
3399 wakaba 1.104 level => $self->{level}->{must});
3400 wakaba 1.99 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3401     #
3402     } elsif ($element_state->{phase} eq 'before-rb') {
3403     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3404     $element_state->{phase} = 'in-rb';
3405     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3406     $self->{onerror}->(node => $child_el,
3407 wakaba 1.104 level => $self->{level}->{should},
3408     type => 'no significant content before');
3409 wakaba 1.99 $element_state->{phase} = 'after-rt';
3410     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3411     $self->{onerror}->(node => $child_el,
3412 wakaba 1.104 level => $self->{level}->{should},
3413     type => 'no significant content before');
3414 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3415     } else {
3416     $self->{onerror}->(node => $child_el,
3417 wakaba 1.104 type => 'element not allowed:ruby base',
3418     level => $self->{level}->{must});
3419 wakaba 1.99 $element_state->{phase} = 'in-rb';
3420     }
3421     } elsif ($element_state->{phase} eq 'in-rb') {
3422     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3423     #$element_state->{phase} = 'in-rb';
3424     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3425     unless ($element_state->{has_significant}) {
3426     $self->{onerror}->(node => $child_el,
3427 wakaba 1.104 level => $self->{level}->{should},
3428     type => 'no significant content before');
3429 wakaba 1.99 }
3430     $element_state->{phase} = 'after-rt';
3431     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3432     unless ($element_state->{has_significant}) {
3433     $self->{onerror}->(node => $child_el,
3434 wakaba 1.104 level => $self->{level}->{should},
3435     type => 'no significant content before');
3436 wakaba 1.99 }
3437     $element_state->{phase} = 'after-rp1';
3438     } else {
3439     $self->{onerror}->(node => $child_el,
3440 wakaba 1.104 type => 'element not allowed:ruby base',
3441     level => $self->{level}->{must});
3442 wakaba 1.99 #$element_state->{phase} = 'in-rb';
3443     }
3444     } elsif ($element_state->{phase} eq 'after-rt') {
3445     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3446     if ($element_state->{has_significant}) {
3447     $element_state->{has_sig} = 1;
3448     delete $element_state->{has_significant};
3449     }
3450     $element_state->{phase} = 'in-rb';
3451     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3452     $self->{onerror}->(node => $child_el,
3453 wakaba 1.104 level => $self->{level}->{should},
3454     type => 'no significant content before');
3455 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3456     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3457     $self->{onerror}->(node => $child_el,
3458 wakaba 1.104 level => $self->{level}->{should},
3459     type => 'no significant content before');
3460 wakaba 1.99 #$element_state->{phase} = 'after-rt';
3461     } else {
3462     $self->{onerror}->(node => $child_el,
3463 wakaba 1.104 type => 'element not allowed:ruby base',
3464     level => $self->{level}->{must});
3465 wakaba 1.99 if ($element_state->{has_significant}) {
3466     $element_state->{has_sig} = 1;
3467     delete $element_state->{has_significant};
3468     }
3469     $element_state->{phase} = 'in-rb';
3470     }
3471     } elsif ($element_state->{phase} eq 'after-rp1') {
3472     if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3473     $element_state->{phase} = 'after-rp-rt';
3474     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3475     $self->{onerror}->(node => $child_el,
3476 wakaba 1.104 type => 'ps element missing',
3477     text => 'rt',
3478     level => $self->{level}->{must});
3479 wakaba 1.99 $element_state->{phase} = 'after-rp2';
3480     } else {
3481     $self->{onerror}->(node => $child_el,
3482 wakaba 1.104 type => 'ps element missing',
3483     text => 'rt',
3484     level => $self->{level}->{must});
3485 wakaba 1.99 $self->{onerror}->(node => $child_el,
3486 wakaba 1.104 type => 'ps element missing',
3487     text => 'rp',
3488     level => $self->{level}->{must});
3489 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3490     $self->{onerror}->(node => $child_el,
3491 wakaba 1.104 type => 'element not allowed:ruby base',
3492     level => $self->{level}->{must});
3493 wakaba 1.99 }
3494     if ($element_state->{has_significant}) {
3495     $element_state->{has_sig} = 1;
3496     delete $element_state->{has_significant};
3497     }
3498     $element_state->{phase} = 'in-rb';
3499     }
3500     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3501     if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3502     $element_state->{phase} = 'after-rp2';
3503     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3504     $self->{onerror}->(node => $child_el,
3505 wakaba 1.104 type => 'ps element missing',
3506     text => 'rp',
3507     level => $self->{level}->{must});
3508 wakaba 1.99 $self->{onerror}->(node => $child_el,
3509 wakaba 1.104 level => $self->{level}->{should},
3510     type => 'no significant content before');
3511 wakaba 1.99 $element_state->{phase} = 'after-rt';
3512     } else {
3513     $self->{onerror}->(node => $child_el,
3514 wakaba 1.104 type => 'ps element missing',
3515     text => 'rp',
3516     level => $self->{level}->{must});
3517 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3518     $self->{onerror}->(node => $child_el,
3519 wakaba 1.104 type => 'element not allowed:ruby base',
3520     level => $self->{level}->{must});
3521 wakaba 1.99 }
3522     if ($element_state->{has_significant}) {
3523     $element_state->{has_sig} = 1;
3524     delete $element_state->{has_significant};
3525     }
3526     $element_state->{phase} = 'in-rb';
3527     }
3528     } elsif ($element_state->{phase} eq 'after-rp2') {
3529     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3530     if ($element_state->{has_significant}) {
3531     $element_state->{has_sig} = 1;
3532     delete $element_state->{has_significant};
3533     }
3534     $element_state->{phase} = 'in-rb';
3535     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3536     $self->{onerror}->(node => $child_el,
3537 wakaba 1.104 level => $self->{level}->{should},
3538     type => 'no significant content before');
3539 wakaba 1.99 $element_state->{phase} = 'after-rt';
3540     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3541     $self->{onerror}->(node => $child_el,
3542 wakaba 1.104 level => $self->{level}->{should},
3543     type => 'no significant content before');
3544 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3545     } else {
3546     $self->{onerror}->(node => $child_el,
3547 wakaba 1.104 type => 'element not allowed:ruby base',
3548     level => $self->{level}->{must});
3549 wakaba 1.99 if ($element_state->{has_significant}) {
3550     $element_state->{has_sig} = 1;
3551     delete $element_state->{has_significant};
3552     }
3553     $element_state->{phase} = 'in-rb';
3554     }
3555     } else {
3556     die "check_child_element: Bad |ruby| phase: $element_state->{phase}";
3557     }
3558     },
3559     check_child_text => sub {
3560     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3561     if ($has_significant) {
3562     if ($element_state->{phase} eq 'before-rb') {
3563     $element_state->{phase} = 'in-rb';
3564     } elsif ($element_state->{phase} eq 'in-rb') {
3565     #
3566     } elsif ($element_state->{phase} eq 'after-rt' or
3567     $element_state->{phase} eq 'after-rp2') {
3568     $element_state->{phase} = 'in-rb';
3569     } elsif ($element_state->{phase} eq 'after-rp1') {
3570     $self->{onerror}->(node => $child_node,
3571 wakaba 1.104 type => 'ps element missing',
3572     text => 'rt',
3573     level => $self->{level}->{must});
3574 wakaba 1.99 $self->{onerror}->(node => $child_node,
3575 wakaba 1.104 type => 'ps element missing',
3576     text => 'rp',
3577     level => $self->{level}->{must});
3578 wakaba 1.99 $element_state->{phase} = 'in-rb';
3579     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3580     $self->{onerror}->(node => $child_node,
3581 wakaba 1.104 type => 'ps element missing',
3582     text => 'rp',
3583     level => $self->{level}->{must});
3584 wakaba 1.99 $element_state->{phase} = 'in-rb';
3585     } else {
3586     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3587     }
3588     }
3589     },
3590     check_end => sub {
3591     my ($self, $item, $element_state) = @_;
3592     $self->_remove_minus_elements ($element_state);
3593    
3594     if ($element_state->{phase} eq 'before-rb') {
3595     $self->{onerror}->(node => $item->{node},
3596 wakaba 1.104 level => $self->{level}->{should},
3597 wakaba 1.99 type => 'no significant content');
3598     $self->{onerror}->(node => $item->{node},
3599 wakaba 1.104 type => 'element missing',
3600     text => 'rt',
3601     level => $self->{level}->{must});
3602 wakaba 1.99 } elsif ($element_state->{phase} eq 'in-rb') {
3603     unless ($element_state->{has_significant}) {
3604     $self->{onerror}->(node => $item->{node},
3605 wakaba 1.104 level => $self->{level}->{should},
3606     type => 'no significant content at the end');
3607 wakaba 1.99 }
3608     $self->{onerror}->(node => $item->{node},
3609 wakaba 1.104 type => 'element missing',
3610     text => 'rt',
3611     level => $self->{level}->{must});
3612 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rt' or
3613     $element_state->{phase} eq 'after-rp2') {
3614     #
3615     } elsif ($element_state->{phase} eq 'after-rp1') {
3616     $self->{onerror}->(node => $item->{node},
3617 wakaba 1.104 type => 'element missing',
3618     text => 'rt',
3619     level => $self->{level}->{must});
3620 wakaba 1.99 $self->{onerror}->(node => $item->{node},
3621 wakaba 1.104 type => 'element missing',
3622     text => 'rp',
3623     level => $self->{level}->{must});
3624 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rp-rt') {
3625     $self->{onerror}->(node => $item->{node},
3626 wakaba 1.104 type => 'element missing',
3627     text => 'rp',
3628     level => $self->{level}->{must});
3629 wakaba 1.99 } else {
3630     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3631     }
3632    
3633     ## NOTE: A modified version of |check_end| of %AnyChecker.
3634     if ($element_state->{has_significant} or $element_state->{has_sig}) {
3635     $item->{real_parent_state}->{has_significant} = 1;
3636     }
3637     },
3638     };
3639    
3640     $Element->{$HTML_NS}->{rt} = {
3641     %HTMLPhrasingContentChecker,
3642     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3643     check_attrs => $GetHTMLAttrsChecker->({}, {
3644     %HTMLAttrStatus,
3645     %HTMLM12NXHTML2CommonAttrStatus,
3646     lang => FEATURE_HTML5_DEFAULT,
3647     }),
3648     };
3649    
3650     $Element->{$HTML_NS}->{rp} = {
3651     %HTMLTextChecker,
3652     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3653     check_attrs => $GetHTMLAttrsChecker->({}, {
3654     %HTMLAttrStatus,
3655     %HTMLM12NXHTML2CommonAttrStatus,
3656     lang => FEATURE_HTML5_DEFAULT,
3657     }),
3658 wakaba 1.100 check_start => sub {
3659 wakaba 1.99 my ($self, $item, $element_state) = @_;
3660     $element_state->{text} = '';
3661 wakaba 1.100
3662     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3663     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3664 wakaba 1.99 },
3665     check_child_text => sub {
3666     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3667     if ($has_significant) {
3668     $element_state->{text} .= $child_node->data;
3669     ## NOTE: |<rp> <!---->(</rp>| is allowed.
3670     }
3671     },
3672     check_end => sub {
3673     my ($self, $item, $element_state) = @_;
3674    
3675     my $p_class = ($item->{parent_state} and
3676     $item->{parent_state}->{phase} and
3677     $item->{parent_state}->{phase} eq 'after-rp2')
3678     ? qr/\p{Pe}/ : qr/\p{Ps}/;
3679     if ($element_state->{text} =~ /\A$p_class\z/) {
3680     #=~ /\A[\x09-\x0D\x20]*${p_class}[\x09-\x0D\x20]*\z/) {
3681     #
3682     } else {
3683     $self->{onerror}->(node => $item->{node},
3684 wakaba 1.104 type => 'rp:syntax error',
3685     level => $self->{level}->{must});
3686 wakaba 1.99 }
3687    
3688     $HTMLTextChecker{check_end}->(@_);
3689     },
3690     };
3691    
3692 wakaba 1.29 =pod
3693    
3694     ## TODO:
3695    
3696     +
3697     + <p>Partly because of the confusion described above, authors are
3698     + strongly recommended to always mark up all paragraphs with the
3699     + <code>p</code> element, and to not have any <code>ins</code> or
3700     + <code>del</code> elements that cross across any <span
3701     + title="paragraph">implied paragraphs</span>.</p>
3702     +
3703     (An informative note)
3704    
3705     <p><code>ins</code> elements should not cross <span
3706     + title="paragraph">implied paragraph</span> boundaries.</p>
3707     (normative)
3708    
3709     + <p><code>del</code> elements should not cross <span
3710     + title="paragraph">implied paragraph</span> boundaries.</p>
3711     (normative)
3712    
3713     =cut
3714    
3715 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
3716 wakaba 1.40 %HTMLTransparentChecker,
3717 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3718 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3719 wakaba 1.1 cite => $HTMLURIAttrChecker,
3720     datetime => $HTMLDatetimeAttrChecker,
3721 wakaba 1.49 }, {
3722     %HTMLAttrStatus,
3723     %HTMLM12NCommonAttrStatus,
3724 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3725 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3726 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3727 wakaba 1.1 }),
3728 wakaba 1.66 check_start => sub {
3729     my ($self, $item, $element_state) = @_;
3730    
3731     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3732 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3733     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3734 wakaba 1.66 },
3735 wakaba 1.1 };
3736    
3737     $Element->{$HTML_NS}->{del} = {
3738 wakaba 1.40 %HTMLTransparentChecker,
3739 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3740 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3741 wakaba 1.1 cite => $HTMLURIAttrChecker,
3742     datetime => $HTMLDatetimeAttrChecker,
3743 wakaba 1.49 }, {
3744     %HTMLAttrStatus,
3745     %HTMLM12NCommonAttrStatus,
3746 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3747 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3748 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3749 wakaba 1.1 }),
3750 wakaba 1.40 check_end => sub {
3751     my ($self, $item, $element_state) = @_;
3752     if ($element_state->{has_significant}) {
3753     ## NOTE: Significantness flag does not propagate.
3754     } elsif ($item->{transparent}) {
3755     #
3756     } else {
3757     $self->{onerror}->(node => $item->{node},
3758 wakaba 1.104 level => $self->{level}->{should},
3759 wakaba 1.40 type => 'no significant content');
3760     }
3761 wakaba 1.1 },
3762 wakaba 1.66 check_start => sub {
3763     my ($self, $item, $element_state) = @_;
3764    
3765     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3766 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3767     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3768 wakaba 1.66 },
3769 wakaba 1.1 };
3770    
3771 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
3772 wakaba 1.72 %HTMLFlowContentChecker,
3773 wakaba 1.48 status => FEATURE_HTML5_FD,
3774 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
3775 wakaba 1.41 check_child_element => sub {
3776     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3777     $child_is_transparent, $element_state) = @_;
3778     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3779     $self->{onerror}->(node => $child_el,
3780     type => 'element not allowed:minus',
3781 wakaba 1.104 level => $self->{level}->{must});
3782 wakaba 1.41 $element_state->{has_non_legend} = 1;
3783     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3784     #
3785     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
3786     if ($element_state->{has_legend_at_first}) {
3787     $self->{onerror}->(node => $child_el,
3788     type => 'element not allowed:figure legend',
3789 wakaba 1.104 level => $self->{level}->{must});
3790 wakaba 1.41 } elsif ($element_state->{has_legend}) {
3791     $self->{onerror}->(node => $element_state->{has_legend},
3792     type => 'element not allowed:figure legend',
3793 wakaba 1.104 level => $self->{level}->{must});
3794 wakaba 1.41 $element_state->{has_legend} = $child_el;
3795     } elsif ($element_state->{has_non_legend}) {
3796     $element_state->{has_legend} = $child_el;
3797     } else {
3798     $element_state->{has_legend_at_first} = 1;
3799 wakaba 1.35 }
3800 wakaba 1.41 delete $element_state->{has_non_legend};
3801     } else {
3802 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3803 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
3804 wakaba 1.41 }
3805     },
3806     check_child_text => sub {
3807     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3808     if ($has_significant) {
3809     $element_state->{has_non_legend} = 1;
3810 wakaba 1.35 }
3811 wakaba 1.41 },
3812     check_end => sub {
3813     my ($self, $item, $element_state) = @_;
3814 wakaba 1.35
3815 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
3816     #
3817     } elsif ($element_state->{has_legend}) {
3818     if ($element_state->{has_non_legend}) {
3819     $self->{onerror}->(node => $element_state->{has_legend},
3820 wakaba 1.35 type => 'element not allowed:figure legend',
3821 wakaba 1.104 level => $self->{level}->{must});
3822 wakaba 1.35 }
3823     }
3824 wakaba 1.41
3825 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3826 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
3827 wakaba 1.35 },
3828     };
3829 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
3830 wakaba 1.1
3831 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
3832     my ($self, $attr) = @_;
3833 wakaba 1.104 $self->{onerror}->(node => $attr,
3834     type => 'unknown attribute',
3835     level => $self->{level}->{uncertain});
3836 wakaba 1.92 };
3837    
3838 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
3839 wakaba 1.40 %HTMLEmptyChecker,
3840 wakaba 1.98 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3841 wakaba 1.40 check_attrs => sub {
3842     my ($self, $item, $element_state) = @_;
3843 wakaba 1.1 $GetHTMLAttrsChecker->({
3844 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3845     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3846     }),
3847 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
3848 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3849 wakaba 1.1 src => $HTMLURIAttrChecker,
3850     usemap => $HTMLUsemapAttrChecker,
3851 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3852 wakaba 1.1 ismap => sub {
3853 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
3854     if (not $self->{flag}->{in_a_href}) {
3855 wakaba 1.15 $self->{onerror}->(node => $attr,
3856 wakaba 1.59 type => 'attribute not allowed:ismap',
3857 wakaba 1.104 level => $self->{level}->{must});
3858 wakaba 1.1 }
3859 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
3860 wakaba 1.1 },
3861 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
3862     ## TODO: HTML4 |name|
3863 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3864 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3865 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3866 wakaba 1.49 }, {
3867     %HTMLAttrStatus,
3868 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3869 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
3870 wakaba 1.98 alt => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3871 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
3872 wakaba 1.98 height => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3873 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
3874 wakaba 1.98 ismap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3875 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3876 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3877     name => FEATURE_M12N10_REC_DEPRECATED,
3878 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
3879 wakaba 1.98 src => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3880     usemap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3881 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
3882 wakaba 1.98 width => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3883 wakaba 1.66 })->($self, $item, $element_state);
3884 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
3885     $self->{onerror}->(node => $item->{node},
3886 wakaba 1.104 type => 'attribute missing',
3887     text => 'alt',
3888     level => $self->{level}->{should});
3889 wakaba 1.1 }
3890 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3891     $self->{onerror}->(node => $item->{node},
3892 wakaba 1.104 type => 'attribute missing',
3893     text => 'src',
3894     level => $self->{level}->{must});
3895 wakaba 1.1 }
3896 wakaba 1.66
3897     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3898     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
3899     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
3900     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
3901 wakaba 1.1 },
3902     };
3903    
3904     $Element->{$HTML_NS}->{iframe} = {
3905 wakaba 1.40 %HTMLTextChecker,
3906 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3907     ## NOTE: Not part of M12N10 Strict
3908 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3909 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3910 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3911 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
3912     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
3913     }),
3914     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
3915 wakaba 1.1 src => $HTMLURIAttrChecker,
3916 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3917 wakaba 1.49 }, {
3918     %HTMLAttrStatus,
3919     %HTMLM12NCommonAttrStatus,
3920     align => FEATURE_XHTML10_REC,
3921 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3922 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
3923 wakaba 1.92 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3924 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3925 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3926     marginheight => FEATURE_M12N10_REC,
3927     marginwidth => FEATURE_M12N10_REC,
3928 wakaba 1.76 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
3929     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3930 wakaba 1.92 sandbox => FEATURE_HTML5_DEFAULT,
3931 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
3932 wakaba 1.92 seemless => FEATURE_HTML5_DEFAULT,
3933 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3934     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3935 wakaba 1.92 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3936 wakaba 1.1 }),
3937 wakaba 1.66 check_start => sub {
3938     my ($self, $item, $element_state) = @_;
3939    
3940     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3941 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3942     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3943 wakaba 1.66 },
3944 wakaba 1.40 };
3945    
3946 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
3947 wakaba 1.40 %HTMLEmptyChecker,
3948 wakaba 1.98 status => FEATURE_HTML5_WD,
3949 wakaba 1.40 check_attrs => sub {
3950     my ($self, $item, $element_state) = @_;
3951 wakaba 1.1 my $has_src;
3952 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3953 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3954     $attr_ns = '' unless defined $attr_ns;
3955     my $attr_ln = $attr->manakai_local_name;
3956     my $checker;
3957 wakaba 1.73
3958     my $status = {
3959     %HTMLAttrStatus,
3960     height => FEATURE_HTML5_DEFAULT,
3961 wakaba 1.98 src => FEATURE_HTML5_WD,
3962     type => FEATURE_HTML5_WD,
3963 wakaba 1.73 width => FEATURE_HTML5_DEFAULT,
3964     }->{$attr_ln};
3965    
3966 wakaba 1.1 if ($attr_ns eq '') {
3967     if ($attr_ln eq 'src') {
3968     $checker = $HTMLURIAttrChecker;
3969     $has_src = 1;
3970     } elsif ($attr_ln eq 'type') {
3971     $checker = $HTMLIMTAttrChecker;
3972 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
3973     $checker = $AttrCheckerNotImplemented; ## TODO: because spec does not define them yet.
3974 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
3975 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
3976     $status = $HTMLDatasetAttrStatus;
3977 wakaba 1.1 } else {
3978     $checker = $HTMLAttrChecker->{$attr_ln}
3979     || sub { }; ## NOTE: Any local attribute is ok.
3980 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
3981 wakaba 1.1 }
3982     }
3983     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3984 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
3985     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3986     || $AttrStatus->{$attr_ns}->{''};
3987     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3988 wakaba 1.62
3989 wakaba 1.1 if ($checker) {
3990 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
3991 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3992 wakaba 1.54 #
3993 wakaba 1.1 } else {
3994 wakaba 1.104 $self->{onerror}->(node => $attr,
3995     type => 'unknown attribute',
3996     level => $self->{level}->{uncertain});
3997 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
3998     }
3999    
4000 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4001 wakaba 1.1 }
4002    
4003     unless ($has_src) {
4004 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4005 wakaba 1.104 type => 'attribute missing',
4006     text => 'src',
4007     level => $self->{level}->{must});
4008 wakaba 1.1 }
4009 wakaba 1.66
4010     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4011 wakaba 1.1 },
4012     };
4013    
4014 wakaba 1.49 ## TODO:
4015     ## {applet} FEATURE_M12N10_REC_DEPRECATED
4016     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
4017    
4018 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
4019 wakaba 1.40 %HTMLTransparentChecker,
4020 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4021 wakaba 1.40 check_attrs => sub {
4022     my ($self, $item, $element_state) = @_;
4023 wakaba 1.1 $GetHTMLAttrsChecker->({
4024 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4025     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4026     }),
4027     archive => $HTMLSpaceURIsAttrChecker,
4028     ## TODO: Relative to @codebase
4029     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4030     classid => $HTMLURIAttrChecker,
4031     codebase => $HTMLURIAttrChecker,
4032     codetype => $HTMLIMTAttrChecker,
4033     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
4034 wakaba 1.1 data => $HTMLURIAttrChecker,
4035 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
4036     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
4037     ## [HTML4] but we don't know how to test this.
4038     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4039 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4040 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
4041 wakaba 1.1 type => $HTMLIMTAttrChecker,
4042     usemap => $HTMLUsemapAttrChecker,
4043 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4044 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4045 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4046 wakaba 1.49 }, {
4047     %HTMLAttrStatus,
4048 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4049 wakaba 1.49 align => FEATURE_XHTML10_REC,
4050 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4051 wakaba 1.49 border => FEATURE_XHTML10_REC,
4052     classid => FEATURE_M12N10_REC,
4053     codebase => FEATURE_M12N10_REC,
4054     codetype => FEATURE_M12N10_REC,
4055 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
4056 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4057 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4058     dataformatas => FEATURE_HTML4_REC_RESERVED,
4059     datasrc => FEATURE_HTML4_REC_RESERVED,
4060 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4061 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4062 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
4063 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4064 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4065 wakaba 1.49 standby => FEATURE_M12N10_REC,
4066 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4067     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4068     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4069 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
4070 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4071 wakaba 1.66 })->($self, $item, $element_state);
4072 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
4073     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
4074     $self->{onerror}->(node => $item->{node},
4075 wakaba 1.104 type => 'attribute missing:data|type',
4076     level => $self->{level}->{must});
4077 wakaba 1.1 }
4078     }
4079 wakaba 1.66
4080     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
4081     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
4082     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
4083     ## TODO: archive
4084     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4085 wakaba 1.1 },
4086 wakaba 1.72 ## NOTE: param*, transparent (Flow)
4087 wakaba 1.41 check_child_element => sub {
4088     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4089     $child_is_transparent, $element_state) = @_;
4090     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4091     $self->{onerror}->(node => $child_el,
4092     type => 'element not allowed:minus',
4093 wakaba 1.104 level => $self->{level}->{must});
4094 wakaba 1.41 $element_state->{has_non_legend} = 1;
4095     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4096     #
4097     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
4098     if ($element_state->{has_non_param}) {
4099 wakaba 1.104 $self->{onerror}->(node => $child_el,
4100 wakaba 1.72 type => 'element not allowed:flow',
4101 wakaba 1.104 level => $self->{level}->{must});
4102 wakaba 1.39 }
4103 wakaba 1.41 } else {
4104 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4105 wakaba 1.41 $element_state->{has_non_param} = 1;
4106 wakaba 1.39 }
4107 wakaba 1.25 },
4108 wakaba 1.41 check_child_text => sub {
4109     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4110     if ($has_significant) {
4111     $element_state->{has_non_param} = 1;
4112     }
4113 wakaba 1.42 },
4114     check_end => sub {
4115     my ($self, $item, $element_state) = @_;
4116     if ($element_state->{has_significant}) {
4117 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
4118 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
4119     ## NOTE: Transparent.
4120     } else {
4121     $self->{onerror}->(node => $item->{node},
4122 wakaba 1.104 level => $self->{level}->{should},
4123 wakaba 1.42 type => 'no significant content');
4124     }
4125     },
4126 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
4127 wakaba 1.1 };
4128 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4129     ## What about |<section><object data><style scoped></style>x</object></section>|?
4130     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4131 wakaba 1.1
4132     $Element->{$HTML_NS}->{param} = {
4133 wakaba 1.40 %HTMLEmptyChecker,
4134 wakaba 1.94 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4135 wakaba 1.40 check_attrs => sub {
4136     my ($self, $item, $element_state) = @_;
4137 wakaba 1.1 $GetHTMLAttrsChecker->({
4138     name => sub { },
4139 wakaba 1.70 type => $HTMLIMTAttrChecker,
4140 wakaba 1.1 value => sub { },
4141 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4142     data => 1, ref => 1, object => 1,
4143     }),
4144 wakaba 1.49 }, {
4145     %HTMLAttrStatus,
4146 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4147 wakaba 1.94 name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4148 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4149 wakaba 1.94 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4150 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4151 wakaba 1.66 })->(@_);
4152 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4153     $self->{onerror}->(node => $item->{node},
4154 wakaba 1.104 type => 'attribute missing',
4155     text => 'name',
4156     level => $self->{level}->{must});
4157 wakaba 1.1 }
4158 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4159     $self->{onerror}->(node => $item->{node},
4160 wakaba 1.104 type => 'attribute missing',
4161     text => 'value',
4162     level => $self->{level}->{must});
4163 wakaba 1.1 }
4164     },
4165     };
4166    
4167     $Element->{$HTML_NS}->{video} = {
4168 wakaba 1.40 %HTMLTransparentChecker,
4169 wakaba 1.48 status => FEATURE_HTML5_LC,
4170 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4171 wakaba 1.1 src => $HTMLURIAttrChecker,
4172     ## TODO: start, loopstart, loopend, end
4173     ## ISSUE: they MUST be "value time offset"s. Value?
4174 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4175 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4176     controls => $GetHTMLBooleanAttrChecker->('controls'),
4177 wakaba 1.59 poster => $HTMLURIAttrChecker,
4178 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4179     width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4180 wakaba 1.50 }, {
4181     %HTMLAttrStatus,
4182     autoplay => FEATURE_HTML5_LC,
4183     controls => FEATURE_HTML5_LC,
4184     end => FEATURE_HTML5_LC,
4185     height => FEATURE_HTML5_LC,
4186     loopend => FEATURE_HTML5_LC,
4187     loopstart => FEATURE_HTML5_LC,
4188     playcount => FEATURE_HTML5_LC,
4189     poster => FEATURE_HTML5_LC,
4190     src => FEATURE_HTML5_LC,
4191     start => FEATURE_HTML5_LC,
4192     width => FEATURE_HTML5_LC,
4193 wakaba 1.1 }),
4194 wakaba 1.42 check_start => sub {
4195     my ($self, $item, $element_state) = @_;
4196     $element_state->{allow_source}
4197     = not $item->{node}->has_attribute_ns (undef, 'src');
4198     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4199     ## NOTE: It might be set true by |check_element|.
4200 wakaba 1.66
4201     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4202     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4203 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4204     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4205 wakaba 1.42 },
4206     check_child_element => sub {
4207     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4208     $child_is_transparent, $element_state) = @_;
4209     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4210     $self->{onerror}->(node => $child_el,
4211     type => 'element not allowed:minus',
4212 wakaba 1.104 level => $self->{level}->{must});
4213 wakaba 1.42 delete $element_state->{allow_source};
4214     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4215     #
4216     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4217 wakaba 1.45 unless ($element_state->{allow_source}) {
4218 wakaba 1.104 $self->{onerror}->(node => $child_el,
4219 wakaba 1.72 type => 'element not allowed:flow',
4220 wakaba 1.104 level => $self->{level}->{must});
4221 wakaba 1.42 }
4222 wakaba 1.45 $element_state->{has_source} = 1;
4223 wakaba 1.1 } else {
4224 wakaba 1.42 delete $element_state->{allow_source};
4225 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4226 wakaba 1.42 }
4227     },
4228     check_child_text => sub {
4229     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4230     if ($has_significant) {
4231     delete $element_state->{allow_source};
4232     }
4233 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4234 wakaba 1.42 },
4235     check_end => sub {
4236     my ($self, $item, $element_state) = @_;
4237     if ($element_state->{has_source} == -1) {
4238     $self->{onerror}->(node => $item->{node},
4239 wakaba 1.104 type => 'child element missing',
4240     text => 'source',
4241     level => $self->{level}->{must});
4242 wakaba 1.1 }
4243 wakaba 1.42
4244     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4245 wakaba 1.1 },
4246     };
4247    
4248     $Element->{$HTML_NS}->{audio} = {
4249 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4250 wakaba 1.48 status => FEATURE_HTML5_LC,
4251 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4252     src => $HTMLURIAttrChecker,
4253     ## TODO: start, loopstart, loopend, end
4254     ## ISSUE: they MUST be "value time offset"s. Value?
4255     ## ISSUE: playcount has no conformance creteria
4256     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4257     controls => $GetHTMLBooleanAttrChecker->('controls'),
4258 wakaba 1.50 }, {
4259     %HTMLAttrStatus,
4260     autoplay => FEATURE_HTML5_LC,
4261     controls => FEATURE_HTML5_LC,
4262     end => FEATURE_HTML5_LC,
4263     loopend => FEATURE_HTML5_LC,
4264     loopstart => FEATURE_HTML5_LC,
4265     playcount => FEATURE_HTML5_LC,
4266     src => FEATURE_HTML5_LC,
4267     start => FEATURE_HTML5_LC,
4268 wakaba 1.42 }),
4269 wakaba 1.1 };
4270    
4271     $Element->{$HTML_NS}->{source} = {
4272 wakaba 1.40 %HTMLEmptyChecker,
4273 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
4274 wakaba 1.40 check_attrs => sub {
4275     my ($self, $item, $element_state) = @_;
4276 wakaba 1.1 $GetHTMLAttrsChecker->({
4277 wakaba 1.90 media => $HTMLMQAttrChecker,
4278     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4279     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4280 wakaba 1.1 type => $HTMLIMTAttrChecker,
4281 wakaba 1.50 }, {
4282     %HTMLAttrStatus,
4283     media => FEATURE_HTML5_DEFAULT,
4284 wakaba 1.90 pixelratio => FEATURE_HTML5_DEFAULT,
4285 wakaba 1.50 src => FEATURE_HTML5_DEFAULT,
4286     type => FEATURE_HTML5_DEFAULT,
4287 wakaba 1.66 })->(@_);
4288 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4289     $self->{onerror}->(node => $item->{node},
4290 wakaba 1.104 type => 'attribute missing',
4291     text => 'src',
4292     level => $self->{level}->{must});
4293 wakaba 1.1 }
4294 wakaba 1.66
4295     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4296 wakaba 1.1 },
4297     };
4298    
4299     $Element->{$HTML_NS}->{canvas} = {
4300 wakaba 1.40 %HTMLTransparentChecker,
4301 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4302 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4303 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4304     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4305 wakaba 1.50 }, {
4306     %HTMLAttrStatus,
4307 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4308     width => FEATURE_HTML5_COMPLETE,
4309 wakaba 1.1 }),
4310     };
4311    
4312     $Element->{$HTML_NS}->{map} = {
4313 wakaba 1.72 %HTMLFlowContentChecker,
4314 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4315 wakaba 1.40 check_attrs => sub {
4316     my ($self, $item, $element_state) = @_;
4317 wakaba 1.4 my $has_id;
4318 wakaba 1.100 my $has_name;
4319 wakaba 1.4 $GetHTMLAttrsChecker->({
4320     id => sub {
4321 wakaba 1.100 ## NOTE: Same as the global |id=""|, with |$self->{map}| registration.
4322 wakaba 1.4 my ($self, $attr) = @_;
4323     my $value = $attr->value;
4324 wakaba 1.100 if (length $value) {
4325 wakaba 1.4 if ($self->{id}->{$value}) {
4326 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
4327     level => $self->{level}->{must});
4328 wakaba 1.4 push @{$self->{id}->{$value}}, $attr;
4329     } else {
4330     $self->{id}->{$value} = [$attr];
4331     }
4332 wakaba 1.1 } else {
4333 wakaba 1.4 ## NOTE: MUST contain at least one character
4334 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
4335     level => $self->{level}->{must});
4336 wakaba 1.1 }
4337 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
4338 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
4339     level => $self->{level}->{must});
4340 wakaba 1.4 }
4341 wakaba 1.100 #$self->{map}->{$value} ||= $attr;
4342     $has_id = [$value, $attr];
4343     },
4344     name => sub {
4345     my ($self, $attr) = @_;
4346     my $value = $attr->value;
4347     if (length $value) {
4348     ## NOTE: Duplication is not non-conforming.
4349     ## NOTE: Space characters are not non-conforming.
4350     #
4351     } else {
4352     $self->{onerror}->(node => $attr,
4353     type => 'empty attribute value',
4354 wakaba 1.104 level => $self->{level}->{must});
4355 wakaba 1.100 }
4356 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4357 wakaba 1.100 $has_name = [$value, $attr];
4358 wakaba 1.4 },
4359 wakaba 1.49 }, {
4360     %HTMLAttrStatus,
4361 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4362     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4363     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4364     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4365 wakaba 1.100 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
4366     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4367 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4368     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4369     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4370     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4371     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4372     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4373     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4374     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4375     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4376     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4377     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4378 wakaba 1.66 })->(@_);
4379 wakaba 1.100
4380     if ($has_name and $has_id) {
4381     if ($has_name->[0] ne $has_id->[0]) {
4382     $self->{onerror}->(node => $has_id->[1],
4383 wakaba 1.104 type => 'id ne name',
4384     level => $self->{level}->{must});
4385 wakaba 1.100 }
4386     } elsif (not $has_name) {
4387     $self->{onerror}->(node => $item->{node},
4388 wakaba 1.104 type => 'attribute missing',
4389     text => 'name',
4390     level => $self->{level}->{must});
4391 wakaba 1.100 }
4392 wakaba 1.4 },
4393 wakaba 1.59 check_start => sub {
4394     my ($self, $item, $element_state) = @_;
4395     $element_state->{in_map_original} = $self->{flag}->{in_map};
4396     $self->{flag}->{in_map} = 1;
4397 wakaba 1.79
4398     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4399     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4400 wakaba 1.59 },
4401     check_end => sub {
4402     my ($self, $item, $element_state) = @_;
4403     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
4404 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4405 wakaba 1.59 },
4406 wakaba 1.1 };
4407    
4408     $Element->{$HTML_NS}->{area} = {
4409 wakaba 1.40 %HTMLEmptyChecker,
4410 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4411 wakaba 1.40 check_attrs => sub {
4412     my ($self, $item, $element_state) = @_;
4413 wakaba 1.1 my %attr;
4414     my $coords;
4415 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4416 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4417     $attr_ns = '' unless defined $attr_ns;
4418     my $attr_ln = $attr->manakai_local_name;
4419     my $checker;
4420 wakaba 1.73 my $status;
4421 wakaba 1.1 if ($attr_ns eq '') {
4422 wakaba 1.73 $status = {
4423     %HTMLAttrStatus,
4424     %HTMLM12NCommonAttrStatus,
4425     accesskey => FEATURE_M12N10_REC,
4426     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4427     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4428 wakaba 1.99 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_M12N10_REC,
4429 wakaba 1.73 hreflang => FEATURE_HTML5_DEFAULT,
4430     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4431     media => FEATURE_HTML5_DEFAULT,
4432     nohref => FEATURE_M12N10_REC,
4433     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4434     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4435     ping => FEATURE_HTML5_DEFAULT,
4436 wakaba 1.99 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC,
4437 wakaba 1.73 shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4438     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4439     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4440     type => FEATURE_HTML5_DEFAULT,
4441     }->{$attr_ln};
4442    
4443 wakaba 1.1 $checker = {
4444 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4445 wakaba 1.1 alt => sub { },
4446     ## NOTE: |alt| value has no conformance creteria.
4447     shape => $GetHTMLEnumeratedAttrChecker->({
4448     circ => -1, circle => 1,
4449     default => 1,
4450     poly => 1, polygon => -1,
4451     rect => 1, rectangle => -1,
4452     }),
4453     coords => sub {
4454     my ($self, $attr) = @_;
4455     my $value = $attr->value;
4456     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4457     $coords = [split /,/, $value];
4458     } else {
4459     $self->{onerror}->(node => $attr,
4460 wakaba 1.104 type => 'coords:syntax error',
4461     level => $self->{level}->{must});
4462 wakaba 1.1 }
4463     },
4464 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4465     target => $HTMLTargetAttrChecker,
4466 wakaba 1.1 href => $HTMLURIAttrChecker,
4467     ping => $HTMLSpaceURIsAttrChecker,
4468 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4469 wakaba 1.1 media => $HTMLMQAttrChecker,
4470     hreflang => $HTMLLanguageTagAttrChecker,
4471     type => $HTMLIMTAttrChecker,
4472     }->{$attr_ln};
4473     if ($checker) {
4474     $attr{$attr_ln} = $attr;
4475 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
4476 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4477     $status = $HTMLDatasetAttrStatus;
4478 wakaba 1.1 } else {
4479     $checker = $HTMLAttrChecker->{$attr_ln};
4480     }
4481     }
4482     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4483 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4484     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4485     || $AttrStatus->{$attr_ns}->{''};
4486     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4487 wakaba 1.62
4488 wakaba 1.1 if ($checker) {
4489 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4490 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4491 wakaba 1.54 #
4492 wakaba 1.1 } else {
4493 wakaba 1.104 $self->{onerror}->(node => $attr,
4494     type => 'unknown attribute',
4495     level => $self->{level}->{uncertain});
4496 wakaba 1.1 ## ISSUE: No comformance createria for unknown attributes in the spec
4497     }
4498 wakaba 1.49
4499 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4500 wakaba 1.1 }
4501    
4502     if (defined $attr{href}) {
4503 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4504 wakaba 1.1 unless (defined $attr{alt}) {
4505 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4506 wakaba 1.104 type => 'attribute missing',
4507     text => 'alt',
4508     level => $self->{level}->{must});
4509 wakaba 1.1 }
4510     } else {
4511     for (qw/target ping rel media hreflang type alt/) {
4512     if (defined $attr{$_}) {
4513     $self->{onerror}->(node => $attr{$_},
4514 wakaba 1.104 type => 'attribute not allowed',
4515     level => $self->{level}->{must});
4516 wakaba 1.1 }
4517     }
4518     }
4519    
4520     my $shape = 'rectangle';
4521     if (defined $attr{shape}) {
4522     $shape = {
4523     circ => 'circle', circle => 'circle',
4524     default => 'default',
4525     poly => 'polygon', polygon => 'polygon',
4526     rect => 'rectangle', rectangle => 'rectangle',
4527     }->{lc $attr{shape}->value} || 'rectangle';
4528     ## TODO: ASCII lowercase?
4529     }
4530    
4531     if ($shape eq 'circle') {
4532     if (defined $attr{coords}) {
4533     if (defined $coords) {
4534     if (@$coords == 3) {
4535     if ($coords->[2] < 0) {
4536     $self->{onerror}->(node => $attr{coords},
4537 wakaba 1.104 type => 'coords:out of range',
4538     index => 2,
4539     value => $coords->[2],
4540     level => $self->{level}->{must});
4541 wakaba 1.1 }
4542     } else {
4543     $self->{onerror}->(node => $attr{coords},
4544 wakaba 1.104 type => 'coords:number not 3',
4545     text => 0+@$coords,
4546     level => $self->{level}->{must});
4547 wakaba 1.1 }
4548     } else {
4549     ## NOTE: A syntax error has been reported.
4550     }
4551     } else {
4552 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4553 wakaba 1.104 type => 'attribute missing',
4554     text => 'coords',
4555     level => $self->{level}->{must});
4556 wakaba 1.1 }
4557     } elsif ($shape eq 'default') {
4558     if (defined $attr{coords}) {
4559     $self->{onerror}->(node => $attr{coords},
4560 wakaba 1.104 type => 'attribute not allowed',
4561     level => $self->{level}->{must});
4562 wakaba 1.1 }
4563     } elsif ($shape eq 'polygon') {
4564     if (defined $attr{coords}) {
4565     if (defined $coords) {
4566     if (@$coords >= 6) {
4567     unless (@$coords % 2 == 0) {
4568     $self->{onerror}->(node => $attr{coords},
4569 wakaba 1.104 type => 'coords:number not even',
4570     text => 0+@$coords,
4571     level => $self->{level}->{must});
4572 wakaba 1.1 }
4573     } else {
4574     $self->{onerror}->(node => $attr{coords},
4575 wakaba 1.104 type => 'coords:number lt 6',
4576     text => 0+@$coords,
4577     level => $self->{level}->{must});
4578 wakaba 1.1 }
4579     } else {
4580     ## NOTE: A syntax error has been reported.
4581     }
4582     } else {
4583 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4584 wakaba 1.104 type => 'attribute missing',
4585     text => 'coords',
4586     level => $self->{level}->{must});
4587 wakaba 1.1 }
4588     } elsif ($shape eq 'rectangle') {
4589     if (defined $attr{coords}) {
4590     if (defined $coords) {
4591     if (@$coords == 4) {
4592     unless ($coords->[0] < $coords->[2]) {
4593     $self->{onerror}->(node => $attr{coords},
4594 wakaba 1.104 type => 'coords:out of range',
4595     index => 0,
4596     value => $coords->[0],
4597     level => $self->{level}->{must});
4598 wakaba 1.1 }
4599     unless ($coords->[1] < $coords->[3]) {
4600     $self->{onerror}->(node => $attr{coords},
4601 wakaba 1.104 type => 'coords:out of range',
4602     index => 1,
4603     value => $coords->[1],
4604     level => $self->{level}->{must});
4605 wakaba 1.1 }
4606     } else {
4607     $self->{onerror}->(node => $attr{coords},
4608 wakaba 1.104 type => 'coords:number not 4',
4609     text => 0+@$coords,
4610     level => $self->{level}->{must});
4611 wakaba 1.1 }
4612     } else {
4613     ## NOTE: A syntax error has been reported.
4614     }
4615     } else {
4616 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4617 wakaba 1.104 type => 'attribute missing',
4618     text => 'coords',
4619     level => $self->{level}->{must});
4620 wakaba 1.1 }
4621     }
4622 wakaba 1.66
4623     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4624 wakaba 1.1 },
4625 wakaba 1.59 check_start => sub {
4626     my ($self, $item, $element_state) = @_;
4627     unless ($self->{flag}->{in_map} or
4628     not $item->{node}->manakai_parent_element) {
4629     $self->{onerror}->(node => $item->{node},
4630     type => 'element not allowed:area',
4631 wakaba 1.104 level => $self->{level}->{must});
4632 wakaba 1.59 }
4633 wakaba 1.79
4634     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4635     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4636 wakaba 1.59 },
4637 wakaba 1.1 };
4638    
4639     $Element->{$HTML_NS}->{table} = {
4640 wakaba 1.40 %HTMLChecker,
4641 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4642 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4643 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4644     cellspacing => $HTMLLengthAttrChecker,
4645 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4646     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4647     lhs => 1, rhs => 1, box => 1, border => 1,
4648     }),
4649     rules => $GetHTMLEnumeratedAttrChecker->({
4650     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
4651     }),
4652     summary => sub {}, ## NOTE: %Text; in HTML4.
4653     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
4654     }, {
4655 wakaba 1.49 %HTMLAttrStatus,
4656 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4657 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4658     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4659     border => FEATURE_M12N10_REC,
4660     cellpadding => FEATURE_M12N10_REC,
4661     cellspacing => FEATURE_M12N10_REC,
4662 wakaba 1.61 cols => FEATURE_RFC1942,
4663 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4664     dataformatas => FEATURE_HTML4_REC_RESERVED,
4665     datapagesize => FEATURE_M12N10_REC,
4666     datasrc => FEATURE_HTML4_REC_RESERVED,
4667     frame => FEATURE_M12N10_REC,
4668 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4669 wakaba 1.49 rules => FEATURE_M12N10_REC,
4670     summary => FEATURE_M12N10_REC,
4671     width => FEATURE_M12N10_REC,
4672     }),
4673 wakaba 1.40 check_start => sub {
4674     my ($self, $item, $element_state) = @_;
4675     $element_state->{phase} = 'before caption';
4676 wakaba 1.66
4677     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4678 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4679     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4680 wakaba 1.40 },
4681     check_child_element => sub {
4682     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4683     $child_is_transparent, $element_state) = @_;
4684     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4685     $self->{onerror}->(node => $child_el,
4686     type => 'element not allowed:minus',
4687 wakaba 1.104 level => $self->{level}->{must});
4688 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4689     #
4690     } elsif ($element_state->{phase} eq 'in tbodys') {
4691     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4692     #$element_state->{phase} = 'in tbodys';
4693     } elsif (not $element_state->{has_tfoot} and
4694     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4695     $element_state->{phase} = 'after tfoot';
4696     $element_state->{has_tfoot} = 1;
4697     } else {
4698 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4699     level => $self->{level}->{must});
4700 wakaba 1.40 }
4701     } elsif ($element_state->{phase} eq 'in trs') {
4702     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4703     #$element_state->{phase} = 'in trs';
4704     } elsif (not $element_state->{has_tfoot} and
4705     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4706     $element_state->{phase} = 'after tfoot';
4707     $element_state->{has_tfoot} = 1;
4708     } else {
4709 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4710     level => $self->{level}->{must});
4711 wakaba 1.40 }
4712     } elsif ($element_state->{phase} eq 'after thead') {
4713     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4714     $element_state->{phase} = 'in tbodys';
4715     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4716     $element_state->{phase} = 'in trs';
4717     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4718     $element_state->{phase} = 'in tbodys';
4719     $element_state->{has_tfoot} = 1;
4720     } else {
4721 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4722     level => $self->{level}->{must});
4723 wakaba 1.40 }
4724     } elsif ($element_state->{phase} eq 'in colgroup') {
4725     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4726     $element_state->{phase} = 'in colgroup';
4727     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4728     $element_state->{phase} = 'after thead';
4729     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4730     $element_state->{phase} = 'in tbodys';
4731     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4732     $element_state->{phase} = 'in trs';
4733     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4734     $element_state->{phase} = 'in tbodys';
4735     $element_state->{has_tfoot} = 1;
4736     } else {
4737 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4738     level => $self->{level}->{must});
4739 wakaba 1.40 }
4740     } elsif ($element_state->{phase} eq 'before caption') {
4741     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4742     $element_state->{phase} = 'in colgroup';
4743     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4744     $element_state->{phase} = 'in colgroup';
4745     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4746     $element_state->{phase} = 'after thead';
4747     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4748     $element_state->{phase} = 'in tbodys';
4749     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4750     $element_state->{phase} = 'in trs';
4751     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4752     $element_state->{phase} = 'in tbodys';
4753     $element_state->{has_tfoot} = 1;
4754     } else {
4755 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4756     level => $self->{level}->{must});
4757 wakaba 1.40 }
4758     } elsif ($element_state->{phase} eq 'after tfoot') {
4759 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4760     level => $self->{level}->{must});
4761 wakaba 1.40 } else {
4762     die "check_child_element: Bad |table| phase: $element_state->{phase}";
4763     }
4764     },
4765     check_child_text => sub {
4766     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4767     if ($has_significant) {
4768 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4769     level => $self->{level}->{must});
4770 wakaba 1.1 }
4771 wakaba 1.40 },
4772     check_end => sub {
4773     my ($self, $item, $element_state) = @_;
4774 wakaba 1.1
4775     ## Table model errors
4776     require Whatpm::HTMLTable;
4777 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
4778 wakaba 1.104 $self->{onerror}->(@_);
4779     }, $self->{level});
4780 wakaba 1.87 Whatpm::HTMLTable->assign_header
4781 wakaba 1.104 ($table, $self->{onerror}, $self->{level});
4782 wakaba 1.87 push @{$self->{return}->{table}}, $table;
4783 wakaba 1.1
4784 wakaba 1.40 $HTMLChecker{check_end}->(@_);
4785 wakaba 1.1 },
4786     };
4787    
4788     $Element->{$HTML_NS}->{caption} = {
4789 wakaba 1.40 %HTMLPhrasingContentChecker,
4790 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4791 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4792     align => $GetHTMLEnumeratedAttrChecker->({
4793     top => 1, bottom => 1, left => 1, right => 1,
4794     }),
4795     }, {
4796 wakaba 1.49 %HTMLAttrStatus,
4797 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4798 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4799 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4800 wakaba 1.49 }),
4801 wakaba 1.1 };
4802    
4803 wakaba 1.69 my %cellalign = (
4804     ## HTML4 %cellhalign;
4805 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4806     left => 1, center => 1, right => 1, justify => 1, char => 1,
4807     }),
4808     char => sub {
4809     my ($self, $attr) = @_;
4810 wakaba 1.69
4811 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
4812    
4813     my $value = $attr->value;
4814     if (length $value != 1) {
4815     $self->{onerror}->(node => $attr, type => 'char:syntax error',
4816 wakaba 1.105 level => $self->{level}->{html4_fact});
4817 wakaba 1.70 }
4818     },
4819 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
4820    
4821 wakaba 1.69 ## HTML4 %cellvalign;
4822 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
4823     top => 1, middle => 1, bottom => 1, baseline => 1,
4824     }),
4825 wakaba 1.69 );
4826    
4827 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
4828 wakaba 1.40 %HTMLEmptyChecker,
4829 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4830 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4831 wakaba 1.69 %cellalign,
4832 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4833     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
4834     ## TODO: "attribute not supported" if |col|.
4835     ## ISSUE: MUST NOT if any |col|?
4836     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
4837 wakaba 1.49 }, {
4838     %HTMLAttrStatus,
4839 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4840 wakaba 1.49 align => FEATURE_M12N10_REC,
4841     char => FEATURE_M12N10_REC,
4842     charoff => FEATURE_M12N10_REC,
4843 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4844 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4845 wakaba 1.49 valign => FEATURE_M12N10_REC,
4846     width => FEATURE_M12N10_REC,
4847 wakaba 1.1 }),
4848 wakaba 1.40 check_child_element => sub {
4849     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4850     $child_is_transparent, $element_state) = @_;
4851     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4852     $self->{onerror}->(node => $child_el,
4853     type => 'element not allowed:minus',
4854 wakaba 1.104 level => $self->{level}->{must});
4855 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4856     #
4857     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
4858     #
4859     } else {
4860 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4861     level => $self->{level}->{must});
4862 wakaba 1.40 }
4863     },
4864     check_child_text => sub {
4865     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4866     if ($has_significant) {
4867 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4868     level => $self->{level}->{must});
4869 wakaba 1.1 }
4870     },
4871     };
4872    
4873     $Element->{$HTML_NS}->{col} = {
4874 wakaba 1.40 %HTMLEmptyChecker,
4875 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4876 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4877 wakaba 1.69 %cellalign,
4878 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4879 wakaba 1.49 }, {
4880     %HTMLAttrStatus,
4881 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4882 wakaba 1.49 align => FEATURE_M12N10_REC,
4883     char => FEATURE_M12N10_REC,
4884     charoff => FEATURE_M12N10_REC,
4885 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4886 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4887 wakaba 1.49 valign => FEATURE_M12N10_REC,
4888     width => FEATURE_M12N10_REC,
4889 wakaba 1.1 }),
4890     };
4891    
4892     $Element->{$HTML_NS}->{tbody} = {
4893 wakaba 1.40 %HTMLChecker,
4894 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4895 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4896     %cellalign,
4897     }, {
4898 wakaba 1.49 %HTMLAttrStatus,
4899 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4900 wakaba 1.49 align => FEATURE_M12N10_REC,
4901     char => FEATURE_M12N10_REC,
4902     charoff => FEATURE_M12N10_REC,
4903 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4904 wakaba 1.49 valign => FEATURE_M12N10_REC,
4905     }),
4906 wakaba 1.40 check_child_element => sub {
4907     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4908     $child_is_transparent, $element_state) = @_;
4909     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4910     $self->{onerror}->(node => $child_el,
4911     type => 'element not allowed:minus',
4912 wakaba 1.104 level => $self->{level}->{must});
4913 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4914     #
4915     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4916 wakaba 1.84 #
4917 wakaba 1.40 } else {
4918 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4919     level => $self->{level}->{must});
4920 wakaba 1.40 }
4921     },
4922     check_child_text => sub {
4923     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4924     if ($has_significant) {
4925 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4926     level => $self->{level}->{must});
4927 wakaba 1.1 }
4928 wakaba 1.40 },
4929 wakaba 1.1 };
4930    
4931     $Element->{$HTML_NS}->{thead} = {
4932 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4933 wakaba 1.1 };
4934    
4935     $Element->{$HTML_NS}->{tfoot} = {
4936 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4937 wakaba 1.1 };
4938    
4939     $Element->{$HTML_NS}->{tr} = {
4940 wakaba 1.40 %HTMLChecker,
4941 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4942 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4943     %cellalign,
4944     bgcolor => $HTMLColorAttrChecker,
4945     }, {
4946 wakaba 1.49 %HTMLAttrStatus,
4947 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4948 wakaba 1.49 align => FEATURE_M12N10_REC,
4949     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4950     char => FEATURE_M12N10_REC,
4951     charoff => FEATURE_M12N10_REC,
4952 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4953 wakaba 1.49 valign => FEATURE_M12N10_REC,
4954     }),
4955 wakaba 1.40 check_child_element => sub {
4956     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4957     $child_is_transparent, $element_state) = @_;
4958     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4959     $self->{onerror}->(node => $child_el,
4960     type => 'element not allowed:minus',
4961 wakaba 1.104 level => $self->{level}->{must});
4962 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4963     #
4964     } elsif ($child_nsuri eq $HTML_NS and
4965     ($child_ln eq 'td' or $child_ln eq 'th')) {
4966 wakaba 1.84 #
4967 wakaba 1.40 } else {
4968 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4969     level => $self->{level}->{must});
4970 wakaba 1.40 }
4971     },
4972     check_child_text => sub {
4973     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4974     if ($has_significant) {
4975 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4976     level => $self->{level}->{must});
4977 wakaba 1.1 }
4978     },
4979     };
4980    
4981     $Element->{$HTML_NS}->{td} = {
4982 wakaba 1.72 %HTMLFlowContentChecker,
4983 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4984 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4985 wakaba 1.69 %cellalign,
4986     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
4987     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
4988     bgcolor => $HTMLColorAttrChecker,
4989 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4990 wakaba 1.87 headers => sub {
4991     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
4992     ## Though that method does not check the |headers| attribute of a
4993     ## |td| element if the element does not form a table, in that case
4994     ## the |td| element is non-conforming anyway.
4995     },
4996 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
4997 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4998 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
4999     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5000 wakaba 1.49 }, {
5001     %HTMLAttrStatus,
5002 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5003     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5004 wakaba 1.49 align => FEATURE_M12N10_REC,
5005 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5006 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5007     char => FEATURE_M12N10_REC,
5008     charoff => FEATURE_M12N10_REC,
5009 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5010 wakaba 1.87 headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5011 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5012 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5013 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5014 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5015     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5016 wakaba 1.49 valign => FEATURE_M12N10_REC,
5017     width => FEATURE_M12N10_REC_DEPRECATED,
5018 wakaba 1.1 }),
5019     };
5020    
5021     $Element->{$HTML_NS}->{th} = {
5022 wakaba 1.40 %HTMLPhrasingContentChecker,
5023 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5024 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5025 wakaba 1.69 %cellalign,
5026     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5027     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5028     bgcolor => $HTMLColorAttrChecker,
5029 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5030 wakaba 1.87 ## TODO: HTML4(?) |headers|
5031 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5032 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5033     scope => $GetHTMLEnumeratedAttrChecker
5034     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5035 wakaba 1.49 }, {
5036     %HTMLAttrStatus,
5037 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5038     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5039 wakaba 1.49 align => FEATURE_M12N10_REC,
5040 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5041 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5042     char => FEATURE_M12N10_REC,
5043     charoff => FEATURE_M12N10_REC,
5044 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5045     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5046 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5047 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5048 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5049 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5050     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5051 wakaba 1.49 valign => FEATURE_M12N10_REC,
5052     width => FEATURE_M12N10_REC_DEPRECATED,
5053 wakaba 1.1 }),
5054     };
5055    
5056 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
5057 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: Flow* [WF2]
5058 wakaba 1.56 ## TODO: form in form is allowed in XML [WF2]
5059 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5060     check_attrs => $GetHTMLAttrsChecker->({
5061 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5062 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
5063     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
5064 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5065     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
5066     method => $GetHTMLEnumeratedAttrChecker->({
5067     get => 1, post => 1, put => 1, delete => 1,
5068     }),
5069 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
5070     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
5071     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
5072 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
5073     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5074 wakaba 1.52 target => $HTMLTargetAttrChecker,
5075     ## TODO: Warn for combination whose behavior is not defined.
5076     }, {
5077     %HTMLAttrStatus,
5078     %HTMLM12NCommonAttrStatus,
5079 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
5080 wakaba 1.52 'accept-charset' => FEATURE_M12N10_REC,
5081 wakaba 1.56 action => FEATURE_WF2 | FEATURE_M12N10_REC,
5082     data => FEATURE_WF2,
5083     enctype => FEATURE_WF2 | FEATURE_M12N10_REC,
5084 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5085 wakaba 1.56 method => FEATURE_WF2 | FEATURE_M12N10_REC,
5086 wakaba 1.52 name => FEATURE_M12N10_REC_DEPRECATED,
5087 wakaba 1.56 onreceived => FEATURE_WF2,
5088 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5089     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5090 wakaba 1.56 replace => FEATURE_WF2,
5091 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5092     sdasuff => FEATURE_HTML20_RFC,
5093 wakaba 1.52 target => FEATURE_M12N10_REC,
5094     }),
5095     ## TODO: Tests
5096     ## TODO: Tests for <nest/> in <form>
5097 wakaba 1.66 check_start => sub {
5098     my ($self, $item, $element_state) = @_;
5099    
5100     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5101     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5102 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5103     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5104 wakaba 1.66 },
5105 wakaba 1.52 };
5106    
5107     $Element->{$HTML_NS}->{fieldset} = {
5108 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: legend, %Flow; ## TODO: legend
5109 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5110 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5111     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5112     ## TODO: form [WF2]
5113     }, {
5114 wakaba 1.52 %HTMLAttrStatus,
5115     %HTMLM12NCommonAttrStatus,
5116 wakaba 1.56 disabled => FEATURE_WF2,
5117     form => FEATURE_WF2,
5118 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5119     }),
5120     ## TODO: Tests
5121     ## TODO: Tests for <nest/> in <fieldset>
5122     };
5123    
5124     $Element->{$HTML_NS}->{input} = {
5125 wakaba 1.56 %HTMLEmptyChecker, ## MUST [WF2]
5126 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5127     check_attrs => $GetHTMLAttrsChecker->({
5128 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5129 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5130 wakaba 1.56 action => $HTMLURIAttrChecker,
5131 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
5132     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
5133     }),
5134     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
5135     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
5136     ## here.
5137 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
5138     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5139 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
5140     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5141 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
5142     ## TODO: form [WF2]
5143     ## TODO: inputmode [WF2]
5144 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
5145 wakaba 1.56 ## TODO: list [WF2]
5146     ## TODO: max [WF2]
5147 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5148 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
5149     get => 1, post => 1, put => 1, delete => 1,
5150     }),
5151     ## TODO: min [WF2]
5152 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5153     readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5154 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5155     required => $GetHTMLBooleanAttrChecker->('required'),
5156 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5157     src => $HTMLURIAttrChecker,
5158 wakaba 1.56 ## TODO: step [WF2]
5159     target => $HTMLTargetAttrChecker,
5160 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |input| attribute has |template|
5161     ## attribute to support the |add| button type (as part of repetition
5162     ## template feature). It conflicts with the |template| global attribute
5163     ## introduced as part of the data template feature.
5164     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5165     ## author requirement.
5166 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5167     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
5168     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
5169 wakaba 1.56 ## [WF2]
5170     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
5171     time => 1, number => 1, range => 1, email => 1, url => 1,
5172     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5173 wakaba 1.52 }),
5174     usemap => $HTMLUsemapAttrChecker,
5175 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]
5176     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
5177 wakaba 1.52 }, {
5178     %HTMLAttrStatus,
5179     %HTMLM12NCommonAttrStatus,
5180 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
5181 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5182 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5183 wakaba 1.56 action => FEATURE_WF2,
5184 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
5185     alt => FEATURE_M12N10_REC,
5186 wakaba 1.56 autocomplete => FEATURE_WF2,
5187     autofocus => FEATURE_WF2,
5188 wakaba 1.52 checked => FEATURE_M12N10_REC,
5189     datafld => FEATURE_HTML4_REC_RESERVED,
5190     dataformatas => FEATURE_HTML4_REC_RESERVED,
5191     datasrc => FEATURE_HTML4_REC_RESERVED,
5192 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5193 wakaba 1.65 enctype => FEATURE_WF2,
5194 wakaba 1.56 form => FEATURE_WF2,
5195     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5196 wakaba 1.52 ismap => FEATURE_M12N10_REC,
5197     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5198 wakaba 1.56 list => FEATURE_WF2,
5199     max => FEATURE_WF2,
5200     maxlength => FEATURE_WF2 | FEATURE_M12N10_REC,
5201     method => FEATURE_WF2,
5202     min => FEATURE_WF2,
5203 wakaba 1.52 name => FEATURE_M12N10_REC,
5204     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5205     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5206     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5207     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5208 wakaba 1.56 readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5209 wakaba 1.65 replace => FEATURE_WF2,
5210 wakaba 1.56 required => FEATURE_WF2,
5211 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5212 wakaba 1.56 size => FEATURE_WF2_DEPRECATED | FEATURE_M12N10_REC,
5213 wakaba 1.52 src => FEATURE_M12N10_REC,
5214 wakaba 1.56 step => FEATURE_WF2,
5215 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5216 wakaba 1.65 target => FEATURE_WF2,
5217 wakaba 1.56 template => FEATURE_WF2,
5218 wakaba 1.52 type => FEATURE_M12N10_REC,
5219     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5220     value => FEATURE_M12N10_REC,
5221     }),
5222     ## TODO: Tests
5223     ## TODO: Tests for <nest/> in <input>
5224 wakaba 1.66 check_start => sub {
5225     my ($self, $item, $element_state) = @_;
5226    
5227     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5228     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5229     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
5230 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5231     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5232 wakaba 1.66 },
5233 wakaba 1.52 };
5234    
5235 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
5236    
5237 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
5238     ## [repetition-block-related] buttons carefully to make clear which block a
5239     ## button applies to.": I have no idea how this can be tested.
5240    
5241 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
5242 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: %Flow; - something [XHTML10]
5243 wakaba 1.52 ## TODO: -A|%formctrl;|form|fieldset [HTML4]
5244     ## TODO: image map (img) in |button| is "illegal" [HTML4].
5245     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5246     check_attrs => $GetHTMLAttrsChecker->({
5247 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5248 wakaba 1.56 action => $HTMLURIAttrChecker,
5249     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5250 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5251 wakaba 1.56 ## TODO: form [WF2]
5252     method => $GetHTMLEnumeratedAttrChecker->({
5253     get => 1, post => 1, put => 1, delete => 1,
5254     }),
5255 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5256 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5257     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5258     target => $HTMLTargetAttrChecker,
5259 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
5260     ## attribute to support the |add| button type (as part of repetition
5261     ## template feature). It conflicts with the |template| global attribute
5262     ## introduced as part of the data template feature.
5263     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5264     ## author requirement.
5265 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5266     button => 1, submit => 1, reset => 1,
5267 wakaba 1.80 ## [WF2]
5268     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5269 wakaba 1.52 }),
5270     value => sub {}, ## NOTE: CDATA [M12N]
5271     }, {
5272     %HTMLAttrStatus,
5273     %HTMLM12NCommonAttrStatus,
5274     accesskey => FEATURE_M12N10_REC,
5275 wakaba 1.56 action => FEATURE_WF2,
5276     autofocus => FEATURE_WF2,
5277 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5278     dataformatas => FEATURE_HTML4_REC_RESERVED,
5279     datasrc => FEATURE_HTML4_REC_RESERVED,
5280 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5281     enctype => FEATURE_WF2,
5282     form => FEATURE_WF2,
5283 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5284 wakaba 1.56 method => FEATURE_WF2,
5285 wakaba 1.52 name => FEATURE_M12N10_REC,
5286     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5287     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5288 wakaba 1.56 oninvalid => FEATURE_WF2,
5289     replace => FEATURE_WF2,
5290 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5291 wakaba 1.56 target => FEATURE_WF2,
5292     template => FEATURE_WF2,
5293 wakaba 1.52 type => FEATURE_M12N10_REC,
5294     value => FEATURE_M12N10_REC,
5295     }),
5296     ## TODO: Tests
5297     ## TODO: Tests for <nest/> in <button>
5298 wakaba 1.66 check_start => sub {
5299     my ($self, $item, $element_state) = @_;
5300    
5301     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5302     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5303 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5304     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5305 wakaba 1.66 },
5306 wakaba 1.52 };
5307    
5308     $Element->{$HTML_NS}->{label} = {
5309     %HTMLPhrasingContentChecker, ## NOTE: %Inline - label [XHTML10] ## TODO: -label
5310 wakaba 1.56 ## TODO: At most one form control [WF2]
5311 wakaba 1.82 status => FEATURE_WF2 | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5312 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5313 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5314 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
5315     }, {
5316     %HTMLAttrStatus,
5317 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5318 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
5319 wakaba 1.52 for => FEATURE_M12N10_REC,
5320     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5321     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5322     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5323     }),
5324     ## TODO: Tests
5325     ## TODO: Tests for <nest/> in <label>
5326     };
5327    
5328     $Element->{$HTML_NS}->{select} = {
5329 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (optgroup|option)* [HTML4] + [WF2] ## TODO: SHOULD avoid empty and visible [WF2]
5330 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
5331     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
5332     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5333 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
5334 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5335 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5336 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5337 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5338 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5339     ## TODO: form [WF2]
5340 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5341     name => sub {}, ## NOTE: CDATA [M12N]
5342 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5343     ## TODO: pattern [WF2] ## TODO: |title| semantics
5344 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5345     }, {
5346     %HTMLAttrStatus,
5347     %HTMLM12NCommonAttrStatus,
5348 wakaba 1.56 accesskey => FEATURE_WF2,
5349     autofocus => FEATURE_WF2,
5350     data => FEATURE_WF2,
5351 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5352     dataformatas => FEATURE_HTML4_REC_RESERVED,
5353     datasrc => FEATURE_HTML4_REC_RESERVED,
5354 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5355     form => FEATURE_WF2,
5356 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5357     multiple => FEATURE_M12N10_REC,
5358     name => FEATURE_M12N10_REC,
5359     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5360     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5361     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5362 wakaba 1.56 oninvalid => FEATURE_WF2,
5363     pattern => FEATURE_WF2,
5364 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5365     sdapref => FEATURE_HTML20_RFC,
5366 wakaba 1.52 size => FEATURE_M12N10_REC,
5367     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5368     }),
5369     ## TODO: Tests
5370     ## TODO: Tests for <nest/> in <select>
5371 wakaba 1.66 check_start => sub {
5372     my ($self, $item, $element_state) = @_;
5373    
5374     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5375     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5376 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5377     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5378 wakaba 1.66 },
5379 wakaba 1.52 };
5380 wakaba 1.1
5381 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
5382 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (transparent | option)*
5383 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
5384 wakaba 1.52 status => FEATURE_WF2,
5385 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5386     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5387     }, {
5388 wakaba 1.52 %HTMLAttrStatus,
5389 wakaba 1.56 data => FEATURE_WF2,
5390 wakaba 1.52 }),
5391     ## TODO: Tests
5392     ## TODO: Tests for <nest/> in <datalist>
5393 wakaba 1.66 check_start => sub {
5394     my ($self, $item, $element_state) = @_;
5395    
5396     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5397 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5398     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5399 wakaba 1.66 },
5400 wakaba 1.52 };
5401 wakaba 1.49
5402 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
5403 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (option|optgroup)* [HTML4] + [WF2] SHOULD avoid empty and visible [WF2]
5404 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5405     check_attrs => $GetHTMLAttrsChecker->({
5406     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5407     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
5408     }, {
5409     %HTMLAttrStatus,
5410     %HTMLM12NCommonAttrStatus,
5411 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5412 wakaba 1.52 label => FEATURE_M12N10_REC,
5413     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5414     }),
5415     ## TODO: Tests
5416     ## TODO: Tests for <nest/> in <optgroup>
5417     };
5418    
5419     $Element->{$HTML_NS}->{option} = {
5420     %HTMLTextChecker,
5421     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5422     check_attrs => $GetHTMLAttrsChecker->({
5423     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5424     label => sub {}, ## NOTE: Text [M12N]
5425     selected => $GetHTMLBooleanAttrChecker->('selected'),
5426     value => sub {}, ## NOTE: CDATA [M12N]
5427     }, {
5428     %HTMLAttrStatus,
5429     %HTMLM12NCommonAttrStatus,
5430 wakaba 1.56 disabled => FEATURE_WF2, FEATURE_M12N10_REC,
5431 wakaba 1.52 label => FEATURE_M12N10_REC,
5432     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5433 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5434     sdapref => FEATURE_HTML20_RFC,
5435 wakaba 1.52 selected => FEATURE_M12N10_REC,
5436     value => FEATURE_M12N10_REC,
5437     }),
5438     ## TODO: Tests
5439     ## TODO: Tests for <nest/> in <option>
5440     };
5441 wakaba 1.49
5442 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
5443     %HTMLTextChecker,
5444     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5445     check_attrs => $GetHTMLAttrsChecker->({
5446 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
5447 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5448 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5449     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
5450 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5451 wakaba 1.56 ## TODO: form [WF2]
5452     ## TODO: inputmode [WF2]
5453     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5454 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5455 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
5456 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5457 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
5458     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5459     oninvalid => $HTMLEventHandlerAttrChecker,
5460     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
5461 wakaba 1.52 }, {
5462     %HTMLAttrStatus,
5463     %HTMLM12NCommonAttrStatus,
5464 wakaba 1.56 accept => FEATURE_WF2,
5465 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5466 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5467 wakaba 1.56 autofocus => FEATURE_WF2,
5468 wakaba 1.52 cols => FEATURE_M12N10_REC,
5469     datafld => FEATURE_HTML4_REC_RESERVED,
5470 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
5471     datasrc => FEATURE_HTML4_REC_RESERVED,
5472 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5473     form => FEATURE_WF2,
5474     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5475 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5476 wakaba 1.56 maxlength => FEATURE_WF2,
5477 wakaba 1.52 name => FEATURE_M12N10_REC,
5478     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5479     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5480     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5481 wakaba 1.56 oninvalid => FEATURE_WF2,
5482 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5483 wakaba 1.56 pattern => FEATURE_WF2,
5484     readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5485     required => FEATURE_WF2,
5486 wakaba 1.61 rows => FEATURE_M12N10_REC,
5487     sdaform => FEATURE_HTML20_RFC,
5488     sdapref => FEATURE_HTML20_RFC,
5489 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5490 wakaba 1.56 wrap => FEATURE_WF2,
5491 wakaba 1.52 }),
5492     ## TODO: Tests
5493     ## TODO: Tests for <nest/> in <textarea>
5494 wakaba 1.66 check_start => sub {
5495     my ($self, $item, $element_state) = @_;
5496    
5497     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5498 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5499     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5500 wakaba 1.66 },
5501 wakaba 1.52 };
5502 wakaba 1.49
5503 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
5504 wakaba 1.56 %HTMLPhrasingContentChecker, ## Inline [WF2]
5505 wakaba 1.52 status => FEATURE_WF2,
5506 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5507     ## TODO: for [WF2]
5508     ## TODO: form [WF2]
5509     ## TODO: name [WF2]
5510     ## onformchange[WF2]
5511     ## onforminput[WF2]
5512     }, {
5513 wakaba 1.52 %HTMLAttrStatus,
5514 wakaba 1.56 for => FEATURE_WF2,
5515     form => FEATURE_WF2,
5516     name => FEATURE_WF2,
5517     onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
5518     onformchange => FEATURE_WF2,
5519     onforminput => FEATURE_WF2,
5520 wakaba 1.52 }),
5521     ## TODO: Tests
5522     ## TODO: Tests for <nest/> in <output>
5523 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
5524 wakaba 1.52 };
5525    
5526     $Element->{$HTML_NS}->{isindex} = {
5527     %HTMLEmptyChecker,
5528 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
5529     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
5530 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5531     prompt => sub {}, ## NOTE: Text [M12N]
5532     }, {
5533     %HTMLAttrStatus,
5534     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5535     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5536     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5537     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5538     prompt => FEATURE_M12N10_REC_DEPRECATED,
5539 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5540 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5541 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5542     }),
5543     ## TODO: Tests
5544     ## TODO: Tests for <nest/> in <isindex>
5545 wakaba 1.66 check_start => sub {
5546     my ($self, $item, $element_state) = @_;
5547    
5548     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5549 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5550     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5551 wakaba 1.66 },
5552 wakaba 1.52 };
5553 wakaba 1.49
5554 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
5555 wakaba 1.40 %HTMLChecker,
5556 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5557 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5558 wakaba 1.91 charset => sub {
5559     my ($self, $attr) = @_;
5560    
5561     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
5562     $self->{onerror}->(type => 'attribute not allowed',
5563     node => $attr,
5564 wakaba 1.104 level => $self->{level}->{must});
5565 wakaba 1.91 }
5566    
5567     $HTMLCharsetChecker->($attr->value, @_);
5568     },
5569 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
5570 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
5571 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
5572     async => $GetHTMLBooleanAttrChecker->('async'),
5573 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
5574 wakaba 1.49 }, {
5575     %HTMLAttrStatus,
5576     %HTMLM12NCommonAttrStatus,
5577 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
5578 wakaba 1.91 charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5579 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5580 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
5581     for => FEATURE_HTML4_REC_RESERVED,
5582 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5583 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
5584 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5585     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5586 wakaba 1.9 }),
5587 wakaba 1.40 check_start => sub {
5588     my ($self, $item, $element_state) = @_;
5589 wakaba 1.1
5590 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
5591     $element_state->{must_be_empty} = 1;
5592 wakaba 1.1 } else {
5593     ## NOTE: No content model conformance in HTML5 spec.
5594 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
5595     my $language = $item->{node}->get_attribute_ns (undef, 'language');
5596 wakaba 1.1 if ((defined $type and $type eq '') or
5597     (defined $language and $language eq '')) {
5598     $type = 'text/javascript';
5599     } elsif (defined $type) {
5600     #
5601     } elsif (defined $language) {
5602     $type = 'text/' . $language;
5603     } else {
5604     $type = 'text/javascript';
5605     }
5606 wakaba 1.93
5607     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)]) {
5608     $type = "$1/$2";
5609     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
5610     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
5611     }
5612     $element_state->{script_type} = $type;
5613 wakaba 1.40 }
5614 wakaba 1.66
5615     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5616 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5617     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5618 wakaba 1.40 },
5619     check_child_element => sub {
5620     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5621     $child_is_transparent, $element_state) = @_;
5622     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5623     $self->{onerror}->(node => $child_el,
5624     type => 'element not allowed:minus',
5625 wakaba 1.104 level => $self->{level}->{must});
5626 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5627     #
5628     } else {
5629     if ($element_state->{must_be_empty}) {
5630     $self->{onerror}->(node => $child_el,
5631 wakaba 1.104 type => 'element not allowed:empty',
5632     level => $self->{level}->{must});
5633 wakaba 1.40 }
5634     }
5635     },
5636     check_child_text => sub {
5637     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5638     if ($has_significant and
5639     $element_state->{must_be_empty}) {
5640     $self->{onerror}->(node => $child_node,
5641 wakaba 1.104 type => 'character not allowed:empty',
5642     level => $self->{level}->{must});
5643 wakaba 1.40 }
5644 wakaba 1.103 $element_state->{text} .= $child_node->text_content;
5645 wakaba 1.40 },
5646     check_end => sub {
5647     my ($self, $item, $element_state) = @_;
5648     unless ($element_state->{must_be_empty}) {
5649 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
5650     ## NOTE: XML content should be checked by THIS instance of checker
5651     ## as part of normal tree validation.
5652 wakaba 1.104 $self->{onerror}->(node => $item->{node},
5653     type => 'XML script lang',
5654     text => $element_state->{script_type},
5655     level => $self->{level}->{uncertain});
5656     ## ISSUE: Should we raise some kind of error for
5657     ## <script type="text/xml">aaaaa</script>?
5658     ## NOTE: ^^^ This is why we throw an "uncertain" error.
5659 wakaba 1.93 } else {
5660     $self->{onsubdoc}->({s => $element_state->{text},
5661     container_node => $item->{node},
5662     media_type => $element_state->{script_type},
5663     is_char_string => 1});
5664     }
5665 wakaba 1.40
5666     $HTMLChecker{check_end}->(@_);
5667 wakaba 1.1 }
5668     },
5669 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
5670     ## NOTE: "When used to include script data, the script data must be embedded
5671     ## inline, the format of the data must be given using the type attribute,
5672     ## and the src attribute must not be specified." - not testable.
5673     ## TODO: It would be possible to err <script type=text/plain src=...>
5674 wakaba 1.1 };
5675 wakaba 1.25 ## ISSUE: Significant check and text child node
5676 wakaba 1.1
5677     ## NOTE: When script is disabled.
5678     $Element->{$HTML_NS}->{noscript} = {
5679 wakaba 1.40 %HTMLTransparentChecker,
5680 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5681     check_attrs => $GetHTMLAttrsChecker->({}, {
5682     %HTMLAttrStatus,
5683     %HTMLM12NCommonAttrStatus,
5684 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5685 wakaba 1.49 }),
5686 wakaba 1.40 check_start => sub {
5687     my ($self, $item, $element_state) = @_;
5688 wakaba 1.3
5689 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
5690 wakaba 1.104 $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript',
5691     level => $self->{level}->{must});
5692 wakaba 1.3 }
5693    
5694 wakaba 1.40 unless ($self->{flag}->{in_head}) {
5695     $self->_add_minus_elements ($element_state,
5696     {$HTML_NS => {noscript => 1}});
5697     }
5698 wakaba 1.79
5699     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5700     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5701 wakaba 1.3 },
5702 wakaba 1.40 check_child_element => sub {
5703     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5704     $child_is_transparent, $element_state) = @_;
5705     if ($self->{flag}->{in_head}) {
5706     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5707     $self->{onerror}->(node => $child_el,
5708     type => 'element not allowed:minus',
5709 wakaba 1.104 level => $self->{level}->{must});
5710 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5711     #
5712     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
5713     #
5714     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
5715     if ($child_el->has_attribute_ns (undef, 'scoped')) {
5716     $self->{onerror}->(node => $child_el,
5717     type => 'element not allowed:head noscript',
5718 wakaba 1.104 level => $self->{level}->{must});
5719 wakaba 1.40 }
5720     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
5721 wakaba 1.47 my $http_equiv_attr
5722     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
5723     if ($http_equiv_attr) {
5724     ## TODO: case
5725     if (lc $http_equiv_attr->value eq 'content-type') {
5726 wakaba 1.40 $self->{onerror}->(node => $child_el,
5727 wakaba 1.34 type => 'element not allowed:head noscript',
5728 wakaba 1.104 level => $self->{level}->{must});
5729 wakaba 1.47 } else {
5730     #
5731 wakaba 1.3 }
5732 wakaba 1.47 } else {
5733     $self->{onerror}->(node => $child_el,
5734     type => 'element not allowed:head noscript',
5735 wakaba 1.104 level => $self->{level}->{must});
5736 wakaba 1.3 }
5737 wakaba 1.40 } else {
5738     $self->{onerror}->(node => $child_el,
5739     type => 'element not allowed:head noscript',
5740 wakaba 1.104 level => $self->{level}->{must});
5741 wakaba 1.40 }
5742     } else {
5743     $HTMLTransparentChecker{check_child_element}->(@_);
5744     }
5745     },
5746     check_child_text => sub {
5747     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5748     if ($self->{flag}->{in_head}) {
5749     if ($has_significant) {
5750     $self->{onerror}->(node => $child_node,
5751 wakaba 1.104 type => 'character not allowed',
5752     level => $self->{level}->{must});
5753 wakaba 1.3 }
5754     } else {
5755 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
5756     }
5757     },
5758     check_end => sub {
5759     my ($self, $item, $element_state) = @_;
5760     $self->_remove_minus_elements ($element_state);
5761     if ($self->{flag}->{in_head}) {
5762     $HTMLChecker{check_end}->(@_);
5763     } else {
5764     $HTMLPhrasingContentChecker{check_end}->(@_);
5765 wakaba 1.3 }
5766 wakaba 1.1 },
5767     };
5768 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
5769 wakaba 1.1
5770     $Element->{$HTML_NS}->{'event-source'} = {
5771 wakaba 1.40 %HTMLEmptyChecker,
5772 wakaba 1.48 status => FEATURE_HTML5_LC,
5773 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5774 wakaba 1.1 src => $HTMLURIAttrChecker,
5775 wakaba 1.50 }, {
5776     %HTMLAttrStatus,
5777     src => FEATURE_HTML5_LC,
5778 wakaba 1.1 }),
5779 wakaba 1.66 check_start => sub {
5780     my ($self, $item, $element_state) = @_;
5781    
5782     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5783 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5784     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5785 wakaba 1.66 },
5786 wakaba 1.1 };
5787    
5788     $Element->{$HTML_NS}->{details} = {
5789 wakaba 1.72 %HTMLFlowContentChecker,
5790 wakaba 1.48 status => FEATURE_HTML5_WD,
5791 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5792 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
5793 wakaba 1.50 }, {
5794     %HTMLAttrStatus,
5795 wakaba 1.59 open => FEATURE_HTML5_WD,
5796 wakaba 1.1 }),
5797 wakaba 1.72 ## NOTE: legend, Flow
5798 wakaba 1.43 check_child_element => sub {
5799     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5800     $child_is_transparent, $element_state) = @_;
5801     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5802     $self->{onerror}->(node => $child_el,
5803     type => 'element not allowed:minus',
5804 wakaba 1.104 level => $self->{level}->{must});
5805 wakaba 1.43 $element_state->{has_non_legend} = 1;
5806     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5807     #
5808     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5809     if ($element_state->{has_non_legend}) {
5810     $self->{onerror}->(node => $child_el,
5811     type => 'element not allowed:details legend',
5812 wakaba 1.104 level => $self->{level}->{must});
5813 wakaba 1.43 }
5814     $element_state->{has_legend} = 1;
5815     $element_state->{has_non_legend} = 1;
5816     } else {
5817 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
5818 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5819     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
5820     ## is conforming?
5821     }
5822     },
5823     check_child_text => sub {
5824     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5825     if ($has_significant) {
5826     $element_state->{has_non_legend} = 1;
5827     }
5828     },
5829     check_end => sub {
5830     my ($self, $item, $element_state) = @_;
5831 wakaba 1.1
5832 wakaba 1.43 unless ($element_state->{has_legend}) {
5833     $self->{onerror}->(node => $item->{node},
5834 wakaba 1.104 type => 'element missing',
5835     text => 'legend',
5836     level => $self->{level}->{must});
5837 wakaba 1.43 }
5838    
5839 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
5840 wakaba 1.43 ## ISSUE: |<details><legend>aa</legend></details>| error?
5841 wakaba 1.1 },
5842     };
5843    
5844     $Element->{$HTML_NS}->{datagrid} = {
5845 wakaba 1.72 %HTMLFlowContentChecker,
5846 wakaba 1.48 status => FEATURE_HTML5_WD,
5847 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5848 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5849     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5850 wakaba 1.50 }, {
5851     %HTMLAttrStatus,
5852     disabled => FEATURE_HTML5_WD,
5853     multiple => FEATURE_HTML5_WD,
5854 wakaba 1.1 }),
5855 wakaba 1.40 check_start => sub {
5856     my ($self, $item, $element_state) = @_;
5857 wakaba 1.1
5858 wakaba 1.40 $self->_add_minus_elements ($element_state,
5859     {$HTML_NS => {a => 1, datagrid => 1}});
5860     $element_state->{phase} = 'any';
5861 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5862     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5863 wakaba 1.40 },
5864 wakaba 1.95 ## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select |
5865     ## datalist | Empty
5866 wakaba 1.40 check_child_element => sub {
5867     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5868     $child_is_transparent, $element_state) = @_;
5869     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5870     $self->{onerror}->(node => $child_el,
5871     type => 'element not allowed:minus',
5872 wakaba 1.104 level => $self->{level}->{must});
5873 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5874     #
5875 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
5876     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5877 wakaba 1.44 if (not $element_state->{has_element} and
5878 wakaba 1.40 $child_nsuri eq $HTML_NS and
5879 wakaba 1.95 {
5880     table => 1, select => 1, datalist => 1,
5881     }->{$child_ln}) {
5882 wakaba 1.40 $self->{onerror}->(node => $child_el,
5883 wakaba 1.104 type => 'element not allowed',
5884     level => $self->{level}->{must});
5885 wakaba 1.40 } else {
5886 wakaba 1.8 #
5887 wakaba 1.1 }
5888 wakaba 1.40 } else {
5889     $self->{onerror}->(node => $child_el,
5890 wakaba 1.104 type => 'element not allowed',
5891     level => $self->{level}->{must});
5892 wakaba 1.40 }
5893 wakaba 1.43 $element_state->{has_element} = 1;
5894 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
5895     if ($child_nsuri eq $HTML_NS and
5896     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
5897     $element_state->{phase} = 'none';
5898 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5899 wakaba 1.40 $element_state->{has_element} = 1;
5900 wakaba 1.72 $element_state->{phase} = 'flow';
5901 wakaba 1.40 } else {
5902     $self->{onerror}->(node => $child_el,
5903 wakaba 1.104 type => 'element not allowed',
5904     level => $self->{level}->{must});
5905 wakaba 1.40 }
5906     } elsif ($element_state->{phase} eq 'none') {
5907     $self->{onerror}->(node => $child_el,
5908 wakaba 1.104 type => 'element not allowed',
5909     level => $self->{level}->{must});
5910 wakaba 1.40 } else {
5911     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
5912     }
5913     },
5914     check_child_text => sub {
5915     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5916     if ($has_significant) {
5917 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
5918 wakaba 1.40 #
5919     } elsif ($element_state->{phase} eq 'any') {
5920 wakaba 1.72 $element_state->{phase} = 'flow';
5921 wakaba 1.40 } else {
5922     $self->{onerror}->(node => $child_node,
5923 wakaba 1.104 type => 'character not allowed',
5924     level => $self->{level}->{must});
5925 wakaba 1.1 }
5926     }
5927 wakaba 1.40 },
5928     check_end => sub {
5929     my ($self, $item, $element_state) = @_;
5930     $self->_remove_minus_elements ($element_state);
5931 wakaba 1.1
5932 wakaba 1.95 if ($element_state->{phase} eq 'flow') {
5933     if ($element_state->{has_significant}) {
5934     $item->{real_parent_state}->{has_significant} = 1;
5935     } elsif ($item->{transparent}) {
5936     #
5937     } else {
5938     $self->{onerror}->(node => $item->{node},
5939     level => $self->{should_level},
5940 wakaba 1.104 type => 'no significant content',
5941     level => $self->{level}->{must});
5942 wakaba 1.95 }
5943     } else {
5944     ## NOTE: Since the content model explicitly allows a |datagird| element
5945     ## being empty, we don't raise "no significant content" error for this
5946     ## element when there is no element. (We should raise an error for
5947     ## |<datagrid><br></datagrid>|, however.)
5948     ## NOTE: As a side-effect, when the |datagrid| element only contains
5949     ## non-conforming content, then the |phase| flag has not changed from
5950     ## |any|, no "no significant content" error is raised neither.
5951     ## NOTE: Another side-effect of the current implementation:
5952     ## |<daragrid><datagrid/></datagrid>| has no "no significant content"
5953     ## error at all.
5954 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5955     }
5956     },
5957 wakaba 1.1 };
5958    
5959     $Element->{$HTML_NS}->{command} = {
5960 wakaba 1.40 %HTMLEmptyChecker,
5961 wakaba 1.48 status => FEATURE_HTML5_WD,
5962 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5963 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
5964     default => $GetHTMLBooleanAttrChecker->('default'),
5965     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5966     hidden => $GetHTMLBooleanAttrChecker->('hidden'),
5967     icon => $HTMLURIAttrChecker,
5968     label => sub { }, ## NOTE: No conformance creteria
5969     radiogroup => sub { }, ## NOTE: No conformance creteria
5970     type => sub {
5971     my ($self, $attr) = @_;
5972     my $value = $attr->value;
5973     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
5974 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
5975     level => $self->{level}->{must});
5976 wakaba 1.1 }
5977     },
5978 wakaba 1.50 }, {
5979     %HTMLAttrStatus,
5980     checked => FEATURE_HTML5_WD,
5981     default => FEATURE_HTML5_WD,
5982     disabled => FEATURE_HTML5_WD,
5983     hidden => FEATURE_HTML5_WD,
5984     icon => FEATURE_HTML5_WD,
5985     label => FEATURE_HTML5_WD,
5986     radiogroup => FEATURE_HTML5_WD,
5987     type => FEATURE_HTML5_WD,
5988 wakaba 1.1 }),
5989 wakaba 1.66 check_start => sub {
5990     my ($self, $item, $element_state) = @_;
5991    
5992     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
5993 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5994     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5995 wakaba 1.66 },
5996 wakaba 1.1 };
5997    
5998     $Element->{$HTML_NS}->{menu} = {
5999 wakaba 1.40 %HTMLPhrasingContentChecker,
6000 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
6001     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
6002     ## NOTE: We don't want any |menu| element warned as deprecated.
6003 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6004 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
6005 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
6006 wakaba 1.1 id => sub {
6007     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
6008     my ($self, $attr) = @_;
6009     my $value = $attr->value;
6010     if (length $value > 0) {
6011     if ($self->{id}->{$value}) {
6012 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
6013     level => $self->{level}->{must});
6014 wakaba 1.1 push @{$self->{id}->{$value}}, $attr;
6015     } else {
6016     $self->{id}->{$value} = [$attr];
6017     }
6018     } else {
6019     ## NOTE: MUST contain at least one character
6020 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
6021     level => $self->{level}->{must});
6022 wakaba 1.1 }
6023     if ($value =~ /[\x09-\x0D\x20]/) {
6024 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
6025     level => $self->{level}->{must});
6026 wakaba 1.1 }
6027     $self->{menu}->{$value} ||= $attr;
6028     ## ISSUE: <menu id=""><p contextmenu=""> match?
6029     },
6030     label => sub { }, ## NOTE: No conformance creteria
6031     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
6032 wakaba 1.49 }, {
6033     %HTMLAttrStatus,
6034     %HTMLM12NCommonAttrStatus,
6035 wakaba 1.61 align => FEATURE_HTML2X_RFC,
6036 wakaba 1.50 autosubmit => FEATURE_HTML5_WD,
6037 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
6038 wakaba 1.50 label => FEATURE_HTML5_WD,
6039     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6040 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6041     sdapref => FEATURE_HTML20_RFC,
6042 wakaba 1.50 type => FEATURE_HTML5_WD,
6043 wakaba 1.1 }),
6044 wakaba 1.40 check_start => sub {
6045     my ($self, $item, $element_state) = @_;
6046     $element_state->{phase} = 'li or phrasing';
6047     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
6048     $self->{flag}->{in_menu} = 1;
6049 wakaba 1.79
6050     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6051     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6052 wakaba 1.40 },
6053     check_child_element => sub {
6054     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6055     $child_is_transparent, $element_state) = @_;
6056     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6057     $self->{onerror}->(node => $child_el,
6058     type => 'element not allowed:minus',
6059 wakaba 1.104 level => $self->{level}->{must});
6060 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6061     #
6062     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
6063     if ($element_state->{phase} eq 'li') {
6064     #
6065     } elsif ($element_state->{phase} eq 'li or phrasing') {
6066     $element_state->{phase} = 'li';
6067     } else {
6068 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6069     level => $self->{level}->{must});
6070 wakaba 1.40 }
6071     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6072     if ($element_state->{phase} eq 'phrasing') {
6073     #
6074     } elsif ($element_state->{phase} eq 'li or phrasing') {
6075     $element_state->{phase} = 'phrasing';
6076     } else {
6077 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6078     level => $self->{level}->{must});
6079 wakaba 1.40 }
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     },
6085     check_child_text => sub {
6086     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6087     if ($has_significant) {
6088     if ($element_state->{phase} eq 'phrasing') {
6089     #
6090     } elsif ($element_state->{phase} eq 'li or phrasing') {
6091     $element_state->{phase} = 'phrasing';
6092     } else {
6093     $self->{onerror}->(node => $child_node,
6094 wakaba 1.104 type => 'character not allowed',
6095     level => $self->{level}->{must});
6096 wakaba 1.1 }
6097     }
6098 wakaba 1.40 },
6099     check_end => sub {
6100     my ($self, $item, $element_state) = @_;
6101     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
6102    
6103     if ($element_state->{phase} eq 'li') {
6104     $HTMLChecker{check_end}->(@_);
6105     } else { # 'phrasing' or 'li or phrasing'
6106     $HTMLPhrasingContentChecker{check_end}->(@_);
6107 wakaba 1.1 }
6108     },
6109 wakaba 1.8 };
6110    
6111     $Element->{$HTML_NS}->{datatemplate} = {
6112 wakaba 1.40 %HTMLChecker,
6113 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6114 wakaba 1.40 check_child_element => sub {
6115     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6116     $child_is_transparent, $element_state) = @_;
6117     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6118     $self->{onerror}->(node => $child_el,
6119     type => 'element not allowed:minus',
6120 wakaba 1.104 level => $self->{level}->{must});
6121 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6122     #
6123     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
6124     #
6125     } else {
6126     $self->{onerror}->(node => $child_el,
6127 wakaba 1.104 type => 'element not allowed:datatemplate',
6128     level => $self->{level}->{must});
6129 wakaba 1.40 }
6130     },
6131     check_child_text => sub {
6132     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6133     if ($has_significant) {
6134 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
6135     level => $self->{level}->{must});
6136 wakaba 1.8 }
6137     },
6138     is_xml_root => 1,
6139     };
6140    
6141     $Element->{$HTML_NS}->{rule} = {
6142 wakaba 1.40 %HTMLChecker,
6143 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6144 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6145 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
6146 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
6147 wakaba 1.50 }, {
6148     %HTMLAttrStatus,
6149     condition => FEATURE_HTML5_AT_RISK,
6150     mode => FEATURE_HTML5_AT_RISK,
6151 wakaba 1.8 }),
6152 wakaba 1.40 check_start => sub {
6153     my ($self, $item, $element_state) = @_;
6154 wakaba 1.79
6155 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
6156 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
6157     $self->{flag}->{in_rule} = 1;
6158    
6159     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6160     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6161 wakaba 1.40 },
6162     check_child_element => sub { },
6163     check_child_text => sub { },
6164     check_end => sub {
6165     my ($self, $item, $element_state) = @_;
6166 wakaba 1.79
6167 wakaba 1.40 $self->_remove_plus_elements ($element_state);
6168 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
6169    
6170 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6171 wakaba 1.8 },
6172     ## NOTE: "MAY be anything that, when the parent |datatemplate|
6173     ## is applied to some conforming data, results in a conforming DOM tree.":
6174     ## We don't check against this.
6175     };
6176    
6177     $Element->{$HTML_NS}->{nest} = {
6178 wakaba 1.40 %HTMLEmptyChecker,
6179 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6180 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6181 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
6182     mode => sub {
6183     my ($self, $attr) = @_;
6184     my $value = $attr->value;
6185     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
6186 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'mode:syntax error',
6187     level => $self->{level}->{must});
6188 wakaba 1.23 }
6189     },
6190 wakaba 1.50 }, {
6191     %HTMLAttrStatus,
6192     filter => FEATURE_HTML5_AT_RISK,
6193     mode => FEATURE_HTML5_AT_RISK,
6194 wakaba 1.8 }),
6195 wakaba 1.1 };
6196    
6197     $Element->{$HTML_NS}->{legend} = {
6198 wakaba 1.40 %HTMLPhrasingContentChecker,
6199 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6200 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6201 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6202 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
6203     # top => 1, bottom => 1, left => 1, right => 1,
6204     # }),
6205     }, {
6206 wakaba 1.49 %HTMLAttrStatus,
6207     %HTMLM12NCommonAttrStatus,
6208     accesskey => FEATURE_M12N10_REC,
6209     align => FEATURE_M12N10_REC_DEPRECATED,
6210 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6211 wakaba 1.49 }),
6212 wakaba 1.1 };
6213    
6214     $Element->{$HTML_NS}->{div} = {
6215 wakaba 1.72 %HTMLFlowContentChecker,
6216 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
6217 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
6218     align => $GetHTMLEnumeratedAttrChecker->({
6219     left => 1, center => 1, right => 1, justify => 1,
6220     }),
6221     }, {
6222 wakaba 1.49 %HTMLAttrStatus,
6223 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6224 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
6225     datafld => FEATURE_HTML4_REC_RESERVED,
6226     dataformatas => FEATURE_HTML4_REC_RESERVED,
6227     datasrc => FEATURE_HTML4_REC_RESERVED,
6228 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6229 wakaba 1.49 }),
6230 wakaba 1.66 check_start => sub {
6231     my ($self, $item, $element_state) = @_;
6232    
6233     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6234 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6235     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6236 wakaba 1.66 },
6237 wakaba 1.1 };
6238    
6239 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
6240 wakaba 1.72 %HTMLFlowContentChecker,
6241 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
6242     check_attrs => $GetHTMLAttrsChecker->({}, {
6243     %HTMLAttrStatus,
6244     %HTMLM12NCommonAttrStatus,
6245     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6246     }),
6247     };
6248    
6249 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
6250 wakaba 1.40 %HTMLTransparentChecker,
6251 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
6252 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
6253     ## TODO: HTML4 |size|, |color|, |face|
6254 wakaba 1.49 }, {
6255     %HTMLAttrStatus,
6256 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6257 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
6258 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6259 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
6260 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6261     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6262 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
6263 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6264 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6265 wakaba 1.49 }),
6266 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
6267     ## it is allowed only in a document with the WYSIWYG signature. The
6268     ## checker does not check whether there is the signature, since the
6269     ## signature is dropped, too, and has never been implemented. (In addition,
6270     ## for any |font| element an "element not defined" error is raised anyway,
6271     ## such that we don't have to raise an additional error.)
6272 wakaba 1.1 };
6273 wakaba 1.49
6274 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
6275     %HTMLEmptyChecker,
6276     status => FEATURE_M12N10_REC_DEPRECATED,
6277     check_attrs => $GetHTMLAttrsChecker->({
6278     ## TODO: color, face, size
6279     }, {
6280     %HTMLAttrStatus,
6281     color => FEATURE_M12N10_REC_DEPRECATED,
6282     face => FEATURE_M12N10_REC_DEPRECATED,
6283     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
6284     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6285     size => FEATURE_M12N10_REC_DEPRECATED,
6286     }),
6287     };
6288    
6289 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
6290     ## class title id cols rows onload onunload style(x10)
6291     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
6292     ## noframes Common, lang(xhtml10)
6293    
6294 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
6295 wakaba 1.56
6296 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
6297     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
6298     ## xmp, listing sdapref[HTML2,0]
6299    
6300 wakaba 1.56 =pod
6301    
6302     WF2: Documents MUST comply to [CHARMOD].
6303     WF2: Vencor extensions MUST NOT be used.
6304    
6305 wakaba 1.61 HTML 2.0 nextid @n
6306    
6307     RFC 2659: CERTS CRYPTOPTS
6308    
6309     ISO-HTML: pre-html, divN
6310 wakaba 1.82
6311     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
6312     di (Common), nl (Common), handler (Common, type), standby (Common),
6313     summary (Common)
6314    
6315 wakaba 1.97 Access & XHTML2: access (LC)
6316 wakaba 1.82
6317     XML Events & XForms (for XHTML2 support; very, very low priority)
6318 wakaba 1.61
6319 wakaba 1.56 =cut
6320 wakaba 1.61
6321     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
6322     ## We added them only to |a|. |link| and |form| might also allow them
6323     ## in theory.
6324 wakaba 1.1
6325     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
6326    
6327     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24