/[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.200 - (hide annotations) (download)
Sat Aug 22 12:29:33 2009 UTC (15 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.199: +11 -3 lines
++ whatpm/t/dom-conformance/ChangeLog	22 Aug 2009 12:28:07 -0000
	* html-form-input-1.dat, html-forms-1.dat: Revised and added test
	data on the |accept| attribute of the |input| element to allow
	white space characters arround tokens (HTML5 revision 2759).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	22 Aug 2009 12:26:35 -0000
	* HTML.pm: Allow white space characters arround tokens in |accept|
	attribute value of |input| elements (HTML5 revision 2759).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24