/[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.216 - (hide annotations) (download)
Sun Aug 30 08:42:08 2009 UTC (15 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.215: +7 -2 lines
++ whatpm/t/dom-conformance/ChangeLog	30 Aug 2009 08:34:54 -0000
	* html-objects-1.dat: Added test cases for nested |video| and
	|audio| elements (HTML5 revision 3488).

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	30 Aug 2009 08:41:54 -0000
	* HTML.pm: Disallow nested |video| and |audio| elements (HTML5
	revision 3488).  (But this does not work at the moment due to the
	bug on the handling of transparent elements).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24