/[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.202 - (hide annotations) (download)
Sun Aug 23 02:35:33 2009 UTC (15 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.201: +23 -6 lines
++ whatpm/t/dom-conformance/ChangeLog	23 Aug 2009 02:34:37 -0000
	* html-form-button.dat: Added test data on interactive content in
	|button| element (HTML5 revision 2392).

	* html-links-1.dat: Added test data on exclusion of interactive
	content (HTML5 revision 2455, HTML5 revision 3162, HTML5 revision
	3338).

2009-08-23  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	23 Aug 2009 02:26:52 -0000
2009-08-23  Wakaba  <wakaba@suika.fam.cx>

	* ContentChecker.pm: Allow <input type=hidden> in context where
	interactive content is disallowed (HTML5 revision 2392).

++ whatpm/Whatpm/ContentChecker/ChangeLog	23 Aug 2009 02:35:21 -0000
	* HTML.pm: Disallow interactive content in |button| element (HTML5
	revision 2392).  Marked |img| and |object| (with |usemap|
	attribute) elements, |iframe| element, and |embed| element as
	interactive content (HTML5 revision 2455, HTML5 revision 3162,
	HTML5 revision 3338).

2009-08-23  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24