/[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.112 - (hide annotations) (download)
Sat Aug 30 14:37:46 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.111: +10 -9 lines
++ whatpm/t/ChangeLog	30 Aug 2008 14:25:03 -0000
	* content-model-4.dat: Some test data for <time> are added (c.f.
	HTML5 revision 2094).

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

	* ContentType.t: Support for image/svg+xml (HTML revision 2096).

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

++ whatpm/Whatpm/ChangeLog	30 Aug 2008 14:23:04 -0000
	* HTMLTable.pm: Zs is not what we want; we want White_Space! (HTML5
	revision 2094).

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

	* ContentType.pm: Support for image/svg+xml (HTML5 revision 2096).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	30 Aug 2008 14:35:43 -0000
	* HTML.pm: Use White_Space instead of Zs for date or time
	string in content (HTML5 revision 2094).  Make "YYYY-MM-DDHH:MM" (that
	misses a white space or "T" literal between day and hour)
	not raise two errors.

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

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.48 status => FEATURE_HTML5_DEFAULT,
3252 wakaba 1.40 %HTMLPhrasingContentChecker,
3253     check_attrs => $GetHTMLAttrsChecker->({
3254 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3255     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3256     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3257     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3258     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3259     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3260 wakaba 1.50 }, {
3261     %HTMLAttrStatus,
3262     high => FEATURE_HTML5_DEFAULT,
3263     low => FEATURE_HTML5_DEFAULT,
3264     max => FEATURE_HTML5_DEFAULT,
3265     min => FEATURE_HTML5_DEFAULT,
3266     optimum => FEATURE_HTML5_DEFAULT,
3267     value => FEATURE_HTML5_DEFAULT,
3268 wakaba 1.1 }),
3269     };
3270    
3271     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3272 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3273 wakaba 1.40 %HTMLPhrasingContentChecker,
3274     check_attrs => $GetHTMLAttrsChecker->({
3275 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3276     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3277 wakaba 1.50 }, {
3278     %HTMLAttrStatus,
3279     max => FEATURE_HTML5_DEFAULT,
3280     value => FEATURE_HTML5_DEFAULT,
3281 wakaba 1.1 }),
3282     };
3283    
3284     $Element->{$HTML_NS}->{code} = {
3285 wakaba 1.40 %HTMLPhrasingContentChecker,
3286 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3287 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3288     %HTMLAttrStatus,
3289 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3290 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3291 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3292 wakaba 1.49 }),
3293 wakaba 1.1 };
3294    
3295     $Element->{$HTML_NS}->{var} = {
3296 wakaba 1.40 %HTMLPhrasingContentChecker,
3297 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3298 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3299     %HTMLAttrStatus,
3300 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3301 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3302 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3303 wakaba 1.49 }),
3304 wakaba 1.1 };
3305    
3306     $Element->{$HTML_NS}->{samp} = {
3307 wakaba 1.40 %HTMLPhrasingContentChecker,
3308 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3309 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3310     %HTMLAttrStatus,
3311 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3312 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3313 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3314 wakaba 1.49 }),
3315 wakaba 1.1 };
3316    
3317     $Element->{$HTML_NS}->{kbd} = {
3318 wakaba 1.40 %HTMLPhrasingContentChecker,
3319 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3320 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3321     %HTMLAttrStatus,
3322 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3323 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3324 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3325 wakaba 1.49 }),
3326 wakaba 1.1 };
3327    
3328     $Element->{$HTML_NS}->{sub} = {
3329 wakaba 1.40 %HTMLPhrasingContentChecker,
3330 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3331 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3332     %HTMLAttrStatus,
3333 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3334 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3335 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3336 wakaba 1.49 }),
3337 wakaba 1.1 };
3338    
3339 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3340 wakaba 1.1
3341     $Element->{$HTML_NS}->{span} = {
3342 wakaba 1.40 %HTMLPhrasingContentChecker,
3343 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3344 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3345     %HTMLAttrStatus,
3346 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3347 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3348     dataformatas => FEATURE_HTML4_REC_RESERVED,
3349     datasrc => FEATURE_HTML4_REC_RESERVED,
3350 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3351 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3352 wakaba 1.49 }),
3353 wakaba 1.1 };
3354    
3355     $Element->{$HTML_NS}->{i} = {
3356 wakaba 1.40 %HTMLPhrasingContentChecker,
3357 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3358     check_attrs => $GetHTMLAttrsChecker->({}, {
3359     %HTMLAttrStatus,
3360     %HTMLM12NCommonAttrStatus,
3361 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3362 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3363 wakaba 1.49 }),
3364 wakaba 1.1 };
3365    
3366 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3367    
3368 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3369     %HTMLPhrasingContentChecker,
3370     status => FEATURE_M12N10_REC,
3371     check_attrs => $GetHTMLAttrsChecker->({}, {
3372     %HTMLAttrStatus,
3373     %HTMLM12NCommonAttrStatus,
3374     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3375     sdaform => FEATURE_HTML20_RFC,
3376     }),
3377     };
3378 wakaba 1.51
3379     $Element->{$HTML_NS}->{s} = {
3380 wakaba 1.40 %HTMLPhrasingContentChecker,
3381 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3382 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3383     %HTMLAttrStatus,
3384     %HTMLM12NCommonAttrStatus,
3385 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3386 wakaba 1.49 }),
3387 wakaba 1.1 };
3388    
3389 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3390    
3391     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3392    
3393 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3394 wakaba 1.40 %HTMLPhrasingContentChecker,
3395 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3396 wakaba 1.40 check_attrs => sub {
3397     my ($self, $item, $element_state) = @_;
3398 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3399     %HTMLAttrStatus,
3400 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3401     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3402     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3403 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3404 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3405     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3406 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3407     sdasuff => FEATURE_HTML2X_RFC,
3408 wakaba 1.49 })->($self, $item, $element_state);
3409 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3410     $self->{onerror}->(node => $item->{node},
3411 wakaba 1.104 type => 'attribute missing',
3412     text => 'dir',
3413     level => $self->{level}->{must});
3414 wakaba 1.1 }
3415     },
3416     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3417     };
3418    
3419 wakaba 1.99 $Element->{$HTML_NS}->{ruby} = {
3420     %HTMLPhrasingContentChecker,
3421     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3422     check_attrs => $GetHTMLAttrsChecker->({}, {
3423     %HTMLAttrStatus,
3424     %HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0
3425     lang => FEATURE_HTML5_DEFAULT,
3426     }),
3427     check_start => sub {
3428     my ($self, $item, $element_state) = @_;
3429    
3430     $element_state->{phase} = 'before-rb';
3431     #$element_state->{has_sig}
3432 wakaba 1.100
3433     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3434     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3435 wakaba 1.99 },
3436     ## NOTE: (phrasing, (rt | (rp, rt, rp)))+
3437     check_child_element => sub {
3438     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3439     $child_is_transparent, $element_state) = @_;
3440     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3441     $self->{onerror}->(node => $child_el,
3442     type => 'element not allowed:minus',
3443 wakaba 1.104 level => $self->{level}->{must});
3444 wakaba 1.99 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3445     #
3446     } elsif ($element_state->{phase} eq 'before-rb') {
3447     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3448     $element_state->{phase} = 'in-rb';
3449     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3450     $self->{onerror}->(node => $child_el,
3451 wakaba 1.104 level => $self->{level}->{should},
3452     type => 'no significant content before');
3453 wakaba 1.99 $element_state->{phase} = 'after-rt';
3454     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3455     $self->{onerror}->(node => $child_el,
3456 wakaba 1.104 level => $self->{level}->{should},
3457     type => 'no significant content before');
3458 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3459     } else {
3460     $self->{onerror}->(node => $child_el,
3461 wakaba 1.104 type => 'element not allowed:ruby base',
3462     level => $self->{level}->{must});
3463 wakaba 1.99 $element_state->{phase} = 'in-rb';
3464     }
3465     } elsif ($element_state->{phase} eq 'in-rb') {
3466     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3467     #$element_state->{phase} = 'in-rb';
3468     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3469     unless ($element_state->{has_significant}) {
3470     $self->{onerror}->(node => $child_el,
3471 wakaba 1.104 level => $self->{level}->{should},
3472     type => 'no significant content before');
3473 wakaba 1.99 }
3474     $element_state->{phase} = 'after-rt';
3475     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3476     unless ($element_state->{has_significant}) {
3477     $self->{onerror}->(node => $child_el,
3478 wakaba 1.104 level => $self->{level}->{should},
3479     type => 'no significant content before');
3480 wakaba 1.99 }
3481     $element_state->{phase} = 'after-rp1';
3482     } else {
3483     $self->{onerror}->(node => $child_el,
3484 wakaba 1.104 type => 'element not allowed:ruby base',
3485     level => $self->{level}->{must});
3486 wakaba 1.99 #$element_state->{phase} = 'in-rb';
3487     }
3488     } elsif ($element_state->{phase} eq 'after-rt') {
3489     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3490     if ($element_state->{has_significant}) {
3491     $element_state->{has_sig} = 1;
3492     delete $element_state->{has_significant};
3493     }
3494     $element_state->{phase} = 'in-rb';
3495     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3496     $self->{onerror}->(node => $child_el,
3497 wakaba 1.104 level => $self->{level}->{should},
3498     type => 'no significant content before');
3499 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3500     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3501     $self->{onerror}->(node => $child_el,
3502 wakaba 1.104 level => $self->{level}->{should},
3503     type => 'no significant content before');
3504 wakaba 1.99 #$element_state->{phase} = 'after-rt';
3505     } else {
3506     $self->{onerror}->(node => $child_el,
3507 wakaba 1.104 type => 'element not allowed:ruby base',
3508     level => $self->{level}->{must});
3509 wakaba 1.99 if ($element_state->{has_significant}) {
3510     $element_state->{has_sig} = 1;
3511     delete $element_state->{has_significant};
3512     }
3513     $element_state->{phase} = 'in-rb';
3514     }
3515     } elsif ($element_state->{phase} eq 'after-rp1') {
3516     if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3517     $element_state->{phase} = 'after-rp-rt';
3518     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3519     $self->{onerror}->(node => $child_el,
3520 wakaba 1.104 type => 'ps element missing',
3521     text => 'rt',
3522     level => $self->{level}->{must});
3523 wakaba 1.99 $element_state->{phase} = 'after-rp2';
3524     } else {
3525     $self->{onerror}->(node => $child_el,
3526 wakaba 1.104 type => 'ps element missing',
3527     text => 'rt',
3528     level => $self->{level}->{must});
3529 wakaba 1.99 $self->{onerror}->(node => $child_el,
3530 wakaba 1.104 type => 'ps element missing',
3531     text => 'rp',
3532     level => $self->{level}->{must});
3533 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3534     $self->{onerror}->(node => $child_el,
3535 wakaba 1.104 type => 'element not allowed:ruby base',
3536     level => $self->{level}->{must});
3537 wakaba 1.99 }
3538     if ($element_state->{has_significant}) {
3539     $element_state->{has_sig} = 1;
3540     delete $element_state->{has_significant};
3541     }
3542     $element_state->{phase} = 'in-rb';
3543     }
3544     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3545     if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3546     $element_state->{phase} = 'after-rp2';
3547     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3548     $self->{onerror}->(node => $child_el,
3549 wakaba 1.104 type => 'ps element missing',
3550     text => 'rp',
3551     level => $self->{level}->{must});
3552 wakaba 1.99 $self->{onerror}->(node => $child_el,
3553 wakaba 1.104 level => $self->{level}->{should},
3554     type => 'no significant content before');
3555 wakaba 1.99 $element_state->{phase} = 'after-rt';
3556     } else {
3557     $self->{onerror}->(node => $child_el,
3558 wakaba 1.104 type => 'ps element missing',
3559     text => 'rp',
3560     level => $self->{level}->{must});
3561 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3562     $self->{onerror}->(node => $child_el,
3563 wakaba 1.104 type => 'element not allowed:ruby base',
3564     level => $self->{level}->{must});
3565 wakaba 1.99 }
3566     if ($element_state->{has_significant}) {
3567     $element_state->{has_sig} = 1;
3568     delete $element_state->{has_significant};
3569     }
3570     $element_state->{phase} = 'in-rb';
3571     }
3572     } elsif ($element_state->{phase} eq 'after-rp2') {
3573     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3574     if ($element_state->{has_significant}) {
3575     $element_state->{has_sig} = 1;
3576     delete $element_state->{has_significant};
3577     }
3578     $element_state->{phase} = 'in-rb';
3579     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3580     $self->{onerror}->(node => $child_el,
3581 wakaba 1.104 level => $self->{level}->{should},
3582     type => 'no significant content before');
3583 wakaba 1.99 $element_state->{phase} = 'after-rt';
3584     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3585     $self->{onerror}->(node => $child_el,
3586 wakaba 1.104 level => $self->{level}->{should},
3587     type => 'no significant content before');
3588 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3589     } else {
3590     $self->{onerror}->(node => $child_el,
3591 wakaba 1.104 type => 'element not allowed:ruby base',
3592     level => $self->{level}->{must});
3593 wakaba 1.99 if ($element_state->{has_significant}) {
3594     $element_state->{has_sig} = 1;
3595     delete $element_state->{has_significant};
3596     }
3597     $element_state->{phase} = 'in-rb';
3598     }
3599     } else {
3600     die "check_child_element: Bad |ruby| phase: $element_state->{phase}";
3601     }
3602     },
3603     check_child_text => sub {
3604     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3605     if ($has_significant) {
3606     if ($element_state->{phase} eq 'before-rb') {
3607     $element_state->{phase} = 'in-rb';
3608     } elsif ($element_state->{phase} eq 'in-rb') {
3609     #
3610     } elsif ($element_state->{phase} eq 'after-rt' or
3611     $element_state->{phase} eq 'after-rp2') {
3612     $element_state->{phase} = 'in-rb';
3613     } elsif ($element_state->{phase} eq 'after-rp1') {
3614     $self->{onerror}->(node => $child_node,
3615 wakaba 1.104 type => 'ps element missing',
3616     text => 'rt',
3617     level => $self->{level}->{must});
3618 wakaba 1.99 $self->{onerror}->(node => $child_node,
3619 wakaba 1.104 type => 'ps element missing',
3620     text => 'rp',
3621     level => $self->{level}->{must});
3622 wakaba 1.99 $element_state->{phase} = 'in-rb';
3623     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3624     $self->{onerror}->(node => $child_node,
3625 wakaba 1.104 type => 'ps element missing',
3626     text => 'rp',
3627     level => $self->{level}->{must});
3628 wakaba 1.99 $element_state->{phase} = 'in-rb';
3629     } else {
3630     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3631     }
3632     }
3633     },
3634     check_end => sub {
3635     my ($self, $item, $element_state) = @_;
3636     $self->_remove_minus_elements ($element_state);
3637    
3638     if ($element_state->{phase} eq 'before-rb') {
3639     $self->{onerror}->(node => $item->{node},
3640 wakaba 1.104 level => $self->{level}->{should},
3641 wakaba 1.99 type => 'no significant content');
3642     $self->{onerror}->(node => $item->{node},
3643 wakaba 1.104 type => 'element missing',
3644     text => 'rt',
3645     level => $self->{level}->{must});
3646 wakaba 1.99 } elsif ($element_state->{phase} eq 'in-rb') {
3647     unless ($element_state->{has_significant}) {
3648     $self->{onerror}->(node => $item->{node},
3649 wakaba 1.104 level => $self->{level}->{should},
3650     type => 'no significant content at the end');
3651 wakaba 1.99 }
3652     $self->{onerror}->(node => $item->{node},
3653 wakaba 1.104 type => 'element missing',
3654     text => 'rt',
3655     level => $self->{level}->{must});
3656 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rt' or
3657     $element_state->{phase} eq 'after-rp2') {
3658     #
3659     } elsif ($element_state->{phase} eq 'after-rp1') {
3660     $self->{onerror}->(node => $item->{node},
3661 wakaba 1.104 type => 'element missing',
3662     text => 'rt',
3663     level => $self->{level}->{must});
3664 wakaba 1.99 $self->{onerror}->(node => $item->{node},
3665 wakaba 1.104 type => 'element missing',
3666     text => 'rp',
3667     level => $self->{level}->{must});
3668 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rp-rt') {
3669     $self->{onerror}->(node => $item->{node},
3670 wakaba 1.104 type => 'element missing',
3671     text => 'rp',
3672     level => $self->{level}->{must});
3673 wakaba 1.99 } else {
3674     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3675     }
3676    
3677     ## NOTE: A modified version of |check_end| of %AnyChecker.
3678     if ($element_state->{has_significant} or $element_state->{has_sig}) {
3679     $item->{real_parent_state}->{has_significant} = 1;
3680     }
3681     },
3682     };
3683    
3684     $Element->{$HTML_NS}->{rt} = {
3685     %HTMLPhrasingContentChecker,
3686     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3687     check_attrs => $GetHTMLAttrsChecker->({}, {
3688     %HTMLAttrStatus,
3689     %HTMLM12NXHTML2CommonAttrStatus,
3690     lang => FEATURE_HTML5_DEFAULT,
3691     }),
3692     };
3693    
3694     $Element->{$HTML_NS}->{rp} = {
3695     %HTMLTextChecker,
3696     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3697     check_attrs => $GetHTMLAttrsChecker->({}, {
3698     %HTMLAttrStatus,
3699     %HTMLM12NXHTML2CommonAttrStatus,
3700     lang => FEATURE_HTML5_DEFAULT,
3701     }),
3702 wakaba 1.100 check_start => sub {
3703 wakaba 1.99 my ($self, $item, $element_state) = @_;
3704     $element_state->{text} = '';
3705 wakaba 1.100
3706     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3707     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3708 wakaba 1.99 },
3709     check_child_text => sub {
3710     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3711     if ($has_significant) {
3712     $element_state->{text} .= $child_node->data;
3713     ## NOTE: |<rp> <!---->(</rp>| is allowed.
3714     }
3715     },
3716     check_end => sub {
3717     my ($self, $item, $element_state) = @_;
3718    
3719     my $p_class = ($item->{parent_state} and
3720     $item->{parent_state}->{phase} and
3721     $item->{parent_state}->{phase} eq 'after-rp2')
3722     ? qr/\p{Pe}/ : qr/\p{Ps}/;
3723     if ($element_state->{text} =~ /\A$p_class\z/) {
3724     #=~ /\A[\x09-\x0D\x20]*${p_class}[\x09-\x0D\x20]*\z/) {
3725     #
3726     } else {
3727     $self->{onerror}->(node => $item->{node},
3728 wakaba 1.104 type => 'rp:syntax error',
3729     level => $self->{level}->{must});
3730 wakaba 1.99 }
3731    
3732     $HTMLTextChecker{check_end}->(@_);
3733     },
3734     };
3735    
3736 wakaba 1.29 =pod
3737    
3738     ## TODO:
3739    
3740     +
3741     + <p>Partly because of the confusion described above, authors are
3742     + strongly recommended to always mark up all paragraphs with the
3743     + <code>p</code> element, and to not have any <code>ins</code> or
3744     + <code>del</code> elements that cross across any <span
3745     + title="paragraph">implied paragraphs</span>.</p>
3746     +
3747     (An informative note)
3748    
3749     <p><code>ins</code> elements should not cross <span
3750     + title="paragraph">implied paragraph</span> boundaries.</p>
3751     (normative)
3752    
3753     + <p><code>del</code> elements should not cross <span
3754     + title="paragraph">implied paragraph</span> boundaries.</p>
3755     (normative)
3756    
3757     =cut
3758    
3759 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
3760 wakaba 1.40 %HTMLTransparentChecker,
3761 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3762 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3763 wakaba 1.1 cite => $HTMLURIAttrChecker,
3764     datetime => $HTMLDatetimeAttrChecker,
3765 wakaba 1.49 }, {
3766     %HTMLAttrStatus,
3767     %HTMLM12NCommonAttrStatus,
3768 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3769 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3770 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3771 wakaba 1.1 }),
3772 wakaba 1.66 check_start => sub {
3773     my ($self, $item, $element_state) = @_;
3774    
3775     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3776 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3777     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3778 wakaba 1.66 },
3779 wakaba 1.1 };
3780    
3781     $Element->{$HTML_NS}->{del} = {
3782 wakaba 1.40 %HTMLTransparentChecker,
3783 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3784 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3785 wakaba 1.1 cite => $HTMLURIAttrChecker,
3786     datetime => $HTMLDatetimeAttrChecker,
3787 wakaba 1.49 }, {
3788     %HTMLAttrStatus,
3789     %HTMLM12NCommonAttrStatus,
3790 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3791 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3792 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3793 wakaba 1.1 }),
3794 wakaba 1.40 check_end => sub {
3795     my ($self, $item, $element_state) = @_;
3796     if ($element_state->{has_significant}) {
3797     ## NOTE: Significantness flag does not propagate.
3798     } elsif ($item->{transparent}) {
3799     #
3800     } else {
3801     $self->{onerror}->(node => $item->{node},
3802 wakaba 1.104 level => $self->{level}->{should},
3803 wakaba 1.40 type => 'no significant content');
3804     }
3805 wakaba 1.1 },
3806 wakaba 1.66 check_start => sub {
3807     my ($self, $item, $element_state) = @_;
3808    
3809     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3810 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3811     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3812 wakaba 1.66 },
3813 wakaba 1.1 };
3814    
3815 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
3816 wakaba 1.72 %HTMLFlowContentChecker,
3817 wakaba 1.48 status => FEATURE_HTML5_FD,
3818 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
3819 wakaba 1.41 check_child_element => sub {
3820     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3821     $child_is_transparent, $element_state) = @_;
3822     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3823     $self->{onerror}->(node => $child_el,
3824     type => 'element not allowed:minus',
3825 wakaba 1.104 level => $self->{level}->{must});
3826 wakaba 1.41 $element_state->{has_non_legend} = 1;
3827     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3828     #
3829     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
3830     if ($element_state->{has_legend_at_first}) {
3831     $self->{onerror}->(node => $child_el,
3832     type => 'element not allowed:figure legend',
3833 wakaba 1.104 level => $self->{level}->{must});
3834 wakaba 1.41 } elsif ($element_state->{has_legend}) {
3835     $self->{onerror}->(node => $element_state->{has_legend},
3836     type => 'element not allowed:figure legend',
3837 wakaba 1.104 level => $self->{level}->{must});
3838 wakaba 1.41 $element_state->{has_legend} = $child_el;
3839     } elsif ($element_state->{has_non_legend}) {
3840     $element_state->{has_legend} = $child_el;
3841     } else {
3842     $element_state->{has_legend_at_first} = 1;
3843 wakaba 1.35 }
3844 wakaba 1.41 delete $element_state->{has_non_legend};
3845     } else {
3846 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3847 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
3848 wakaba 1.41 }
3849     },
3850     check_child_text => sub {
3851     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3852     if ($has_significant) {
3853     $element_state->{has_non_legend} = 1;
3854 wakaba 1.35 }
3855 wakaba 1.41 },
3856     check_end => sub {
3857     my ($self, $item, $element_state) = @_;
3858 wakaba 1.35
3859 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
3860     #
3861     } elsif ($element_state->{has_legend}) {
3862     if ($element_state->{has_non_legend}) {
3863     $self->{onerror}->(node => $element_state->{has_legend},
3864 wakaba 1.35 type => 'element not allowed:figure legend',
3865 wakaba 1.104 level => $self->{level}->{must});
3866 wakaba 1.35 }
3867     }
3868 wakaba 1.41
3869 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3870 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
3871 wakaba 1.35 },
3872     };
3873 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
3874 wakaba 1.1
3875 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
3876     my ($self, $attr) = @_;
3877 wakaba 1.104 $self->{onerror}->(node => $attr,
3878     type => 'unknown attribute',
3879     level => $self->{level}->{uncertain});
3880 wakaba 1.92 };
3881    
3882 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
3883 wakaba 1.40 %HTMLEmptyChecker,
3884 wakaba 1.98 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3885 wakaba 1.40 check_attrs => sub {
3886     my ($self, $item, $element_state) = @_;
3887 wakaba 1.1 $GetHTMLAttrsChecker->({
3888 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3889     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3890     }),
3891 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
3892 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3893 wakaba 1.1 src => $HTMLURIAttrChecker,
3894     usemap => $HTMLUsemapAttrChecker,
3895 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3896 wakaba 1.1 ismap => sub {
3897 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
3898     if (not $self->{flag}->{in_a_href}) {
3899 wakaba 1.15 $self->{onerror}->(node => $attr,
3900 wakaba 1.59 type => 'attribute not allowed:ismap',
3901 wakaba 1.104 level => $self->{level}->{must});
3902 wakaba 1.1 }
3903 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
3904 wakaba 1.1 },
3905 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
3906     ## TODO: HTML4 |name|
3907 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3908 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3909 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3910 wakaba 1.49 }, {
3911     %HTMLAttrStatus,
3912 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3913 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
3914 wakaba 1.98 alt => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3915 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
3916 wakaba 1.98 height => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3917 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
3918 wakaba 1.98 ismap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3919 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3920 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3921     name => FEATURE_M12N10_REC_DEPRECATED,
3922 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
3923 wakaba 1.98 src => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3924     usemap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3925 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
3926 wakaba 1.98 width => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3927 wakaba 1.66 })->($self, $item, $element_state);
3928 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
3929     $self->{onerror}->(node => $item->{node},
3930 wakaba 1.104 type => 'attribute missing',
3931     text => 'alt',
3932     level => $self->{level}->{should});
3933 wakaba 1.1 }
3934 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3935     $self->{onerror}->(node => $item->{node},
3936 wakaba 1.104 type => 'attribute missing',
3937     text => 'src',
3938     level => $self->{level}->{must});
3939 wakaba 1.1 }
3940 wakaba 1.66
3941     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3942     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
3943     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
3944     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
3945 wakaba 1.1 },
3946     };
3947    
3948     $Element->{$HTML_NS}->{iframe} = {
3949 wakaba 1.40 %HTMLTextChecker,
3950 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3951     ## NOTE: Not part of M12N10 Strict
3952 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3953 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3954 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3955 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
3956     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
3957     }),
3958     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
3959 wakaba 1.1 src => $HTMLURIAttrChecker,
3960 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3961 wakaba 1.49 }, {
3962     %HTMLAttrStatus,
3963     %HTMLM12NCommonAttrStatus,
3964     align => FEATURE_XHTML10_REC,
3965 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3966 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
3967 wakaba 1.92 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3968 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3969 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3970     marginheight => FEATURE_M12N10_REC,
3971     marginwidth => FEATURE_M12N10_REC,
3972 wakaba 1.76 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
3973     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3974 wakaba 1.92 sandbox => FEATURE_HTML5_DEFAULT,
3975 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
3976 wakaba 1.92 seemless => FEATURE_HTML5_DEFAULT,
3977 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3978     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3979 wakaba 1.92 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3980 wakaba 1.1 }),
3981 wakaba 1.66 check_start => sub {
3982     my ($self, $item, $element_state) = @_;
3983    
3984     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3985 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3986     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3987 wakaba 1.66 },
3988 wakaba 1.40 };
3989    
3990 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
3991 wakaba 1.40 %HTMLEmptyChecker,
3992 wakaba 1.98 status => FEATURE_HTML5_WD,
3993 wakaba 1.40 check_attrs => sub {
3994     my ($self, $item, $element_state) = @_;
3995 wakaba 1.1 my $has_src;
3996 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3997 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3998     $attr_ns = '' unless defined $attr_ns;
3999     my $attr_ln = $attr->manakai_local_name;
4000     my $checker;
4001 wakaba 1.73
4002     my $status = {
4003     %HTMLAttrStatus,
4004     height => FEATURE_HTML5_DEFAULT,
4005 wakaba 1.98 src => FEATURE_HTML5_WD,
4006     type => FEATURE_HTML5_WD,
4007 wakaba 1.73 width => FEATURE_HTML5_DEFAULT,
4008     }->{$attr_ln};
4009    
4010 wakaba 1.1 if ($attr_ns eq '') {
4011     if ($attr_ln eq 'src') {
4012     $checker = $HTMLURIAttrChecker;
4013     $has_src = 1;
4014     } elsif ($attr_ln eq 'type') {
4015     $checker = $HTMLIMTAttrChecker;
4016 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
4017     $checker = $AttrCheckerNotImplemented; ## TODO: because spec does not define them yet.
4018 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
4019 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4020     $status = $HTMLDatasetAttrStatus;
4021 wakaba 1.1 } else {
4022     $checker = $HTMLAttrChecker->{$attr_ln}
4023     || sub { }; ## NOTE: Any local attribute is ok.
4024 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
4025 wakaba 1.1 }
4026     }
4027     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4028 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4029     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4030     || $AttrStatus->{$attr_ns}->{''};
4031     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4032 wakaba 1.62
4033 wakaba 1.1 if ($checker) {
4034 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
4035 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4036 wakaba 1.54 #
4037 wakaba 1.1 } else {
4038 wakaba 1.104 $self->{onerror}->(node => $attr,
4039     type => 'unknown attribute',
4040     level => $self->{level}->{uncertain});
4041 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
4042     }
4043    
4044 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4045 wakaba 1.1 }
4046    
4047     unless ($has_src) {
4048 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4049 wakaba 1.104 type => 'attribute missing',
4050     text => 'src',
4051     level => $self->{level}->{must});
4052 wakaba 1.1 }
4053 wakaba 1.66
4054     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4055 wakaba 1.1 },
4056     };
4057    
4058 wakaba 1.49 ## TODO:
4059     ## {applet} FEATURE_M12N10_REC_DEPRECATED
4060     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
4061    
4062 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
4063 wakaba 1.40 %HTMLTransparentChecker,
4064 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4065 wakaba 1.40 check_attrs => sub {
4066     my ($self, $item, $element_state) = @_;
4067 wakaba 1.1 $GetHTMLAttrsChecker->({
4068 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4069     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4070     }),
4071     archive => $HTMLSpaceURIsAttrChecker,
4072     ## TODO: Relative to @codebase
4073     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4074     classid => $HTMLURIAttrChecker,
4075     codebase => $HTMLURIAttrChecker,
4076     codetype => $HTMLIMTAttrChecker,
4077     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
4078 wakaba 1.1 data => $HTMLURIAttrChecker,
4079 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
4080     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
4081     ## [HTML4] but we don't know how to test this.
4082     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4083 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4084 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
4085 wakaba 1.1 type => $HTMLIMTAttrChecker,
4086     usemap => $HTMLUsemapAttrChecker,
4087 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4088 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4089 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4090 wakaba 1.49 }, {
4091     %HTMLAttrStatus,
4092 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4093 wakaba 1.49 align => FEATURE_XHTML10_REC,
4094 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4095 wakaba 1.49 border => FEATURE_XHTML10_REC,
4096     classid => FEATURE_M12N10_REC,
4097     codebase => FEATURE_M12N10_REC,
4098     codetype => FEATURE_M12N10_REC,
4099 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
4100 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4101 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4102     dataformatas => FEATURE_HTML4_REC_RESERVED,
4103     datasrc => FEATURE_HTML4_REC_RESERVED,
4104 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4105 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4106 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
4107 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4108 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4109 wakaba 1.49 standby => FEATURE_M12N10_REC,
4110 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4111     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4112     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4113 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
4114 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4115 wakaba 1.66 })->($self, $item, $element_state);
4116 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
4117     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
4118     $self->{onerror}->(node => $item->{node},
4119 wakaba 1.104 type => 'attribute missing:data|type',
4120     level => $self->{level}->{must});
4121 wakaba 1.1 }
4122     }
4123 wakaba 1.66
4124     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
4125     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
4126     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
4127     ## TODO: archive
4128     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4129 wakaba 1.1 },
4130 wakaba 1.72 ## NOTE: param*, transparent (Flow)
4131 wakaba 1.41 check_child_element => sub {
4132     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4133     $child_is_transparent, $element_state) = @_;
4134     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4135     $self->{onerror}->(node => $child_el,
4136     type => 'element not allowed:minus',
4137 wakaba 1.104 level => $self->{level}->{must});
4138 wakaba 1.41 $element_state->{has_non_legend} = 1;
4139     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4140     #
4141     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
4142     if ($element_state->{has_non_param}) {
4143 wakaba 1.104 $self->{onerror}->(node => $child_el,
4144 wakaba 1.72 type => 'element not allowed:flow',
4145 wakaba 1.104 level => $self->{level}->{must});
4146 wakaba 1.39 }
4147 wakaba 1.41 } else {
4148 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4149 wakaba 1.41 $element_state->{has_non_param} = 1;
4150 wakaba 1.39 }
4151 wakaba 1.25 },
4152 wakaba 1.41 check_child_text => sub {
4153     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4154     if ($has_significant) {
4155     $element_state->{has_non_param} = 1;
4156     }
4157 wakaba 1.42 },
4158     check_end => sub {
4159     my ($self, $item, $element_state) = @_;
4160     if ($element_state->{has_significant}) {
4161 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
4162 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
4163     ## NOTE: Transparent.
4164     } else {
4165     $self->{onerror}->(node => $item->{node},
4166 wakaba 1.104 level => $self->{level}->{should},
4167 wakaba 1.42 type => 'no significant content');
4168     }
4169     },
4170 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
4171 wakaba 1.1 };
4172 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4173     ## What about |<section><object data><style scoped></style>x</object></section>|?
4174     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4175 wakaba 1.1
4176     $Element->{$HTML_NS}->{param} = {
4177 wakaba 1.40 %HTMLEmptyChecker,
4178 wakaba 1.94 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4179 wakaba 1.40 check_attrs => sub {
4180     my ($self, $item, $element_state) = @_;
4181 wakaba 1.1 $GetHTMLAttrsChecker->({
4182     name => sub { },
4183 wakaba 1.70 type => $HTMLIMTAttrChecker,
4184 wakaba 1.1 value => sub { },
4185 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4186     data => 1, ref => 1, object => 1,
4187     }),
4188 wakaba 1.49 }, {
4189     %HTMLAttrStatus,
4190 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4191 wakaba 1.94 name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4192 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4193 wakaba 1.94 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4194 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4195 wakaba 1.66 })->(@_);
4196 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4197     $self->{onerror}->(node => $item->{node},
4198 wakaba 1.104 type => 'attribute missing',
4199     text => 'name',
4200     level => $self->{level}->{must});
4201 wakaba 1.1 }
4202 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4203     $self->{onerror}->(node => $item->{node},
4204 wakaba 1.104 type => 'attribute missing',
4205     text => 'value',
4206     level => $self->{level}->{must});
4207 wakaba 1.1 }
4208     },
4209     };
4210    
4211     $Element->{$HTML_NS}->{video} = {
4212 wakaba 1.40 %HTMLTransparentChecker,
4213 wakaba 1.48 status => FEATURE_HTML5_LC,
4214 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4215 wakaba 1.1 src => $HTMLURIAttrChecker,
4216     ## TODO: start, loopstart, loopend, end
4217     ## ISSUE: they MUST be "value time offset"s. Value?
4218 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4219 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4220     controls => $GetHTMLBooleanAttrChecker->('controls'),
4221 wakaba 1.59 poster => $HTMLURIAttrChecker,
4222 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4223     width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4224 wakaba 1.50 }, {
4225     %HTMLAttrStatus,
4226     autoplay => FEATURE_HTML5_LC,
4227     controls => FEATURE_HTML5_LC,
4228     end => FEATURE_HTML5_LC,
4229     height => FEATURE_HTML5_LC,
4230     loopend => FEATURE_HTML5_LC,
4231     loopstart => FEATURE_HTML5_LC,
4232     playcount => FEATURE_HTML5_LC,
4233     poster => FEATURE_HTML5_LC,
4234     src => FEATURE_HTML5_LC,
4235     start => FEATURE_HTML5_LC,
4236     width => FEATURE_HTML5_LC,
4237 wakaba 1.1 }),
4238 wakaba 1.42 check_start => sub {
4239     my ($self, $item, $element_state) = @_;
4240     $element_state->{allow_source}
4241     = not $item->{node}->has_attribute_ns (undef, 'src');
4242     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4243     ## NOTE: It might be set true by |check_element|.
4244 wakaba 1.66
4245     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4246     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4247 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4248     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4249 wakaba 1.42 },
4250     check_child_element => sub {
4251     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4252     $child_is_transparent, $element_state) = @_;
4253     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4254     $self->{onerror}->(node => $child_el,
4255     type => 'element not allowed:minus',
4256 wakaba 1.104 level => $self->{level}->{must});
4257 wakaba 1.42 delete $element_state->{allow_source};
4258     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4259     #
4260     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4261 wakaba 1.45 unless ($element_state->{allow_source}) {
4262 wakaba 1.104 $self->{onerror}->(node => $child_el,
4263 wakaba 1.72 type => 'element not allowed:flow',
4264 wakaba 1.104 level => $self->{level}->{must});
4265 wakaba 1.42 }
4266 wakaba 1.45 $element_state->{has_source} = 1;
4267 wakaba 1.1 } else {
4268 wakaba 1.42 delete $element_state->{allow_source};
4269 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4270 wakaba 1.42 }
4271     },
4272     check_child_text => sub {
4273     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4274     if ($has_significant) {
4275     delete $element_state->{allow_source};
4276     }
4277 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4278 wakaba 1.42 },
4279     check_end => sub {
4280     my ($self, $item, $element_state) = @_;
4281     if ($element_state->{has_source} == -1) {
4282     $self->{onerror}->(node => $item->{node},
4283 wakaba 1.104 type => 'child element missing',
4284     text => 'source',
4285     level => $self->{level}->{must});
4286 wakaba 1.1 }
4287 wakaba 1.42
4288     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4289 wakaba 1.1 },
4290     };
4291    
4292     $Element->{$HTML_NS}->{audio} = {
4293 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4294 wakaba 1.48 status => FEATURE_HTML5_LC,
4295 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4296     src => $HTMLURIAttrChecker,
4297     ## TODO: start, loopstart, loopend, end
4298     ## ISSUE: they MUST be "value time offset"s. Value?
4299     ## ISSUE: playcount has no conformance creteria
4300     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4301     controls => $GetHTMLBooleanAttrChecker->('controls'),
4302 wakaba 1.50 }, {
4303     %HTMLAttrStatus,
4304     autoplay => FEATURE_HTML5_LC,
4305     controls => FEATURE_HTML5_LC,
4306     end => FEATURE_HTML5_LC,
4307     loopend => FEATURE_HTML5_LC,
4308     loopstart => FEATURE_HTML5_LC,
4309     playcount => FEATURE_HTML5_LC,
4310     src => FEATURE_HTML5_LC,
4311     start => FEATURE_HTML5_LC,
4312 wakaba 1.42 }),
4313 wakaba 1.1 };
4314    
4315     $Element->{$HTML_NS}->{source} = {
4316 wakaba 1.40 %HTMLEmptyChecker,
4317 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
4318 wakaba 1.40 check_attrs => sub {
4319     my ($self, $item, $element_state) = @_;
4320 wakaba 1.1 $GetHTMLAttrsChecker->({
4321 wakaba 1.90 media => $HTMLMQAttrChecker,
4322     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4323     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4324 wakaba 1.1 type => $HTMLIMTAttrChecker,
4325 wakaba 1.50 }, {
4326     %HTMLAttrStatus,
4327     media => FEATURE_HTML5_DEFAULT,
4328 wakaba 1.90 pixelratio => FEATURE_HTML5_DEFAULT,
4329 wakaba 1.50 src => FEATURE_HTML5_DEFAULT,
4330     type => FEATURE_HTML5_DEFAULT,
4331 wakaba 1.66 })->(@_);
4332 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4333     $self->{onerror}->(node => $item->{node},
4334 wakaba 1.104 type => 'attribute missing',
4335     text => 'src',
4336     level => $self->{level}->{must});
4337 wakaba 1.1 }
4338 wakaba 1.66
4339     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4340 wakaba 1.1 },
4341     };
4342    
4343     $Element->{$HTML_NS}->{canvas} = {
4344 wakaba 1.40 %HTMLTransparentChecker,
4345 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4346 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4347 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4348     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4349 wakaba 1.50 }, {
4350     %HTMLAttrStatus,
4351 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4352     width => FEATURE_HTML5_COMPLETE,
4353 wakaba 1.1 }),
4354     };
4355    
4356     $Element->{$HTML_NS}->{map} = {
4357 wakaba 1.72 %HTMLFlowContentChecker,
4358 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4359 wakaba 1.40 check_attrs => sub {
4360     my ($self, $item, $element_state) = @_;
4361 wakaba 1.4 my $has_id;
4362 wakaba 1.100 my $has_name;
4363 wakaba 1.4 $GetHTMLAttrsChecker->({
4364     id => sub {
4365 wakaba 1.100 ## NOTE: Same as the global |id=""|, with |$self->{map}| registration.
4366 wakaba 1.4 my ($self, $attr) = @_;
4367     my $value = $attr->value;
4368 wakaba 1.100 if (length $value) {
4369 wakaba 1.4 if ($self->{id}->{$value}) {
4370 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
4371     level => $self->{level}->{must});
4372 wakaba 1.4 push @{$self->{id}->{$value}}, $attr;
4373     } else {
4374     $self->{id}->{$value} = [$attr];
4375     }
4376 wakaba 1.1 } else {
4377 wakaba 1.4 ## NOTE: MUST contain at least one character
4378 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
4379     level => $self->{level}->{must});
4380 wakaba 1.1 }
4381 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
4382 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
4383     level => $self->{level}->{must});
4384 wakaba 1.4 }
4385 wakaba 1.100 #$self->{map}->{$value} ||= $attr;
4386     $has_id = [$value, $attr];
4387     },
4388     name => sub {
4389     my ($self, $attr) = @_;
4390     my $value = $attr->value;
4391     if (length $value) {
4392     ## NOTE: Duplication is not non-conforming.
4393     ## NOTE: Space characters are not non-conforming.
4394     #
4395     } else {
4396     $self->{onerror}->(node => $attr,
4397     type => 'empty attribute value',
4398 wakaba 1.104 level => $self->{level}->{must});
4399 wakaba 1.100 }
4400 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4401 wakaba 1.100 $has_name = [$value, $attr];
4402 wakaba 1.4 },
4403 wakaba 1.49 }, {
4404     %HTMLAttrStatus,
4405 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4406     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4407     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4408     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4409 wakaba 1.100 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
4410     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4411 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4412     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4413     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4414     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4415     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4416     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4417     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4418     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4419     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4420     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4421     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4422 wakaba 1.66 })->(@_);
4423 wakaba 1.100
4424     if ($has_name and $has_id) {
4425     if ($has_name->[0] ne $has_id->[0]) {
4426     $self->{onerror}->(node => $has_id->[1],
4427 wakaba 1.104 type => 'id ne name',
4428     level => $self->{level}->{must});
4429 wakaba 1.100 }
4430     } elsif (not $has_name) {
4431     $self->{onerror}->(node => $item->{node},
4432 wakaba 1.104 type => 'attribute missing',
4433     text => 'name',
4434     level => $self->{level}->{must});
4435 wakaba 1.100 }
4436 wakaba 1.4 },
4437 wakaba 1.59 check_start => sub {
4438     my ($self, $item, $element_state) = @_;
4439     $element_state->{in_map_original} = $self->{flag}->{in_map};
4440     $self->{flag}->{in_map} = 1;
4441 wakaba 1.79
4442     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4443     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4444 wakaba 1.59 },
4445     check_end => sub {
4446     my ($self, $item, $element_state) = @_;
4447     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
4448 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4449 wakaba 1.59 },
4450 wakaba 1.1 };
4451    
4452     $Element->{$HTML_NS}->{area} = {
4453 wakaba 1.40 %HTMLEmptyChecker,
4454 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4455 wakaba 1.40 check_attrs => sub {
4456     my ($self, $item, $element_state) = @_;
4457 wakaba 1.1 my %attr;
4458     my $coords;
4459 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4460 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4461     $attr_ns = '' unless defined $attr_ns;
4462     my $attr_ln = $attr->manakai_local_name;
4463     my $checker;
4464 wakaba 1.73 my $status;
4465 wakaba 1.1 if ($attr_ns eq '') {
4466 wakaba 1.73 $status = {
4467     %HTMLAttrStatus,
4468     %HTMLM12NCommonAttrStatus,
4469     accesskey => FEATURE_M12N10_REC,
4470     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4471     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4472 wakaba 1.99 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_M12N10_REC,
4473 wakaba 1.73 hreflang => FEATURE_HTML5_DEFAULT,
4474     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4475     media => FEATURE_HTML5_DEFAULT,
4476     nohref => FEATURE_M12N10_REC,
4477     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4478     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4479     ping => FEATURE_HTML5_DEFAULT,
4480 wakaba 1.99 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC,
4481 wakaba 1.73 shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4482     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4483     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4484     type => FEATURE_HTML5_DEFAULT,
4485     }->{$attr_ln};
4486    
4487 wakaba 1.1 $checker = {
4488 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4489 wakaba 1.1 alt => sub { },
4490     ## NOTE: |alt| value has no conformance creteria.
4491     shape => $GetHTMLEnumeratedAttrChecker->({
4492     circ => -1, circle => 1,
4493     default => 1,
4494     poly => 1, polygon => -1,
4495     rect => 1, rectangle => -1,
4496     }),
4497     coords => sub {
4498     my ($self, $attr) = @_;
4499     my $value = $attr->value;
4500     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4501     $coords = [split /,/, $value];
4502     } else {
4503     $self->{onerror}->(node => $attr,
4504 wakaba 1.104 type => 'coords:syntax error',
4505     level => $self->{level}->{must});
4506 wakaba 1.1 }
4507     },
4508 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4509     target => $HTMLTargetAttrChecker,
4510 wakaba 1.1 href => $HTMLURIAttrChecker,
4511     ping => $HTMLSpaceURIsAttrChecker,
4512 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4513 wakaba 1.1 media => $HTMLMQAttrChecker,
4514     hreflang => $HTMLLanguageTagAttrChecker,
4515     type => $HTMLIMTAttrChecker,
4516     }->{$attr_ln};
4517     if ($checker) {
4518     $attr{$attr_ln} = $attr;
4519 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
4520 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4521     $status = $HTMLDatasetAttrStatus;
4522 wakaba 1.1 } else {
4523     $checker = $HTMLAttrChecker->{$attr_ln};
4524     }
4525     }
4526     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4527 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4528     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4529     || $AttrStatus->{$attr_ns}->{''};
4530     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4531 wakaba 1.62
4532 wakaba 1.1 if ($checker) {
4533 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4534 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4535 wakaba 1.54 #
4536 wakaba 1.1 } else {
4537 wakaba 1.104 $self->{onerror}->(node => $attr,
4538     type => 'unknown attribute',
4539     level => $self->{level}->{uncertain});
4540 wakaba 1.1 ## ISSUE: No comformance createria for unknown attributes in the spec
4541     }
4542 wakaba 1.49
4543 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4544 wakaba 1.1 }
4545    
4546     if (defined $attr{href}) {
4547 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4548 wakaba 1.1 unless (defined $attr{alt}) {
4549 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4550 wakaba 1.104 type => 'attribute missing',
4551     text => 'alt',
4552     level => $self->{level}->{must});
4553 wakaba 1.1 }
4554     } else {
4555     for (qw/target ping rel media hreflang type alt/) {
4556     if (defined $attr{$_}) {
4557     $self->{onerror}->(node => $attr{$_},
4558 wakaba 1.104 type => 'attribute not allowed',
4559     level => $self->{level}->{must});
4560 wakaba 1.1 }
4561     }
4562     }
4563    
4564     my $shape = 'rectangle';
4565     if (defined $attr{shape}) {
4566     $shape = {
4567     circ => 'circle', circle => 'circle',
4568     default => 'default',
4569     poly => 'polygon', polygon => 'polygon',
4570     rect => 'rectangle', rectangle => 'rectangle',
4571     }->{lc $attr{shape}->value} || 'rectangle';
4572     ## TODO: ASCII lowercase?
4573     }
4574    
4575     if ($shape eq 'circle') {
4576     if (defined $attr{coords}) {
4577     if (defined $coords) {
4578     if (@$coords == 3) {
4579     if ($coords->[2] < 0) {
4580     $self->{onerror}->(node => $attr{coords},
4581 wakaba 1.104 type => 'coords:out of range',
4582     index => 2,
4583     value => $coords->[2],
4584     level => $self->{level}->{must});
4585 wakaba 1.1 }
4586     } else {
4587     $self->{onerror}->(node => $attr{coords},
4588 wakaba 1.104 type => 'coords:number not 3',
4589     text => 0+@$coords,
4590     level => $self->{level}->{must});
4591 wakaba 1.1 }
4592     } else {
4593     ## NOTE: A syntax error has been reported.
4594     }
4595     } else {
4596 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4597 wakaba 1.104 type => 'attribute missing',
4598     text => 'coords',
4599     level => $self->{level}->{must});
4600 wakaba 1.1 }
4601     } elsif ($shape eq 'default') {
4602     if (defined $attr{coords}) {
4603     $self->{onerror}->(node => $attr{coords},
4604 wakaba 1.104 type => 'attribute not allowed',
4605     level => $self->{level}->{must});
4606 wakaba 1.1 }
4607     } elsif ($shape eq 'polygon') {
4608     if (defined $attr{coords}) {
4609     if (defined $coords) {
4610     if (@$coords >= 6) {
4611     unless (@$coords % 2 == 0) {
4612     $self->{onerror}->(node => $attr{coords},
4613 wakaba 1.104 type => 'coords:number not even',
4614     text => 0+@$coords,
4615     level => $self->{level}->{must});
4616 wakaba 1.1 }
4617     } else {
4618     $self->{onerror}->(node => $attr{coords},
4619 wakaba 1.104 type => 'coords:number lt 6',
4620     text => 0+@$coords,
4621     level => $self->{level}->{must});
4622 wakaba 1.1 }
4623     } else {
4624     ## NOTE: A syntax error has been reported.
4625     }
4626     } else {
4627 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4628 wakaba 1.104 type => 'attribute missing',
4629     text => 'coords',
4630     level => $self->{level}->{must});
4631 wakaba 1.1 }
4632     } elsif ($shape eq 'rectangle') {
4633     if (defined $attr{coords}) {
4634     if (defined $coords) {
4635     if (@$coords == 4) {
4636     unless ($coords->[0] < $coords->[2]) {
4637     $self->{onerror}->(node => $attr{coords},
4638 wakaba 1.104 type => 'coords:out of range',
4639     index => 0,
4640     value => $coords->[0],
4641     level => $self->{level}->{must});
4642 wakaba 1.1 }
4643     unless ($coords->[1] < $coords->[3]) {
4644     $self->{onerror}->(node => $attr{coords},
4645 wakaba 1.104 type => 'coords:out of range',
4646     index => 1,
4647     value => $coords->[1],
4648     level => $self->{level}->{must});
4649 wakaba 1.1 }
4650     } else {
4651     $self->{onerror}->(node => $attr{coords},
4652 wakaba 1.104 type => 'coords:number not 4',
4653     text => 0+@$coords,
4654     level => $self->{level}->{must});
4655 wakaba 1.1 }
4656     } else {
4657     ## NOTE: A syntax error has been reported.
4658     }
4659     } else {
4660 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4661 wakaba 1.104 type => 'attribute missing',
4662     text => 'coords',
4663     level => $self->{level}->{must});
4664 wakaba 1.1 }
4665     }
4666 wakaba 1.66
4667     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4668 wakaba 1.1 },
4669 wakaba 1.59 check_start => sub {
4670     my ($self, $item, $element_state) = @_;
4671     unless ($self->{flag}->{in_map} or
4672     not $item->{node}->manakai_parent_element) {
4673     $self->{onerror}->(node => $item->{node},
4674     type => 'element not allowed:area',
4675 wakaba 1.104 level => $self->{level}->{must});
4676 wakaba 1.59 }
4677 wakaba 1.79
4678     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4679     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4680 wakaba 1.59 },
4681 wakaba 1.1 };
4682    
4683     $Element->{$HTML_NS}->{table} = {
4684 wakaba 1.40 %HTMLChecker,
4685 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4686 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4687 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4688     cellspacing => $HTMLLengthAttrChecker,
4689 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4690     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4691     lhs => 1, rhs => 1, box => 1, border => 1,
4692     }),
4693     rules => $GetHTMLEnumeratedAttrChecker->({
4694     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
4695     }),
4696     summary => sub {}, ## NOTE: %Text; in HTML4.
4697     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
4698     }, {
4699 wakaba 1.49 %HTMLAttrStatus,
4700 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4701 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4702     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4703     border => FEATURE_M12N10_REC,
4704     cellpadding => FEATURE_M12N10_REC,
4705     cellspacing => FEATURE_M12N10_REC,
4706 wakaba 1.61 cols => FEATURE_RFC1942,
4707 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4708     dataformatas => FEATURE_HTML4_REC_RESERVED,
4709     datapagesize => FEATURE_M12N10_REC,
4710     datasrc => FEATURE_HTML4_REC_RESERVED,
4711     frame => FEATURE_M12N10_REC,
4712 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4713 wakaba 1.49 rules => FEATURE_M12N10_REC,
4714     summary => FEATURE_M12N10_REC,
4715     width => FEATURE_M12N10_REC,
4716     }),
4717 wakaba 1.40 check_start => sub {
4718     my ($self, $item, $element_state) = @_;
4719     $element_state->{phase} = 'before caption';
4720 wakaba 1.66
4721     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4722 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4723     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4724 wakaba 1.40 },
4725     check_child_element => sub {
4726     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4727     $child_is_transparent, $element_state) = @_;
4728     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4729     $self->{onerror}->(node => $child_el,
4730     type => 'element not allowed:minus',
4731 wakaba 1.104 level => $self->{level}->{must});
4732 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4733     #
4734     } elsif ($element_state->{phase} eq 'in tbodys') {
4735     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4736     #$element_state->{phase} = 'in tbodys';
4737     } elsif (not $element_state->{has_tfoot} and
4738     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4739     $element_state->{phase} = 'after tfoot';
4740     $element_state->{has_tfoot} = 1;
4741     } else {
4742 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4743     level => $self->{level}->{must});
4744 wakaba 1.40 }
4745     } elsif ($element_state->{phase} eq 'in trs') {
4746     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4747     #$element_state->{phase} = 'in trs';
4748     } elsif (not $element_state->{has_tfoot} and
4749     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4750     $element_state->{phase} = 'after tfoot';
4751     $element_state->{has_tfoot} = 1;
4752     } else {
4753 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4754     level => $self->{level}->{must});
4755 wakaba 1.40 }
4756     } elsif ($element_state->{phase} eq 'after thead') {
4757     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4758     $element_state->{phase} = 'in tbodys';
4759     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4760     $element_state->{phase} = 'in trs';
4761     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4762     $element_state->{phase} = 'in tbodys';
4763     $element_state->{has_tfoot} = 1;
4764     } else {
4765 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4766     level => $self->{level}->{must});
4767 wakaba 1.40 }
4768     } elsif ($element_state->{phase} eq 'in colgroup') {
4769     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4770     $element_state->{phase} = 'in colgroup';
4771     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4772     $element_state->{phase} = 'after thead';
4773     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4774     $element_state->{phase} = 'in tbodys';
4775     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4776     $element_state->{phase} = 'in trs';
4777     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4778     $element_state->{phase} = 'in tbodys';
4779     $element_state->{has_tfoot} = 1;
4780     } else {
4781 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4782     level => $self->{level}->{must});
4783 wakaba 1.40 }
4784     } elsif ($element_state->{phase} eq 'before caption') {
4785     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4786     $element_state->{phase} = 'in colgroup';
4787     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4788     $element_state->{phase} = 'in colgroup';
4789     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4790     $element_state->{phase} = 'after thead';
4791     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4792     $element_state->{phase} = 'in tbodys';
4793     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4794     $element_state->{phase} = 'in trs';
4795     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4796     $element_state->{phase} = 'in tbodys';
4797     $element_state->{has_tfoot} = 1;
4798     } else {
4799 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4800     level => $self->{level}->{must});
4801 wakaba 1.40 }
4802     } elsif ($element_state->{phase} eq 'after tfoot') {
4803 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4804     level => $self->{level}->{must});
4805 wakaba 1.40 } else {
4806     die "check_child_element: Bad |table| phase: $element_state->{phase}";
4807     }
4808     },
4809     check_child_text => sub {
4810     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4811     if ($has_significant) {
4812 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4813     level => $self->{level}->{must});
4814 wakaba 1.1 }
4815 wakaba 1.40 },
4816     check_end => sub {
4817     my ($self, $item, $element_state) = @_;
4818 wakaba 1.1
4819     ## Table model errors
4820     require Whatpm::HTMLTable;
4821 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
4822 wakaba 1.104 $self->{onerror}->(@_);
4823     }, $self->{level});
4824 wakaba 1.87 Whatpm::HTMLTable->assign_header
4825 wakaba 1.104 ($table, $self->{onerror}, $self->{level});
4826 wakaba 1.87 push @{$self->{return}->{table}}, $table;
4827 wakaba 1.1
4828 wakaba 1.40 $HTMLChecker{check_end}->(@_);
4829 wakaba 1.1 },
4830     };
4831    
4832     $Element->{$HTML_NS}->{caption} = {
4833 wakaba 1.40 %HTMLPhrasingContentChecker,
4834 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4835 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4836     align => $GetHTMLEnumeratedAttrChecker->({
4837     top => 1, bottom => 1, left => 1, right => 1,
4838     }),
4839     }, {
4840 wakaba 1.49 %HTMLAttrStatus,
4841 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4842 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4843 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4844 wakaba 1.49 }),
4845 wakaba 1.1 };
4846    
4847 wakaba 1.69 my %cellalign = (
4848     ## HTML4 %cellhalign;
4849 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4850     left => 1, center => 1, right => 1, justify => 1, char => 1,
4851     }),
4852     char => sub {
4853     my ($self, $attr) = @_;
4854 wakaba 1.69
4855 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
4856    
4857     my $value = $attr->value;
4858     if (length $value != 1) {
4859     $self->{onerror}->(node => $attr, type => 'char:syntax error',
4860 wakaba 1.105 level => $self->{level}->{html4_fact});
4861 wakaba 1.70 }
4862     },
4863 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
4864    
4865 wakaba 1.69 ## HTML4 %cellvalign;
4866 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
4867     top => 1, middle => 1, bottom => 1, baseline => 1,
4868     }),
4869 wakaba 1.69 );
4870    
4871 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
4872 wakaba 1.40 %HTMLEmptyChecker,
4873 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4874 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4875 wakaba 1.69 %cellalign,
4876 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4877     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
4878     ## TODO: "attribute not supported" if |col|.
4879     ## ISSUE: MUST NOT if any |col|?
4880     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
4881 wakaba 1.49 }, {
4882     %HTMLAttrStatus,
4883 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4884 wakaba 1.49 align => FEATURE_M12N10_REC,
4885     char => FEATURE_M12N10_REC,
4886     charoff => FEATURE_M12N10_REC,
4887 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4888 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4889 wakaba 1.49 valign => FEATURE_M12N10_REC,
4890     width => FEATURE_M12N10_REC,
4891 wakaba 1.1 }),
4892 wakaba 1.40 check_child_element => sub {
4893     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4894     $child_is_transparent, $element_state) = @_;
4895     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4896     $self->{onerror}->(node => $child_el,
4897     type => 'element not allowed:minus',
4898 wakaba 1.104 level => $self->{level}->{must});
4899 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4900     #
4901     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
4902     #
4903     } else {
4904 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4905     level => $self->{level}->{must});
4906 wakaba 1.40 }
4907     },
4908     check_child_text => sub {
4909     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4910     if ($has_significant) {
4911 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4912     level => $self->{level}->{must});
4913 wakaba 1.1 }
4914     },
4915     };
4916    
4917     $Element->{$HTML_NS}->{col} = {
4918 wakaba 1.40 %HTMLEmptyChecker,
4919 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4920 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4921 wakaba 1.69 %cellalign,
4922 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4923 wakaba 1.49 }, {
4924     %HTMLAttrStatus,
4925 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4926 wakaba 1.49 align => FEATURE_M12N10_REC,
4927     char => FEATURE_M12N10_REC,
4928     charoff => FEATURE_M12N10_REC,
4929 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4930 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4931 wakaba 1.49 valign => FEATURE_M12N10_REC,
4932     width => FEATURE_M12N10_REC,
4933 wakaba 1.1 }),
4934     };
4935    
4936     $Element->{$HTML_NS}->{tbody} = {
4937 wakaba 1.40 %HTMLChecker,
4938 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4939 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4940     %cellalign,
4941     }, {
4942 wakaba 1.49 %HTMLAttrStatus,
4943 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4944 wakaba 1.49 align => FEATURE_M12N10_REC,
4945     char => FEATURE_M12N10_REC,
4946     charoff => FEATURE_M12N10_REC,
4947 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4948 wakaba 1.49 valign => FEATURE_M12N10_REC,
4949     }),
4950 wakaba 1.40 check_child_element => sub {
4951     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4952     $child_is_transparent, $element_state) = @_;
4953     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4954     $self->{onerror}->(node => $child_el,
4955     type => 'element not allowed:minus',
4956 wakaba 1.104 level => $self->{level}->{must});
4957 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4958     #
4959     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4960 wakaba 1.84 #
4961 wakaba 1.40 } else {
4962 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4963     level => $self->{level}->{must});
4964 wakaba 1.40 }
4965     },
4966     check_child_text => sub {
4967     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4968     if ($has_significant) {
4969 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4970     level => $self->{level}->{must});
4971 wakaba 1.1 }
4972 wakaba 1.40 },
4973 wakaba 1.1 };
4974    
4975     $Element->{$HTML_NS}->{thead} = {
4976 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4977 wakaba 1.1 };
4978    
4979     $Element->{$HTML_NS}->{tfoot} = {
4980 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4981 wakaba 1.1 };
4982    
4983     $Element->{$HTML_NS}->{tr} = {
4984 wakaba 1.40 %HTMLChecker,
4985 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4986 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4987     %cellalign,
4988     bgcolor => $HTMLColorAttrChecker,
4989     }, {
4990 wakaba 1.49 %HTMLAttrStatus,
4991 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4992 wakaba 1.49 align => FEATURE_M12N10_REC,
4993     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4994     char => FEATURE_M12N10_REC,
4995     charoff => FEATURE_M12N10_REC,
4996 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4997 wakaba 1.49 valign => FEATURE_M12N10_REC,
4998     }),
4999 wakaba 1.40 check_child_element => sub {
5000     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5001     $child_is_transparent, $element_state) = @_;
5002     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5003     $self->{onerror}->(node => $child_el,
5004     type => 'element not allowed:minus',
5005 wakaba 1.104 level => $self->{level}->{must});
5006 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5007     #
5008     } elsif ($child_nsuri eq $HTML_NS and
5009     ($child_ln eq 'td' or $child_ln eq 'th')) {
5010 wakaba 1.84 #
5011 wakaba 1.40 } else {
5012 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5013     level => $self->{level}->{must});
5014 wakaba 1.40 }
5015     },
5016     check_child_text => sub {
5017     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5018     if ($has_significant) {
5019 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5020     level => $self->{level}->{must});
5021 wakaba 1.1 }
5022     },
5023     };
5024    
5025     $Element->{$HTML_NS}->{td} = {
5026 wakaba 1.72 %HTMLFlowContentChecker,
5027 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5028 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5029 wakaba 1.69 %cellalign,
5030     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5031     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5032     bgcolor => $HTMLColorAttrChecker,
5033 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5034 wakaba 1.87 headers => sub {
5035     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
5036     ## Though that method does not check the |headers| attribute of a
5037     ## |td| element if the element does not form a table, in that case
5038     ## the |td| element is non-conforming anyway.
5039     },
5040 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5041 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5042 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
5043     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5044 wakaba 1.49 }, {
5045     %HTMLAttrStatus,
5046 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5047     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5048 wakaba 1.49 align => FEATURE_M12N10_REC,
5049 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5050 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5051     char => FEATURE_M12N10_REC,
5052     charoff => FEATURE_M12N10_REC,
5053 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5054 wakaba 1.87 headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5055 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5056 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5057 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5058 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5059     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5060 wakaba 1.49 valign => FEATURE_M12N10_REC,
5061     width => FEATURE_M12N10_REC_DEPRECATED,
5062 wakaba 1.1 }),
5063     };
5064    
5065     $Element->{$HTML_NS}->{th} = {
5066 wakaba 1.40 %HTMLPhrasingContentChecker,
5067 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5068 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5069 wakaba 1.69 %cellalign,
5070     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5071     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5072     bgcolor => $HTMLColorAttrChecker,
5073 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5074 wakaba 1.87 ## TODO: HTML4(?) |headers|
5075 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5076 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5077     scope => $GetHTMLEnumeratedAttrChecker
5078     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5079 wakaba 1.49 }, {
5080     %HTMLAttrStatus,
5081 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5082     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5083 wakaba 1.49 align => FEATURE_M12N10_REC,
5084 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5085 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5086     char => FEATURE_M12N10_REC,
5087     charoff => FEATURE_M12N10_REC,
5088 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5089     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5090 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5091 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5092 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5093 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5094     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5095 wakaba 1.49 valign => FEATURE_M12N10_REC,
5096     width => FEATURE_M12N10_REC_DEPRECATED,
5097 wakaba 1.1 }),
5098     };
5099    
5100 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
5101 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: Flow* [WF2]
5102 wakaba 1.56 ## TODO: form in form is allowed in XML [WF2]
5103 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5104     check_attrs => $GetHTMLAttrsChecker->({
5105 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5106 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
5107     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
5108 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5109     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
5110     method => $GetHTMLEnumeratedAttrChecker->({
5111     get => 1, post => 1, put => 1, delete => 1,
5112     }),
5113 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
5114     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
5115     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
5116 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
5117     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5118 wakaba 1.52 target => $HTMLTargetAttrChecker,
5119     ## TODO: Warn for combination whose behavior is not defined.
5120     }, {
5121     %HTMLAttrStatus,
5122     %HTMLM12NCommonAttrStatus,
5123 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
5124 wakaba 1.52 'accept-charset' => FEATURE_M12N10_REC,
5125 wakaba 1.56 action => FEATURE_WF2 | FEATURE_M12N10_REC,
5126     data => FEATURE_WF2,
5127     enctype => FEATURE_WF2 | FEATURE_M12N10_REC,
5128 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5129 wakaba 1.56 method => FEATURE_WF2 | FEATURE_M12N10_REC,
5130 wakaba 1.52 name => FEATURE_M12N10_REC_DEPRECATED,
5131 wakaba 1.56 onreceived => FEATURE_WF2,
5132 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5133     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5134 wakaba 1.56 replace => FEATURE_WF2,
5135 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5136     sdasuff => FEATURE_HTML20_RFC,
5137 wakaba 1.52 target => FEATURE_M12N10_REC,
5138     }),
5139     ## TODO: Tests
5140     ## TODO: Tests for <nest/> in <form>
5141 wakaba 1.66 check_start => sub {
5142     my ($self, $item, $element_state) = @_;
5143    
5144     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5145     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5146 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5147     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5148 wakaba 1.66 },
5149 wakaba 1.52 };
5150    
5151     $Element->{$HTML_NS}->{fieldset} = {
5152 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: legend, %Flow; ## TODO: legend
5153 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5154 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5155     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5156     ## TODO: form [WF2]
5157     }, {
5158 wakaba 1.52 %HTMLAttrStatus,
5159     %HTMLM12NCommonAttrStatus,
5160 wakaba 1.56 disabled => FEATURE_WF2,
5161     form => FEATURE_WF2,
5162 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5163     }),
5164     ## TODO: Tests
5165     ## TODO: Tests for <nest/> in <fieldset>
5166     };
5167    
5168     $Element->{$HTML_NS}->{input} = {
5169 wakaba 1.56 %HTMLEmptyChecker, ## MUST [WF2]
5170 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5171     check_attrs => $GetHTMLAttrsChecker->({
5172 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5173 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5174 wakaba 1.56 action => $HTMLURIAttrChecker,
5175 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
5176     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
5177     }),
5178     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
5179     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
5180     ## here.
5181 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
5182     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5183 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
5184     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5185 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
5186     ## TODO: form [WF2]
5187     ## TODO: inputmode [WF2]
5188 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
5189 wakaba 1.56 ## TODO: list [WF2]
5190     ## TODO: max [WF2]
5191 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5192 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
5193     get => 1, post => 1, put => 1, delete => 1,
5194     }),
5195     ## TODO: min [WF2]
5196 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5197     readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5198 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5199     required => $GetHTMLBooleanAttrChecker->('required'),
5200 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5201     src => $HTMLURIAttrChecker,
5202 wakaba 1.56 ## TODO: step [WF2]
5203     target => $HTMLTargetAttrChecker,
5204 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |input| attribute has |template|
5205     ## attribute to support the |add| button type (as part of repetition
5206     ## template feature). It conflicts with the |template| global attribute
5207     ## introduced as part of the data template feature.
5208     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5209     ## author requirement.
5210 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5211     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
5212     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
5213 wakaba 1.56 ## [WF2]
5214     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
5215     time => 1, number => 1, range => 1, email => 1, url => 1,
5216     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5217 wakaba 1.52 }),
5218     usemap => $HTMLUsemapAttrChecker,
5219 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]
5220     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
5221 wakaba 1.52 }, {
5222     %HTMLAttrStatus,
5223     %HTMLM12NCommonAttrStatus,
5224 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
5225 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5226 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5227 wakaba 1.56 action => FEATURE_WF2,
5228 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
5229     alt => FEATURE_M12N10_REC,
5230 wakaba 1.56 autocomplete => FEATURE_WF2,
5231     autofocus => FEATURE_WF2,
5232 wakaba 1.52 checked => FEATURE_M12N10_REC,
5233     datafld => FEATURE_HTML4_REC_RESERVED,
5234     dataformatas => FEATURE_HTML4_REC_RESERVED,
5235     datasrc => FEATURE_HTML4_REC_RESERVED,
5236 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5237 wakaba 1.65 enctype => FEATURE_WF2,
5238 wakaba 1.56 form => FEATURE_WF2,
5239     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5240 wakaba 1.52 ismap => FEATURE_M12N10_REC,
5241     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5242 wakaba 1.56 list => FEATURE_WF2,
5243     max => FEATURE_WF2,
5244     maxlength => FEATURE_WF2 | FEATURE_M12N10_REC,
5245     method => FEATURE_WF2,
5246     min => FEATURE_WF2,
5247 wakaba 1.52 name => FEATURE_M12N10_REC,
5248     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5249     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5250     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5251     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5252 wakaba 1.56 readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5253 wakaba 1.65 replace => FEATURE_WF2,
5254 wakaba 1.56 required => FEATURE_WF2,
5255 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5256 wakaba 1.56 size => FEATURE_WF2_DEPRECATED | FEATURE_M12N10_REC,
5257 wakaba 1.52 src => FEATURE_M12N10_REC,
5258 wakaba 1.56 step => FEATURE_WF2,
5259 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5260 wakaba 1.65 target => FEATURE_WF2,
5261 wakaba 1.56 template => FEATURE_WF2,
5262 wakaba 1.52 type => FEATURE_M12N10_REC,
5263     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5264     value => FEATURE_M12N10_REC,
5265     }),
5266     ## TODO: Tests
5267     ## TODO: Tests for <nest/> in <input>
5268 wakaba 1.66 check_start => sub {
5269     my ($self, $item, $element_state) = @_;
5270    
5271     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5272     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5273     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
5274 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5275     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5276 wakaba 1.66 },
5277 wakaba 1.52 };
5278    
5279 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
5280    
5281 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
5282     ## [repetition-block-related] buttons carefully to make clear which block a
5283     ## button applies to.": I have no idea how this can be tested.
5284    
5285 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
5286 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: %Flow; - something [XHTML10]
5287 wakaba 1.52 ## TODO: -A|%formctrl;|form|fieldset [HTML4]
5288     ## TODO: image map (img) in |button| is "illegal" [HTML4].
5289     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5290     check_attrs => $GetHTMLAttrsChecker->({
5291 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5292 wakaba 1.56 action => $HTMLURIAttrChecker,
5293     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5294 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5295 wakaba 1.56 ## TODO: form [WF2]
5296     method => $GetHTMLEnumeratedAttrChecker->({
5297     get => 1, post => 1, put => 1, delete => 1,
5298     }),
5299 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5300 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5301     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5302     target => $HTMLTargetAttrChecker,
5303 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
5304     ## attribute to support the |add| button type (as part of repetition
5305     ## template feature). It conflicts with the |template| global attribute
5306     ## introduced as part of the data template feature.
5307     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5308     ## author requirement.
5309 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5310     button => 1, submit => 1, reset => 1,
5311 wakaba 1.80 ## [WF2]
5312     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5313 wakaba 1.52 }),
5314     value => sub {}, ## NOTE: CDATA [M12N]
5315     }, {
5316     %HTMLAttrStatus,
5317     %HTMLM12NCommonAttrStatus,
5318     accesskey => FEATURE_M12N10_REC,
5319 wakaba 1.56 action => FEATURE_WF2,
5320     autofocus => FEATURE_WF2,
5321 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5322     dataformatas => FEATURE_HTML4_REC_RESERVED,
5323     datasrc => FEATURE_HTML4_REC_RESERVED,
5324 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5325     enctype => FEATURE_WF2,
5326     form => FEATURE_WF2,
5327 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5328 wakaba 1.56 method => FEATURE_WF2,
5329 wakaba 1.52 name => FEATURE_M12N10_REC,
5330     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5331     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5332 wakaba 1.56 oninvalid => FEATURE_WF2,
5333     replace => FEATURE_WF2,
5334 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5335 wakaba 1.56 target => FEATURE_WF2,
5336     template => FEATURE_WF2,
5337 wakaba 1.52 type => FEATURE_M12N10_REC,
5338     value => FEATURE_M12N10_REC,
5339     }),
5340     ## TODO: Tests
5341     ## TODO: Tests for <nest/> in <button>
5342 wakaba 1.66 check_start => sub {
5343     my ($self, $item, $element_state) = @_;
5344    
5345     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5346     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5347 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5348     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5349 wakaba 1.66 },
5350 wakaba 1.52 };
5351    
5352     $Element->{$HTML_NS}->{label} = {
5353     %HTMLPhrasingContentChecker, ## NOTE: %Inline - label [XHTML10] ## TODO: -label
5354 wakaba 1.56 ## TODO: At most one form control [WF2]
5355 wakaba 1.82 status => FEATURE_WF2 | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5356 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5357 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5358 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
5359     }, {
5360     %HTMLAttrStatus,
5361 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5362 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
5363 wakaba 1.52 for => FEATURE_M12N10_REC,
5364     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5365     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5366     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5367     }),
5368     ## TODO: Tests
5369     ## TODO: Tests for <nest/> in <label>
5370     };
5371    
5372     $Element->{$HTML_NS}->{select} = {
5373 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (optgroup|option)* [HTML4] + [WF2] ## TODO: SHOULD avoid empty and visible [WF2]
5374 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
5375     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
5376     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5377 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
5378 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5379 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5380 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5381 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5382 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5383     ## TODO: form [WF2]
5384 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5385     name => sub {}, ## NOTE: CDATA [M12N]
5386 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5387     ## TODO: pattern [WF2] ## TODO: |title| semantics
5388 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5389     }, {
5390     %HTMLAttrStatus,
5391     %HTMLM12NCommonAttrStatus,
5392 wakaba 1.56 accesskey => FEATURE_WF2,
5393     autofocus => FEATURE_WF2,
5394     data => FEATURE_WF2,
5395 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5396     dataformatas => FEATURE_HTML4_REC_RESERVED,
5397     datasrc => FEATURE_HTML4_REC_RESERVED,
5398 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5399     form => FEATURE_WF2,
5400 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5401     multiple => FEATURE_M12N10_REC,
5402     name => FEATURE_M12N10_REC,
5403     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5404     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5405     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5406 wakaba 1.56 oninvalid => FEATURE_WF2,
5407     pattern => FEATURE_WF2,
5408 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5409     sdapref => FEATURE_HTML20_RFC,
5410 wakaba 1.52 size => FEATURE_M12N10_REC,
5411     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5412     }),
5413     ## TODO: Tests
5414     ## TODO: Tests for <nest/> in <select>
5415 wakaba 1.66 check_start => sub {
5416     my ($self, $item, $element_state) = @_;
5417    
5418     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5419     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5420 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5421     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5422 wakaba 1.66 },
5423 wakaba 1.52 };
5424 wakaba 1.1
5425 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
5426 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (transparent | option)*
5427 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
5428 wakaba 1.52 status => FEATURE_WF2,
5429 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5430     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5431     }, {
5432 wakaba 1.52 %HTMLAttrStatus,
5433 wakaba 1.56 data => FEATURE_WF2,
5434 wakaba 1.52 }),
5435     ## TODO: Tests
5436     ## TODO: Tests for <nest/> in <datalist>
5437 wakaba 1.66 check_start => sub {
5438     my ($self, $item, $element_state) = @_;
5439    
5440     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5441 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5442     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5443 wakaba 1.66 },
5444 wakaba 1.52 };
5445 wakaba 1.49
5446 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
5447 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (option|optgroup)* [HTML4] + [WF2] SHOULD avoid empty and visible [WF2]
5448 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5449     check_attrs => $GetHTMLAttrsChecker->({
5450     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5451     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
5452     }, {
5453     %HTMLAttrStatus,
5454     %HTMLM12NCommonAttrStatus,
5455 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5456 wakaba 1.52 label => FEATURE_M12N10_REC,
5457     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5458     }),
5459     ## TODO: Tests
5460     ## TODO: Tests for <nest/> in <optgroup>
5461     };
5462    
5463     $Element->{$HTML_NS}->{option} = {
5464     %HTMLTextChecker,
5465     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5466     check_attrs => $GetHTMLAttrsChecker->({
5467     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5468     label => sub {}, ## NOTE: Text [M12N]
5469     selected => $GetHTMLBooleanAttrChecker->('selected'),
5470     value => sub {}, ## NOTE: CDATA [M12N]
5471     }, {
5472     %HTMLAttrStatus,
5473     %HTMLM12NCommonAttrStatus,
5474 wakaba 1.56 disabled => FEATURE_WF2, FEATURE_M12N10_REC,
5475 wakaba 1.52 label => FEATURE_M12N10_REC,
5476     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5477 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5478     sdapref => FEATURE_HTML20_RFC,
5479 wakaba 1.52 selected => FEATURE_M12N10_REC,
5480     value => FEATURE_M12N10_REC,
5481     }),
5482     ## TODO: Tests
5483     ## TODO: Tests for <nest/> in <option>
5484     };
5485 wakaba 1.49
5486 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
5487     %HTMLTextChecker,
5488     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5489     check_attrs => $GetHTMLAttrsChecker->({
5490 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
5491 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5492 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5493     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
5494 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5495 wakaba 1.56 ## TODO: form [WF2]
5496     ## TODO: inputmode [WF2]
5497     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5498 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5499 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
5500 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5501 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
5502     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5503     oninvalid => $HTMLEventHandlerAttrChecker,
5504     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
5505 wakaba 1.52 }, {
5506     %HTMLAttrStatus,
5507     %HTMLM12NCommonAttrStatus,
5508 wakaba 1.56 accept => FEATURE_WF2,
5509 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5510 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5511 wakaba 1.56 autofocus => FEATURE_WF2,
5512 wakaba 1.52 cols => FEATURE_M12N10_REC,
5513     datafld => FEATURE_HTML4_REC_RESERVED,
5514 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
5515     datasrc => FEATURE_HTML4_REC_RESERVED,
5516 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5517     form => FEATURE_WF2,
5518     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5519 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5520 wakaba 1.56 maxlength => FEATURE_WF2,
5521 wakaba 1.52 name => FEATURE_M12N10_REC,
5522     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5523     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5524     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5525 wakaba 1.56 oninvalid => FEATURE_WF2,
5526 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5527 wakaba 1.56 pattern => FEATURE_WF2,
5528     readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5529     required => FEATURE_WF2,
5530 wakaba 1.61 rows => FEATURE_M12N10_REC,
5531     sdaform => FEATURE_HTML20_RFC,
5532     sdapref => FEATURE_HTML20_RFC,
5533 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5534 wakaba 1.56 wrap => FEATURE_WF2,
5535 wakaba 1.52 }),
5536     ## TODO: Tests
5537     ## TODO: Tests for <nest/> in <textarea>
5538 wakaba 1.66 check_start => sub {
5539     my ($self, $item, $element_state) = @_;
5540    
5541     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5542 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5543     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5544 wakaba 1.66 },
5545 wakaba 1.52 };
5546 wakaba 1.49
5547 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
5548 wakaba 1.56 %HTMLPhrasingContentChecker, ## Inline [WF2]
5549 wakaba 1.52 status => FEATURE_WF2,
5550 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5551     ## TODO: for [WF2]
5552     ## TODO: form [WF2]
5553     ## TODO: name [WF2]
5554     ## onformchange[WF2]
5555     ## onforminput[WF2]
5556     }, {
5557 wakaba 1.52 %HTMLAttrStatus,
5558 wakaba 1.56 for => FEATURE_WF2,
5559     form => FEATURE_WF2,
5560     name => FEATURE_WF2,
5561     onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
5562     onformchange => FEATURE_WF2,
5563     onforminput => FEATURE_WF2,
5564 wakaba 1.52 }),
5565     ## TODO: Tests
5566     ## TODO: Tests for <nest/> in <output>
5567 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
5568 wakaba 1.52 };
5569    
5570     $Element->{$HTML_NS}->{isindex} = {
5571     %HTMLEmptyChecker,
5572 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
5573     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
5574 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5575     prompt => sub {}, ## NOTE: Text [M12N]
5576     }, {
5577     %HTMLAttrStatus,
5578     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5579     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5580     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5581     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5582     prompt => FEATURE_M12N10_REC_DEPRECATED,
5583 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5584 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5585 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5586     }),
5587     ## TODO: Tests
5588     ## TODO: Tests for <nest/> in <isindex>
5589 wakaba 1.66 check_start => sub {
5590     my ($self, $item, $element_state) = @_;
5591    
5592     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5593 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5594     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5595 wakaba 1.66 },
5596 wakaba 1.52 };
5597 wakaba 1.49
5598 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
5599 wakaba 1.40 %HTMLChecker,
5600 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5601 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5602 wakaba 1.91 charset => sub {
5603     my ($self, $attr) = @_;
5604    
5605     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
5606     $self->{onerror}->(type => 'attribute not allowed',
5607     node => $attr,
5608 wakaba 1.104 level => $self->{level}->{must});
5609 wakaba 1.91 }
5610    
5611     $HTMLCharsetChecker->($attr->value, @_);
5612     },
5613 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
5614 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
5615 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
5616     async => $GetHTMLBooleanAttrChecker->('async'),
5617 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
5618 wakaba 1.49 }, {
5619     %HTMLAttrStatus,
5620     %HTMLM12NCommonAttrStatus,
5621 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
5622 wakaba 1.91 charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5623 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5624 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
5625     for => FEATURE_HTML4_REC_RESERVED,
5626 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5627 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
5628 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5629     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5630 wakaba 1.9 }),
5631 wakaba 1.40 check_start => sub {
5632     my ($self, $item, $element_state) = @_;
5633 wakaba 1.1
5634 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
5635     $element_state->{must_be_empty} = 1;
5636 wakaba 1.1 } else {
5637     ## NOTE: No content model conformance in HTML5 spec.
5638 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
5639     my $language = $item->{node}->get_attribute_ns (undef, 'language');
5640 wakaba 1.1 if ((defined $type and $type eq '') or
5641     (defined $language and $language eq '')) {
5642     $type = 'text/javascript';
5643     } elsif (defined $type) {
5644     #
5645     } elsif (defined $language) {
5646     $type = 'text/' . $language;
5647     } else {
5648     $type = 'text/javascript';
5649     }
5650 wakaba 1.93
5651     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)]) {
5652     $type = "$1/$2";
5653     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
5654     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
5655     }
5656     $element_state->{script_type} = $type;
5657 wakaba 1.40 }
5658 wakaba 1.66
5659     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5660 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5661     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5662 wakaba 1.107
5663     $element_state->{text} = '';
5664 wakaba 1.40 },
5665     check_child_element => sub {
5666     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5667     $child_is_transparent, $element_state) = @_;
5668     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5669     $self->{onerror}->(node => $child_el,
5670     type => 'element not allowed:minus',
5671 wakaba 1.104 level => $self->{level}->{must});
5672 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5673     #
5674     } else {
5675     if ($element_state->{must_be_empty}) {
5676     $self->{onerror}->(node => $child_el,
5677 wakaba 1.104 type => 'element not allowed:empty',
5678     level => $self->{level}->{must});
5679 wakaba 1.40 }
5680     }
5681     },
5682     check_child_text => sub {
5683     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5684     if ($has_significant and
5685     $element_state->{must_be_empty}) {
5686     $self->{onerror}->(node => $child_node,
5687 wakaba 1.104 type => 'character not allowed:empty',
5688     level => $self->{level}->{must});
5689 wakaba 1.40 }
5690 wakaba 1.103 $element_state->{text} .= $child_node->text_content;
5691 wakaba 1.40 },
5692     check_end => sub {
5693     my ($self, $item, $element_state) = @_;
5694     unless ($element_state->{must_be_empty}) {
5695 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
5696     ## NOTE: XML content should be checked by THIS instance of checker
5697     ## as part of normal tree validation.
5698 wakaba 1.104 $self->{onerror}->(node => $item->{node},
5699     type => 'XML script lang',
5700     text => $element_state->{script_type},
5701     level => $self->{level}->{uncertain});
5702     ## ISSUE: Should we raise some kind of error for
5703     ## <script type="text/xml">aaaaa</script>?
5704     ## NOTE: ^^^ This is why we throw an "uncertain" error.
5705 wakaba 1.93 } else {
5706     $self->{onsubdoc}->({s => $element_state->{text},
5707     container_node => $item->{node},
5708     media_type => $element_state->{script_type},
5709     is_char_string => 1});
5710     }
5711 wakaba 1.40
5712     $HTMLChecker{check_end}->(@_);
5713 wakaba 1.1 }
5714     },
5715 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
5716     ## NOTE: "When used to include script data, the script data must be embedded
5717     ## inline, the format of the data must be given using the type attribute,
5718     ## and the src attribute must not be specified." - not testable.
5719     ## TODO: It would be possible to err <script type=text/plain src=...>
5720 wakaba 1.1 };
5721 wakaba 1.25 ## ISSUE: Significant check and text child node
5722 wakaba 1.1
5723     ## NOTE: When script is disabled.
5724     $Element->{$HTML_NS}->{noscript} = {
5725 wakaba 1.40 %HTMLTransparentChecker,
5726 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5727     check_attrs => $GetHTMLAttrsChecker->({}, {
5728     %HTMLAttrStatus,
5729     %HTMLM12NCommonAttrStatus,
5730 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5731 wakaba 1.49 }),
5732 wakaba 1.40 check_start => sub {
5733     my ($self, $item, $element_state) = @_;
5734 wakaba 1.3
5735 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
5736 wakaba 1.104 $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript',
5737     level => $self->{level}->{must});
5738 wakaba 1.3 }
5739    
5740 wakaba 1.40 unless ($self->{flag}->{in_head}) {
5741     $self->_add_minus_elements ($element_state,
5742     {$HTML_NS => {noscript => 1}});
5743     }
5744 wakaba 1.79
5745     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5746     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5747 wakaba 1.3 },
5748 wakaba 1.40 check_child_element => sub {
5749     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5750     $child_is_transparent, $element_state) = @_;
5751     if ($self->{flag}->{in_head}) {
5752     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5753     $self->{onerror}->(node => $child_el,
5754     type => 'element not allowed:minus',
5755 wakaba 1.104 level => $self->{level}->{must});
5756 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5757     #
5758     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
5759     #
5760     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
5761     if ($child_el->has_attribute_ns (undef, 'scoped')) {
5762     $self->{onerror}->(node => $child_el,
5763     type => 'element not allowed:head noscript',
5764 wakaba 1.104 level => $self->{level}->{must});
5765 wakaba 1.40 }
5766     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
5767 wakaba 1.47 my $http_equiv_attr
5768     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
5769     if ($http_equiv_attr) {
5770     ## TODO: case
5771     if (lc $http_equiv_attr->value eq 'content-type') {
5772 wakaba 1.40 $self->{onerror}->(node => $child_el,
5773 wakaba 1.34 type => 'element not allowed:head noscript',
5774 wakaba 1.104 level => $self->{level}->{must});
5775 wakaba 1.47 } else {
5776     #
5777 wakaba 1.3 }
5778 wakaba 1.47 } else {
5779     $self->{onerror}->(node => $child_el,
5780     type => 'element not allowed:head noscript',
5781 wakaba 1.104 level => $self->{level}->{must});
5782 wakaba 1.3 }
5783 wakaba 1.40 } else {
5784     $self->{onerror}->(node => $child_el,
5785     type => 'element not allowed:head noscript',
5786 wakaba 1.104 level => $self->{level}->{must});
5787 wakaba 1.40 }
5788     } else {
5789     $HTMLTransparentChecker{check_child_element}->(@_);
5790     }
5791     },
5792     check_child_text => sub {
5793     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5794     if ($self->{flag}->{in_head}) {
5795     if ($has_significant) {
5796     $self->{onerror}->(node => $child_node,
5797 wakaba 1.104 type => 'character not allowed',
5798     level => $self->{level}->{must});
5799 wakaba 1.3 }
5800     } else {
5801 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
5802     }
5803     },
5804     check_end => sub {
5805     my ($self, $item, $element_state) = @_;
5806     $self->_remove_minus_elements ($element_state);
5807     if ($self->{flag}->{in_head}) {
5808     $HTMLChecker{check_end}->(@_);
5809     } else {
5810     $HTMLPhrasingContentChecker{check_end}->(@_);
5811 wakaba 1.3 }
5812 wakaba 1.1 },
5813     };
5814 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
5815 wakaba 1.1
5816     $Element->{$HTML_NS}->{'event-source'} = {
5817 wakaba 1.40 %HTMLEmptyChecker,
5818 wakaba 1.48 status => FEATURE_HTML5_LC,
5819 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5820 wakaba 1.1 src => $HTMLURIAttrChecker,
5821 wakaba 1.50 }, {
5822     %HTMLAttrStatus,
5823     src => FEATURE_HTML5_LC,
5824 wakaba 1.1 }),
5825 wakaba 1.66 check_start => sub {
5826     my ($self, $item, $element_state) = @_;
5827    
5828     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5829 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5830     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5831 wakaba 1.66 },
5832 wakaba 1.1 };
5833    
5834     $Element->{$HTML_NS}->{details} = {
5835 wakaba 1.72 %HTMLFlowContentChecker,
5836 wakaba 1.48 status => FEATURE_HTML5_WD,
5837 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5838 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
5839 wakaba 1.50 }, {
5840     %HTMLAttrStatus,
5841 wakaba 1.59 open => FEATURE_HTML5_WD,
5842 wakaba 1.1 }),
5843 wakaba 1.72 ## NOTE: legend, Flow
5844 wakaba 1.43 check_child_element => sub {
5845     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5846     $child_is_transparent, $element_state) = @_;
5847     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5848     $self->{onerror}->(node => $child_el,
5849     type => 'element not allowed:minus',
5850 wakaba 1.104 level => $self->{level}->{must});
5851 wakaba 1.43 $element_state->{has_non_legend} = 1;
5852     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5853     #
5854     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5855     if ($element_state->{has_non_legend}) {
5856     $self->{onerror}->(node => $child_el,
5857     type => 'element not allowed:details legend',
5858 wakaba 1.104 level => $self->{level}->{must});
5859 wakaba 1.43 }
5860     $element_state->{has_legend} = 1;
5861     $element_state->{has_non_legend} = 1;
5862     } else {
5863 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
5864 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5865     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
5866     ## is conforming?
5867     }
5868     },
5869     check_child_text => sub {
5870     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5871     if ($has_significant) {
5872     $element_state->{has_non_legend} = 1;
5873     }
5874     },
5875     check_end => sub {
5876     my ($self, $item, $element_state) = @_;
5877 wakaba 1.1
5878 wakaba 1.43 unless ($element_state->{has_legend}) {
5879     $self->{onerror}->(node => $item->{node},
5880 wakaba 1.110 type => 'child element missing',
5881 wakaba 1.104 text => 'legend',
5882     level => $self->{level}->{must});
5883 wakaba 1.43 }
5884    
5885 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
5886 wakaba 1.43 ## ISSUE: |<details><legend>aa</legend></details>| error?
5887 wakaba 1.1 },
5888     };
5889    
5890     $Element->{$HTML_NS}->{datagrid} = {
5891 wakaba 1.72 %HTMLFlowContentChecker,
5892 wakaba 1.48 status => FEATURE_HTML5_WD,
5893 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5894 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5895     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5896 wakaba 1.50 }, {
5897     %HTMLAttrStatus,
5898     disabled => FEATURE_HTML5_WD,
5899     multiple => FEATURE_HTML5_WD,
5900 wakaba 1.1 }),
5901 wakaba 1.40 check_start => sub {
5902     my ($self, $item, $element_state) = @_;
5903 wakaba 1.1
5904 wakaba 1.40 $self->_add_minus_elements ($element_state,
5905     {$HTML_NS => {a => 1, datagrid => 1}});
5906     $element_state->{phase} = 'any';
5907 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5908     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5909 wakaba 1.40 },
5910 wakaba 1.95 ## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select |
5911     ## datalist | Empty
5912 wakaba 1.40 check_child_element => sub {
5913     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5914     $child_is_transparent, $element_state) = @_;
5915     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5916     $self->{onerror}->(node => $child_el,
5917     type => 'element not allowed:minus',
5918 wakaba 1.104 level => $self->{level}->{must});
5919 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5920     #
5921 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
5922     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5923 wakaba 1.44 if (not $element_state->{has_element} and
5924 wakaba 1.40 $child_nsuri eq $HTML_NS and
5925 wakaba 1.95 {
5926     table => 1, select => 1, datalist => 1,
5927     }->{$child_ln}) {
5928 wakaba 1.40 $self->{onerror}->(node => $child_el,
5929 wakaba 1.104 type => 'element not allowed',
5930     level => $self->{level}->{must});
5931 wakaba 1.40 } else {
5932 wakaba 1.8 #
5933 wakaba 1.1 }
5934 wakaba 1.40 } else {
5935     $self->{onerror}->(node => $child_el,
5936 wakaba 1.104 type => 'element not allowed',
5937     level => $self->{level}->{must});
5938 wakaba 1.40 }
5939 wakaba 1.43 $element_state->{has_element} = 1;
5940 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
5941     if ($child_nsuri eq $HTML_NS and
5942     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
5943     $element_state->{phase} = 'none';
5944 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5945 wakaba 1.40 $element_state->{has_element} = 1;
5946 wakaba 1.72 $element_state->{phase} = 'flow';
5947 wakaba 1.40 } else {
5948     $self->{onerror}->(node => $child_el,
5949 wakaba 1.104 type => 'element not allowed',
5950     level => $self->{level}->{must});
5951 wakaba 1.40 }
5952     } elsif ($element_state->{phase} eq 'none') {
5953     $self->{onerror}->(node => $child_el,
5954 wakaba 1.104 type => 'element not allowed',
5955     level => $self->{level}->{must});
5956 wakaba 1.40 } else {
5957     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
5958     }
5959     },
5960     check_child_text => sub {
5961     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5962     if ($has_significant) {
5963 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
5964 wakaba 1.40 #
5965     } elsif ($element_state->{phase} eq 'any') {
5966 wakaba 1.72 $element_state->{phase} = 'flow';
5967 wakaba 1.40 } else {
5968     $self->{onerror}->(node => $child_node,
5969 wakaba 1.104 type => 'character not allowed',
5970     level => $self->{level}->{must});
5971 wakaba 1.1 }
5972     }
5973 wakaba 1.40 },
5974     check_end => sub {
5975     my ($self, $item, $element_state) = @_;
5976     $self->_remove_minus_elements ($element_state);
5977 wakaba 1.1
5978 wakaba 1.95 if ($element_state->{phase} eq 'flow') {
5979     if ($element_state->{has_significant}) {
5980     $item->{real_parent_state}->{has_significant} = 1;
5981     } elsif ($item->{transparent}) {
5982     #
5983     } else {
5984     $self->{onerror}->(node => $item->{node},
5985 wakaba 1.104 type => 'no significant content',
5986 wakaba 1.110 level => $self->{level}->{should});
5987 wakaba 1.95 }
5988     } else {
5989     ## NOTE: Since the content model explicitly allows a |datagird| element
5990     ## being empty, we don't raise "no significant content" error for this
5991     ## element when there is no element. (We should raise an error for
5992     ## |<datagrid><br></datagrid>|, however.)
5993     ## NOTE: As a side-effect, when the |datagrid| element only contains
5994     ## non-conforming content, then the |phase| flag has not changed from
5995     ## |any|, no "no significant content" error is raised neither.
5996     ## NOTE: Another side-effect of the current implementation:
5997     ## |<daragrid><datagrid/></datagrid>| has no "no significant content"
5998     ## error at all.
5999 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6000     }
6001     },
6002 wakaba 1.1 };
6003    
6004     $Element->{$HTML_NS}->{command} = {
6005 wakaba 1.40 %HTMLEmptyChecker,
6006 wakaba 1.48 status => FEATURE_HTML5_WD,
6007 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6008 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
6009     default => $GetHTMLBooleanAttrChecker->('default'),
6010     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6011     hidden => $GetHTMLBooleanAttrChecker->('hidden'),
6012     icon => $HTMLURIAttrChecker,
6013     label => sub { }, ## NOTE: No conformance creteria
6014     radiogroup => sub { }, ## NOTE: No conformance creteria
6015     type => sub {
6016     my ($self, $attr) = @_;
6017     my $value = $attr->value;
6018     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
6019 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
6020     level => $self->{level}->{must});
6021 wakaba 1.1 }
6022     },
6023 wakaba 1.50 }, {
6024     %HTMLAttrStatus,
6025     checked => FEATURE_HTML5_WD,
6026     default => FEATURE_HTML5_WD,
6027     disabled => FEATURE_HTML5_WD,
6028     hidden => FEATURE_HTML5_WD,
6029     icon => FEATURE_HTML5_WD,
6030     label => FEATURE_HTML5_WD,
6031     radiogroup => FEATURE_HTML5_WD,
6032     type => FEATURE_HTML5_WD,
6033 wakaba 1.1 }),
6034 wakaba 1.66 check_start => sub {
6035     my ($self, $item, $element_state) = @_;
6036    
6037     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
6038 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6039     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6040 wakaba 1.66 },
6041 wakaba 1.1 };
6042    
6043     $Element->{$HTML_NS}->{menu} = {
6044 wakaba 1.40 %HTMLPhrasingContentChecker,
6045 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
6046     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
6047     ## NOTE: We don't want any |menu| element warned as deprecated.
6048 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6049 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
6050 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
6051 wakaba 1.1 id => sub {
6052     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
6053     my ($self, $attr) = @_;
6054     my $value = $attr->value;
6055     if (length $value > 0) {
6056     if ($self->{id}->{$value}) {
6057 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
6058     level => $self->{level}->{must});
6059 wakaba 1.1 push @{$self->{id}->{$value}}, $attr;
6060     } else {
6061     $self->{id}->{$value} = [$attr];
6062     }
6063     } else {
6064     ## NOTE: MUST contain at least one character
6065 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
6066     level => $self->{level}->{must});
6067 wakaba 1.1 }
6068     if ($value =~ /[\x09-\x0D\x20]/) {
6069 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
6070     level => $self->{level}->{must});
6071 wakaba 1.1 }
6072     $self->{menu}->{$value} ||= $attr;
6073     ## ISSUE: <menu id=""><p contextmenu=""> match?
6074     },
6075     label => sub { }, ## NOTE: No conformance creteria
6076     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
6077 wakaba 1.49 }, {
6078     %HTMLAttrStatus,
6079     %HTMLM12NCommonAttrStatus,
6080 wakaba 1.61 align => FEATURE_HTML2X_RFC,
6081 wakaba 1.50 autosubmit => FEATURE_HTML5_WD,
6082 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
6083 wakaba 1.50 label => FEATURE_HTML5_WD,
6084     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6085 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6086     sdapref => FEATURE_HTML20_RFC,
6087 wakaba 1.50 type => FEATURE_HTML5_WD,
6088 wakaba 1.1 }),
6089 wakaba 1.40 check_start => sub {
6090     my ($self, $item, $element_state) = @_;
6091     $element_state->{phase} = 'li or phrasing';
6092     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
6093     $self->{flag}->{in_menu} = 1;
6094 wakaba 1.79
6095     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6096     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6097 wakaba 1.40 },
6098     check_child_element => sub {
6099     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6100     $child_is_transparent, $element_state) = @_;
6101     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6102     $self->{onerror}->(node => $child_el,
6103     type => 'element not allowed:minus',
6104 wakaba 1.104 level => $self->{level}->{must});
6105 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6106     #
6107     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
6108     if ($element_state->{phase} eq 'li') {
6109     #
6110     } elsif ($element_state->{phase} eq 'li or phrasing') {
6111     $element_state->{phase} = 'li';
6112     } else {
6113 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6114     level => $self->{level}->{must});
6115 wakaba 1.40 }
6116     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6117     if ($element_state->{phase} eq 'phrasing') {
6118     #
6119     } elsif ($element_state->{phase} eq 'li or phrasing') {
6120     $element_state->{phase} = 'phrasing';
6121     } else {
6122 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6123     level => $self->{level}->{must});
6124 wakaba 1.40 }
6125     } else {
6126 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6127     level => $self->{level}->{must});
6128 wakaba 1.40 }
6129     },
6130     check_child_text => sub {
6131     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6132     if ($has_significant) {
6133     if ($element_state->{phase} eq 'phrasing') {
6134     #
6135     } elsif ($element_state->{phase} eq 'li or phrasing') {
6136     $element_state->{phase} = 'phrasing';
6137     } else {
6138     $self->{onerror}->(node => $child_node,
6139 wakaba 1.104 type => 'character not allowed',
6140     level => $self->{level}->{must});
6141 wakaba 1.1 }
6142     }
6143 wakaba 1.40 },
6144     check_end => sub {
6145     my ($self, $item, $element_state) = @_;
6146     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
6147    
6148     if ($element_state->{phase} eq 'li') {
6149     $HTMLChecker{check_end}->(@_);
6150     } else { # 'phrasing' or 'li or phrasing'
6151     $HTMLPhrasingContentChecker{check_end}->(@_);
6152 wakaba 1.1 }
6153     },
6154 wakaba 1.8 };
6155    
6156     $Element->{$HTML_NS}->{datatemplate} = {
6157 wakaba 1.40 %HTMLChecker,
6158 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6159 wakaba 1.40 check_child_element => sub {
6160     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6161     $child_is_transparent, $element_state) = @_;
6162     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6163     $self->{onerror}->(node => $child_el,
6164     type => 'element not allowed:minus',
6165 wakaba 1.104 level => $self->{level}->{must});
6166 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6167     #
6168     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
6169     #
6170     } else {
6171     $self->{onerror}->(node => $child_el,
6172 wakaba 1.104 type => 'element not allowed:datatemplate',
6173     level => $self->{level}->{must});
6174 wakaba 1.40 }
6175     },
6176     check_child_text => sub {
6177     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6178     if ($has_significant) {
6179 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
6180     level => $self->{level}->{must});
6181 wakaba 1.8 }
6182     },
6183     is_xml_root => 1,
6184     };
6185    
6186     $Element->{$HTML_NS}->{rule} = {
6187 wakaba 1.40 %HTMLChecker,
6188 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6189 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6190 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
6191 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
6192 wakaba 1.50 }, {
6193     %HTMLAttrStatus,
6194     condition => FEATURE_HTML5_AT_RISK,
6195     mode => FEATURE_HTML5_AT_RISK,
6196 wakaba 1.8 }),
6197 wakaba 1.40 check_start => sub {
6198     my ($self, $item, $element_state) = @_;
6199 wakaba 1.79
6200 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
6201 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
6202     $self->{flag}->{in_rule} = 1;
6203    
6204     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6205     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6206 wakaba 1.40 },
6207     check_child_element => sub { },
6208     check_child_text => sub { },
6209     check_end => sub {
6210     my ($self, $item, $element_state) = @_;
6211 wakaba 1.79
6212 wakaba 1.40 $self->_remove_plus_elements ($element_state);
6213 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
6214    
6215 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6216 wakaba 1.8 },
6217     ## NOTE: "MAY be anything that, when the parent |datatemplate|
6218     ## is applied to some conforming data, results in a conforming DOM tree.":
6219     ## We don't check against this.
6220     };
6221    
6222     $Element->{$HTML_NS}->{nest} = {
6223 wakaba 1.40 %HTMLEmptyChecker,
6224 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6225 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6226 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
6227     mode => sub {
6228     my ($self, $attr) = @_;
6229     my $value = $attr->value;
6230     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
6231 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'mode:syntax error',
6232     level => $self->{level}->{must});
6233 wakaba 1.23 }
6234     },
6235 wakaba 1.50 }, {
6236     %HTMLAttrStatus,
6237     filter => FEATURE_HTML5_AT_RISK,
6238     mode => FEATURE_HTML5_AT_RISK,
6239 wakaba 1.8 }),
6240 wakaba 1.1 };
6241    
6242     $Element->{$HTML_NS}->{legend} = {
6243 wakaba 1.40 %HTMLPhrasingContentChecker,
6244 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6245 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6246 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6247 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
6248     # top => 1, bottom => 1, left => 1, right => 1,
6249     # }),
6250     }, {
6251 wakaba 1.49 %HTMLAttrStatus,
6252     %HTMLM12NCommonAttrStatus,
6253     accesskey => FEATURE_M12N10_REC,
6254     align => FEATURE_M12N10_REC_DEPRECATED,
6255 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6256 wakaba 1.49 }),
6257 wakaba 1.1 };
6258    
6259     $Element->{$HTML_NS}->{div} = {
6260 wakaba 1.72 %HTMLFlowContentChecker,
6261 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
6262 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
6263     align => $GetHTMLEnumeratedAttrChecker->({
6264     left => 1, center => 1, right => 1, justify => 1,
6265     }),
6266     }, {
6267 wakaba 1.49 %HTMLAttrStatus,
6268 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6269 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
6270     datafld => FEATURE_HTML4_REC_RESERVED,
6271     dataformatas => FEATURE_HTML4_REC_RESERVED,
6272     datasrc => FEATURE_HTML4_REC_RESERVED,
6273 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6274 wakaba 1.49 }),
6275 wakaba 1.66 check_start => sub {
6276     my ($self, $item, $element_state) = @_;
6277    
6278     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6279 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6280     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6281 wakaba 1.66 },
6282 wakaba 1.1 };
6283    
6284 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
6285 wakaba 1.72 %HTMLFlowContentChecker,
6286 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
6287     check_attrs => $GetHTMLAttrsChecker->({}, {
6288     %HTMLAttrStatus,
6289     %HTMLM12NCommonAttrStatus,
6290     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6291     }),
6292     };
6293    
6294 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
6295 wakaba 1.40 %HTMLTransparentChecker,
6296 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
6297 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
6298     ## TODO: HTML4 |size|, |color|, |face|
6299 wakaba 1.49 }, {
6300     %HTMLAttrStatus,
6301 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6302 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
6303 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6304 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
6305 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6306     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6307 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
6308 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6309 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6310 wakaba 1.49 }),
6311 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
6312     ## it is allowed only in a document with the WYSIWYG signature. The
6313     ## checker does not check whether there is the signature, since the
6314     ## signature is dropped, too, and has never been implemented. (In addition,
6315     ## for any |font| element an "element not defined" error is raised anyway,
6316     ## such that we don't have to raise an additional error.)
6317 wakaba 1.1 };
6318 wakaba 1.49
6319 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
6320     %HTMLEmptyChecker,
6321     status => FEATURE_M12N10_REC_DEPRECATED,
6322     check_attrs => $GetHTMLAttrsChecker->({
6323     ## TODO: color, face, size
6324     }, {
6325     %HTMLAttrStatus,
6326     color => FEATURE_M12N10_REC_DEPRECATED,
6327     face => FEATURE_M12N10_REC_DEPRECATED,
6328     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
6329     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6330     size => FEATURE_M12N10_REC_DEPRECATED,
6331     }),
6332     };
6333    
6334 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
6335     ## class title id cols rows onload onunload style(x10)
6336     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
6337     ## noframes Common, lang(xhtml10)
6338    
6339 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
6340 wakaba 1.56
6341 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
6342     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
6343     ## xmp, listing sdapref[HTML2,0]
6344    
6345 wakaba 1.56 =pod
6346    
6347     WF2: Documents MUST comply to [CHARMOD].
6348     WF2: Vencor extensions MUST NOT be used.
6349    
6350 wakaba 1.61 HTML 2.0 nextid @n
6351    
6352     RFC 2659: CERTS CRYPTOPTS
6353    
6354     ISO-HTML: pre-html, divN
6355 wakaba 1.82
6356     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
6357     di (Common), nl (Common), handler (Common, type), standby (Common),
6358     summary (Common)
6359    
6360 wakaba 1.97 Access & XHTML2: access (LC)
6361 wakaba 1.82
6362     XML Events & XForms (for XHTML2 support; very, very low priority)
6363 wakaba 1.61
6364 wakaba 1.56 =cut
6365 wakaba 1.61
6366     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
6367     ## We added them only to |a|. |link| and |form| might also allow them
6368     ## in theory.
6369 wakaba 1.1
6370     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
6371    
6372     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24