/[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.205 - (hide annotations) (download)
Sun Aug 23 06:37:14 2009 UTC (15 years, 11 months ago) by wakaba
Branch: MAIN
Changes since 1.204: +30 -6 lines
++ whatpm/t/dom-conformance/ChangeLog	23 Aug 2009 06:36:33 -0000
	* html-objects-1.dat: Revised test results on |autoplay|
	attributes as per HTML5 revision 3344.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	23 Aug 2009 06:36:54 -0000
	* HTML.pm: Discourage use of the |autoplay| attribute (HTML5
	revision 3344).

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24