/[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.201 - (hide annotations) (download)
Sun Aug 23 02:02:57 2009 UTC (17 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.200: +54 -33 lines
++ whatpm/t/dom-conformance/ChangeLog	23 Aug 2009 02:02:16 -0000
2009-08-23  Wakaba  <wakaba@suika.fam.cx>

	* html-texts-1.dat: Revised and added test data on |time| and
	|datetime| attribute of the |time| element content model/attribute
	value syntax.

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

	* HTML.pm: Revised the syntax of the |time| element and its
	|datetime| attribute to sync with the latest spec definition.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24