File Coverage

blib/lib/HTML/Valid/Tagset.pm
Criterion Covered Total %
statement 54 63 85.7
branch 8 16 50.0
condition 2 3 66.6
subroutine 14 14 100.0
pod 4 6 66.6
total 82 102 80.3


line stmt bran cond sub pod time code
1             package HTML::Valid::Tagset;
2 2     2   115044 use parent Exporter;
  2         594  
  2         10  
3             our @EXPORT_OK = qw/
4             %boolean_attr
5             %canTighten
6             %emptyElement
7             %isBlock
8             %isBodyElement
9             %isCDATA_Parent
10             %isFormElement
11             %isHTML2
12             %isHTML3
13             %isHTML4
14             %isHTML5
15             %isHeadElement
16             %isHeadOrBodyElement
17             %isInline
18             %isKnown
19             %isList
20             %isObsolete
21             %isPhraseMarkup
22             %isProprietary
23             %isTableElement
24             %is_Possible_Strict_P_Content
25             %linkElements
26             %optionalEndTag
27             @allTags
28             @p_closure_barriers
29             test_taginfo
30             attributes
31             all_attributes
32             attr_type
33             tag_attr_ok
34             /;
35             our %EXPORT_TAGS = (all => \@EXPORT_OK);
36 2     2   164 use warnings;
  2         3  
  2         46  
37 2     2   8 use strict;
  2         2  
  2         27  
38 2     2   9 use utf8;
  2         3  
  2         11  
39 2     2   44 use Carp;
  2         4  
  2         104  
40 2     2   751 use HTML::Valid;
  2         5  
  2         119  
41              
42             our $VERSION = $HTML::Valid::VERSION;
43              
44 2         428 use vars qw(
45             $VERSION
46 2     2   12 );
  2         10  
47              
48             use constant {
49             # Rejected #define __LEXER_H__
50             # Rejected #define digit 1u
51             # Rejected #define letter 2u
52             # Rejected #define namechar 4u
53             # Rejected #define white 8u
54             # Rejected #define newline 16u
55             # Rejected #define lowercase 32u
56             # Rejected #define uppercase 64u
57             # Rejected #define digithex 128u
58             # Rejected #define CM_UNKNOWN 0
59 2         4142 CM_EMPTY => (1 << 0),
60             CM_HTML => (1 << 1),
61             CM_HEAD => (1 << 2),
62             CM_BLOCK => (1 << 3),
63             CM_INLINE => (1 << 4),
64             CM_LIST => (1 << 5),
65             CM_DEFLIST => (1 << 6),
66             CM_TABLE => (1 << 7),
67             CM_ROWGRP => (1 << 8),
68             CM_ROW => (1 << 9),
69             CM_FIELD => (1 << 10),
70             CM_OBJECT => (1 << 11),
71             CM_PARAM => (1 << 12),
72             CM_FRAMES => (1 << 13),
73             CM_HEADING => (1 << 14),
74             CM_OPT => (1 << 15),
75             CM_IMG => (1 << 16),
76             CM_MIXED => (1 << 17),
77             CM_NO_INDENT => (1 << 18),
78             CM_OBSOLETE => (1 << 19),
79             CM_NEW => (1 << 20),
80             CM_OMITST => (1 << 21),
81             # Rejected #define xxxx 0u
82             HT20 => 1,
83             HT32 => 2,
84             H40S => 4,
85             H40T => 8,
86             H40F => 16,
87             H41S => 32,
88             H41T => 64,
89             H41F => 128,
90             X10S => 256,
91             X10T => 512,
92             X10F => 1024,
93             XH11 => 2048,
94             XB10 => 4096,
95             VERS_SUN => 8192,
96             VERS_NETSCAPE => 16384,
97             VERS_MICROSOFT => 32768,
98             VERS_XML => 65536,
99             HT50 => 131072,
100             XH50 => 262144,
101             # Rejected #define VERS_UNKNOWN (xxxx)
102             VERS_HTML20 => (1),
103             VERS_HTML32 => (2),
104             VERS_HTML40_STRICT => (4|32|256),
105             VERS_HTML40_LOOSE => (8|64|512),
106             VERS_FRAMESET => (16|128|1024),
107             VERS_XHTML11 => (2048),
108             VERS_BASIC => (4096),
109             VERS_HTML5 => (131072|262144),
110             VERS_HTML40 => ((4|32|256)|(8|64|512)|(16|128|1024)),
111             VERS_IFRAME => ((8|64|512)|(16|128|1024)),
112             VERS_LOOSE => ((1)|(2)|((8|64|512)|(16|128|1024))),
113             VERS_EVENTS => (((4|32|256)|(8|64|512)|(16|128|1024))|(2048)),
114             VERS_FROM32 => ((2)|((4|32|256)|(8|64|512)|(16|128|1024))),
115             VERS_FROM40 => (((4|32|256)|(8|64|512)|(16|128|1024))|(2048)|(4096)),
116             VERS_XHTML => (256|512|1024|2048|4096|262144),
117             VERS_ALL => ((1)|(2)|(((4|32|256)|(8|64|512)|(16|128|1024))|(2048)|(4096))|262144|131072),
118             VERS_PROPRIETARY => (16384|32768|8192),
119 2     2   14 };
  2         4  
120              
121             my $taginfo = HTML::Valid::tag_information ();
122              
123             our @allTags = sort keys %$taginfo;
124              
125             our %emptyElement;
126             our %optionalEndTag;
127             our %linkElements;
128             our %boolean_attr;
129             our %isHeadElement;
130             our %isBodyElement;
131             our %isPhraseMarkup;
132             our %isProprietary;
133             our %isHeadOrBodyElement;
134             our %isList;
135             our %isTableElement;
136             our %isFormElement;
137             our %isKnown;
138             our %canTighten;
139             our %isHTML5;
140             our %isHTML4;
141             our %isHTML3;
142             our %isHTML2;
143             our %isObsolete;
144             our %isInline;
145             our %isBlock;
146              
147             for my $tag (@allTags) {
148             $isKnown{$tag} = 1;
149             my $ti = $taginfo->{$tag};
150             my $versions = $ti->[1];
151             my $model = $ti->[2];
152             if ($model & CM_EMPTY) {
153             $emptyElement{$tag} = 1;
154             }
155             if ($model & CM_OPT) {
156             $optionalEndTag{$tag} = 1;
157             }
158             if ($model & CM_FIELD) {
159             $isFormElement{$tag} = 1;
160             }
161             # See tidy-html5.c
162             if ($model & (CM_TABLE|CM_ROWGRP|CM_ROW)) {
163             $isTableElement{$tag} = 1;
164             }
165             if ($model & CM_HEAD) {
166             $isHeadElement{$tag} = 1;
167             if ($model & ~CM_HEAD) {
168             $isHeadOrBodyElement{$tag} = 1;
169             $isBodyElement{$tag} = 1;
170             }
171             }
172             else {
173             $isBodyElement{$tag} = 1;
174             }
175             if ($model & CM_OBSOLETE) {
176             $isObsolete{$tag} = 1;
177             }
178             if ($model & CM_INLINE) {
179             $isPhraseMarkup{$tag} = 1;
180             $isInline{$tag} = 1;
181             }
182             if ($model & CM_BLOCK) {
183             $isBlock{$tag} = 1;
184             }
185             if ($versions & VERS_HTML5) {
186             $isHTML5{$tag} = 1;
187             }
188             if ($versions & VERS_HTML40) {
189             $isHTML4{$tag} = 1;
190             }
191             if ($versions & VERS_HTML32) {
192             $isHTML3{$tag} = 1;
193             }
194             if ($versions & VERS_HTML20) {
195             $isHTML2{$tag} = 1;
196             }
197             if ($versions & VERS_PROPRIETARY) {
198             $isProprietary{$tag} = 1;
199             }
200             }
201              
202              
203             # Start of compatibility with HTML::Tagset
204              
205             @isFormElement{qw/input button label/} = (1)x3;
206              
207             # Does not exist in tidy-html classifications.
208              
209             %isList = map {; $_ => 1 } qw(ul ol dir menu);
210              
211             %canTighten = %isKnown;
212             delete @canTighten{
213             keys(%isPhraseMarkup), 'input', 'select',
214             'xmp', 'listing', 'plaintext', 'pre',
215             };
216              
217             our %isCDATA_Parent = map {; $_ => 1 }
218             qw(script style xmp listing plaintext);
219              
220             # End of compatibility with HTML::Tagset
221              
222             # @p_closure_barriers is a conceptual error in HTML::Tagset, see the
223             # documentation under "Issues with HTML::Tagset".
224              
225             our @p_closure_barriers = ();
226              
227             # %is_Possible_Strict_P_Content is just the inline elements, the
228             # content of HTML::Tagset makes absolutely no sense here and is
229             # contradictory to the specification it claims to be based on.
230              
231             our %is_Possible_Strict_P_Content = %isInline;
232              
233             our %attr2type = (
234             'abbr' => 'pcdata',
235             'accept' => 'xtype',
236             'accept-charset' => 'charset',
237             'accesskey' => 'character',
238             'action' => 'action',
239             'add_date' => 'pcdata',
240             'align' => 'align',
241             'alink' => 'color',
242             'alt' => 'pcdata',
243             'archive' => 'urls',
244             'aria-activedescendant' => 'pcdata',
245             'aria-atomic' => 'pcdata',
246             'aria-autocomplete' => 'pcdata',
247             'aria-busy' => 'pcdata',
248             'aria-checked' => 'pcdata',
249             'aria-controls' => 'pcdata',
250             'aria-describedby' => 'pcdata',
251             'aria-disabled' => 'pcdata',
252             'aria-dropeffect' => 'pcdata',
253             'aria-expanded' => 'pcdata',
254             'aria-flowto' => 'pcdata',
255             'aria-grabbed' => 'pcdata',
256             'aria-haspopup' => 'pcdata',
257             'aria-hidden' => 'pcdata',
258             'aria-invalid' => 'pcdata',
259             'aria-label' => 'pcdata',
260             'aria-labelledby' => 'pcdata',
261             'aria-level' => 'pcdata',
262             'aria-live' => 'pcdata',
263             'aria-multiline' => 'pcdata',
264             'aria-multiselectable' => 'pcdata',
265             'aria-orientation' => 'pcdata',
266             'aria-owns' => 'pcdata',
267             'aria-posinset' => 'pcdata',
268             'aria-pressed' => 'pcdata',
269             'aria-readonly' => 'pcdata',
270             'aria-relevant' => 'pcdata',
271             'aria-required' => 'pcdata',
272             'aria-selected' => 'pcdata',
273             'aria-setsize' => 'pcdata',
274             'aria-sort' => 'pcdata',
275             'aria-valuemax' => 'pcdata',
276             'aria-valuemin' => 'pcdata',
277             'aria-valuenow' => 'pcdata',
278             'aria-valuetext' => 'pcdata',
279             'async' => 'bool',
280             'autocomplete' => 'pcdata',
281             'autofocus' => 'pcdata',
282             'autoplay' => 'pcdata',
283             'axis' => 'pcdata',
284             'background' => 'url',
285             'baseprofile' => 'pcdata',
286             'bgcolor' => 'color',
287             'bgproperties' => 'pcdata',
288             'border' => 'border',
289             'bordercolor' => 'color',
290             'bottommargin' => 'number',
291             'cellpadding' => 'length',
292             'cellspacing' => 'length',
293             'challenge' => 'pcdata',
294             'char' => 'character',
295             'charoff' => 'length',
296             'charset' => 'charset',
297             'checked' => 'bool',
298             'cite' => 'url',
299             'class' => 'pcdata',
300             'classid' => 'url',
301             'clear' => 'clear',
302             'code' => 'pcdata',
303             'codebase' => 'url',
304             'codetype' => 'xtype',
305             'color' => 'color',
306             'cols' => 'cols',
307             'colspan' => 'number',
308             'compact' => 'bool',
309             'content' => 'pcdata',
310             'contenteditable' => 'pcdata',
311             'contentscripttype' => 'pcdata',
312             'contentstyletype' => 'pcdata',
313             'contextmenu' => 'pcdata',
314             'controls' => 'pcdata',
315             'coords' => 'coords',
316             'data' => 'url',
317             'datafld' => 'pcdata',
318             'dataformatas' => 'pcdata',
319             'datapagesize' => 'number',
320             'datasrc' => 'url',
321             'datetime' => 'date',
322             'declare' => 'bool',
323             'default' => 'pcdata',
324             'defer' => 'bool',
325             'dir' => 'textdir',
326             'dirname' => 'pcdata',
327             'disabled' => 'bool',
328             'display' => 'pcdata',
329             'draggable' => 'pcdata',
330             'dropzone' => 'pcdata',
331             'encoding' => 'pcdata',
332             'enctype' => 'xtype',
333             'event' => 'pcdata',
334             'face' => 'pcdata',
335             'for' => 'idref',
336             'form' => 'pcdata',
337             'formaction' => 'pcdata',
338             'formenctype' => 'pcdata',
339             'formmethod' => 'pcdata',
340             'formnovalidate' => 'pcdata',
341             'formtarget' => 'pcdata',
342             'frame' => 'tframe',
343             'frameborder' => 'fborder',
344             'framespacing' => 'number',
345             'gridx' => 'number',
346             'gridy' => 'number',
347             'headers' => 'idrefs',
348             'height' => 'length',
349             'hidden' => 'pcdata',
350             'high' => 'pcdata',
351             'href' => 'url',
352             'hreflang' => 'lang',
353             'hspace' => 'number',
354             'http-equiv' => 'pcdata',
355             'icon' => 'pcdata',
356             'id' => 'iddef',
357             'ismap' => 'bool',
358             'itemid' => 'pcdata',
359             'itemprop' => 'pcdata',
360             'itemref' => 'pcdata',
361             'itemscope' => 'bool',
362             'itemtype' => 'url',
363             'keytype' => 'pcdata',
364             'kind' => 'pcdata',
365             'label' => 'pcdata',
366             'lang' => 'lang',
367             'language' => 'pcdata',
368             'last_modified' => 'pcdata',
369             'last_visit' => 'pcdata',
370             'leftmargin' => 'number',
371             'link' => 'color',
372             'list' => 'pcdata',
373             'longdesc' => 'url',
374             'loop' => 'pcdata',
375             'low' => 'pcdata',
376             'lowsrc' => 'url',
377             'manifest' => 'pcdata',
378             'marginheight' => 'number',
379             'marginwidth' => 'number',
380             'max' => 'pcdata',
381             'maxlength' => 'number',
382             'media' => 'media',
383             'mediagroup' => 'pcdata',
384             'method' => 'fsubmit',
385             'methods' => 'pcdata',
386             'min' => 'pcdata',
387             'multiple' => 'bool',
388             'n' => 'pcdata',
389             'name' => 'name',
390             'nohref' => 'bool',
391             'noresize' => 'bool',
392             'noshade' => 'bool',
393             'novalidate' => 'pcdata',
394             'nowrap' => 'bool',
395             'object' => 'pcdata',
396             'onabort' => 'pcdata',
397             'onafterprint' => 'pcdata',
398             'onafterupdate' => 'script',
399             'onbeforeprint' => 'pcdata',
400             'onbeforeunload' => 'script',
401             'onbeforeupdate' => 'script',
402             'onblur' => 'script',
403             'oncanplay' => 'pcdata',
404             'oncanplaythrough' => 'pcdata',
405             'onchange' => 'script',
406             'onclick' => 'script',
407             'oncontextmenu' => 'pcdata',
408             'oncuechange' => 'pcdata',
409             'ondataavailable' => 'script',
410             'ondatasetchanged' => 'script',
411             'ondatasetcomplete' => 'script',
412             'ondblclick' => 'script',
413             'ondrag' => 'pcdata',
414             'ondragend' => 'pcdata',
415             'ondragenter' => 'pcdata',
416             'ondragleave' => 'pcdata',
417             'ondragover' => 'pcdata',
418             'ondragstart' => 'pcdata',
419             'ondrop' => 'pcdata',
420             'ondurationchange' => 'pcdata',
421             'onemptied' => 'pcdata',
422             'onended' => 'pcdata',
423             'onerror' => 'pcdata',
424             'onerrorupdate' => 'script',
425             'onfocus' => 'script',
426             'onhashchange' => 'pcdata',
427             'oninput' => 'pcdata',
428             'oninvalid' => 'pcdata',
429             'onkeydown' => 'script',
430             'onkeypress' => 'script',
431             'onkeyup' => 'script',
432             'onload' => 'script',
433             'onloadeddata' => 'pcdata',
434             'onloadedmetadata' => 'pcdata',
435             'onloadstart' => 'pcdata',
436             'onmessage' => 'pcdata',
437             'onmousedown' => 'script',
438             'onmousemove' => 'script',
439             'onmouseout' => 'script',
440             'onmouseover' => 'script',
441             'onmouseup' => 'script',
442             'onmousewheel' => 'pcdata',
443             'onoffline' => 'pcdata',
444             'ononline' => 'pcdata',
445             'onpagehide' => 'pcdata',
446             'onpageshow' => 'pcdata',
447             'onpause' => 'pcdata',
448             'onplay' => 'pcdata',
449             'onplaying' => 'pcdata',
450             'onpopstate' => 'pcdata',
451             'onprogress' => 'pcdata',
452             'onratechange' => 'pcdata',
453             'onreadystatechange' => 'pcdata',
454             'onredo' => 'pcdata',
455             'onreset' => 'script',
456             'onresize' => 'pcdata',
457             'onrowenter' => 'script',
458             'onrowexit' => 'script',
459             'onscroll' => 'pcdata',
460             'onseeked' => 'pcdata',
461             'onseeking' => 'pcdata',
462             'onselect' => 'script',
463             'onshow' => 'pcdata',
464             'onstalled' => 'pcdata',
465             'onstorage' => 'pcdata',
466             'onsubmit' => 'script',
467             'onsuspend' => 'pcdata',
468             'ontimeupdate' => 'pcdata',
469             'onundo' => 'pcdata',
470             'onunload' => 'script',
471             'onvolumechange' => 'pcdata',
472             'onwaiting' => 'pcdata',
473             'open' => 'pcdata',
474             'optimum' => 'pcdata',
475             'pattern' => 'pcdata',
476             'placeholder' => 'pcdata',
477             'poster' => 'pcdata',
478             'preload' => 'pcdata',
479             'preserveaspectratio' => 'pcdata',
480             'profile' => 'url',
481             'prompt' => 'pcdata',
482             'pubdate' => 'pcdata',
483             'radiogroup' => 'pcdata',
484             'rbspan' => 'number',
485             'readonly' => 'bool',
486             'rel' => 'linktypes',
487             'required' => 'pcdata',
488             'rev' => 'linktypes',
489             'reversed' => 'pcdata',
490             'rightmargin' => 'number',
491             'role' => 'pcdata',
492             'rows' => 'number',
493             'rowspan' => 'number',
494             'rules' => 'trules',
495             'sandbox' => 'pcdata',
496             'scheme' => 'pcdata',
497             'scope' => 'scope',
498             'scoped' => 'pcdata',
499             'scrolling' => 'scroll',
500             'sdaform' => 'pcdata',
501             'sdapref' => 'pcdata',
502             'sdasuff' => 'pcdata',
503             'seamless' => 'pcdata',
504             'selected' => 'bool',
505             'shape' => 'shape',
506             'showgrid' => 'bool',
507             'showgridx' => 'bool',
508             'showgridy' => 'bool',
509             'size' => 'number',
510             'sizes' => 'pcdata',
511             'span' => 'number',
512             'spellcheck' => 'pcdata',
513             'src' => 'url',
514             'srcdoc' => 'pcdata',
515             'srclang' => 'pcdata',
516             'srcset' => 'pcdata',
517             'standby' => 'pcdata',
518             'start' => 'number',
519             'step' => 'pcdata',
520             'style' => 'pcdata',
521             'summary' => 'pcdata',
522             'tabindex' => 'number',
523             'target' => 'target',
524             'text' => 'color',
525             'title' => 'pcdata',
526             'topmargin' => 'number',
527             'type' => 'type',
528             'urn' => 'pcdata',
529             'usemap' => 'url',
530             'valign' => 'valign',
531             'value' => 'pcdata',
532             'valuetype' => 'vtype',
533             'version' => 'pcdata',
534             'viewbox' => 'pcdata',
535             'vlink' => 'color',
536             'vspace' => 'number',
537             'width' => 'length',
538             'wrap' => 'pcdata',
539             'x' => 'pcdata',
540             'xml:lang' => 'lang',
541             'xml:space' => 'pcdata',
542             'xmlns' => 'pcdata',
543             'y' => 'pcdata',
544             'zoomandpan' => 'pcdata',
545             );
546              
547              
548              
549             # Private routine, for testing we got the tag info.
550              
551             sub test_taginfo
552             {
553 1     1 0 10710 my ($tag) = @_;
554 1         5 return $taginfo->{$tag};
555             }
556              
557             # Valid arguments for attributes.
558              
559             my %attributes_key = (
560             standard => 1,
561             );
562              
563             sub options
564             {
565 5     5 0 8 my (%options) = @_;
566 5         5 my $version;
567 5 100 66     21 if (! $options{standard} || $options{standard} eq 'html5') {
    50          
    0          
    0          
568 4         5 $version = VERS_HTML5;
569             }
570             elsif ($options{standard} eq 'html2') {
571 1         3 $version = VERS_HTML20;
572             }
573             elsif ($options{standard} eq 'html3') {
574 0         0 $version = VERS_HTML32;
575             }
576             elsif ($options{standard} eq 'html4') {
577 0         0 $version = VERS_HTML40;
578             }
579             else {
580 0         0 carp "Unknown HTML version variable $options{standard}: this should be either html2, html3, html4, html5 (the default), or empty for html5. Defaulting to html5.";
581 0         0 $version = VERS_HTML5;
582             }
583 5         9 for my $k (keys %options) {
584 1 50       4 if (! $attributes_key{$k}) {
585 0         0 carp "Unknown key $k: valid keys for attributes are " . join (', ', sort keys %attributes_key) . "\n";
586             }
587             }
588 5         8 return $version;
589             }
590              
591             sub attributes
592             {
593 3     3 1 11753 my ($tag, %options) = @_;
594 3         8 my $version = options (%options);
595 3         7 $tag = lc $tag;
596 3 50       9 if (! $isKnown{$tag}) {
597 0         0 carp "Request for unknown HTML tag '$tag' in attributes: returning undefined value";
598 0         0 return undef;
599             }
600             # Look up the HTML Tidy ID of this tag in our hash table.
601 3         6 my $tagid = $taginfo->{$tag}[0];
602             # Get the attributes.
603 3         82 my $attr = HTML::Valid::tag_attr ($tagid, $version);
604 3         8 return $attr;
605             }
606              
607             sub all_attributes
608             {
609 1     1 1 321 return HTML::Valid::all_attributes ();
610             }
611              
612             # Store of tag/version/attribute valid combinations.
613              
614             my %tag_attr;
615              
616             sub tag_attr_ok
617             {
618 2     2 1 492 my ($tag, $attr, %options) = @_;
619 2         4 $tag = lc $tag;
620 2 50       7 if (! $isKnown{$tag}) {
621 0         0 carp "Request for unknown HTML tag '$tag' in attributes: returning undefined value";
622 0         0 return undef;
623             }
624 2         5 my $version = options (%options);
625             # If we haven't looked up attributes for this tag and version yet,
626             # look them up.
627 2 100       6 if (! $tag_attr{$tag}{$version}) {
628 1         3 my $tagid = $taginfo->{$tag}[0];
629 1         54 my $attrs = HTML::Valid::tag_attr ($tagid, $version);
630 1         4 for my $attribute (@$attrs) {
631             # Store the attributes.
632 140         209 $tag_attr{$tag}{$version}{$attribute} = 1;
633             }
634             }
635 2         8 return $tag_attr{$tag}{$version}{$attr};
636             }
637              
638             sub attr_type
639             {
640 1     1 1 3 my ($attr) = @_;
641 1         4 return $attr2type{$attr};
642             }
643              
644             1;