/[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.110 - (hide annotations) (download)
Sat Aug 30 10:26:39 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.109: +10 -6 lines
++ whatpm/t/ChangeLog	30 Aug 2008 10:22:30 -0000
	* ContentChecker.t: Updated for latest version of the
	Whatpm::ContentChecker module.

	* content-model-1.dat, content-model-2.dat, content-model-3.dat,
	content-model-4.dat, content-model-6.dat, content-model-atom-1.dat,
	content-model-atom-2.dat, content-model-atom-threading-1.dat,
	table-1.dat: Results updated.

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

++ whatpm/Whatpm/ChangeLog	30 Aug 2008 10:24:24 -0000
	* ContentChecker.pm: Error level definition for |xml_id_error|
	was missing.

	* URIChecker.pm: The end of the URL should be marked as the
	error location for an empty path error.  The position
	between the userinfo and the port components should be
	marked as the error location for an empty host error.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	30 Aug 2008 10:26:28 -0000
2008-08-30  Wakaba  <wakaba@suika.fam.cx>

	* Atom.pm: s/element missing/child element missing/ for
	consistency.

	* HTML.pm: Typos fixed.
	(pre): "No significant content" error was unintentionally
	disabled.  s/element missing/child element missing/ for
	consistency.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24