/[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.118 - (hide annotations) (download)
Sun Aug 31 12:40:09 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.117: +31 -5 lines
++ whatpm/t/ChangeLog	31 Aug 2008 12:28:19 -0000
	* content-model-1.dat, content-model-2.dat: |event-source|
	is renamed as |eventsource| (HTML5 revision 1863).

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

	* content-model-2.dat: Uppercase letters are not allowed
	as embed custom attributes (HTML5 revision 1946).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	31 Aug 2008 12:28:43 -0000
	* HTML.pm: |event-source| renamed as |eventsource| (HTML5 revision
	1863).

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

	* HTML.pm: Uppercase attribute names for embed elements
	are disallowed (HTML5 revision 1946).

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24