#include "tidy-int.h"
#if SUPPORT_ACCESSIBILITY_CHECKS
#include "access.h"
#include "message.h"
#include "tags.h"
#include "attrs.h"
#include "tmbstr.h"
static const ctmbstr imageExtensions[] =
{".jpg", ".gif", ".tif", ".pct", ".pic", ".iff", ".dib",
".tga", ".pcx", ".png", ".jpeg", ".tiff", ".bmp"};
#define N_IMAGE_EXTS (sizeof(imageExtensions)/sizeof(ctmbstr))
static const ctmbstr soundExtensions[] =
{".wav", ".au", ".aiff", ".snd", ".ra", ".rm"};
static const int soundExtErrCodes[] =
{
AUDIO_MISSING_TEXT_WAV,
AUDIO_MISSING_TEXT_AU,
AUDIO_MISSING_TEXT_AIFF,
AUDIO_MISSING_TEXT_SND,
AUDIO_MISSING_TEXT_RA,
AUDIO_MISSING_TEXT_RM
};
#define N_AUDIO_EXTS (sizeof(soundExtensions)/sizeof(ctmbstr))
static const ctmbstr mediaExtensions[] =
{".mpg", ".mov", ".asx", ".avi", ".ivf", ".m1v", ".mmm", ".mp2v",
".mpa", ".mpe", ".mpeg", ".ram", ".smi", ".smil", ".swf",
".wm", ".wma", ".wmv"};
#define N_MEDIA_EXTS (sizeof(mediaExtensions)/sizeof(ctmbstr))
static const ctmbstr frameExtensions[] =
{".htm", ".html", ".shtm", ".shtml", ".cfm", ".cfml",
".asp", ".cgi", ".pl", ".smil"};
#define N_FRAME_EXTS (sizeof(frameExtensions)/sizeof(ctmbstr))
static const int colorValues[][3] =
{
{ 0, 0, 0},
{128,128,128},
{192,192,192},
{255,255,255},
{192, 0, 0},
{255, 0, 0},
{128, 0,128},
{255, 0,255},
{ 0,128, 0},
{ 0,255, 0},
{128,128, 0},
{255,255, 0},
{ 0, 0,128},
{ 0, 0,255},
{ 0,128,128},
{ 0,255,255}
};
#define N_COLOR_VALS (sizeof(colorValues)/(sizeof(int[3]))
static const ctmbstr colorNames[] =
{
"black",
"silver",
"grey",
"white",
"maroon",
"red",
"purple",
"fuchsia",
"green",
"lime",
"olive",
"yellow",
"navy",
"blue",
"teal",
"aqua"
};
#define N_COLOR_NAMES (sizeof(colorNames)/sizeof(ctmbstr))
#define N_COLORS N_COLOR_NAMES
static void InitAccessibilityChecks( TidyDocImpl* doc, int level123 );
static void FreeAccessibilityChecks( TidyDocImpl* doc );
static Bool GetRgb( ctmbstr color, int rgb[3] );
static Bool CompareColors( const int rgbBG[3], const int rgbFG[3] );
static int ctox( tmbchar ch );
static void CheckFormControls( TidyDocImpl* doc, Node* node );
static void MetaDataPresent( TidyDocImpl* doc, Node* node );
static void CheckEmbed( TidyDocImpl* doc, Node* node );
static void CheckListUsage( TidyDocImpl* doc, Node* node );
static void GetFileExtension( ctmbstr path, tmbchar *ext, uint maxExt )
{
int i = TY_(tmbstrlen)(path) - 1;
ext[0] = '\0';
do {
if ( path[i] == '/' || path[i] == '\\' )
break;
else if ( path[i] == '.' )
{
TY_(tmbstrncpy)( ext, path+i, maxExt );
break;
}
} while ( --i > 0 );
}
static Bool IsImage( ctmbstr iType )
{
uint i;
tmbchar ext[20];
GetFileExtension( iType, ext, sizeof(ext) );
for (i = 0; i < N_IMAGE_EXTS; i++)
{
if ( TY_(tmbstrcasecmp)(ext, imageExtensions[i]) == 0 )
return yes;
}
return no;
}
static int IsSoundFile( ctmbstr sType )
{
uint i;
tmbchar ext[ 20 ];
GetFileExtension( sType, ext, sizeof(ext) );
for (i = 0; i < N_AUDIO_EXTS; i++)
{
if ( TY_(tmbstrcasecmp)(ext, soundExtensions[i]) == 0 )
return soundExtErrCodes[i];
}
return 0;
}
static Bool IsValidSrcExtension( ctmbstr sType )
{
uint i;
tmbchar ext[20];
GetFileExtension( sType, ext, sizeof(ext) );
for (i = 0; i < N_FRAME_EXTS; i++)
{
if ( TY_(tmbstrcasecmp)(ext, frameExtensions[i]) == 0 )
return yes;
}
return no;
}
static Bool IsValidMediaExtension( ctmbstr sType )
{
uint i;
tmbchar ext[20];
GetFileExtension( sType, ext, sizeof(ext) );
for (i = 0; i < N_MEDIA_EXTS; i++)
{
if ( TY_(tmbstrcasecmp)(ext, mediaExtensions[i]) == 0 )
return yes;
}
return no;
}
static Bool IsWhitespace( ctmbstr pString )
{
Bool isWht = yes;
ctmbstr cp;
for ( cp = pString; isWht && cp && *cp; ++cp )
{
isWht = TY_(IsWhite)( *cp );
}
return isWht;
}
static Bool hasValue( AttVal* av )
{
return ( av && ! IsWhitespace(av->value) );
}
static Bool IsPlaceholderAlt( ctmbstr txt )
{
return ( strstr(txt, "image") != NULL ||
strstr(txt, "photo") != NULL );
}
static Bool IsPlaceHolderObject( ctmbstr txt )
{
return ( strstr(txt, "object") != NULL );
}
static Bool EndsWithBytes( ctmbstr txt )
{
uint len = TY_(tmbstrlen)( txt );
return ( len >= 5 && TY_(tmbstrcmp)(txt+len-5, "bytes") == 0 );
}
static ctmbstr textFromOneNode( TidyDocImpl* doc, Node* node )
{
uint i;
uint x = 0;
tmbstr txt = doc->access.text;
if ( node )
{
for (i = node->start; i < node->end; ++i, ++x )
{
txt[x] = doc->lexer->lexbuf[i];
if ( x >= sizeof(doc->access.text)-1 )
break;
}
}
txt[x] = '\0';
return txt;
}
static void getTextNode( TidyDocImpl* doc, Node* node )
{
tmbstr txtnod = doc->access.textNode;
if ( TY_(nodeIsText)(node) )
{
uint i;
for (i = node->start; i < node->end; i++)
{
if ( doc->access.counter >= TEXTBUF_SIZE-1 )
return;
txtnod[ doc->access.counter++ ] = doc->lexer->lexbuf[i];
}
for ( node = node->content; node != NULL; node = node->next )
getTextNode( doc, node );
}
}
static tmbstr getTextNodeClear( TidyDocImpl* doc, Node* node )
{
ClearMemory( doc->access.textNode, TEXTBUF_SIZE );
doc->access.counter = 0;
getTextNode( doc, node->content );
return doc->access.textNode;
}
static Bool Level1_Enabled( TidyDocImpl* doc )
{
return doc->access.PRIORITYCHK == 1 ||
doc->access.PRIORITYCHK == 2 ||
doc->access.PRIORITYCHK == 3;
}
static Bool Level2_Enabled( TidyDocImpl* doc )
{
return doc->access.PRIORITYCHK == 2 ||
doc->access.PRIORITYCHK == 3;
}
static Bool Level3_Enabled( TidyDocImpl* doc )
{
return doc->access.PRIORITYCHK == 3;
}
static void CheckColorAvailable( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
if ( nodeIsIMG(node) )
TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_IMAGE );
else if ( nodeIsAPPLET(node) )
TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_APPLET );
else if ( nodeIsOBJECT(node) )
TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_OBJECT );
else if ( nodeIsSCRIPT(node) )
TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_SCRIPT );
else if ( nodeIsINPUT(node) )
TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_INPUT );
}
}
static void CheckColorContrast( TidyDocImpl* doc, Node* node )
{
int rgbBG[3] = {255,255,255};
if (Level3_Enabled( doc ))
{
Bool gotBG = yes;
AttVal* av;
for ( av = node->attributes; av; av = av->next )
{
if ( attrIsBGCOLOR(av) )
{
if ( hasValue(av) )
gotBG = GetRgb( av->value, rgbBG );
}
}
for ( av = node->attributes; gotBG && av != NULL; av = av->next )
{
uint errcode = 0;
if ( attrIsTEXT(av) )
errcode = COLOR_CONTRAST_TEXT;
else if ( attrIsLINK(av) )
errcode = COLOR_CONTRAST_LINK;
else if ( attrIsALINK(av) )
errcode = COLOR_CONTRAST_ACTIVE_LINK;
else if ( attrIsVLINK(av) )
errcode = COLOR_CONTRAST_VISITED_LINK;
if ( errcode && hasValue(av) )
{
int rgbFG[3] = {0, 0, 0};
if ( GetRgb(av->value, rgbFG) &&
!CompareColors(rgbBG, rgbFG) )
{
TY_(ReportAccessWarning)( doc, node, errcode );
}
}
}
}
}
static int minmax( int i1, int i2 )
{
return MAX(i1, i2) - MIN(i1,i2);
}
static int brightness( const int rgb[3] )
{
return ((rgb[0]*299) + (rgb[1]*587) + (rgb[2]*114)) / 1000;
}
static Bool CompareColors( const int rgbBG[3], const int rgbFG[3] )
{
int brightBG = brightness( rgbBG );
int brightFG = brightness( rgbFG );
int diffBright = minmax( brightBG, brightFG );
int diffColor = minmax( rgbBG[0], rgbFG[0] )
+ minmax( rgbBG[1], rgbFG[1] )
+ minmax( rgbBG[2], rgbFG[2] );
return ( diffBright > 180 &&
diffColor > 500 );
}
static Bool GetRgb( ctmbstr color, int rgb[] )
{
uint x;
for (x = 0; x < N_COLORS; x++)
{
if ( strstr(colorNames[x], color) != NULL )
{
rgb[0] = colorValues[x][0];
rgb[1] = colorValues[x][1];
rgb[2] = colorValues[x][2];
return yes;
}
}
if ( TY_(tmbstrlen)(color) == 7 && color[0] == '#' )
{
rgb[0] = (ctox(color[1]) * 16) + ctox(color[2]);
rgb[1] = (ctox(color[3]) * 16) + ctox(color[4]);
rgb[2] = (ctox(color[5]) * 16) + ctox(color[6]);
return yes;
}
return no;
}
static int ctox( tmbchar ch )
{
if ( ch >= '0' && ch <= '9' )
{
return ch - '0';
}
else if ( ch >= 'a' && ch <= 'f' )
{
return ch - 'a' + 10;
}
else if ( ch >= 'A' && ch <= 'F' )
{
return ch - 'A' + 10;
}
return -1;
}
static void CheckImage( TidyDocImpl* doc, Node* node )
{
Bool HasAlt = no;
Bool HasIsMap = no;
Bool HasLongDesc = no;
Bool HasDLINK = no;
Bool HasValidHeight = no;
Bool HasValidWidthBullet = no;
Bool HasValidWidthHR = no;
Bool HasTriggeredMissingLongDesc = no;
AttVal* av;
if (Level1_Enabled( doc ))
{
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsALT(av) )
{
if (av->value != NULL)
{
if ((TY_(tmbstrlen)(av->value) < 150) &&
(IsPlaceholderAlt (av->value) == no) &&
(IsPlaceHolderObject (av->value) == no) &&
(EndsWithBytes (av->value) == no) &&
(IsImage (av->value) == no))
{
HasAlt = yes;
}
else if (TY_(tmbstrlen)(av->value) > 150)
{
HasAlt = yes;
TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_TOO_LONG );
}
else if (IsImage (av->value) == yes)
{
HasAlt = yes;
TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_FILENAME);
}
else if (IsPlaceholderAlt (av->value) == yes)
{
HasAlt = yes;
TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_PLACEHOLDER);
}
else if (EndsWithBytes (av->value) == yes)
{
HasAlt = yes;
TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_FILE_SIZE);
}
}
}
else if ( attrIsWIDTH(av) )
{
if ( hasValue(av) )
{
int width = atoi( av->value );
if ( width < 30 )
HasValidWidthBullet = yes;
if ( width > 150 )
HasValidWidthHR = yes;
}
}
else if ( attrIsHEIGHT(av) )
{
if ( hasValue(av) && atoi(av->value) < 30 )
HasValidHeight = yes;
}
else if ( attrIsLONGDESC(av) )
{
if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
HasLongDesc = yes;
}
else if ( attrIsUSEMAP(av) )
{
if ( hasValue(av) )
doc->access.HasUseMap = yes;
}
else if ( attrIsISMAP(av) )
{
HasIsMap = yes;
}
}
if ( nodeIsA(node->next) )
{
node = node->next;
if (node->content != NULL && (node->content)->tag == NULL)
{
ctmbstr word = textFromOneNode( doc, node->content);
if ((TY_(tmbstrcmp)(word,"d") == 0)||
(TY_(tmbstrcmp)(word,"D") == 0))
{
HasDLINK = yes;
}
}
}
if ( node->next && !node->next->tag )
{
node = node->next;
if ( nodeIsA(node->next) )
{
node = node->next;
if (node->content != NULL && node->content->tag == NULL)
{
ctmbstr word = textFromOneNode( doc, node->content );
if ((TY_(tmbstrcmp)(word, "d") == 0)||
(TY_(tmbstrcmp)(word, "D") == 0))
{
HasDLINK = yes;
}
}
}
}
if ((HasAlt == no)&&
(HasValidWidthBullet == yes)&&
(HasValidHeight == yes))
{
}
if ((HasAlt == no)&&
(HasValidWidthHR == yes)&&
(HasValidHeight == yes))
{
}
if (HasAlt == no)
{
TY_(ReportAccessError)( doc, node, IMG_MISSING_ALT);
}
if ((HasLongDesc == no)&&
(HasValidHeight ==yes)&&
((HasValidWidthHR == yes)||
(HasValidWidthBullet == yes)))
{
HasTriggeredMissingLongDesc = yes;
}
if (HasTriggeredMissingLongDesc == no)
{
if ((HasDLINK == yes)&&
(HasLongDesc == no))
{
TY_(ReportAccessWarning)( doc, node, IMG_MISSING_LONGDESC);
}
if ((HasLongDesc == yes)&&
(HasDLINK == no))
{
TY_(ReportAccessWarning)( doc, node, IMG_MISSING_DLINK);
}
if ((HasLongDesc == no)&&
(HasDLINK == no))
{
TY_(ReportAccessWarning)( doc, node, IMG_MISSING_LONGDESC_DLINK);
}
}
if (HasIsMap == yes)
{
TY_(ReportAccessError)( doc, node, IMAGE_MAP_SERVER_SIDE_REQUIRES_CONVERSION);
TY_(ReportAccessWarning)( doc, node, IMG_MAP_SERVER_REQUIRES_TEXT_LINKS);
}
}
}
static void CheckApplet( TidyDocImpl* doc, Node* node )
{
Bool HasAlt = no;
Bool HasDescription = no;
AttVal* av;
if (Level1_Enabled( doc ))
{
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsALT(av) )
{
if (av->value != NULL)
{
HasAlt = yes;
}
}
}
if (HasAlt == no)
{
if (node->content != NULL)
{
ctmbstr word = NULL;
if ( node->content->tag == NULL )
word = textFromOneNode( doc, node->content);
if ( node->content->content != NULL &&
node->content->content->tag == NULL )
{
word = textFromOneNode( doc, node->content->content);
}
if ( word != NULL && !IsWhitespace(word) )
HasDescription = yes;
}
}
if ( !HasDescription && !HasAlt )
{
TY_(ReportAccessError)( doc, node, APPLET_MISSING_ALT );
}
}
}
static void CheckObject( TidyDocImpl* doc, Node* node )
{
Bool HasAlt = no;
Bool HasDescription = no;
if (Level1_Enabled( doc ))
{
if ( node->content != NULL)
{
if ( node->content->type != TextNode )
{
Node* tnode = node->content;
AttVal* av;
for ( av=tnode->attributes; av; av = av->next )
{
if ( attrIsALT(av) )
{
HasAlt = yes;
break;
}
}
}
if ( !HasAlt )
{
ctmbstr word = NULL;
if ( TY_(nodeIsText)(node->content) )
word = textFromOneNode( doc, node->content );
if ( word == NULL &&
TY_(nodeIsText)(node->content->content) )
{
word = textFromOneNode( doc, node->content->content );
}
if ( word != NULL && !IsWhitespace(word) )
HasDescription = yes;
}
}
if ( !HasAlt && !HasDescription )
{
TY_(ReportAccessError)( doc, node, OBJECT_MISSING_ALT );
}
}
}
static Bool CheckMissingStyleSheets( TidyDocImpl* doc, Node* node )
{
AttVal* av;
Node* content;
Bool sspresent = no;
for ( content = node->content;
!sspresent && content != NULL;
content = content->next )
{
sspresent = ( nodeIsLINK(content) ||
nodeIsSTYLE(content) ||
nodeIsFONT(content) ||
nodeIsBASEFONT(content) );
for ( av = content->attributes;
!sspresent && av != NULL;
av = av->next )
{
sspresent = ( attrIsSTYLE(av) || attrIsTEXT(av) ||
attrIsVLINK(av) || attrIsALINK(av) ||
attrIsLINK(av) );
if ( !sspresent && attrIsREL(av) )
{
sspresent = AttrValueIs(av, "stylesheet");
}
}
if ( ! sspresent )
sspresent = CheckMissingStyleSheets( doc, content );
}
return sspresent;
}
static void CheckFrame( TidyDocImpl* doc, Node* node )
{
Bool HasTitle = no;
AttVal* av;
doc->access.numFrames++;
if (Level1_Enabled( doc ))
{
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsLONGDESC(av) )
{
if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
{
doc->access.HasCheckedLongDesc++;
}
}
else if ( attrIsSRC(av) )
{
if ( hasValue(av) && !IsValidSrcExtension(av->value) )
{
TY_(ReportAccessError)( doc, node, FRAME_SRC_INVALID );
}
}
else if ( attrIsTITLE(av) )
{
if ( hasValue(av) )
HasTitle = yes;
if ( !HasTitle )
{
if ( av->value == NULL || TY_(tmbstrlen)(av->value) == 0 )
{
HasTitle = yes;
TY_(ReportAccessError)( doc, node, FRAME_TITLE_INVALID_NULL);
}
else
{
if ( IsWhitespace(av->value) && TY_(tmbstrlen)(av->value) > 0 )
{
HasTitle = yes;
TY_(ReportAccessError)( doc, node, FRAME_TITLE_INVALID_SPACES );
}
}
}
}
}
if ( !HasTitle )
{
TY_(ReportAccessError)( doc, node, FRAME_MISSING_TITLE);
}
if ( doc->access.numFrames==3 && doc->access.HasCheckedLongDesc<3 )
{
doc->access.numFrames = 0;
TY_(ReportAccessWarning)( doc, node, FRAME_MISSING_LONGDESC );
}
}
}
static void CheckIFrame( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
AttVal* av = attrGetSRC( node );
if ( hasValue(av) )
{
if ( !IsValidSrcExtension(av->value) )
TY_(ReportAccessError)( doc, node, FRAME_SRC_INVALID );
}
}
}
static void CheckAnchorAccess( TidyDocImpl* doc, Node* node )
{
AttVal* av;
Bool HasDescription = no;
Bool HasTriggeredLink = no;
for ( av = node->attributes; av != NULL; av = av->next )
{
if (Level1_Enabled( doc ))
{
if ( attrIsHREF(av) )
{
if ( hasValue(av) )
{
tmbchar ext[ 20 ];
GetFileExtension (av->value, ext, sizeof(ext) );
if ( IsValidMediaExtension(av->value) )
{
TY_(ReportAccessError)( doc, node, MULTIMEDIA_REQUIRES_TEXT );
}
if ( TY_(tmbstrlen)(ext) < 6 && TY_(tmbstrlen)(ext) > 0 )
{
int errcode = IsSoundFile( av->value );
if ( errcode )
{
if (node->next != NULL)
{
if (node->next->tag == NULL)
{
ctmbstr word = textFromOneNode( doc, node->next);
if (IsWhitespace (word) == no)
{
HasDescription = yes;
}
}
}
if ( !HasDescription )
{
TY_(ReportAccessError)( doc, node, errcode );
}
}
}
}
}
}
if (Level2_Enabled( doc ))
{
if ( attrIsTARGET(av) )
{
if (AttrValueIs(av, "_new"))
{
TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
}
else if (AttrValueIs(av, "_blank"))
{
TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
}
}
}
}
if (Level2_Enabled( doc ))
{
if ((node->content != NULL)&&
(node->content->tag == NULL))
{
ctmbstr word = textFromOneNode( doc, node->content);
if ((word != NULL)&&
(IsWhitespace (word) == no))
{
if (TY_(tmbstrcmp) (word, "more") == 0)
{
HasTriggeredLink = yes;
}
if (TY_(tmbstrcmp) (word, "click here") == 0)
{
TY_(ReportAccessWarning)( doc, node, LINK_TEXT_NOT_MEANINGFUL_CLICK_HERE);
}
if (HasTriggeredLink == no)
{
if (TY_(tmbstrlen)(word) < 6)
{
TY_(ReportAccessWarning)( doc, node, LINK_TEXT_NOT_MEANINGFUL);
}
}
if (TY_(tmbstrlen)(word) > 60)
{
TY_(ReportAccessWarning)( doc, node, LINK_TEXT_TOO_LONG);
}
}
}
if (node->content == NULL)
{
TY_(ReportAccessWarning)( doc, node, LINK_TEXT_MISSING);
}
}
}
static void CheckArea( TidyDocImpl* doc, Node* node )
{
Bool HasAlt = no;
AttVal* av;
for (av = node->attributes; av != NULL; av = av->next)
{
if (Level1_Enabled( doc ))
{
if ( attrIsALT(av) )
{
if (av->value != NULL)
{
HasAlt = yes;
}
}
}
if (Level2_Enabled( doc ))
{
if ( attrIsTARGET(av) )
{
if (AttrValueIs(av, "_new"))
{
TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
}
else if (AttrValueIs(av, "_blank"))
{
TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
}
}
}
}
if (Level1_Enabled( doc ))
{
if (HasAlt == no)
{
TY_(ReportAccessError)( doc, node, AREA_MISSING_ALT);
}
}
}
static void CheckScriptAcc( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
if ( node->next == NULL || !nodeIsNOSCRIPT(node->next) )
{
TY_(ReportAccessError)( doc, node, SCRIPT_MISSING_NOSCRIPT);
}
}
}
static void CheckRows( TidyDocImpl* doc, Node* node )
{
int numTR = 0;
int numValidTH = 0;
doc->access.CheckedHeaders++;
for(;;)
{
if (node == NULL)
{
break;
}
else
{
numTR++;
if ( nodeIsTH(node) )
{
doc->access.HasTH = yes;
if ( node->content && TY_(nodeIsText)(node->content->content) )
{
ctmbstr word = textFromOneNode( doc, node->content->content);
if ( !IsWhitespace(word) )
numValidTH++;
}
}
}
node = node->next;
}
if (numTR == numValidTH)
{
doc->access.HasValidRowHeaders = yes;
}
if ( numTR >= 2 &&
numTR > numValidTH &&
numValidTH >= 2 &&
doc->access.HasTH == yes )
{
doc->access.HasInvalidRowHeader = yes;
}
}
static void CheckColumns( TidyDocImpl* doc, Node* node )
{
Node* tnode;
int numTH = 0;
Bool isMissingHeader = no;
doc->access.CheckedHeaders++;
if ( nodeIsTH(node->content) )
{
doc->access.HasTH = yes;
for ( tnode = node->content; tnode; tnode = tnode->next )
{
if ( nodeIsTH(tnode) )
{
if ( TY_(nodeIsText)(tnode->content) )
{
ctmbstr word = textFromOneNode( doc, tnode->content);
if ( !IsWhitespace(word) )
numTH++;
}
}
else
{
isMissingHeader = yes;
}
}
}
if ( !isMissingHeader && numTH > 0 )
doc->access.HasValidColumnHeaders = yes;
if ( isMissingHeader && numTH >= 2 )
doc->access.HasInvalidColumnHeader = yes;
}
static void CheckTH( TidyDocImpl* doc, Node* node )
{
Bool HasAbbr = no;
ctmbstr word = NULL;
AttVal* av;
if (Level3_Enabled( doc ))
{
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsABBR(av) )
{
if ((av->value != NULL)&&
(IsWhitespace (av->value) == no))
{
HasAbbr = yes;
}
if ((av->value == NULL)||
(TY_(tmbstrlen)(av->value) == 0))
{
HasAbbr = yes;
TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_NULL);
}
if ((IsWhitespace (av->value) == yes)&&
(TY_(tmbstrlen)(av->value) > 0))
{
HasAbbr = yes;
TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_SPACES);
}
}
}
word = textFromOneNode( doc, node->content);
if ((word != NULL)&&
(IsWhitespace (word) == no))
{
if ((TY_(tmbstrlen)(word) > 15)&&
(HasAbbr == no))
{
TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR);
}
}
}
}
static void CheckMultiHeaders( TidyDocImpl* doc, Node* node )
{
Node* TNode;
Node* temp;
Bool validColSpanRows = yes;
Bool validColSpanColumns = yes;
int flag = 0;
if (Level1_Enabled( doc ))
{
if (node->content != NULL)
{
TNode = node->content;
while (TNode != NULL)
{
if ( nodeIsTR(TNode) )
{
if (TNode->content != NULL)
{
temp = TNode->content;
if ( nodeIsTH(temp) )
{
AttVal* av;
for (av = temp->attributes; av != NULL; av = av->next)
{
if ( attrIsROWSPAN(av) )
{
if (atoi(av->value) > 1)
{
validColSpanRows = no;
}
}
}
}
if (flag == 0)
{
while (temp != NULL)
{
if ( nodeIsTH(temp) )
{
AttVal* av;
for (av = temp->attributes; av != NULL; av = av->next)
{
if ( attrIsCOLSPAN(av) )
{
if (atoi(av->value) > 1)
{
validColSpanColumns = no;
}
}
}
}
temp = temp->next;
}
flag = 1;
}
}
}
TNode = TNode->next;
}
if (validColSpanRows == no)
{
TY_(ReportAccessWarning)( doc, node, DATA_TABLE_REQUIRE_MARKUP_ROW_HEADERS );
TY_(DisplayHTMLTableAlgorithm)( doc );
}
if (validColSpanColumns == no)
{
TY_(ReportAccessWarning)( doc, node, DATA_TABLE_REQUIRE_MARKUP_COLUMN_HEADERS );
TY_(DisplayHTMLTableAlgorithm)( doc );
}
}
}
}
static void CheckTable( TidyDocImpl* doc, Node* node )
{
Node* TNode;
Node* temp;
tmbstr word = NULL;
int numTR = 0;
Bool HasSummary = no;
Bool HasCaption = no;
if (Level3_Enabled( doc ))
{
AttVal* av;
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsSUMMARY(av) )
{
if ( hasValue(av) )
{
HasSummary = yes;
if (AttrContains(av, "summary") &&
AttrContains(av, "table"))
{
TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_PLACEHOLDER );
}
}
if ( av->value == NULL || TY_(tmbstrlen)(av->value) == 0 )
{
HasSummary = yes;
TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_NULL );
}
else if ( IsWhitespace(av->value) && TY_(tmbstrlen)(av->value) > 0 )
{
HasSummary = yes;
TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_SPACES );
}
}
}
if (node->content == NULL)
{
TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS);
return;
}
}
if (Level1_Enabled( doc ))
{
CheckMultiHeaders( doc, node );
}
if (Level2_Enabled( doc ))
{
if ( nodeIsCAPTION(node->content) )
{
TNode = node->content;
if (TNode->content->tag == NULL)
{
word = getTextNodeClear( doc, TNode);
}
if ( !IsWhitespace(word) )
{
HasCaption = yes;
}
}
if (HasCaption == no)
{
TY_(ReportAccessError)( doc, node, TABLE_MISSING_CAPTION);
}
}
if (node->content != NULL)
{
if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
{
CheckColumns( doc, node->content->next );
}
else if ( nodeIsTR(node->content) )
{
CheckColumns( doc, node->content );
}
}
if ( ! doc->access.HasValidColumnHeaders )
{
if (node->content != NULL)
{
if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
{
CheckRows( doc, node->content->next);
}
else if ( nodeIsTR(node->content) )
{
CheckRows( doc, node->content);
}
}
}
if (Level3_Enabled( doc ))
{
if (HasSummary == no)
{
TY_(ReportAccessError)( doc, node, TABLE_MISSING_SUMMARY);
}
}
if (Level2_Enabled( doc ))
{
if (node->content != NULL)
{
temp = node->content;
while (temp != NULL)
{
if ( nodeIsTR(temp) )
{
numTR++;
}
temp = temp->next;
}
if (numTR == 1)
{
TY_(ReportAccessWarning)( doc, node, LAYOUT_TABLES_LINEARIZE_PROPERLY);
}
}
if ( doc->access.HasTH )
{
TY_(ReportAccessWarning)( doc, node, LAYOUT_TABLE_INVALID_MARKUP);
}
}
if (Level1_Enabled( doc ))
{
if ( doc->access.CheckedHeaders == 2 )
{
if ( !doc->access.HasValidRowHeaders &&
!doc->access.HasValidColumnHeaders &&
!doc->access.HasInvalidRowHeader &&
!doc->access.HasInvalidColumnHeader )
{
TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS);
}
if ( !doc->access.HasValidRowHeaders &&
doc->access.HasInvalidRowHeader )
{
TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS_ROW);
}
if ( !doc->access.HasValidColumnHeaders &&
doc->access.HasInvalidColumnHeader )
{
TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS_COLUMN);
}
}
}
}
static void CheckASCII( TidyDocImpl* doc, Node* node )
{
Node* temp1;
Node* temp2;
tmbstr skipOver = NULL;
Bool IsAscii = no;
int HasSkipOverLink = 0;
uint i, x;
int newLines = -1;
tmbchar compareLetter;
int matchingCount = 0;
AttVal* av;
if (Level1_Enabled( doc ) && node->content)
{
for (i = node->content->start + 1; i < node->content->end; i++)
{
matchingCount = 0;
if (doc->lexer->lexbuf[i] == '\n')
{
newLines++;
}
compareLetter = doc->lexer->lexbuf[i];
for (x = i; x < i + 5; x++)
{
if (doc->lexer->lexbuf[x] == compareLetter)
{
matchingCount++;
}
else
{
break;
}
}
if (matchingCount >= 5)
{
break;
}
}
if (newLines >= 6 || matchingCount >= 5)
{
IsAscii = yes;
}
if (IsAscii == yes)
{
if (node->prev != NULL && node->prev->prev != NULL)
{
temp1 = node->prev->prev;
for (av = temp1->attributes; av != NULL; av = av->next)
{
if ( attrIsHREF(av) && hasValue(av) )
{
skipOver = av->value;
HasSkipOverLink++;
}
}
}
}
}
if (Level2_Enabled( doc ))
{
if (HasSkipOverLink == 1)
{
if ( nodeIsA(node->next) )
{
temp2 = node->next;
for (av = temp2->attributes; av != NULL; av = av->next)
{
if ( attrIsNAME(av) && hasValue(av) )
{
if ( strstr(skipOver, av->value) != NULL )
{
HasSkipOverLink++;
}
}
}
}
}
if (IsAscii == yes)
{
TY_(ReportAccessError)( doc, node, ASCII_REQUIRES_DESCRIPTION);
if (Level3_Enabled( doc ) && (HasSkipOverLink < 2))
TY_(ReportAccessError)( doc, node, SKIPOVER_ASCII_ART);
}
}
}
static void CheckFormControls( TidyDocImpl* doc, Node* node )
{
if ( !doc->access.HasValidFor &&
doc->access.HasValidId )
{
TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY_FOR);
}
if ( !doc->access.HasValidId &&
doc->access.HasValidFor )
{
TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY_ID);
}
if ( !doc->access.HasValidId &&
!doc->access.HasValidFor )
{
TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY);
}
}
static void CheckLabel( TidyDocImpl* doc, Node* node )
{
if (Level2_Enabled( doc ))
{
AttVal* av = attrGetFOR( node );
if ( hasValue(av) )
doc->access.HasValidFor = yes;
if ( ++doc->access.ForID == 2 )
{
doc->access.ForID = 0;
CheckFormControls( doc, node );
}
}
}
static void CheckInputLabel( TidyDocImpl* doc, Node* node )
{
if (Level2_Enabled( doc ))
{
AttVal* av;
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsID(av) && hasValue(av) )
doc->access.HasValidId = yes;
}
if ( ++doc->access.ForID == 2 )
{
doc->access.ForID = 0;
CheckFormControls( doc, node );
}
}
}
static void CheckInputAttributes( TidyDocImpl* doc, Node* node )
{
Bool HasAlt = no;
Bool MustHaveAlt = no;
AttVal* av;
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsTYPE(av) && hasValue(av) )
{
if (Level1_Enabled( doc ))
{
if (AttrValueIs(av, "image"))
{
MustHaveAlt = yes;
}
}
}
if ( attrIsALT(av) && hasValue(av) )
{
HasAlt = yes;
}
}
if ( MustHaveAlt && !HasAlt )
{
TY_(ReportAccessError)( doc, node, IMG_BUTTON_MISSING_ALT );
}
}
static void CheckFrameSet( TidyDocImpl* doc, Node* node )
{
Node* temp;
Bool HasNoFrames = no;
if (Level1_Enabled( doc ))
{
if (node->content != NULL)
{
temp = node->content;
while (temp != NULL)
{
if ( nodeIsA(temp) )
{
TY_(ReportAccessError)( doc, temp, NOFRAMES_INVALID_LINK);
}
else if ( nodeIsNOFRAMES(temp) )
{
HasNoFrames = yes;
if ( temp->content && nodeIsP(temp->content->content) )
{
Node* para = temp->content->content;
if ( TY_(nodeIsText)(para->content) )
{
ctmbstr word = textFromOneNode( doc, para->content );
if ( word && strstr(word, "browser") != NULL )
{
TY_(ReportAccessError)( doc, para, NOFRAMES_INVALID_CONTENT );
}
}
}
else if (temp->content == NULL)
{
TY_(ReportAccessError)( doc, temp, NOFRAMES_INVALID_NO_VALUE);
}
else if ( temp->content &&
IsWhitespace(textFromOneNode(doc, temp->content)) )
{
TY_(ReportAccessError)( doc, temp, NOFRAMES_INVALID_NO_VALUE);
}
}
temp = temp->next;
}
}
if (HasNoFrames == no)
{
TY_(ReportAccessError)( doc, node, FRAME_MISSING_NOFRAMES);
}
}
}
static void CheckHeaderNesting( TidyDocImpl* doc, Node* node )
{
Node* temp;
uint i;
int numWords = 1;
Bool IsValidIncrease = no;
Bool NeedsDescription = no;
if (Level2_Enabled( doc ))
{
if (node->content != NULL && node->content->tag == NULL)
{
ctmbstr word = textFromOneNode( doc, node->content);
for(i = 0; i < TY_(tmbstrlen)(word); i++)
{
if (word[i] == ' ')
{
numWords++;
}
}
if (numWords > 20)
{
NeedsDescription = yes;
}
}
if ( TY_(nodeIsHeader)(node) )
{
uint level = TY_(nodeHeaderLevel)( node );
IsValidIncrease = yes;
for ( temp = node->next; temp != NULL; temp = temp->next )
{
uint nested = TY_(nodeHeaderLevel)( temp );
if ( nested >= level )
{
IsValidIncrease = ( nested <= level + 1 );
break;
}
}
}
if ( !IsValidIncrease )
TY_(ReportAccessWarning)( doc, node, HEADERS_IMPROPERLY_NESTED );
if ( NeedsDescription )
TY_(ReportAccessWarning)( doc, node, HEADER_USED_FORMAT_TEXT );
}
}
static void CheckParagraphHeader( TidyDocImpl* doc, Node* node )
{
Bool IsNotHeader = no;
Node* temp;
if (Level2_Enabled( doc ))
{
if (node->content != NULL)
{
if (node->content->tag != NULL)
{
temp = node->content;
while (temp != NULL)
{
if (temp->tag == NULL)
{
IsNotHeader = yes;
break;
}
temp = temp->next;
}
}
if ( !IsNotHeader )
{
if ( nodeIsSTRONG(node->content) )
{
TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_BOLD);
}
if ( nodeIsU(node->content) )
{
TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_UNDERLINE);
}
if ( nodeIsEM(node->content) )
{
TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_ITALICS);
}
}
}
}
}
static void CheckEmbed( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
AttVal* av = attrGetSRC( node );
if ( hasValue(av) && IsValidMediaExtension(av->value) )
{
TY_(ReportAccessError)( doc, node, MULTIMEDIA_REQUIRES_TEXT );
}
}
}
static void CheckHTMLAccess( TidyDocImpl* doc, Node* node )
{
Bool ValidLang = no;
if (Level3_Enabled( doc ))
{
AttVal* av = attrGetLANG( node );
if ( av )
{
ValidLang = yes;
if ( !hasValue(av) )
TY_(ReportAccessError)( doc, node, LANGUAGE_INVALID );
}
if ( !ValidLang )
TY_(ReportAccessError)( doc, node, LANGUAGE_NOT_IDENTIFIED );
}
}
static void CheckBlink( TidyDocImpl* doc, Node* node )
{
if (Level2_Enabled( doc ))
{
if ( TY_(nodeIsText)(node->content) )
{
ctmbstr word = textFromOneNode( doc, node->content );
if ( !IsWhitespace(word) )
{
TY_(ReportAccessError)( doc, node, REMOVE_BLINK_MARQUEE );
}
}
}
}
static void CheckMarquee( TidyDocImpl* doc, Node* node )
{
if (Level2_Enabled( doc ))
{
if ( TY_(nodeIsText)(node) )
{
ctmbstr word = textFromOneNode( doc, node->content);
if ( !IsWhitespace(word) )
{
TY_(ReportAccessError)( doc, node, REMOVE_BLINK_MARQUEE );
}
}
}
}
static void CheckLink( TidyDocImpl* doc, Node* node )
{
Bool HasRel = no;
Bool HasType = no;
if (Level1_Enabled( doc ))
{
AttVal* av;
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsREL(av) && hasValue(av) )
{
if (AttrContains(av, "stylesheet"))
HasRel = yes;
}
if ( attrIsTYPE(av) && hasValue(av) )
{
HasType = yes;
}
}
if (HasRel && HasType)
TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_LINK );
}
}
static void CheckStyle( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ELEMENT );
}
}
static void DynamicContent( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
uint msgcode = 0;
if ( nodeIsAPPLET(node) )
msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_APPLET;
else if ( nodeIsSCRIPT(node) )
msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_SCRIPT;
else if ( nodeIsOBJECT(node) )
msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_OBJECT;
if ( msgcode )
TY_(ReportAccessWarning)( doc, node, msgcode );
}
}
static void ProgrammaticObjects( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
int msgcode = 0;
if ( nodeIsSCRIPT(node) )
msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_SCRIPT;
else if ( nodeIsOBJECT(node) )
msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_OBJECT;
else if ( nodeIsEMBED(node) )
msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_EMBED;
else if ( nodeIsAPPLET(node) )
msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_APPLET;
if ( msgcode )
TY_(ReportAccessWarning)( doc, node, msgcode );
}
}
static void AccessibleCompatible( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
int msgcode = 0;
if ( nodeIsSCRIPT(node) )
msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_SCRIPT;
else if ( nodeIsOBJECT(node) )
msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_OBJECT;
else if ( nodeIsEMBED(node) )
msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_EMBED;
else if ( nodeIsAPPLET(node) )
msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_APPLET;
if ( msgcode )
TY_(ReportAccessWarning)( doc, node, msgcode );
}
}
static int WordCount( TidyDocImpl* doc, Node* node )
{
int wc = 0;
if (Level1_Enabled( doc ))
{
if ( TY_(nodeIsText)( node ) )
{
tmbchar ch;
ctmbstr word = textFromOneNode( doc, node );
if ( !IsWhitespace(word) )
{
++wc;
while ( (ch = *word++) && wc < 5 )
{
if ( ch == ' ')
++wc;
}
}
}
for ( node = node->content; wc < 5 && node; node = node->next )
{
wc += WordCount( doc, node );
}
}
return wc;
}
static void CheckFlicker( TidyDocImpl* doc, Node* node )
{
if (Level1_Enabled( doc ))
{
int msgcode = 0;
if ( nodeIsSCRIPT(node) )
msgcode = REMOVE_FLICKER_SCRIPT;
else if ( nodeIsOBJECT(node) )
msgcode = REMOVE_FLICKER_OBJECT;
else if ( nodeIsEMBED(node) )
msgcode = REMOVE_FLICKER_EMBED;
else if ( nodeIsAPPLET(node) )
msgcode = REMOVE_FLICKER_APPLET;
else if ( nodeIsIMG(node) )
{
AttVal* av = attrGetSRC( node );
if ( hasValue(av) )
{
tmbchar ext[20];
GetFileExtension( av->value, ext, sizeof(ext) );
if ( TY_(tmbstrcasecmp)(ext, ".gif") == 0 )
msgcode = REMOVE_FLICKER_ANIMATED_GIF;
}
}
if ( msgcode )
TY_(ReportAccessWarning)( doc, node, msgcode );
}
}
static void CheckDeprecated( TidyDocImpl* doc, Node* node )
{
if (Level2_Enabled( doc ))
{
int msgcode = 0;
if ( nodeIsAPPLET(node) )
msgcode = REPLACE_DEPRECATED_HTML_APPLET;
else if ( nodeIsBASEFONT(node) )
msgcode = REPLACE_DEPRECATED_HTML_BASEFONT;
else if ( nodeIsCENTER(node) )
msgcode = REPLACE_DEPRECATED_HTML_CENTER;
else if ( nodeIsDIR(node) )
msgcode = REPLACE_DEPRECATED_HTML_DIR;
else if ( nodeIsFONT(node) )
msgcode = REPLACE_DEPRECATED_HTML_FONT;
else if ( nodeIsISINDEX(node) )
msgcode = REPLACE_DEPRECATED_HTML_ISINDEX;
else if ( nodeIsMENU(node) )
msgcode = REPLACE_DEPRECATED_HTML_MENU;
else if ( nodeIsS(node) )
msgcode = REPLACE_DEPRECATED_HTML_S;
else if ( nodeIsSTRIKE(node) )
msgcode = REPLACE_DEPRECATED_HTML_STRIKE;
else if ( nodeIsU(node) )
msgcode = REPLACE_DEPRECATED_HTML_U;
if ( msgcode )
TY_(ReportAccessError)( doc, node, msgcode );
}
}
static void CheckScriptKeyboardAccessible( TidyDocImpl* doc, Node* node )
{
Node* content;
int HasOnMouseDown = 0;
int HasOnMouseUp = 0;
int HasOnClick = 0;
int HasOnMouseOut = 0;
int HasOnMouseOver = 0;
int HasOnMouseMove = 0;
if (Level2_Enabled( doc ))
{
AttVal* av;
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsOnMOUSEDOWN(av) )
HasOnMouseDown++;
if ( attrIsOnMOUSEUP(av) )
HasOnMouseUp++;
if ( attrIsOnCLICK(av) )
HasOnClick++;
if ( attrIsOnMOUSEOUT(av) )
HasOnMouseOut++;
if ( attrIsOnMOUSEOVER(av) )
HasOnMouseOver++;
if ( attrIsOnMOUSEMOVE(av) )
HasOnMouseMove++;
if ( attrIsOnKEYDOWN(av) )
HasOnMouseDown++;
if ( attrIsOnKEYUP(av) )
HasOnMouseUp++;
if ( attrIsOnKEYPRESS(av) )
HasOnClick++;
if ( attrIsOnBLUR(av) )
HasOnMouseOut++;
}
if ( HasOnMouseDown == 1 )
TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_DOWN);
if ( HasOnMouseUp == 1 )
TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_UP);
if ( HasOnClick == 1 )
TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_CLICK);
if ( HasOnMouseOut == 1 )
TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OUT);
if ( HasOnMouseOver == 1 )
TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OVER);
if ( HasOnMouseMove == 1 )
TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_MOVE);
for ( content = node->content; content != NULL; content = content->next )
CheckScriptKeyboardAccessible( doc, content );
}
}
static Bool CheckMetaData( TidyDocImpl* doc, Node* node, Bool HasMetaData )
{
Bool HasHttpEquiv = no;
Bool HasContent = no;
Bool ContainsAttr = no;
if (Level2_Enabled( doc ))
{
if ( nodeIsMETA(node) )
{
AttVal* av;
for (av = node->attributes; av != NULL; av = av->next)
{
if ( attrIsHTTP_EQUIV(av) && hasValue(av) )
{
ContainsAttr = yes;
if (AttrValueIs(av, "refresh"))
{
HasHttpEquiv = yes;
TY_(ReportAccessError)( doc, node, REMOVE_AUTO_REFRESH );
}
}
if ( attrIsCONTENT(av) && hasValue(av) )
{
ContainsAttr = yes;
if ( TY_(tmbstrncmp)(av->value, "http:", 5) == 0)
{
HasContent = yes;
TY_(ReportAccessError)( doc, node, REMOVE_AUTO_REDIRECT);
}
}
}
if ( HasContent || HasHttpEquiv )
{
HasMetaData = yes;
TY_(ReportAccessError)( doc, node, METADATA_MISSING_REDIRECT_AUTOREFRESH);
}
else
{
if ( ContainsAttr && !HasContent && !HasHttpEquiv )
HasMetaData = yes;
}
}
if ( !HasMetaData &&
nodeIsADDRESS(node) &&
nodeIsA(node->content) )
{
HasMetaData = yes;
}
if ( !HasMetaData &&
nodeIsTITLE(node) &&
TY_(nodeIsText)(node->content) )
{
ctmbstr word = textFromOneNode( doc, node->content );
if ( !IsWhitespace(word) )
HasMetaData = yes;
}
if ( nodeIsLINK(node) )
{
AttVal* av = attrGetREL(node);
HasMetaData = yes;
if (AttrContains(av, "stylesheet"))
{
TY_(ReportAccessError)( doc, node, METADATA_MISSING_LINK );
}
}
for ( node = node->content; node; node = node->next )
{
HasMetaData = CheckMetaData( doc, node, HasMetaData );
}
}
return HasMetaData;
}
static void MetaDataPresent( TidyDocImpl* doc, Node* node )
{
if (Level2_Enabled( doc ))
{
TY_(ReportAccessError)( doc, node, METADATA_MISSING );
}
}
static void CheckDocType( TidyDocImpl* doc )
{
if (Level2_Enabled( doc ))
{
Node* DTnode = TY_(FindDocType)(doc);
if (DTnode && DTnode->end != 0)
{
ctmbstr word = textFromOneNode( doc, DTnode);
if ((strstr (word, "HTML PUBLIC") == NULL) &&
(strstr (word, "html PUBLIC") == NULL))
DTnode = NULL;
}
if (!DTnode)
TY_(ReportAccessError)( doc, &doc->root, DOCTYPE_MISSING);
}
}
static Bool urlMatch( ctmbstr url1, ctmbstr url2 )
{
return ( TY_(tmbstrcmp)( url1, url2 ) == 0 );
}
static Bool FindLinkA( TidyDocImpl* doc, Node* node, ctmbstr url )
{
Bool found = no;
for ( node = node->content; !found && node; node = node->next )
{
if ( nodeIsA(node) )
{
AttVal* href = attrGetHREF( node );
found = ( hasValue(href) && urlMatch(url, href->value) );
}
else
found = FindLinkA( doc, node, url );
}
return found;
}
static void CheckMapLinks( TidyDocImpl* doc, Node* node )
{
Node* child;
if (!Level3_Enabled( doc ))
return;
for ( child = node->content; child != NULL; child = child->next )
{
if ( nodeIsAREA(child) )
{
AttVal* href = attrGetHREF( child );
if ( hasValue(href) &&
!FindLinkA( doc, &doc->root, href->value ) )
{
TY_(ReportAccessError)( doc, node, IMG_MAP_CLIENT_MISSING_TEXT_LINKS );
}
}
}
}
static void CheckForStyleAttribute( TidyDocImpl* doc, Node* node )
{
Node* content;
if (Level1_Enabled( doc ))
{
AttVal* style = attrGetSTYLE( node );
if ( hasValue(style) )
{
TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ATTR );
}
}
for ( content = node->content; content != NULL; content = content->next )
CheckForStyleAttribute( doc, content );
}
static void CheckForListElements( TidyDocImpl* doc, Node* node )
{
if ( nodeIsLI(node) )
{
doc->access.ListElements++;
}
else if ( nodeIsOL(node) || nodeIsUL(node) )
{
doc->access.OtherListElements++;
}
for ( node = node->content; node != NULL; node = node->next )
{
CheckForListElements( doc, node );
}
}
static void CheckListUsage( TidyDocImpl* doc, Node* node )
{
int msgcode = 0;
if (!Level2_Enabled( doc ))
return;
if ( nodeIsOL(node) )
msgcode = LIST_USAGE_INVALID_OL;
else if ( nodeIsUL(node) )
msgcode = LIST_USAGE_INVALID_UL;
if ( msgcode )
{
if ( !nodeIsLI(node->content) ) {
TY_(ReportAccessWarning)( doc, node, msgcode );
} else if ( node->implicit ) {
TY_(ReportAccessWarning)( doc, node, LIST_USAGE_INVALID_LI );
}
}
else if ( nodeIsLI(node) )
{
if ( node->parent == NULL ||
( !nodeIsOL(node->parent) && !nodeIsUL(node->parent) ) )
{
TY_(ReportAccessWarning)( doc, node, LIST_USAGE_INVALID_LI );
} else if ( node->implicit && node->parent &&
( nodeIsOL(node->parent) || nodeIsUL(node->parent) ) ) {
msgcode = nodeIsUL(node->parent) ?
LIST_USAGE_INVALID_UL : LIST_USAGE_INVALID_OL;
TY_(ReportAccessWarning)( doc, node, msgcode );
}
}
}
static void InitAccessibilityChecks( TidyDocImpl* doc, int level123 )
{
ClearMemory( &doc->access, sizeof(doc->access) );
doc->access.PRIORITYCHK = level123;
}
static void FreeAccessibilityChecks( TidyDocImpl* ARG_UNUSED(doc) )
{
}
static void AccessibilityCheckNode( TidyDocImpl* doc, Node* node )
{
Node* content;
if ( nodeIsBODY(node) )
{
CheckColorContrast( doc, node );
}
else if ( nodeIsHEAD(node) )
{
if ( !CheckMetaData( doc, node, no ) )
MetaDataPresent( doc, node );
}
else if ( nodeIsA(node) )
{
CheckAnchorAccess( doc, node );
}
else if ( nodeIsIMG(node) )
{
CheckFlicker( doc, node );
CheckColorAvailable( doc, node );
CheckImage( doc, node );
}
else if ( nodeIsMAP(node) )
{
CheckMapLinks( doc, node );
}
else if ( nodeIsAREA(node) )
{
CheckArea( doc, node );
}
else if ( nodeIsAPPLET(node) )
{
CheckDeprecated( doc, node );
ProgrammaticObjects( doc, node );
DynamicContent( doc, node );
AccessibleCompatible( doc, node );
CheckFlicker( doc, node );
CheckColorAvailable( doc, node );
CheckApplet(doc, node );
}
else if ( nodeIsOBJECT(node) )
{
ProgrammaticObjects( doc, node );
DynamicContent( doc, node );
AccessibleCompatible( doc, node );
CheckFlicker( doc, node );
CheckColorAvailable( doc, node );
CheckObject( doc, node );
}
else if ( nodeIsFRAME(node) )
{
CheckFrame( doc, node );
}
else if ( nodeIsIFRAME(node) )
{
CheckIFrame( doc, node );
}
else if ( nodeIsSCRIPT(node) )
{
DynamicContent( doc, node );
ProgrammaticObjects( doc, node );
AccessibleCompatible( doc, node );
CheckFlicker( doc, node );
CheckColorAvailable( doc, node );
CheckScriptAcc( doc, node );
}
else if ( nodeIsTABLE(node) )
{
CheckColorContrast( doc, node );
CheckTable( doc, node );
}
else if ( nodeIsPRE(node) || nodeIsXMP(node) )
{
CheckASCII( doc, node );
}
else if ( nodeIsLABEL(node) )
{
CheckLabel( doc, node );
}
else if ( nodeIsINPUT(node) )
{
CheckColorAvailable( doc, node );
CheckInputLabel( doc, node );
CheckInputAttributes( doc, node );
}
else if ( nodeIsFRAMESET(node) )
{
CheckFrameSet( doc, node );
}
else if ( TY_(nodeIsHeader)(node) )
{
CheckHeaderNesting( doc, node );
}
else if ( nodeIsP(node) )
{
CheckParagraphHeader( doc, node );
}
else if ( nodeIsHTML(node) )
{
CheckHTMLAccess( doc, node );
}
else if ( nodeIsBLINK(node) )
{
CheckBlink( doc, node );
}
else if ( nodeIsMARQUEE(node) )
{
CheckMarquee( doc, node );
}
else if ( nodeIsLINK(node) )
{
CheckLink( doc, node );
}
else if ( nodeIsSTYLE(node) )
{
CheckColorContrast( doc, node );
CheckStyle( doc, node );
}
else if ( nodeIsEMBED(node) )
{
CheckEmbed( doc, node );
ProgrammaticObjects( doc, node );
AccessibleCompatible( doc, node );
CheckFlicker( doc, node );
}
else if ( nodeIsBASEFONT(node) ||
nodeIsCENTER(node) ||
nodeIsISINDEX(node) ||
nodeIsU(node) ||
nodeIsFONT(node) ||
nodeIsDIR(node) ||
nodeIsS(node) ||
nodeIsSTRIKE(node) ||
nodeIsMENU(node) )
{
CheckDeprecated( doc, node );
}
else if ( nodeIsTH(node) )
{
CheckTH( doc, node );
}
else if ( nodeIsLI(node) || nodeIsOL(node) || nodeIsUL(node) )
{
CheckListUsage( doc, node );
}
for ( content = node->content; content != NULL; content = content->next )
{
AccessibilityCheckNode( doc, content );
}
}
void TY_(AccessibilityChecks)( TidyDocImpl* doc )
{
InitAccessibilityChecks( doc, cfg(doc, TidyAccessibilityCheckLevel) );
TY_(AccessibilityHelloMessage)( doc );
CheckScriptKeyboardAccessible( doc, &doc->root );
CheckForStyleAttribute( doc, &doc->root );
CheckDocType( doc );
if ( Level2_Enabled( doc )
&& ! CheckMissingStyleSheets( doc, &doc->root ) )
{
TY_(ReportAccessWarning)( doc, &doc->root, STYLE_SHEET_CONTROL_PRESENTATION );
}
CheckForListElements( doc, &doc->root );
AccessibilityCheckNode( doc, &doc->root );
FreeAccessibilityChecks( doc );
}
#endif