/[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.99 - (hide annotations) (download)
Sun Jun 8 12:22:54 2008 UTC (17 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.98: +322 -12 lines
++ whatpm/t/ChangeLog	8 Jun 2008 12:20:25 -0000
	* content-model-1.dat: Test data for ruby are added (HTML5 revision
	1704).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	8 Jun 2008 12:22:16 -0000
2008-06-08  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for ruby, rt, and rp elements (HTML5
	revision 1704).  The |href| attribute is also extended
	as a common attribute by RDFa Last Call Working Draft.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24