21
21
E .update ({
22
22
"unrecognized-attribute" :
23
23
_ (u"Unrecognized attribute '%(attributeName)s' in <%(tagName)s>" ),
24
+ "missing-required-attribute" :
25
+ _ (u"Missing required attribute '%(attributeName)s' in <%(tagName)s>" ),
24
26
})
25
27
26
- globalAttributes = ['id' , 'title' , 'lang' , 'dir' , 'class' , 'irrelevant' ]
28
+ globalAttributes = ['class' , 'contenteditable' , 'contextmenu' , 'dir' ,
29
+ 'draggable' , 'id' , 'irrelevant' , 'lang' , 'ref' , 'tabindex' , 'template' ,
30
+ 'title' , 'onabort' , 'onbeforeunload' , 'onblur' , 'onchange' , 'onclick' ,
31
+ 'oncontextmenu' , 'ondblclick' , 'ondrag' , 'ondragend' , 'ondragenter' ,
32
+ 'ondragleave' , 'ondragover' , 'ondragstart' , 'ondrop' , 'onerror' ,
33
+ 'onfocus' , 'onkeydown' , 'onkeypress' , 'onkeyup' , 'onload' , 'onmessage' ,
34
+ 'onmousedown' , 'onmousemove' , 'onmouseout' , 'onmouseover' , 'onmouseup' ,
35
+ 'onmousewheel' , 'onresize' , 'onscroll' , 'onselect' , 'onsubmit' , 'onunload' ]
36
+ # XXX lang in HTML only, xml:lang in XHTML only
37
+
38
+ modAttributes = ['cite' , 'datetime' ]
39
+ mediaAttributes = ['src' , 'autoplay' , 'start' , 'loopstart' , 'loopend' , 'end' ,
40
+ 'loopcount' , 'controls' ],
27
41
allowedAttributeMap = {
28
- 'html' : globalAttributes + ['xmlns' ]
42
+ 'html' : ['xmlns' ],
43
+ 'base' : ['href' , 'target' ],
44
+ 'link' : ['href' , 'rel' , 'media' , 'hreflang' , 'type' ],
45
+ 'meta' : ['name' , 'http-equiv' , 'content' , 'charset' ], # XXX charset in HTML only
46
+ 'style' : ['media' , 'type' , 'scoped' ],
47
+ 'blockquote' : ['cite' ],
48
+ 'ol' : ['start' ],
49
+ 'li' : ['value' ], # XXX depends on parent
50
+ 'a' : ['href' , 'target' , 'ping' , 'rel' , 'media' , 'hreflang' , 'type' ],
51
+ 'q' : ['cite' ],
52
+ 'time' : ['datetime' ],
53
+ 'meter' : ['value' , 'min' , 'low' , 'high' , 'max' , 'optimum' ],
54
+ 'progress' : ['value' , 'max' ],
55
+ 'ins' : modAttributes ,
56
+ 'del' : modAttributes ,
57
+ 'img' : ['alt' , 'src' , 'usemap' , 'ismap' , 'height' , 'width' ], # XXX ismap depends on parent
58
+ 'iframe' : ['src' ],
59
+ 'object' : ['data' , 'type' , 'usemap' , 'height' , 'width' ],
60
+ 'param' : ['name' , 'value' ],
61
+ 'video' : mediaAttributes ,
62
+ 'audio' : mediaAttributes ,
63
+ 'source' : ['src' , 'type' , 'media' ],
64
+ 'canvas' : ['height' , 'width' ],
65
+ 'area' : ['alt' , 'coords' , 'shape' , 'href' , 'target' , 'ping' , 'rel' ,
66
+ 'media' , 'hreflang' , 'type' ],
67
+ 'colgroup' : ['span' ], # XXX only if element contains no <col> elements
68
+ 'col' : ['span' ],
69
+ 'td' : ['colspan' , 'rowspan' ],
70
+ 'th' : ['colspan' , 'rowspan' , 'scope' ],
71
+ # XXX form elements
72
+ 'script' : ['src' , 'defer' , 'async' , 'type' ],
73
+ 'event-source' : ['src' ],
74
+ 'details' : ['open' ],
75
+ 'datagrid' : ['multiple' , 'disabled' ],
76
+ 'command' : ['type' , 'label' , 'icon' , 'hidden' , 'disabled' , 'checked' ,
77
+ 'radiogroup' , 'default' ],
78
+ 'menu' : ['type' , 'label' , 'autosubmit' ],
79
+ 'font' : ['style' ]
80
+ }
81
+
82
+ requiredAttributeMap = {
83
+ 'link' : ['href' , 'rel' ],
84
+ 'bdo' : ['dir' ],
85
+ 'img' : ['src' ],
86
+ 'embed' : ['src' ],
87
+ 'object' : [], # XXX one of 'data' or 'type' is required
88
+ 'param' : ['name' , 'value' ],
89
+ 'source' : ['src' ],
90
+ 'map' : ['id' ],
29
91
}
30
92
31
93
class HTMLConformanceChecker (_base .Filter ):
@@ -38,13 +100,28 @@ def __iter__(self):
38
100
type = token ["type" ]
39
101
if type == "StartTag" :
40
102
name = token ["name" ].lower ()
41
- if name in allowedAttributeMap .keys ():
42
- allowedAttributes = allowedAttributeMap [name ]
103
+ if name == 'embed' :
104
+ # XXX spec says "any attributes w/o namespace"
105
+ pass
106
+ else :
107
+ if name in allowedAttributeMap .keys ():
108
+ allowedAttributes = globalAttributes + \
109
+ allowedAttributeMap [name ]
110
+ else :
111
+ allowedAttributes = globalAttributes
43
112
for attrName , attrValue in token ["data" ]:
44
113
if attrName .lower () not in allowedAttributes :
45
114
yield {"type" : "ParseError" ,
46
115
"data" : "unrecognized-attribute" ,
47
116
"datavars" : {"tagName" : name ,
48
117
"attributeName" : attrName }}
49
-
118
+ if name in requiredAttributeMap .keys ():
119
+ attrsPresent = [attrName for attrName , attrValue
120
+ in token ["data" ]]
121
+ for attrName in requiredAttributeMap [name ]:
122
+ if attrName not in attrsPresent :
123
+ yield {"type" : "ParseError" ,
124
+ "data" : "missing-required-attribute" ,
125
+ "datavars" : {"tagName" : name ,
126
+ "attributeName" : attrName }}
50
127
yield token
0 commit comments