You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3306 lines
93 KiB

/* access.c -- carry out accessibility checks
Copyright University of Toronto
Portions (c) 1998-2009 (W3C) MIT, ERCIM, Keio University
See tidy.h for the copyright notice.
*/
/*********************************************************************
* AccessibilityChecks
*
* Carries out processes for all accessibility checks. Traverses
* through all the content within the tree and evaluates the tags for
* accessibility.
*
* To perform the following checks, 'AccessibilityChecks' must be
* called AFTER the tree structure has been formed.
*
* If, in the command prompt, there is no specification of which
* accessibility priorities to check, no accessibility checks will be
* performed. (ie. '1' for priority 1, '2' for priorities 1 and 2,
* and '3') for priorities 1, 2 and 3.)
*
* Copyright University of Toronto
* Programmed by: Mike Lam and Chris Ridpath
* Modifications by : Terry Teague (TRT)
*
* Reference document: http://www.w3.org/TR/WAI-WEBCONTENT/
*********************************************************************/
#include "tidy-int.h"
#if SUPPORT_ACCESSIBILITY_CHECKS
#include "access.h"
#include "message.h"
#include "tags.h"
#include "attrs.h"
#include "tmbstr.h"
/*
The accessibility checks to perform depending on user's desire.
1. priority 1
2. priority 1 & 2
3. priority 1, 2, & 3
*/
/* List of possible image types */
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))
/* List of possible sound file types */
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))
/* List of possible media extensions */
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))
/* List of possible frame sources */
static const ctmbstr frameExtensions[] =
{".htm", ".html", ".shtm", ".shtml", ".cfm", ".cfml",
".asp", ".cgi", ".pl", ".smil"};
#define N_FRAME_EXTS (sizeof(frameExtensions)/sizeof(ctmbstr))
/* List of possible colour values */
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]))
/* These arrays are used to convert color names to their RGB values */
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
/* function prototypes */
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 CheckMapAccess( TidyDocImpl* doc, Node* node, Node* front);
static void GetMapLinks( TidyDocImpl* doc, Node* node, Node* front);
static void CompareAnchorLinks( TidyDocImpl* doc, Node* front, int counter);
static void FindMissingLinks( TidyDocImpl* doc, Node* node, int counter);
*/
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 );
/*
GetFileExtension takes a path and returns the extension
portion of the path (if any).
*/
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 );
}
/************************************************************************
* IsImage
*
* Checks if the given filename is an image file.
* Returns 'yes' if it is, 'no' if it's not.
************************************************************************/
static Bool IsImage( ctmbstr iType )
{
uint i;
/* Get the file extension */
tmbchar ext[20];
GetFileExtension( iType, ext, sizeof(ext) );
/* Compare it to the array of known image file extensions */
for (i = 0; i < N_IMAGE_EXTS; i++)
{
if ( TY_(tmbstrcasecmp)(ext, imageExtensions[i]) == 0 )
return yes;
}
return no;
}
/***********************************************************************
* IsSoundFile
*
* Checks if the given filename is a sound file.
* Returns 'yes' if it is, 'no' if it's not.
***********************************************************************/
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;
}
/***********************************************************************
* IsValidSrcExtension
*
* Checks if the 'SRC' value within the FRAME element is valid
* The 'SRC' extension must end in ".htm", ".html", ".shtm", ".shtml",
* ".cfm", ".cfml", ".asp", ".cgi", ".pl", or ".smil"
*
* Returns yes if it is, returns no otherwise.
***********************************************************************/
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;
}
/*********************************************************************
* IsValidMediaExtension
*
* Checks to warn the user that syncronized text equivalents are
* required if multimedia is used.
*********************************************************************/
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;
}
/************************************************************************
* IsWhitespace
*
* Checks if the given string is all whitespace.
* Returns 'yes' if it is, 'no' if it's not.
************************************************************************/
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) );
}
/***********************************************************************
* IsPlaceholderAlt
*
* Checks to see if there is an image and photo place holder contained
* in the ALT text.
*
* Returns 'yes' if there is, 'no' if not.
***********************************************************************/
static Bool IsPlaceholderAlt( ctmbstr txt )
{
return ( strstr(txt, "image") != NULL ||
strstr(txt, "photo") != NULL );
}
/***********************************************************************
* IsPlaceholderTitle
*
* Checks to see if there is an TITLE place holder contained
* in the 'ALT' text.
*
* Returns 'yes' if there is, 'no' if not.
static Bool IsPlaceHolderTitle( ctmbstr txt )
{
return ( strstr(txt, "title") != NULL );
}
***********************************************************************/
/***********************************************************************
* IsPlaceHolderObject
*
* Checks to see if there is an OBJECT place holder contained
* in the 'ALT' text.
*
* Returns 'yes' if there is, 'no' if not.
***********************************************************************/
static Bool IsPlaceHolderObject( ctmbstr txt )
{
return ( strstr(txt, "object") != NULL );
}
/**********************************************************
* EndsWithBytes
*
* Checks to see if the ALT text ends with 'bytes'
* Returns 'yes', if true, 'no' otherwise.
**********************************************************/
static Bool EndsWithBytes( ctmbstr txt )
{
uint len = TY_(tmbstrlen)( txt );
return ( len >= 5 && TY_(tmbstrcmp)(txt+len-5, "bytes") == 0 );
}
/*******************************************************
* textFromOneNode
*
* Returns a list of characters contained within one
* text node.
*******************************************************/
static ctmbstr textFromOneNode( TidyDocImpl* doc, Node* node )
{
uint i;
uint x = 0;
tmbstr txt = doc->access.text;
if ( node )
{
/* Copy contents of a text node */
for (i = node->start; i < node->end; ++i, ++x )
{
txt[x] = doc->lexer->lexbuf[i];
/* Check buffer overflow */
if ( x >= sizeof(doc->access.text)-1 )
break;
}
}
txt[x] = '\0';
return txt;
}
/*********************************************************
* getTextNode
*
* Locates text nodes within a container element.
* Retrieves text that are found contained within
* text nodes, and concatenates the text.
*********************************************************/
static void getTextNode( TidyDocImpl* doc, Node* node )
{
tmbstr txtnod = doc->access.textNode;
/*
Continues to traverse through container element until it no
longer contains any more contents
*/
/* If the tag of the node is NULL, then grab the text within the node */
if ( TY_(nodeIsText)(node) )
{
uint i;
/* Retrieves each character found within the text node */
for (i = node->start; i < node->end; i++)
{
/* The text must not exceed buffer */
if ( doc->access.counter >= TEXTBUF_SIZE-1 )
return;
txtnod[ doc->access.counter++ ] = doc->lexer->lexbuf[i];
}
/* Traverses through the contents within a container element */
for ( node = node->content; node != NULL; node = node->next )
getTextNode( doc, node );
}
}
/**********************************************************
* getTextNodeClear
*
* Clears the current 'textNode' and reloads it with new
* text. The textNode must be cleared before use.
**********************************************************/
static tmbstr getTextNodeClear( TidyDocImpl* doc, Node* node )
{
/* Clears list */
TidyClearMemory( doc->access.textNode, TEXTBUF_SIZE );
doc->access.counter = 0;
getTextNode( doc, node->content );
return doc->access.textNode;
}
/**********************************************************
* LevelX_Enabled
*
* Tell whether access "X" is enabled.
**********************************************************/
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;
}
/********************************************************
* CheckColorAvailable
*
* Verify that information conveyed with color is
* available without color.
********************************************************/
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 );
}
}
/*********************************************************************
* CheckColorContrast
*
* Checks elements for color contrast. Must have valid contrast for
* valid visibility.
*
* This logic is extremely fragile as it does not recognize
* the fact that color is inherited by many components and
* that BG and FG colors are often set separately. E.g. the
* background color may be set by for the body or a table
* or a cell. The foreground color may be set by any text
* element (p, h1, h2, input, textarea), either explicitly
* or by style. Ergo, this test will not handle most real
* world cases. It's a start, however.
*********************************************************************/
static void CheckColorContrast( TidyDocImpl* doc, Node* node )
{
int rgbBG[3] = {255,255,255}; /* Black text on white BG */
if (Level3_Enabled( doc ))
{
Bool gotBG = yes;
AttVal* av;
/* Check for 'BGCOLOR' first to compare with other color attributes */
for ( av = node->attributes; av; av = av->next )
{
if ( attrIsBGCOLOR(av) )
{
if ( hasValue(av) )
gotBG = GetRgb( av->value, rgbBG );
}
}
/*
Search for COLOR attributes to compare with background color
Must have valid colour contrast
*/
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}; /* Black text */
if ( GetRgb(av->value, rgbFG) &&
!CompareColors(rgbBG, rgbFG) )
{
TY_(ReportAccessWarning)( doc, node, errcode );
}
}
}
}
}
/**************************************************************
* CompareColors
*
* Compares two RGB colors for good contrast.
**************************************************************/
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 );
}
/*********************************************************************
* GetRgb
*
* Gets the red, green and blue values for this attribute for the
* background.
*
* Example: If attribute is BGCOLOR="#121005" then red = 18, green = 16,
* blue = 5.
*********************************************************************/
static Bool GetRgb( ctmbstr color, int rgb[] )
{
uint x;
/* Check if we have a color name */
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;
}
}
/*
No color name so must be hex values
Is this a number in hexadecimal format?
*/
/* Must be 7 characters in the RGB value (including '#') */
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;
}
/*******************************************************************
* ctox
*
* Converts a character to a number.
* Example: if given character is 'A' then returns 10.
*
* Returns the number that the character represents. Returns -1 if not a
* valid number.
*******************************************************************/
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;
}
/***********************************************************
* CheckImage
*
* Checks all image attributes for specific elements to
* check for validity of the values contained within
* the attributes. An appropriate warning message is displayed
* to indicate the error.
***********************************************************/
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 ))
{
/* Checks all image attributes for invalid values within attributes */
for (av = node->attributes; av != NULL; av = av->next)
{
/*
Checks for valid ALT attribute.
The length of the alt text must be less than 150 characters
long.
*/
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);
}
}
}
/*
Checks for width values of 'bullets' and 'horizontal
rules' for validity.
Valid pixel width for 'bullets' must be < 30, and > 150 for
horizontal rules.
*/
else if ( attrIsWIDTH(av) )
{
/* Longdesc attribute needed if width attribute is not present. */
if ( hasValue(av) )
{
int width = atoi( av->value );
if ( width < 30 )
HasValidWidthBullet = yes;
if ( width > 150 )
HasValidWidthHR = yes;
}
}
/*
Checks for height values of 'bullets' and horizontal
rules for validity.
Valid pixel height for 'bullets' and horizontal rules
mustt be < 30.
*/
else if ( attrIsHEIGHT(av) )
{
/* Longdesc attribute needed if height attribute not present. */
if ( hasValue(av) && atoi(av->value) < 30 )
HasValidHeight = yes;
}
/*
Checks for longdesc and determines validity.
The length of the 'longdesc' must be > 1
*/
else if ( attrIsLONGDESC(av) )
{
if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
HasLongDesc = yes;
}
/*
Checks for 'USEMAP' attribute. Ensures that
text links are provided for client-side image maps
*/
else if ( attrIsUSEMAP(av) )
{
if ( hasValue(av) )
doc->access.HasUseMap = yes;
}
else if ( attrIsISMAP(av) )
{
HasIsMap = yes;
}
}
/*
Check to see if a dLINK is present. The ANCHOR element must
be present following the IMG element. The text found between
the ANCHOR tags must be < 6 characters long, and must contain
the letter 'd'.
*/
if ( nodeIsA(node->next) )
{
node = node->next;
/*
Node following the anchor must be a text node
for dLINK to exist
*/
if (node->content != NULL && (node->content)->tag == NULL)
{
/* Number of characters found within the text node */
ctmbstr word = textFromOneNode( doc, node->content);
if ((TY_(tmbstrcmp)(word,"d") == 0)||
(TY_(tmbstrcmp)(word,"D") == 0))
{
HasDLINK = yes;
}
}
}
/*
Special case check for dLINK. This will occur if there is
whitespace between the <img> and <a> elements. Ignores
whitespace and continues check for dLINK.
*/
if ( node->next && !node->next->tag )
{
node = node->next;
if ( nodeIsA(node->next) )
{
node = node->next;
/*
Node following the ANCHOR must be a text node
for dLINK to exist
*/
if (node->content != NULL && node->content->tag == NULL)
{
/* Number of characters found within the text node */
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);
}
}
}
/***********************************************************
* CheckApplet
*
* Checks APPLET element to check for validity pertaining
* the 'ALT' attribute. An appropriate warning message is
* displayed to indicate the error. An appropriate warning
* message is displayed to indicate the error. If no 'ALT'
* text is present, then there must be alternate content
* within the APPLET element.
***********************************************************/
static void CheckApplet( TidyDocImpl* doc, Node* node )
{
Bool HasAlt = no;
Bool HasDescription = no;
AttVal* av;
if (Level1_Enabled( doc ))
{
/* Checks for attributes within the APPLET element */
for (av = node->attributes; av != NULL; av = av->next)
{
/*
Checks for valid ALT attribute.
The length of the alt text must be > 4 characters in length
but must be < 150 characters long.
*/
if ( attrIsALT(av) )
{
if (av->value != NULL)
{
HasAlt = yes;
}
}
}
if (HasAlt == no)
{
/* Must have alternate text representation for that element */
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 );
}
}
}
/*******************************************************************
* CheckObject
*
* Checks to verify whether the OBJECT element contains
* 'ALT' text, and to see that the sound file selected is
* of a valid sound file type. OBJECT must have an alternate text
* representation.
*******************************************************************/
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;
}
}
}
/* Must have alternate text representation for that element */
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 );
}
}
}
/***************************************************************
* CheckMissingStyleSheets
*
* Ensures that stylesheets are used to control the presentation.
***************************************************************/
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;
}
/*******************************************************************
* CheckFrame
*
* Checks if the URL is valid and to check if a 'LONGDESC' is needed
* within the FRAME element. If a 'LONGDESC' is needed, the value must
* be valid. The URL must end with the file extension, htm, or html.
* Also, checks to ensure that the 'SRC' and 'TITLE' values are valid.
*******************************************************************/
static void CheckFrame( TidyDocImpl* doc, Node* node )
{
Bool HasTitle = no;
AttVal* av;
doc->access.numFrames++;
if (Level1_Enabled( doc ))
{
/* Checks for attributes within the FRAME element */
for (av = node->attributes; av != NULL; av = av->next)
{
/* Checks if 'LONGDESC' value is valid only if present */
if ( attrIsLONGDESC(av) )
{
if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
{
doc->access.HasCheckedLongDesc++;
}
}
/* Checks for valid 'SRC' value within the frame element */
else if ( attrIsSRC(av) )
{
if ( hasValue(av) && !IsValidSrcExtension(av->value) )
{
TY_(ReportAccessError)( doc, node, FRAME_SRC_INVALID );
}
}
/* Checks for valid 'TITLE' value within frame element */
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 );
}
}
}
}
}