20
20
from sets import ImmutableSet as frozenset
21
21
import _base
22
22
import iso639codes
23
- from html5lib .constants import E , spaceCharacters
23
+ from html5lib .constants import E , spaceCharacters , digits
24
24
from html5lib import tokenizer
25
25
import gettext
26
26
_ = gettext .gettext
58
58
_ (u"The contextmenu attribute must point to an ID defined on a <menu> element." ),
59
59
"invalid-lang-code" :
60
60
_ (u"Invalid language code: '%(attributeName)s' attibute on <%(tagName)s>." ),
61
+ "invalid-integer-value" :
62
+ _ (u"Value must be an integer: '%(attributeName)s' attribute on <%tagName)s>." ),
61
63
})
62
64
63
65
globalAttributes = frozenset (('class' , 'contenteditable' , 'contextmenu' , 'dir' ,
@@ -250,41 +252,9 @@ def __iter__(self):
250
252
yield token
251
253
for t in self .eof () or []: yield t
252
254
253
- def checkAttributeValues (self , token ):
254
- tagName = token .get ("name" , "" )
255
- fakeToken = {"tagName" : tagName .capitalize ()}
256
- for attrName , attrValue in token .get ("data" , []):
257
- attrName = attrName .lower ()
258
- fakeToken ["attributeName" ] = attrName .capitalize ()
259
- method = getattr (self , "validateAttributeValue%(tagName)s%(attributeName)s" % fakeToken , None )
260
- if method :
261
- for t in method (token , tagName , attrName , attrValue ) or []: yield t
262
- else :
263
- method = getattr (self , "validateAttributeValue%(attributeName)s" % fakeToken , None )
264
- if method :
265
- for t in method (token , tagName , attrName , attrValue ) or []: yield t
266
-
267
- def eof (self ):
268
- for token in self .thingsThatPointToAnID :
269
- tagName = token .get ("name" , "" ).lower ()
270
- attrsDict = token ["data" ] # by now html5parser has "normalized" the attrs list into a dict.
271
- # hooray for obscure side effects!
272
- attrValue = attrsDict .get ("contextmenu" , "" )
273
- if attrValue and (attrValue not in self .IDsWeHaveKnownAndLoved ):
274
- yield {"type" : "ParseError" ,
275
- "data" : "id-does-not-exist" ,
276
- "datavars" : {"tagName" : tagName ,
277
- "attributeName" : "contextmenu" ,
278
- "attributeValue" : attrValue }}
279
- else :
280
- for refToken in self .thingsThatDefineAnID :
281
- id = refToken .get ("data" , {}).get ("id" , "" )
282
- if not id : continue
283
- if id == attrValue :
284
- if refToken .get ("name" , "" ).lower () != "menu" :
285
- yield {"type" : "ParseError" ,
286
- "data" : "contextmenu-must-point-to-menu" }
287
- break
255
+ ##########################################################################
256
+ # Start tag validation
257
+ ##########################################################################
288
258
289
259
def validateStartTag (self , token ):
290
260
for t in self .checkUnknownStartTag (token ) or []: yield t
@@ -324,6 +294,10 @@ def validateStartTagInput(self, token):
324
294
"datavars" : {"attributeName" : attrName ,
325
295
"inputType" : inputType }}
326
296
297
+ ##########################################################################
298
+ # Start tag validation helpers
299
+ ##########################################################################
300
+
327
301
def checkUnknownStartTag (self , token ):
328
302
# check for recognized tag name
329
303
name = token .get ("name" , "" ).lower ()
@@ -356,6 +330,24 @@ def checkStartTagUnknownAttributes(self, token):
356
330
"datavars" : {"tagName" : name ,
357
331
"attributeName" : attrName }}
358
332
333
+ ##########################################################################
334
+ # Attribute validation
335
+ ##########################################################################
336
+
337
+ def checkAttributeValues (self , token ):
338
+ tagName = token .get ("name" , "" )
339
+ fakeToken = {"tagName" : tagName .capitalize ()}
340
+ for attrName , attrValue in token .get ("data" , []):
341
+ attrName = attrName .lower ()
342
+ fakeToken ["attributeName" ] = attrName .capitalize ()
343
+ method = getattr (self , "validateAttributeValue%(tagName)s%(attributeName)s" % fakeToken , None )
344
+ if method :
345
+ for t in method (token , tagName , attrName , attrValue ) or []: yield t
346
+ else :
347
+ method = getattr (self , "validateAttributeValue%(attributeName)s" % fakeToken , None )
348
+ if method :
349
+ for t in method (token , tagName , attrName , attrValue ) or []: yield t
350
+
359
351
def validateAttributeValueClass (self , token , tagName , attrName , attrValue ):
360
352
for t in self .checkTokenList (tagName , attrName , attrValue ) or []:
361
353
yield t
@@ -385,38 +377,6 @@ def validateAttributeValueLang(self, token, tagName, attrName, attrValue):
385
377
"attributeName" : attrName ,
386
378
"attributeValue" : attrValue }}
387
379
388
- def checkEnumeratedValue (self , token , tagName , attrName , attrValue , enumeratedValues ):
389
- if not attrValue and ('' not in enumeratedValues ):
390
- yield {"type" : "ParseError" ,
391
- "data" : "attribute-value-can-not-be-blank" ,
392
- "datavars" : {"tagName" : tagName ,
393
- "attributeName" : attrName }}
394
- return
395
- attrValue = attrValue .lower ()
396
- if attrValue not in enumeratedValues :
397
- yield {"type" : "ParseError" ,
398
- "data" : "invalid-enumerated-value" ,
399
- "datavars" : {"tagName" : tagName ,
400
- "attributeName" : attrName ,
401
- "enumeratedValues" : tuple (enumeratedValues )}}
402
- yield {"type" : "ParseError" ,
403
- "data" : "invalid-attribute-value" ,
404
- "datavars" : {"tagName" : tagName ,
405
- "attributeName" : attrName }}
406
-
407
- def checkBooleanValue (self , token , tagName , attrName , attrValue ):
408
- enumeratedValues = frozenset ((attrName , '' ))
409
- if attrValue not in enumeratedValues :
410
- yield {"type" : "ParseError" ,
411
- "data" : "invalid-boolean-value" ,
412
- "datavars" : {"tagName" : tagName ,
413
- "attributeName" : attrName ,
414
- "enumeratedValues" : tuple (enumeratedValues )}}
415
- yield {"type" : "ParseError" ,
416
- "data" : "invalid-attribute-value" ,
417
- "datavars" : {"tagName" : tagName ,
418
- "attributeName" : attrName }}
419
-
420
380
def validateAttributeValueContextmenu (self , token , tagName , attrName , attrValue ):
421
381
for t in self .checkIDValue (token , tagName , attrName , attrValue ) or []: yield t
422
382
self .thingsThatPointToAnID .append (token )
@@ -436,6 +396,13 @@ def validateAttributeValueId(self, token, tagName, attrName, attrValue):
436
396
self .IDsWeHaveKnownAndLoved .append (attrValue )
437
397
self .thingsThatDefineAnID .append (token )
438
398
399
+ def validateAttributeValueTabindex (self , token , tagName , attrName , attrValue ):
400
+ for t in self .checkIntegerValue (token , tagName , attrName , attrValue ) or []: yield t
401
+
402
+ ##########################################################################
403
+ # Attribute validation helpers
404
+ ##########################################################################
405
+
439
406
def checkIDValue (self , token , tagName , attrName , attrValue ):
440
407
if not attrValue :
441
408
yield {"type" : "ParseError" ,
@@ -474,3 +441,102 @@ def checkTokenList(self, tagName, attrName, attrValue):
474
441
currentValue = ''
475
442
else :
476
443
currentValue += c
444
+
445
+ def checkEnumeratedValue (self , token , tagName , attrName , attrValue , enumeratedValues ):
446
+ if not attrValue and ('' not in enumeratedValues ):
447
+ yield {"type" : "ParseError" ,
448
+ "data" : "attribute-value-can-not-be-blank" ,
449
+ "datavars" : {"tagName" : tagName ,
450
+ "attributeName" : attrName }}
451
+ return
452
+ attrValue = attrValue .lower ()
453
+ if attrValue not in enumeratedValues :
454
+ yield {"type" : "ParseError" ,
455
+ "data" : "invalid-enumerated-value" ,
456
+ "datavars" : {"tagName" : tagName ,
457
+ "attributeName" : attrName ,
458
+ "enumeratedValues" : tuple (enumeratedValues )}}
459
+ yield {"type" : "ParseError" ,
460
+ "data" : "invalid-attribute-value" ,
461
+ "datavars" : {"tagName" : tagName ,
462
+ "attributeName" : attrName }}
463
+
464
+ def checkBooleanValue (self , token , tagName , attrName , attrValue ):
465
+ enumeratedValues = frozenset ((attrName , '' ))
466
+ if attrValue not in enumeratedValues :
467
+ yield {"type" : "ParseError" ,
468
+ "data" : "invalid-boolean-value" ,
469
+ "datavars" : {"tagName" : tagName ,
470
+ "attributeName" : attrName ,
471
+ "enumeratedValues" : tuple (enumeratedValues )}}
472
+ yield {"type" : "ParseError" ,
473
+ "data" : "invalid-attribute-value" ,
474
+ "datavars" : {"tagName" : tagName ,
475
+ "attributeName" : attrName }}
476
+
477
+ def checkIntegerValue (self , token , tagName , attrName , attrValue ):
478
+ sign = 1
479
+ numberString = ''
480
+ state = 'begin' # ('begin', 'initial-number', 'number', 'trailing-junk')
481
+ error = {"type" : "ParseError" ,
482
+ "data" : "invalid-integer-value" ,
483
+ "datavars" : {"tagName" : tagName ,
484
+ "attributeName" : attrName ,
485
+ "attributeValue" : attrValue }}
486
+ for c in attrValue :
487
+ if state == 'begin' :
488
+ if c in spaceCharacters :
489
+ pass
490
+ elif c == '-' :
491
+ sign = - 1
492
+ state = 'initial-number'
493
+ elif c in digits :
494
+ numberString += c
495
+ state = 'in-number'
496
+ else :
497
+ yield error
498
+ return
499
+ elif state == 'initial-number' :
500
+ if c not in digits :
501
+ yield error
502
+ return
503
+ numberString += c
504
+ state = 'in-number'
505
+ elif state == 'in-number' :
506
+ if c in digits :
507
+ numberString += c
508
+ else :
509
+ state = 'trailing-junk'
510
+ elif state == 'trailing-junk' :
511
+ pass
512
+ if not numberString :
513
+ yield {"type" : "ParseError" ,
514
+ "data" : "attribute-value-can-not-be-blank" ,
515
+ "datavars" : {"tagName" : tagName ,
516
+ "attributeName" : attrName }}
517
+
518
+ ##########################################################################
519
+ # Whole document validation (IDs, etc.)
520
+ ##########################################################################
521
+
522
+ def eof (self ):
523
+ for token in self .thingsThatPointToAnID :
524
+ tagName = token .get ("name" , "" ).lower ()
525
+ attrsDict = token ["data" ] # by now html5parser has "normalized" the attrs list into a dict.
526
+ # hooray for obscure side effects!
527
+ attrValue = attrsDict .get ("contextmenu" , "" )
528
+ if attrValue and (attrValue not in self .IDsWeHaveKnownAndLoved ):
529
+ yield {"type" : "ParseError" ,
530
+ "data" : "id-does-not-exist" ,
531
+ "datavars" : {"tagName" : tagName ,
532
+ "attributeName" : "contextmenu" ,
533
+ "attributeValue" : attrValue }}
534
+ else :
535
+ for refToken in self .thingsThatDefineAnID :
536
+ id = refToken .get ("data" , {}).get ("id" , "" )
537
+ if not id : continue
538
+ if id == attrValue :
539
+ if refToken .get ("name" , "" ).lower () != "menu" :
540
+ yield {"type" : "ParseError" ,
541
+ "data" : "contextmenu-must-point-to-menu" }
542
+ break
0 commit comments