30
30
"unknown-attribute" :
31
31
_ (u"Unknown '%(attributeName)s' attribute on <%(tagName)s>." ),
32
32
"missing-required-attribute" :
33
- _ (u"Missing required '%(attributeName)s' attribute on <%(tagName)s>." ),
33
+ _ (u"The '%(attributeName)s' attribute is required on <%(tagName)s>." ),
34
34
"unknown-input-type" :
35
- _ (u"Illegal value for <input type> attribute: '%(inputType)s'." ),
35
+ _ (u"Illegal value for attribute on <input type= '%(inputType)s'> ." ),
36
36
"attribute-not-allowed-on-this-input-type" :
37
- _ (u"'%(attributeName)s' attribute is not allowed on <input type=%(inputType)s>." ),
37
+ _ (u"The '%(attributeName)s' attribute is not allowed on <input type=%(inputType)s>." ),
38
38
"deprecated-attribute" :
39
- _ (u"'%(attributeName)s' attribute is deprecated on <%(tagName)s>." ),
39
+ _ (u"This attribute is deprecated: '%(attributeName)s' attribute on <%(tagName)s>." ),
40
40
"duplicate-value-in-token-list" :
41
- _ (u"Duplicate value '%(attributeValue)s' in token list in '%(attributeName)s' attribute on <%(tagName)s>." ),
41
+ _ (u"Duplicate value in token list: '%(attributeValue)s' in '%(attributeName)s' attribute on <%(tagName)s>." ),
42
42
"invalid-attribute-value" :
43
- _ (u"Invalid value for '%(attributeName)s' attribute on <%(tagName)s>." ),
43
+ _ (u"Invalid attribute value: '%(attributeName)s' attribute on <%(tagName)s>." ),
44
44
"space-in-id" :
45
- _ (u"Illegal space character in ID attribute on <%(tagName)s>." ),
45
+ _ (u"Whitespace is not allowed here: '%(attributeName)s' attribute on <%(tagName)s>." ),
46
46
"duplicate-id" :
47
- _ (u"Duplicate ID on <%(tagName)s>." ),
47
+ _ (u"This ID was already defined earlier: 'id' attribute on <%(tagName)s>." ),
48
48
"attribute-value-can-not-be-blank" :
49
- _ (u"Value can not be blank: '%(attributeName)s' attribute on <%(tagName)s>." ),
49
+ _ (u"This value can not be blank: '%(attributeName)s' attribute on <%(tagName)s>." ),
50
+ "id-does-not-exist" :
51
+ _ (u"This value refers to a non-existent ID: '%(attributeName)s' attribute on <%(tagName)s>." ),
52
+ "contextmenu-must-point-to-menu" :
53
+ _ (u"The contextmenu attribute must point to an ID defined on a <menu> element." ),
50
54
})
51
55
52
56
globalAttributes = frozenset (('class' , 'contenteditable' , 'contextmenu' , 'dir' ,
@@ -237,6 +241,7 @@ def __iter__(self):
237
241
if method :
238
242
for t in method (token ) or []: yield t
239
243
yield token
244
+ for t in self .eof () or []: yield t
240
245
241
246
def checkAttributeValues (self , token ):
242
247
tagName = token .get ("name" , "" )
@@ -252,6 +257,28 @@ def checkAttributeValues(self, token):
252
257
if method :
253
258
for t in method (token , tagName , attrName , attrValue ) or []: yield t
254
259
260
+ def eof (self ):
261
+ for token in self .thingsThatPointToAnID :
262
+ tagName = token .get ("name" , "" ).lower ()
263
+ attrsDict = token ["data" ] # by now html5parser has "normalized" the attrs list into a dict.
264
+ # hooray for obscure side effects!
265
+ attrValue = attrsDict .get ("contextmenu" , "" )
266
+ if attrValue and (attrValue not in self .IDsWeHaveKnownAndLoved ):
267
+ yield {"type" : "ParseError" ,
268
+ "data" : "id-does-not-exist" ,
269
+ "datavars" : {"tagName" : tagName ,
270
+ "attributeName" : "contextmenu" ,
271
+ "attributeValue" : attrValue }}
272
+ else :
273
+ for refToken in self .thingsThatDefineAnID :
274
+ id = refToken .get ("data" , {}).get ("id" , "" )
275
+ if not id : continue
276
+ if id == attrValue :
277
+ if refToken .get ("name" , "" ).lower () != "menu" :
278
+ yield {"type" : "ParseError" ,
279
+ "data" : "contextmenu-must-point-to-menu" }
280
+ break
281
+
255
282
def validateStartTag (self , token ):
256
283
for t in self .checkUnknownStartTag (token ) or []: yield t
257
284
for t in self .checkStartTagRequiredAttributes (token ) or []: yield t
@@ -338,30 +365,37 @@ def validateAttributeValueContenteditable(self, token, tagName, attrName, attrVa
338
365
"datavars" : {"tagName" : tagName ,
339
366
"attributeName" : attrName }}
340
367
368
+ def validateAttributeValueContextmenu (self , token , tagName , attrName , attrValue ):
369
+ for t in self .checkIDValue (token , tagName , attrName , attrValue ) or []: yield t
370
+ self .thingsThatPointToAnID .append (token )
371
+
341
372
def validateAttributeValueId (self , token , tagName , attrName , attrValue ):
342
373
# This method has side effects. It adds 'token' to the list of
343
374
# things that define an ID (self.thingsThatDefineAnID) so that we can
344
375
# later check 1) whether an ID is duplicated, and 2) whether all the
345
376
# things that point to something else by ID (like <label for> or
346
377
# <span contextmenu>) point to an ID that actually exists somewhere.
347
- if not attrValue :
348
- yield {"type" : "ParseError" ,
349
- "data" : "attribute-value-can-not-be-blank" ,
350
- "datavars" : {"tagName" : tagName ,
351
- "attributeName" : attrName }}
352
- return
353
-
378
+ for t in self .checkIDValue (token , tagName , attrName , attrValue ) or []: yield t
379
+ if not attrValue : return
354
380
if attrValue in self .IDsWeHaveKnownAndLoved :
355
381
yield {"type" : "ParseError" ,
356
382
"data" : "duplicate-id" ,
357
383
"datavars" : {"tagName" : tagName }}
358
384
self .IDsWeHaveKnownAndLoved .append (attrValue )
359
385
self .thingsThatDefineAnID .append (token )
386
+
387
+ def checkIDValue (self , token , tagName , attrName , attrValue ):
388
+ if not attrValue :
389
+ yield {"type" : "ParseError" ,
390
+ "data" : "attribute-value-can-not-be-blank" ,
391
+ "datavars" : {"tagName" : tagName ,
392
+ "attributeName" : attrName }}
360
393
for c in attrValue :
361
394
if c in spaceCharacters :
362
395
yield {"type" : "ParseError" ,
363
396
"data" : "space-in-id" ,
364
- "datavars" : {"tagName" : tagName }}
397
+ "datavars" : {"tagName" : tagName ,
398
+ "attributeName" : attrName }}
365
399
yield {"type" : "ParseError" ,
366
400
"data" : "invalid-attribute-value" ,
367
401
"datavars" : {"tagName" : tagName ,
0 commit comments