/[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.114 - (hide annotations) (download)
Sat Aug 30 16:09:07 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.113: +15 -7 lines
++ whatpm/t/ChangeLog	30 Aug 2008 15:42:55 -0000
2008-08-31  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: <embed> w/o src="" is conforming (HTML5
	revision 1929).

++ whatpm/Whatpm/ContentChecker/ChangeLog	30 Aug 2008 16:08:38 -0000
2008-08-31  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Element status updated.

2008-08-31  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: <embed> without src="" is conforming (HTML5
	revision 1929).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24