1 /* access.c -- carry out accessibility checks
3 Copyright University of Toronto
4 Portions (c) 1998-2009 (W3C) MIT, ERCIM, Keio University
5 See tidy.h for the copyright notice.
9 /*********************************************************************
12 * Carries out processes for all accessibility checks. Traverses
13 * through all the content within the tree and evaluates the tags for
16 * To perform the following checks, 'AccessibilityChecks' must be
17 * called AFTER the tree structure has been formed.
19 * If, in the command prompt, there is no specification of which
20 * accessibility priorities to check, no accessibility checks will be
21 * performed. (ie. '1' for priority 1, '2' for priorities 1 and 2,
22 * and '3') for priorities 1, 2 and 3.)
24 * Copyright University of Toronto
25 * Programmed by: Mike Lam and Chris Ridpath
26 * Modifications by : Terry Teague (TRT)
28 * Reference document: http://www.w3.org/TR/WAI-WEBCONTENT/
29 *********************************************************************/
34 #if SUPPORT_ACCESSIBILITY_CHECKS
44 The accessibility checks to perform depending on user's desire.
51 /* List of possible image types */
52 static const ctmbstr imageExtensions[] =
53 {".jpg", ".gif", ".tif", ".pct", ".pic", ".iff", ".dib",
54 ".tga", ".pcx", ".png", ".jpeg", ".tiff", ".bmp"};
56 #define N_IMAGE_EXTS (sizeof(imageExtensions)/sizeof(ctmbstr))
58 /* List of possible sound file types */
59 static const ctmbstr soundExtensions[] =
60 {".wav", ".au", ".aiff", ".snd", ".ra", ".rm"};
62 static const int soundExtErrCodes[] =
64 AUDIO_MISSING_TEXT_WAV,
65 AUDIO_MISSING_TEXT_AU,
66 AUDIO_MISSING_TEXT_AIFF,
67 AUDIO_MISSING_TEXT_SND,
68 AUDIO_MISSING_TEXT_RA,
72 #define N_AUDIO_EXTS (sizeof(soundExtensions)/sizeof(ctmbstr))
74 /* List of possible media extensions */
75 static const ctmbstr mediaExtensions[] =
76 {".mpg", ".mov", ".asx", ".avi", ".ivf", ".m1v", ".mmm", ".mp2v",
77 ".mpa", ".mpe", ".mpeg", ".ram", ".smi", ".smil", ".swf",
78 ".wm", ".wma", ".wmv"};
80 #define N_MEDIA_EXTS (sizeof(mediaExtensions)/sizeof(ctmbstr))
82 /* List of possible frame sources */
83 static const ctmbstr frameExtensions[] =
84 {".htm", ".html", ".shtm", ".shtml", ".cfm", ".cfml",
85 ".asp", ".cgi", ".pl", ".smil"};
87 #define N_FRAME_EXTS (sizeof(frameExtensions)/sizeof(ctmbstr))
89 /* List of possible colour values */
90 static const int colorValues[][3] =
110 #define N_COLOR_VALS (sizeof(colorValues)/(sizeof(int[3]))
112 /* These arrays are used to convert color names to their RGB values */
113 static const ctmbstr colorNames[] =
133 #define N_COLOR_NAMES (sizeof(colorNames)/sizeof(ctmbstr))
134 #define N_COLORS N_COLOR_NAMES
137 /* function prototypes */
138 static void InitAccessibilityChecks( TidyDocImpl* doc, int level123 );
139 static void FreeAccessibilityChecks( TidyDocImpl* doc );
141 static Bool GetRgb( ctmbstr color, int rgb[3] );
142 static Bool CompareColors( const int rgbBG[3], const int rgbFG[3] );
143 static int ctox( tmbchar ch );
146 static void CheckMapAccess( TidyDocImpl* doc, Node* node, Node* front);
147 static void GetMapLinks( TidyDocImpl* doc, Node* node, Node* front);
148 static void CompareAnchorLinks( TidyDocImpl* doc, Node* front, int counter);
149 static void FindMissingLinks( TidyDocImpl* doc, Node* node, int counter);
151 static void CheckFormControls( TidyDocImpl* doc, Node* node );
152 static void MetaDataPresent( TidyDocImpl* doc, Node* node );
153 static void CheckEmbed( TidyDocImpl* doc, Node* node );
154 static void CheckListUsage( TidyDocImpl* doc, Node* node );
157 GetFileExtension takes a path and returns the extension
158 portion of the path (if any).
161 static void GetFileExtension( ctmbstr path, tmbchar *ext, uint maxExt )
163 int i = TY_(tmbstrlen)(path) - 1;
168 if ( path[i] == '/' || path[i] == '\\' )
170 else if ( path[i] == '.' )
172 TY_(tmbstrncpy)( ext, path+i, maxExt );
178 /************************************************************************
181 * Checks if the given filename is an image file.
182 * Returns 'yes' if it is, 'no' if it's not.
183 ************************************************************************/
185 static Bool IsImage( ctmbstr iType )
189 /* Get the file extension */
191 GetFileExtension( iType, ext, sizeof(ext) );
193 /* Compare it to the array of known image file extensions */
194 for (i = 0; i < N_IMAGE_EXTS; i++)
196 if ( TY_(tmbstrcasecmp)(ext, imageExtensions[i]) == 0 )
204 /***********************************************************************
207 * Checks if the given filename is a sound file.
208 * Returns 'yes' if it is, 'no' if it's not.
209 ***********************************************************************/
211 static int IsSoundFile( ctmbstr sType )
215 GetFileExtension( sType, ext, sizeof(ext) );
217 for (i = 0; i < N_AUDIO_EXTS; i++)
219 if ( TY_(tmbstrcasecmp)(ext, soundExtensions[i]) == 0 )
220 return soundExtErrCodes[i];
226 /***********************************************************************
227 * IsValidSrcExtension
229 * Checks if the 'SRC' value within the FRAME element is valid
230 * The 'SRC' extension must end in ".htm", ".html", ".shtm", ".shtml",
231 * ".cfm", ".cfml", ".asp", ".cgi", ".pl", or ".smil"
233 * Returns yes if it is, returns no otherwise.
234 ***********************************************************************/
236 static Bool IsValidSrcExtension( ctmbstr sType )
240 GetFileExtension( sType, ext, sizeof(ext) );
242 for (i = 0; i < N_FRAME_EXTS; i++)
244 if ( TY_(tmbstrcasecmp)(ext, frameExtensions[i]) == 0 )
251 /*********************************************************************
252 * IsValidMediaExtension
254 * Checks to warn the user that syncronized text equivalents are
255 * required if multimedia is used.
256 *********************************************************************/
258 static Bool IsValidMediaExtension( ctmbstr sType )
262 GetFileExtension( sType, ext, sizeof(ext) );
264 for (i = 0; i < N_MEDIA_EXTS; i++)
266 if ( TY_(tmbstrcasecmp)(ext, mediaExtensions[i]) == 0 )
273 /************************************************************************
276 * Checks if the given string is all whitespace.
277 * Returns 'yes' if it is, 'no' if it's not.
278 ************************************************************************/
280 static Bool IsWhitespace( ctmbstr pString )
285 for ( cp = pString; isWht && cp && *cp; ++cp )
287 isWht = TY_(IsWhite)( *cp );
292 static Bool hasValue( AttVal* av )
294 return ( av && ! IsWhitespace(av->value) );
297 /***********************************************************************
300 * Checks to see if there is an image and photo place holder contained
303 * Returns 'yes' if there is, 'no' if not.
304 ***********************************************************************/
306 static Bool IsPlaceholderAlt( ctmbstr txt )
308 return ( strstr(txt, "image") != NULL ||
309 strstr(txt, "photo") != NULL );
313 /***********************************************************************
316 * Checks to see if there is an TITLE place holder contained
319 * Returns 'yes' if there is, 'no' if not.
321 static Bool IsPlaceHolderTitle( ctmbstr txt )
323 return ( strstr(txt, "title") != NULL );
325 ***********************************************************************/
328 /***********************************************************************
329 * IsPlaceHolderObject
331 * Checks to see if there is an OBJECT place holder contained
334 * Returns 'yes' if there is, 'no' if not.
335 ***********************************************************************/
337 static Bool IsPlaceHolderObject( ctmbstr txt )
339 return ( strstr(txt, "object") != NULL );
343 /**********************************************************
346 * Checks to see if the ALT text ends with 'bytes'
347 * Returns 'yes', if true, 'no' otherwise.
348 **********************************************************/
350 static Bool EndsWithBytes( ctmbstr txt )
352 uint len = TY_(tmbstrlen)( txt );
353 return ( len >= 5 && TY_(tmbstrcmp)(txt+len-5, "bytes") == 0 );
357 /*******************************************************
360 * Returns a list of characters contained within one
362 *******************************************************/
364 static ctmbstr textFromOneNode( TidyDocImpl* doc, Node* node )
368 tmbstr txt = doc->access.text;
372 /* Copy contents of a text node */
373 for (i = node->start; i < node->end; ++i, ++x )
375 txt[x] = doc->lexer->lexbuf[i];
377 /* Check buffer overflow */
378 if ( x >= sizeof(doc->access.text)-1 )
388 /*********************************************************
391 * Locates text nodes within a container element.
392 * Retrieves text that are found contained within
393 * text nodes, and concatenates the text.
394 *********************************************************/
396 static void getTextNode( TidyDocImpl* doc, Node* node )
398 tmbstr txtnod = doc->access.textNode;
401 Continues to traverse through container element until it no
402 longer contains any more contents
405 /* If the tag of the node is NULL, then grab the text within the node */
406 if ( TY_(nodeIsText)(node) )
410 /* Retrieves each character found within the text node */
411 for (i = node->start; i < node->end; i++)
413 /* The text must not exceed buffer */
414 if ( doc->access.counter >= TEXTBUF_SIZE-1 )
417 txtnod[ doc->access.counter++ ] = doc->lexer->lexbuf[i];
420 /* Traverses through the contents within a container element */
421 for ( node = node->content; node != NULL; node = node->next )
422 getTextNode( doc, node );
427 /**********************************************************
430 * Clears the current 'textNode' and reloads it with new
431 * text. The textNode must be cleared before use.
432 **********************************************************/
434 static tmbstr getTextNodeClear( TidyDocImpl* doc, Node* node )
437 TidyClearMemory( doc->access.textNode, TEXTBUF_SIZE );
438 doc->access.counter = 0;
440 getTextNode( doc, node->content );
441 return doc->access.textNode;
444 /**********************************************************
447 * Tell whether access "X" is enabled.
448 **********************************************************/
450 static Bool Level1_Enabled( TidyDocImpl* doc )
452 return doc->access.PRIORITYCHK == 1 ||
453 doc->access.PRIORITYCHK == 2 ||
454 doc->access.PRIORITYCHK == 3;
456 static Bool Level2_Enabled( TidyDocImpl* doc )
458 return doc->access.PRIORITYCHK == 2 ||
459 doc->access.PRIORITYCHK == 3;
461 static Bool Level3_Enabled( TidyDocImpl* doc )
463 return doc->access.PRIORITYCHK == 3;
466 /********************************************************
467 * CheckColorAvailable
469 * Verify that information conveyed with color is
470 * available without color.
471 ********************************************************/
473 static void CheckColorAvailable( TidyDocImpl* doc, Node* node )
475 if (Level1_Enabled( doc ))
477 if ( nodeIsIMG(node) )
478 TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_IMAGE );
480 else if ( nodeIsAPPLET(node) )
481 TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_APPLET );
483 else if ( nodeIsOBJECT(node) )
484 TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_OBJECT );
486 else if ( nodeIsSCRIPT(node) )
487 TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_SCRIPT );
489 else if ( nodeIsINPUT(node) )
490 TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_INPUT );
494 /*********************************************************************
497 * Checks elements for color contrast. Must have valid contrast for
500 * This logic is extremely fragile as it does not recognize
501 * the fact that color is inherited by many components and
502 * that BG and FG colors are often set separately. E.g. the
503 * background color may be set by for the body or a table
504 * or a cell. The foreground color may be set by any text
505 * element (p, h1, h2, input, textarea), either explicitly
506 * or by style. Ergo, this test will not handle most real
507 * world cases. It's a start, however.
508 *********************************************************************/
510 static void CheckColorContrast( TidyDocImpl* doc, Node* node )
512 int rgbBG[3] = {255,255,255}; /* Black text on white BG */
514 if (Level3_Enabled( doc ))
519 /* Check for 'BGCOLOR' first to compare with other color attributes */
520 for ( av = node->attributes; av; av = av->next )
522 if ( attrIsBGCOLOR(av) )
525 gotBG = GetRgb( av->value, rgbBG );
530 Search for COLOR attributes to compare with background color
531 Must have valid colour contrast
533 for ( av = node->attributes; gotBG && av != NULL; av = av->next )
536 if ( attrIsTEXT(av) )
537 errcode = COLOR_CONTRAST_TEXT;
538 else if ( attrIsLINK(av) )
539 errcode = COLOR_CONTRAST_LINK;
540 else if ( attrIsALINK(av) )
541 errcode = COLOR_CONTRAST_ACTIVE_LINK;
542 else if ( attrIsVLINK(av) )
543 errcode = COLOR_CONTRAST_VISITED_LINK;
545 if ( errcode && hasValue(av) )
547 int rgbFG[3] = {0, 0, 0}; /* Black text */
549 if ( GetRgb(av->value, rgbFG) &&
550 !CompareColors(rgbBG, rgbFG) )
552 TY_(ReportAccessWarning)( doc, node, errcode );
560 /**************************************************************
563 * Compares two RGB colors for good contrast.
564 **************************************************************/
565 static int minmax( int i1, int i2 )
567 return MAX(i1, i2) - MIN(i1,i2);
569 static int brightness( const int rgb[3] )
571 return ((rgb[0]*299) + (rgb[1]*587) + (rgb[2]*114)) / 1000;
574 static Bool CompareColors( const int rgbBG[3], const int rgbFG[3] )
576 int brightBG = brightness( rgbBG );
577 int brightFG = brightness( rgbFG );
579 int diffBright = minmax( brightBG, brightFG );
581 int diffColor = minmax( rgbBG[0], rgbFG[0] )
582 + minmax( rgbBG[1], rgbFG[1] )
583 + minmax( rgbBG[2], rgbFG[2] );
585 return ( diffBright > 180 &&
590 /*********************************************************************
593 * Gets the red, green and blue values for this attribute for the
596 * Example: If attribute is BGCOLOR="#121005" then red = 18, green = 16,
598 *********************************************************************/
600 static Bool GetRgb( ctmbstr color, int rgb[] )
604 /* Check if we have a color name */
605 for (x = 0; x < N_COLORS; x++)
607 if ( strstr(colorNames[x], color) != NULL )
609 rgb[0] = colorValues[x][0];
610 rgb[1] = colorValues[x][1];
611 rgb[2] = colorValues[x][2];
617 No color name so must be hex values
618 Is this a number in hexadecimal format?
621 /* Must be 7 characters in the RGB value (including '#') */
622 if ( TY_(tmbstrlen)(color) == 7 && color[0] == '#' )
624 rgb[0] = (ctox(color[1]) * 16) + ctox(color[2]);
625 rgb[1] = (ctox(color[3]) * 16) + ctox(color[4]);
626 rgb[2] = (ctox(color[5]) * 16) + ctox(color[6]);
634 /*******************************************************************
637 * Converts a character to a number.
638 * Example: if given character is 'A' then returns 10.
640 * Returns the number that the character represents. Returns -1 if not a
642 *******************************************************************/
644 static int ctox( tmbchar ch )
646 if ( ch >= '0' && ch <= '9' )
650 else if ( ch >= 'a' && ch <= 'f' )
652 return ch - 'a' + 10;
654 else if ( ch >= 'A' && ch <= 'F' )
656 return ch - 'A' + 10;
662 /***********************************************************
665 * Checks all image attributes for specific elements to
666 * check for validity of the values contained within
667 * the attributes. An appropriate warning message is displayed
668 * to indicate the error.
669 ***********************************************************/
671 static void CheckImage( TidyDocImpl* doc, Node* node )
675 Bool HasLongDesc = no;
677 Bool HasValidHeight = no;
678 Bool HasValidWidthBullet = no;
679 Bool HasValidWidthHR = no;
680 Bool HasTriggeredMissingLongDesc = no;
684 if (Level1_Enabled( doc ))
686 /* Checks all image attributes for invalid values within attributes */
687 for (av = node->attributes; av != NULL; av = av->next)
690 Checks for valid ALT attribute.
691 The length of the alt text must be less than 150 characters
696 if (av->value != NULL)
698 if ((TY_(tmbstrlen)(av->value) < 150) &&
699 (IsPlaceholderAlt (av->value) == no) &&
700 (IsPlaceHolderObject (av->value) == no) &&
701 (EndsWithBytes (av->value) == no) &&
702 (IsImage (av->value) == no))
707 else if (TY_(tmbstrlen)(av->value) > 150)
710 TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_TOO_LONG );
713 else if (IsImage (av->value) == yes)
716 TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_FILENAME);
719 else if (IsPlaceholderAlt (av->value) == yes)
722 TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_PLACEHOLDER);
725 else if (EndsWithBytes (av->value) == yes)
728 TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_FILE_SIZE);
734 Checks for width values of 'bullets' and 'horizontal
737 Valid pixel width for 'bullets' must be < 30, and > 150 for
740 else if ( attrIsWIDTH(av) )
742 /* Longdesc attribute needed if width attribute is not present. */
745 int width = atoi( av->value );
747 HasValidWidthBullet = yes;
750 HasValidWidthHR = yes;
755 Checks for height values of 'bullets' and horizontal
758 Valid pixel height for 'bullets' and horizontal rules
761 else if ( attrIsHEIGHT(av) )
763 /* Longdesc attribute needed if height attribute not present. */
764 if ( hasValue(av) && atoi(av->value) < 30 )
765 HasValidHeight = yes;
769 Checks for longdesc and determines validity.
770 The length of the 'longdesc' must be > 1
772 else if ( attrIsLONGDESC(av) )
774 if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
779 Checks for 'USEMAP' attribute. Ensures that
780 text links are provided for client-side image maps
782 else if ( attrIsUSEMAP(av) )
785 doc->access.HasUseMap = yes;
788 else if ( attrIsISMAP(av) )
796 Check to see if a dLINK is present. The ANCHOR element must
797 be present following the IMG element. The text found between
798 the ANCHOR tags must be < 6 characters long, and must contain
801 if ( nodeIsA(node->next) )
806 Node following the anchor must be a text node
810 if (node->content != NULL && (node->content)->tag == NULL)
812 /* Number of characters found within the text node */
813 ctmbstr word = textFromOneNode( doc, node->content);
815 if ((TY_(tmbstrcmp)(word,"d") == 0)||
816 (TY_(tmbstrcmp)(word,"D") == 0))
824 Special case check for dLINK. This will occur if there is
825 whitespace between the <img> and <a> elements. Ignores
826 whitespace and continues check for dLINK.
829 if ( node->next && !node->next->tag )
833 if ( nodeIsA(node->next) )
838 Node following the ANCHOR must be a text node
841 if (node->content != NULL && node->content->tag == NULL)
843 /* Number of characters found within the text node */
844 ctmbstr word = textFromOneNode( doc, node->content );
846 if ((TY_(tmbstrcmp)(word, "d") == 0)||
847 (TY_(tmbstrcmp)(word, "D") == 0))
856 (HasValidWidthBullet == yes)&&
857 (HasValidHeight == yes))
862 (HasValidWidthHR == yes)&&
863 (HasValidHeight == yes))
869 TY_(ReportAccessError)( doc, node, IMG_MISSING_ALT);
872 if ((HasLongDesc == no)&&
873 (HasValidHeight ==yes)&&
874 ((HasValidWidthHR == yes)||
875 (HasValidWidthBullet == yes)))
877 HasTriggeredMissingLongDesc = yes;
880 if (HasTriggeredMissingLongDesc == no)
882 if ((HasDLINK == yes)&&
885 TY_(ReportAccessWarning)( doc, node, IMG_MISSING_LONGDESC);
888 if ((HasLongDesc == yes)&&
891 TY_(ReportAccessWarning)( doc, node, IMG_MISSING_DLINK);
894 if ((HasLongDesc == no)&&
897 TY_(ReportAccessWarning)( doc, node, IMG_MISSING_LONGDESC_DLINK);
903 TY_(ReportAccessError)( doc, node, IMAGE_MAP_SERVER_SIDE_REQUIRES_CONVERSION);
905 TY_(ReportAccessWarning)( doc, node, IMG_MAP_SERVER_REQUIRES_TEXT_LINKS);
911 /***********************************************************
914 * Checks APPLET element to check for validity pertaining
915 * the 'ALT' attribute. An appropriate warning message is
916 * displayed to indicate the error. An appropriate warning
917 * message is displayed to indicate the error. If no 'ALT'
918 * text is present, then there must be alternate content
919 * within the APPLET element.
920 ***********************************************************/
922 static void CheckApplet( TidyDocImpl* doc, Node* node )
925 Bool HasDescription = no;
929 if (Level1_Enabled( doc ))
931 /* Checks for attributes within the APPLET element */
932 for (av = node->attributes; av != NULL; av = av->next)
935 Checks for valid ALT attribute.
936 The length of the alt text must be > 4 characters in length
937 but must be < 150 characters long.
942 if (av->value != NULL)
951 /* Must have alternate text representation for that element */
952 if (node->content != NULL)
956 if ( node->content->tag == NULL )
957 word = textFromOneNode( doc, node->content);
959 if ( node->content->content != NULL &&
960 node->content->content->tag == NULL )
962 word = textFromOneNode( doc, node->content->content);
965 if ( word != NULL && !IsWhitespace(word) )
966 HasDescription = yes;
970 if ( !HasDescription && !HasAlt )
972 TY_(ReportAccessError)( doc, node, APPLET_MISSING_ALT );
978 /*******************************************************************
981 * Checks to verify whether the OBJECT element contains
982 * 'ALT' text, and to see that the sound file selected is
983 * of a valid sound file type. OBJECT must have an alternate text
985 *******************************************************************/
987 static void CheckObject( TidyDocImpl* doc, Node* node )
990 Bool HasDescription = no;
992 if (Level1_Enabled( doc ))
994 if ( node->content != NULL)
996 if ( node->content->type != TextNode )
998 Node* tnode = node->content;
1001 for ( av=tnode->attributes; av; av = av->next )
1003 if ( attrIsALT(av) )
1011 /* Must have alternate text representation for that element */
1014 ctmbstr word = NULL;
1016 if ( TY_(nodeIsText)(node->content) )
1017 word = textFromOneNode( doc, node->content );
1019 if ( word == NULL &&
1020 TY_(nodeIsText)(node->content->content) )
1022 word = textFromOneNode( doc, node->content->content );
1025 if ( word != NULL && !IsWhitespace(word) )
1026 HasDescription = yes;
1030 if ( !HasAlt && !HasDescription )
1032 TY_(ReportAccessError)( doc, node, OBJECT_MISSING_ALT );
1038 /***************************************************************
1039 * CheckMissingStyleSheets
1041 * Ensures that stylesheets are used to control the presentation.
1042 ***************************************************************/
1044 static Bool CheckMissingStyleSheets( TidyDocImpl* doc, Node* node )
1048 Bool sspresent = no;
1050 for ( content = node->content;
1051 !sspresent && content != NULL;
1052 content = content->next )
1054 sspresent = ( nodeIsLINK(content) ||
1055 nodeIsSTYLE(content) ||
1056 nodeIsFONT(content) ||
1057 nodeIsBASEFONT(content) );
1059 for ( av = content->attributes;
1060 !sspresent && av != NULL;
1063 sspresent = ( attrIsSTYLE(av) || attrIsTEXT(av) ||
1064 attrIsVLINK(av) || attrIsALINK(av) ||
1067 if ( !sspresent && attrIsREL(av) )
1069 sspresent = AttrValueIs(av, "stylesheet");
1074 sspresent = CheckMissingStyleSheets( doc, content );
1080 /*******************************************************************
1083 * Checks if the URL is valid and to check if a 'LONGDESC' is needed
1084 * within the FRAME element. If a 'LONGDESC' is needed, the value must
1085 * be valid. The URL must end with the file extension, htm, or html.
1086 * Also, checks to ensure that the 'SRC' and 'TITLE' values are valid.
1087 *******************************************************************/
1089 static void CheckFrame( TidyDocImpl* doc, Node* node )
1094 doc->access.numFrames++;
1096 if (Level1_Enabled( doc ))
1098 /* Checks for attributes within the FRAME element */
1099 for (av = node->attributes; av != NULL; av = av->next)
1101 /* Checks if 'LONGDESC' value is valid only if present */
1102 if ( attrIsLONGDESC(av) )
1104 if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
1106 doc->access.HasCheckedLongDesc++;
1110 /* Checks for valid 'SRC' value within the frame element */
1111 else if ( attrIsSRC(av) )
1113 if ( hasValue(av) && !IsValidSrcExtension(av->value) )
1115 TY_(ReportAccessError)( doc, node, FRAME_SRC_INVALID );
1119 /* Checks for valid 'TITLE' value within frame element */
1120 else if ( attrIsTITLE(av) )
1127 if ( av->value == NULL || TY_(tmbstrlen)(av->value) == 0 )
1130 TY_(ReportAccessError)( doc, node, FRAME_TITLE_INVALID_NULL);
1134 if ( IsWhitespace(av->value) && TY_(tmbstrlen)(av->value) > 0 )
1137 TY_(ReportAccessError)( doc, node, FRAME_TITLE_INVALID_SPACES );
1146 TY_(ReportAccessError)( doc, node, FRAME_MISSING_TITLE);
1149 if ( doc->access.numFrames==3 && doc->access.HasCheckedLongDesc<3 )
1151 doc->access.numFrames = 0;
1152 TY_(ReportAccessWarning)( doc, node, FRAME_MISSING_LONGDESC );
1158 /****************************************************************
1161 * Checks if 'SRC' value is valid. Must end in appropriate
1163 ****************************************************************/
1165 static void CheckIFrame( TidyDocImpl* doc, Node* node )
1167 if (Level1_Enabled( doc ))
1169 /* Checks for valid 'SRC' value within the IFRAME element */
1170 AttVal* av = attrGetSRC( node );
1173 if ( !IsValidSrcExtension(av->value) )
1174 TY_(ReportAccessError)( doc, node, FRAME_SRC_INVALID );
1180 /**********************************************************************
1183 * Checks that the sound file is valid, and to ensure that
1184 * text transcript is present describing the 'HREF' within the
1185 * ANCHOR element. Also checks to see ensure that the 'TARGET' attribute
1186 * (if it exists) is not NULL and does not contain '_new' or '_blank'.
1187 **********************************************************************/
1189 static void CheckAnchorAccess( TidyDocImpl* doc, Node* node )
1192 Bool HasDescription = no;
1193 Bool HasTriggeredLink = no;
1195 /* Checks for attributes within the ANCHOR element */
1196 for ( av = node->attributes; av != NULL; av = av->next )
1198 if (Level1_Enabled( doc ))
1200 /* Must be of valid sound file type */
1201 if ( attrIsHREF(av) )
1206 GetFileExtension (av->value, ext, sizeof(ext) );
1208 /* Checks to see if multimedia is used */
1209 if ( IsValidMediaExtension(av->value) )
1211 TY_(ReportAccessError)( doc, node, MULTIMEDIA_REQUIRES_TEXT );
1215 Checks for validity of sound file, and checks to see if
1216 the file is described within the document, or by a link
1217 that is present which gives the description.
1219 if ( TY_(tmbstrlen)(ext) < 6 && TY_(tmbstrlen)(ext) > 0 )
1221 int errcode = IsSoundFile( av->value );
1224 if (node->next != NULL)
1226 if (node->next->tag == NULL)
1228 ctmbstr word = textFromOneNode( doc, node->next);
1230 /* Must contain at least one letter in the text */
1231 if (IsWhitespace (word) == no)
1233 HasDescription = yes;
1238 /* Must contain text description of sound file */
1239 if ( !HasDescription )
1241 TY_(ReportAccessError)( doc, node, errcode );
1249 if (Level2_Enabled( doc ))
1251 /* Checks 'TARGET' attribute for validity if it exists */
1252 if ( attrIsTARGET(av) )
1254 if (AttrValueIs(av, "_new"))
1256 TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
1258 else if (AttrValueIs(av, "_blank"))
1260 TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
1266 if (Level2_Enabled( doc ))
1268 if ((node->content != NULL)&&
1269 (node->content->tag == NULL))
1271 ctmbstr word = textFromOneNode( doc, node->content);
1273 if ((word != NULL)&&
1274 (IsWhitespace (word) == no))
1276 if (TY_(tmbstrcmp) (word, "more") == 0)
1278 HasTriggeredLink = yes;
1281 if (TY_(tmbstrcmp) (word, "click here") == 0)
1283 TY_(ReportAccessWarning)( doc, node, LINK_TEXT_NOT_MEANINGFUL_CLICK_HERE);
1286 if (HasTriggeredLink == no)
1288 if (TY_(tmbstrlen)(word) < 6)
1290 TY_(ReportAccessWarning)( doc, node, LINK_TEXT_NOT_MEANINGFUL);
1294 if (TY_(tmbstrlen)(word) > 60)
1296 TY_(ReportAccessWarning)( doc, node, LINK_TEXT_TOO_LONG);
1302 if (node->content == NULL)
1304 TY_(ReportAccessWarning)( doc, node, LINK_TEXT_MISSING);
1310 /************************************************************
1313 * Checks attributes within the AREA element to
1314 * determine if the 'ALT' text and 'HREF' values are valid.
1315 * Also checks to see ensure that the 'TARGET' attribute
1316 * (if it exists) is not NULL and does not contain '_new'
1318 ************************************************************/
1320 static void CheckArea( TidyDocImpl* doc, Node* node )
1325 /* Checks all attributes within the AREA element */
1326 for (av = node->attributes; av != NULL; av = av->next)
1328 if (Level1_Enabled( doc ))
1331 Checks for valid ALT attribute.
1332 The length of the alt text must be > 4 characters long
1333 but must be less than 150 characters long.
1336 if ( attrIsALT(av) )
1338 /* The check for validity */
1339 if (av->value != NULL)
1346 if (Level2_Enabled( doc ))
1348 if ( attrIsTARGET(av) )
1350 if (AttrValueIs(av, "_new"))
1352 TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
1354 else if (AttrValueIs(av, "_blank"))
1356 TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
1362 if (Level1_Enabled( doc ))
1364 /* AREA must contain alt text */
1367 TY_(ReportAccessError)( doc, node, AREA_MISSING_ALT);
1373 /***************************************************
1376 * Checks the SCRIPT element to ensure that a
1377 * NOSCRIPT section follows the SCRIPT.
1378 ***************************************************/
1380 static void CheckScriptAcc( TidyDocImpl* doc, Node* node )
1382 if (Level1_Enabled( doc ))
1384 /* NOSCRIPT element must appear immediately following SCRIPT element */
1385 if ( node->next == NULL || !nodeIsNOSCRIPT(node->next) )
1387 TY_(ReportAccessError)( doc, node, SCRIPT_MISSING_NOSCRIPT);
1393 /**********************************************************
1396 * Check to see that each table has a row of headers if
1397 * a column of columns doesn't exist.
1398 **********************************************************/
1400 static void CheckRows( TidyDocImpl* doc, Node* node )
1405 doc->access.CheckedHeaders++;
1407 for (; node != NULL; node = node->next )
1410 if ( nodeIsTH(node->content) )
1412 doc->access.HasTH = yes;
1413 if ( TY_(nodeIsText)(node->content->content) )
1415 ctmbstr word = textFromOneNode( doc, node->content->content);
1416 if ( !IsWhitespace(word) )
1422 if (numTR == numValidTH)
1423 doc->access.HasValidRowHeaders = yes;
1426 numTR > numValidTH &&
1428 doc->access.HasTH == yes )
1429 doc->access.HasInvalidRowHeader = yes;
1433 /**********************************************************
1436 * Check to see that each table has a column of headers if
1437 * a row of columns doesn't exist.
1438 **********************************************************/
1440 static void CheckColumns( TidyDocImpl* doc, Node* node )
1444 Bool isMissingHeader = no;
1446 doc->access.CheckedHeaders++;
1448 /* Table must have row of headers if headers for columns don't exist */
1449 if ( nodeIsTH(node->content) )
1451 doc->access.HasTH = yes;
1453 for ( tnode = node->content; tnode; tnode = tnode->next )
1455 if ( nodeIsTH(tnode) )
1457 if ( TY_(nodeIsText)(tnode->content) )
1459 ctmbstr word = textFromOneNode( doc, tnode->content);
1460 if ( !IsWhitespace(word) )
1466 isMissingHeader = yes;
1471 if ( !isMissingHeader && numTH > 0 )
1472 doc->access.HasValidColumnHeaders = yes;
1474 if ( isMissingHeader && numTH >= 2 )
1475 doc->access.HasInvalidColumnHeader = yes;
1479 /*****************************************************
1482 * Checks to see if the header provided for a table
1483 * requires an abbreviation. (only required if the
1484 * length of the header is greater than 15 characters)
1485 *****************************************************/
1487 static void CheckTH( TidyDocImpl* doc, Node* node )
1490 ctmbstr word = NULL;
1493 if (Level3_Enabled( doc ))
1495 /* Checks TH element for 'ABBR' attribute */
1496 for (av = node->attributes; av != NULL; av = av->next)
1498 if ( attrIsABBR(av) )
1500 /* Value must not be NULL and must be less than 15 characters */
1501 if ((av->value != NULL)&&
1502 (IsWhitespace (av->value) == no))
1507 if ((av->value == NULL)||
1508 (TY_(tmbstrlen)(av->value) == 0))
1511 TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_NULL);
1514 if ((IsWhitespace (av->value) == yes)&&
1515 (TY_(tmbstrlen)(av->value) > 0))
1518 TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_SPACES);
1523 /* If the header is greater than 15 characters, an abbreviation is needed */
1524 word = textFromOneNode( doc, node->content);
1526 if ((word != NULL)&&
1527 (IsWhitespace (word) == no))
1529 /* Must have 'ABBR' attribute if header is > 15 characters */
1530 if ((TY_(tmbstrlen)(word) > 15)&&
1533 TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR);
1540 /*****************************************************************
1543 * Layout tables should make sense when linearized.
1544 * TABLE must contain at least one TH element.
1545 * This technique applies only to tables used for layout purposes,
1546 * not to data tables. Checks for column of multiple headers.
1547 *****************************************************************/
1549 static void CheckMultiHeaders( TidyDocImpl* doc, Node* node )
1554 Bool validColSpanRows = yes;
1555 Bool validColSpanColumns = yes;
1559 if (Level1_Enabled( doc ))
1561 if (node->content != NULL)
1563 TNode = node->content;
1566 Checks for column of multiple headers found
1567 within a data table.
1569 while (TNode != NULL)
1571 if ( nodeIsTR(TNode) )
1573 flag = 0; /* Issue #168 - access test 5-2-1-2 */
1574 if (TNode->content != NULL)
1576 temp = TNode->content;
1578 /* The number of TH elements found within TR element */
1581 while (temp != NULL)
1584 Must contain at least one TH element
1585 within in the TR element
1587 if ( nodeIsTH(temp) )
1590 for (av = temp->attributes; av != NULL; av = av->next)
1592 if ( attrIsCOLSPAN(av)
1593 && (atoi(av->value) > 1) )
1594 validColSpanColumns = no;
1596 if ( attrIsROWSPAN(av)
1597 && (atoi(av->value) > 1) )
1598 validColSpanRows = no;
1610 TNode = TNode->next;
1613 /* Displays HTML 4 Table Algorithm when multiple column of headers used */
1614 if (validColSpanRows == no)
1616 TY_(ReportAccessWarning)( doc, node, DATA_TABLE_REQUIRE_MARKUP_ROW_HEADERS );
1617 TY_(DisplayHTMLTableAlgorithm)( doc );
1620 if (validColSpanColumns == no)
1622 TY_(ReportAccessWarning)( doc, node, DATA_TABLE_REQUIRE_MARKUP_COLUMN_HEADERS );
1623 TY_(DisplayHTMLTableAlgorithm)( doc );
1630 /****************************************************
1633 * Checks the TABLE element to ensure that the
1634 * table is not missing any headers. Must have either
1635 * a row or column of headers.
1636 ****************************************************/
1638 static void CheckTable( TidyDocImpl* doc, Node* node )
1647 Bool HasSummary = no;
1648 Bool HasCaption = no;
1650 if (Level3_Enabled( doc ))
1653 /* Table must have a 'SUMMARY' describing the purpose of the table */
1654 for (av = node->attributes; av != NULL; av = av->next)
1656 if ( attrIsSUMMARY(av) )
1662 if (AttrContains(av, "summary") &&
1663 AttrContains(av, "table"))
1665 TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_PLACEHOLDER );
1669 if ( av->value == NULL || TY_(tmbstrlen)(av->value) == 0 )
1672 TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_NULL );
1674 else if ( IsWhitespace(av->value) && TY_(tmbstrlen)(av->value) > 0 )
1677 TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_SPACES );
1682 /* TABLE must have content. */
1683 if (node->content == NULL)
1685 TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS);
1691 if (Level1_Enabled( doc ))
1693 /* Checks for multiple headers */
1694 CheckMultiHeaders( doc, node );
1697 if (Level2_Enabled( doc ))
1699 /* Table must have a CAPTION describing the purpose of the table */
1700 if ( nodeIsCAPTION(node->content) )
1702 TNode = node->content;
1704 if (TNode->content && TNode->content->tag == NULL)
1706 word = getTextNodeClear( doc, TNode);
1709 if ( !IsWhitespace(word) )
1715 if (HasCaption == no)
1717 TY_(ReportAccessError)( doc, node, TABLE_MISSING_CAPTION);
1722 if (node->content != NULL)
1724 if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
1726 CheckColumns( doc, node->content->next );
1728 else if ( nodeIsTR(node->content) )
1730 CheckColumns( doc, node->content );
1734 if ( ! doc->access.HasValidColumnHeaders )
1736 if (node->content != NULL)
1738 if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
1740 CheckRows( doc, node->content->next);
1742 else if ( nodeIsTR(node->content) )
1744 CheckRows( doc, node->content);
1750 if (Level3_Enabled( doc ))
1752 /* Suppress warning for missing 'SUMMARY for HTML 2.0 and HTML 3.2 */
1753 if (HasSummary == no)
1755 TY_(ReportAccessError)( doc, node, TABLE_MISSING_SUMMARY);
1759 if (Level2_Enabled( doc ))
1761 if (node->content != NULL)
1763 temp = node->content;
1765 while (temp != NULL)
1767 if ( nodeIsTR(temp) )
1777 TY_(ReportAccessWarning)( doc, node, LAYOUT_TABLES_LINEARIZE_PROPERLY);
1781 if ( doc->access.HasTH )
1783 TY_(ReportAccessWarning)( doc, node, LAYOUT_TABLE_INVALID_MARKUP);
1787 if (Level1_Enabled( doc ))
1789 if ( doc->access.CheckedHeaders == 2 )
1791 if ( !doc->access.HasValidRowHeaders &&
1792 !doc->access.HasValidColumnHeaders &&
1793 !doc->access.HasInvalidRowHeader &&
1794 !doc->access.HasInvalidColumnHeader )
1796 TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS);
1799 if ( !doc->access.HasValidRowHeaders &&
1800 doc->access.HasInvalidRowHeader )
1802 TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS_ROW);
1805 if ( !doc->access.HasValidColumnHeaders &&
1806 doc->access.HasInvalidColumnHeader )
1808 TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS_COLUMN);
1815 /***************************************************
1818 * Checks for valid text equivalents for XMP and PRE
1819 * elements for ASCII art. Ensures that there is
1820 * a skip over link to skip multi-lined ASCII art.
1821 ***************************************************/
1823 static void CheckASCII( TidyDocImpl* doc, Node* node )
1828 tmbstr skipOver = NULL;
1830 int HasSkipOverLink = 0;
1834 tmbchar compareLetter;
1835 int matchingCount = 0;
1838 if (Level1_Enabled( doc ) && node->content)
1841 Checks the text within the PRE and XMP tags to see if ascii
1844 for (i = node->content->start + 1; i < node->content->end; i++)
1848 /* Counts the number of lines of text */
1849 if (doc->lexer->lexbuf[i] == '\n')
1854 compareLetter = doc->lexer->lexbuf[i];
1856 /* Counts consecutive character matches */
1857 for (x = i; x < i + 5; x++)
1859 if (doc->lexer->lexbuf[x] == compareLetter)
1870 /* Must have at least 5 consecutive character matches */
1871 if (matchingCount >= 5)
1878 Must have more than 6 lines of text OR 5 or more consecutive
1879 letters that are the same for there to be ascii art
1881 if (newLines >= 6 || matchingCount >= 5)
1886 /* Checks for skip over link if ASCII art is present */
1889 if (node->prev != NULL && node->prev->prev != NULL)
1891 temp1 = node->prev->prev;
1893 /* Checks for 'HREF' attribute */
1894 for (av = temp1->attributes; av != NULL; av = av->next)
1896 if ( attrIsHREF(av) && hasValue(av) )
1898 skipOver = av->value;
1906 if (Level2_Enabled( doc ))
1909 Checks for A element following PRE to ensure proper skipover link
1910 only if there is an A element preceding PRE.
1912 if (HasSkipOverLink == 1)
1914 if ( nodeIsA(node->next) )
1918 /* Checks for 'NAME' attribute */
1919 for (av = temp2->attributes; av != NULL; av = av->next)
1921 if ( attrIsNAME(av) && hasValue(av) )
1924 Value within the 'HREF' attribute must be the same
1925 as the value within the 'NAME' attribute for valid
1928 if ( strstr(skipOver, av->value) != NULL )
1939 TY_(ReportAccessError)( doc, node, ASCII_REQUIRES_DESCRIPTION);
1940 if (Level3_Enabled( doc ) && (HasSkipOverLink < 2))
1941 TY_(ReportAccessError)( doc, node, SKIPOVER_ASCII_ART);
1948 /***********************************************************
1951 * <form> must have valid 'FOR' attribute, and <label> must
1952 * have valid 'ID' attribute for valid form control.
1953 ***********************************************************/
1955 static void CheckFormControls( TidyDocImpl* doc, Node* node )
1957 if ( !doc->access.HasValidFor &&
1958 doc->access.HasValidId )
1960 TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY_FOR);
1963 if ( !doc->access.HasValidId &&
1964 doc->access.HasValidFor )
1966 TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY_ID);
1969 if ( !doc->access.HasValidId &&
1970 !doc->access.HasValidFor )
1972 TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY);
1977 /************************************************************
1980 * Check for valid 'FOR' attribute within the LABEL element
1981 ************************************************************/
1983 static void CheckLabel( TidyDocImpl* doc, Node* node )
1985 if (Level2_Enabled( doc ))
1987 /* Checks for valid 'FOR' attribute */
1988 AttVal* av = attrGetFOR( node );
1990 doc->access.HasValidFor = yes;
1992 if ( ++doc->access.ForID == 2 )
1994 doc->access.ForID = 0;
1995 CheckFormControls( doc, node );
2001 /************************************************************
2004 * Checks for valid 'ID' attribute within the INPUT element.
2005 * Checks to see if there is a LABEL directly before
2006 * or after the INPUT element determined by the 'TYPE'.
2007 * Each INPUT element must have a LABEL describing the form.
2008 ************************************************************/
2010 static void CheckInputLabel( TidyDocImpl* doc, Node* node )
2012 if (Level2_Enabled( doc ))
2016 /* Checks attributes within the INPUT element */
2017 for (av = node->attributes; av != NULL; av = av->next)
2019 /* Must have valid 'ID' value */
2020 if ( attrIsID(av) && hasValue(av) )
2021 doc->access.HasValidId = yes;
2024 if ( ++doc->access.ForID == 2 )
2026 doc->access.ForID = 0;
2027 CheckFormControls( doc, node );
2033 /***************************************************************
2034 * CheckInputAttributes
2036 * INPUT element must have a valid 'ALT' attribute if the
2037 * 'VALUE' attribute is present.
2038 ***************************************************************/
2040 static void CheckInputAttributes( TidyDocImpl* doc, Node* node )
2043 Bool MustHaveAlt = no;
2046 /* Checks attributes within the INPUT element */
2047 for (av = node->attributes; av != NULL; av = av->next)
2049 /* 'VALUE' must be found if the 'TYPE' is 'text' or 'checkbox' */
2050 if ( attrIsTYPE(av) && hasValue(av) )
2052 if (Level1_Enabled( doc ))
2054 if (AttrValueIs(av, "image"))
2062 if ( attrIsALT(av) && hasValue(av) )
2068 if ( MustHaveAlt && !HasAlt )
2070 TY_(ReportAccessError)( doc, node, IMG_BUTTON_MISSING_ALT );
2076 /***************************************************************
2079 * Frameset must have valid NOFRAME section. Must contain some
2080 * text but must not contain information telling user to update
2082 ***************************************************************/
2084 static void CheckFrameSet( TidyDocImpl* doc, Node* node )
2087 Bool HasNoFrames = no;
2089 if (Level1_Enabled( doc ))
2091 if ( doc->badAccess & BA_INVALID_LINK_NOFRAMES )
2093 TY_(ReportAccessError)( doc, node, NOFRAMES_INVALID_LINK);
2094 doc->badAccess &= ~BA_INVALID_LINK_NOFRAMES; /* emit only once */
2096 for ( temp = node->content; temp != NULL ; temp = temp->next )
2098 if ( nodeIsNOFRAMES(temp) )
2102 if ( temp->content && nodeIsP(temp->content->content) )
2104 Node* para = temp->content->content;
2105 if ( TY_(nodeIsText)(para->content) )
2107 ctmbstr word = textFromOneNode( doc, para->content );
2108 if ( word && strstr(word, "browser") != NULL )
2109 TY_(ReportAccessError)( doc, para, NOFRAMES_INVALID_CONTENT );
2112 else if (temp->content == NULL)
2113 TY_(ReportAccessError)( doc, temp, NOFRAMES_INVALID_NO_VALUE);
2114 else if ( temp->content &&
2115 IsWhitespace(textFromOneNode(doc, temp->content)) )
2116 TY_(ReportAccessError)( doc, temp, NOFRAMES_INVALID_NO_VALUE);
2120 if (HasNoFrames == no)
2121 TY_(ReportAccessError)( doc, node, FRAME_MISSING_NOFRAMES);
2126 /***********************************************************
2127 * CheckHeaderNesting
2129 * Checks for heading increases and decreases. Headings must
2130 * not increase by more than one header level, but may
2131 * decrease at from any level to any level. Text within
2132 * headers must not be more than 20 words in length.
2133 ***********************************************************/
2135 static void CheckHeaderNesting( TidyDocImpl* doc, Node* node )
2141 Bool IsValidIncrease = no;
2142 Bool NeedsDescription = no;
2144 if (Level2_Enabled( doc ))
2147 Text within header element cannot contain more than 20 words without
2148 a separate description
2150 if (node->content != NULL && node->content->tag == NULL)
2152 ctmbstr word = textFromOneNode( doc, node->content);
2154 for (i = 0; i < TY_(tmbstrlen)(word); i++)
2164 NeedsDescription = yes;
2168 /* Header following must be same level or same plus 1 for
2169 ** valid heading increase size. E.g. H1 -> H1, H2. H3 -> H3, H4
2171 if ( TY_(nodeIsHeader)(node) )
2173 uint level = TY_(nodeHeaderLevel)( node );
2174 IsValidIncrease = yes;
2176 for ( temp = node->next; temp != NULL; temp = temp->next )
2178 uint nested = TY_(nodeHeaderLevel)( temp );
2179 if ( nested >= level )
2181 IsValidIncrease = ( nested <= level + 1 );
2187 if ( !IsValidIncrease )
2188 TY_(ReportAccessWarning)( doc, node, HEADERS_IMPROPERLY_NESTED );
2190 if ( NeedsDescription )
2191 TY_(ReportAccessWarning)( doc, node, HEADER_USED_FORMAT_TEXT );
2196 /*************************************************************
2197 * CheckParagraphHeader
2199 * Checks to ensure that P elements are not headings. Must be
2200 * greater than 10 words in length, and they must not be in bold,
2201 * or italics, or underlined, etc.
2202 *************************************************************/
2204 static void CheckParagraphHeader( TidyDocImpl* doc, Node* node )
2206 Bool IsNotHeader = no;
2209 if (Level2_Enabled( doc ))
2211 /* Cannot contain text formatting elements */
2212 if (node->content != NULL)
2214 if (node->content->tag != NULL)
2216 temp = node->content;
2218 while (temp != NULL)
2220 if (temp->tag == NULL)
2232 if ( nodeIsSTRONG(node->content) )
2234 TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_BOLD);
2237 if ( nodeIsU(node->content) )
2239 TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_UNDERLINE);
2242 if ( nodeIsEM(node->content) )
2244 TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_ITALICS);
2252 /****************************************************************
2255 * Checks to see if 'SRC' is a multimedia type. Must have
2256 * syncronized captions if used.
2257 ****************************************************************/
2259 static void CheckEmbed( TidyDocImpl* doc, Node* node )
2261 if (Level1_Enabled( doc ))
2263 AttVal* av = attrGetSRC( node );
2264 if ( hasValue(av) && IsValidMediaExtension(av->value) )
2266 TY_(ReportAccessError)( doc, node, MULTIMEDIA_REQUIRES_TEXT );
2272 /*********************************************************************
2275 * Checks HTML element for valid 'LANG' attribute. Must be a valid
2276 * language. ie. 'fr' or 'en'
2277 ********************************************************************/
2279 static void CheckHTMLAccess( TidyDocImpl* doc, Node* node )
2281 Bool ValidLang = no;
2283 if (Level3_Enabled( doc ))
2285 AttVal* av = attrGetLANG( node );
2289 if ( !hasValue(av) )
2290 TY_(ReportAccessError)( doc, node, LANGUAGE_INVALID );
2293 TY_(ReportAccessError)( doc, node, LANGUAGE_NOT_IDENTIFIED );
2298 /********************************************************
2301 * Document must not contain the BLINK element.
2302 * It is invalid HTML/XHTML.
2303 *********************************************************/
2305 static void CheckBlink( TidyDocImpl* doc, Node* node )
2308 if (Level2_Enabled( doc ))
2310 /* Checks to see if text is found within the BLINK element. */
2311 if ( TY_(nodeIsText)(node->content) )
2313 ctmbstr word = textFromOneNode( doc, node->content );
2314 if ( !IsWhitespace(word) )
2316 TY_(ReportAccessError)( doc, node, REMOVE_BLINK_MARQUEE );
2323 /********************************************************
2326 * Document must not contain the MARQUEE element.
2327 * It is invalid HTML/XHTML.
2328 ********************************************************/
2331 static void CheckMarquee( TidyDocImpl* doc, Node* node )
2333 if (Level2_Enabled( doc ))
2335 /* Checks to see if there is text in between the MARQUEE element */
2336 if ( TY_(nodeIsText)(node) )
2338 ctmbstr word = textFromOneNode( doc, node->content);
2339 if ( !IsWhitespace(word) )
2341 TY_(ReportAccessError)( doc, node, REMOVE_BLINK_MARQUEE );
2348 /**********************************************************
2351 * 'REL' attribute within the LINK element must not contain
2352 * 'stylesheet'. HTML/XHTML document is unreadable when
2353 * style sheets are applied. -- CPR huh?
2354 **********************************************************/
2356 static void CheckLink( TidyDocImpl* doc, Node* node )
2361 if (Level1_Enabled( doc ))
2364 /* Check for valid 'REL' and 'TYPE' attribute */
2365 for (av = node->attributes; av != NULL; av = av->next)
2367 if ( attrIsREL(av) && hasValue(av) )
2369 if (AttrContains(av, "stylesheet"))
2373 if ( attrIsTYPE(av) && hasValue(av) )
2379 if (HasRel && HasType)
2380 TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_LINK );
2385 /*******************************************************
2388 * Document must not contain STYLE element. HTML/XHTML
2389 * document is unreadable when style sheets are applied.
2390 *******************************************************/
2392 static void CheckStyle( TidyDocImpl* doc, Node* node )
2394 if (Level1_Enabled( doc ))
2396 TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ELEMENT );
2401 /*************************************************************
2404 * Verify that equivalents of dynamic content are updated and
2405 * available as often as the dynamic content.
2406 *************************************************************/
2409 static void DynamicContent( TidyDocImpl* doc, Node* node )
2411 if (Level1_Enabled( doc ))
2414 if ( nodeIsAPPLET(node) )
2415 msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_APPLET;
2416 else if ( nodeIsSCRIPT(node) )
2417 msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_SCRIPT;
2418 else if ( nodeIsOBJECT(node) )
2419 msgcode = TEXT_EQUIVALENTS_REQUIRE_UPDATING_OBJECT;
2422 TY_(ReportAccessWarning)( doc, node, msgcode );
2427 /*************************************************************
2428 * ProgrammaticObjects
2430 * Verify that the page is usable when programmatic objects
2432 *************************************************************/
2434 static void ProgrammaticObjects( TidyDocImpl* doc, Node* node )
2436 if (Level1_Enabled( doc ))
2439 if ( nodeIsSCRIPT(node) )
2440 msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_SCRIPT;
2441 else if ( nodeIsOBJECT(node) )
2442 msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_OBJECT;
2443 else if ( nodeIsEMBED(node) )
2444 msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_EMBED;
2445 else if ( nodeIsAPPLET(node) )
2446 msgcode = PROGRAMMATIC_OBJECTS_REQUIRE_TESTING_APPLET;
2449 TY_(ReportAccessWarning)( doc, node, msgcode );
2454 /*************************************************************
2455 * AccessibleCompatible
2457 * Verify that programmatic objects are directly accessible.
2458 *************************************************************/
2460 static void AccessibleCompatible( TidyDocImpl* doc, Node* node )
2462 if (Level1_Enabled( doc ))
2465 if ( nodeIsSCRIPT(node) )
2466 msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_SCRIPT;
2467 else if ( nodeIsOBJECT(node) )
2468 msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_OBJECT;
2469 else if ( nodeIsEMBED(node) )
2470 msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_EMBED;
2471 else if ( nodeIsAPPLET(node) )
2472 msgcode = ENSURE_PROGRAMMATIC_OBJECTS_ACCESSIBLE_APPLET;
2475 TY_(ReportAccessWarning)( doc, node, msgcode );
2480 /********************************************************
2483 * Counts the number of words in the document. Must have
2484 * more than 3 words to verify changes in natural
2485 * language of document.
2487 * CPR - Not sure what intent is here, but this
2488 * routine has nothing to do with changes in language.
2489 * It seems like a bad idea to emit this message for
2490 * every document with _more_ than 3 words!
2491 ********************************************************/
2493 static int WordCount( TidyDocImpl* doc, Node* node )
2497 if (Level1_Enabled( doc ))
2499 /* Count the number of words found within a text node */
2500 if ( TY_(nodeIsText)( node ) )
2503 ctmbstr word = textFromOneNode( doc, node );
2504 if ( !IsWhitespace(word) )
2507 while ( (ch = *word++) && wc < 5 )
2515 for ( node = node->content; wc < 5 && node; node = node->next )
2517 wc += WordCount( doc, node );
2524 /**************************************************
2527 * Verify that the page does not cause flicker.
2528 **************************************************/
2530 static void CheckFlicker( TidyDocImpl* doc, Node* node )
2532 if (Level1_Enabled( doc ))
2535 if ( nodeIsSCRIPT(node) )
2536 msgcode = REMOVE_FLICKER_SCRIPT;
2537 else if ( nodeIsOBJECT(node) )
2538 msgcode = REMOVE_FLICKER_OBJECT;
2539 else if ( nodeIsEMBED(node) )
2540 msgcode = REMOVE_FLICKER_EMBED;
2541 else if ( nodeIsAPPLET(node) )
2542 msgcode = REMOVE_FLICKER_APPLET;
2544 /* Checks for animated gif within the <img> tag. */
2545 else if ( nodeIsIMG(node) )
2547 AttVal* av = attrGetSRC( node );
2551 GetFileExtension( av->value, ext, sizeof(ext) );
2552 if ( TY_(tmbstrcasecmp)(ext, ".gif") == 0 )
2553 msgcode = REMOVE_FLICKER_ANIMATED_GIF;
2558 TY_(ReportAccessWarning)( doc, node, msgcode );
2563 /**********************************************************
2566 * APPLET, BASEFONT, CENTER, FONT, ISINDEX,
2567 * S, STRIKE, and U should not be used. Becomes deprecated
2568 * HTML if any of the above are used.
2569 **********************************************************/
2571 static void CheckDeprecated( TidyDocImpl* doc, Node* node )
2573 if (Level2_Enabled( doc ))
2576 if ( nodeIsAPPLET(node) )
2577 msgcode = REPLACE_DEPRECATED_HTML_APPLET;
2578 else if ( nodeIsBASEFONT(node) )
2579 msgcode = REPLACE_DEPRECATED_HTML_BASEFONT;
2580 else if ( nodeIsCENTER(node) )
2581 msgcode = REPLACE_DEPRECATED_HTML_CENTER;
2582 else if ( nodeIsDIR(node) )
2583 msgcode = REPLACE_DEPRECATED_HTML_DIR;
2584 else if ( nodeIsFONT(node) )
2585 msgcode = REPLACE_DEPRECATED_HTML_FONT;
2586 else if ( nodeIsISINDEX(node) )
2587 msgcode = REPLACE_DEPRECATED_HTML_ISINDEX;
2588 else if ( nodeIsMENU(node) )
2589 msgcode = REPLACE_DEPRECATED_HTML_MENU;
2590 else if ( nodeIsS(node) )
2591 msgcode = REPLACE_DEPRECATED_HTML_S;
2592 else if ( nodeIsSTRIKE(node) )
2593 msgcode = REPLACE_DEPRECATED_HTML_STRIKE;
2594 else if ( nodeIsU(node) )
2595 msgcode = REPLACE_DEPRECATED_HTML_U;
2598 TY_(ReportAccessError)( doc, node, msgcode );
2603 /************************************************************
2604 * CheckScriptKeyboardAccessible
2606 * Elements must have a device independent event handler if
2607 * they have any of the following device dependent event
2609 ************************************************************/
2611 static void CheckScriptKeyboardAccessible( TidyDocImpl* doc, Node* node )
2614 int HasOnMouseDown = 0;
2615 int HasOnMouseUp = 0;
2617 int HasOnMouseOut = 0;
2618 int HasOnMouseOver = 0;
2619 int HasOnMouseMove = 0;
2621 if (Level2_Enabled( doc ))
2624 /* Checks all elements for their attributes */
2625 for (av = node->attributes; av != NULL; av = av->next)
2627 /* Must also have 'ONKEYDOWN' attribute with 'ONMOUSEDOWN' */
2628 if ( attrIsOnMOUSEDOWN(av) )
2631 /* Must also have 'ONKEYUP' attribute with 'ONMOUSEUP' */
2632 if ( attrIsOnMOUSEUP(av) )
2635 /* Must also have 'ONKEYPRESS' attribute with 'ONCLICK' */
2636 if ( attrIsOnCLICK(av) )
2639 /* Must also have 'ONBLUR' attribute with 'ONMOUSEOUT' */
2640 if ( attrIsOnMOUSEOUT(av) )
2643 if ( attrIsOnMOUSEOVER(av) )
2646 if ( attrIsOnMOUSEMOVE(av) )
2649 if ( attrIsOnKEYDOWN(av) )
2652 if ( attrIsOnKEYUP(av) )
2655 if ( attrIsOnKEYPRESS(av) )
2658 if ( attrIsOnBLUR(av) )
2662 if ( HasOnMouseDown == 1 )
2663 TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_DOWN);
2665 if ( HasOnMouseUp == 1 )
2666 TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_UP);
2668 if ( HasOnClick == 1 )
2669 TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_CLICK);
2670 if ( HasOnMouseOut == 1 )
2671 TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OUT);
2673 if ( HasOnMouseOver == 1 )
2674 TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OVER);
2676 if ( HasOnMouseMove == 1 )
2677 TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_MOVE);
2679 /* Recursively check all child nodes.
2681 for ( content = node->content; content != NULL; content = content->next )
2682 CheckScriptKeyboardAccessible( doc, content );
2687 /**********************************************************
2690 * Must have at least one of these elements in the document.
2691 * META, LINK, TITLE or ADDRESS. <meta> must contain
2692 * a "content" attribute that doesn't contain a URL, and
2693 * an "http-Equiv" attribute that doesn't contain 'refresh'.
2694 **********************************************************/
2697 static Bool CheckMetaData( TidyDocImpl* doc, Node* node, Bool HasMetaData )
2699 Bool HasHttpEquiv = no;
2700 Bool HasContent = no;
2701 Bool ContainsAttr = no;
2703 if (Level2_Enabled( doc ))
2705 if ( nodeIsMETA(node) )
2708 for (av = node->attributes; av != NULL; av = av->next)
2710 if ( attrIsHTTP_EQUIV(av) && hasValue(av) )
2714 /* Must not have an auto-refresh */
2715 if (AttrValueIs(av, "refresh"))
2718 TY_(ReportAccessError)( doc, node, REMOVE_AUTO_REFRESH );
2722 if ( attrIsCONTENT(av) && hasValue(av) )
2726 /* If the value is not an integer, then it must not be a URL */
2727 if ( TY_(tmbstrncmp)(av->value, "http:", 5) == 0)
2730 TY_(ReportAccessError)( doc, node, REMOVE_AUTO_REDIRECT);
2735 if ( HasContent || HasHttpEquiv )
2738 TY_(ReportAccessError)( doc, node, METADATA_MISSING_REDIRECT_AUTOREFRESH);
2742 if ( ContainsAttr && !HasContent && !HasHttpEquiv )
2747 if ( !HasMetaData &&
2748 nodeIsADDRESS(node) &&
2749 nodeIsA(node->content) )
2754 if ( !HasMetaData &&
2755 !nodeIsTITLE(node) &&
2756 TY_(nodeIsText)(node->content) )
2758 ctmbstr word = textFromOneNode( doc, node->content );
2759 if ( !IsWhitespace(word) )
2763 if( !HasMetaData && nodeIsLINK(node) )
2765 AttVal* av = attrGetREL(node);
2766 if( !AttrContains(av, "stylesheet") )
2770 /* Check for MetaData */
2771 for ( node = node->content; node; node = node->next )
2773 HasMetaData = CheckMetaData( doc, node, HasMetaData );
2780 /*******************************************************
2783 * Determines if MetaData is present in document
2784 *******************************************************/
2786 static void MetaDataPresent( TidyDocImpl* doc, Node* node )
2788 if (Level2_Enabled( doc ))
2790 TY_(ReportAccessError)( doc, node, METADATA_MISSING );
2795 /*****************************************************
2798 * Checks that every HTML/XHTML document contains a
2799 * '!DOCTYPE' before the root node. ie. <HTML>
2800 *****************************************************/
2802 static void CheckDocType( TidyDocImpl* doc )
2804 if (Level2_Enabled( doc ))
2806 Node* DTnode = TY_(FindDocType)(doc);
2808 /* If the doctype has been added by tidy, DTnode->end will be 0. */
2809 if (DTnode && DTnode->end != 0)
2811 ctmbstr word = textFromOneNode( doc, DTnode);
2812 if ((strstr (word, "HTML PUBLIC") == NULL) &&
2813 (strstr (word, "html PUBLIC") == NULL))
2817 TY_(ReportAccessError)( doc, &doc->root, DOCTYPE_MISSING);
2823 /********************************************************
2826 * Checks to see if an HREF for A element matches HREF
2827 * for AREA element. There must be an HREF attribute
2828 * of an A element for every HREF of an AREA element.
2829 ********************************************************/
2831 static Bool urlMatch( ctmbstr url1, ctmbstr url2 )
2833 /* TODO: Make host part case-insensitive and
2834 ** remainder case-sensitive.
2836 return ( TY_(tmbstrcmp)( url1, url2 ) == 0 );
2839 static Bool FindLinkA( TidyDocImpl* doc, Node* node, ctmbstr url )
2842 for ( node = node->content; !found && node; node = node->next )
2844 if ( nodeIsA(node) )
2846 AttVal* href = attrGetHREF( node );
2847 found = ( hasValue(href) && urlMatch(url, href->value) );
2850 found = FindLinkA( doc, node, url );
2855 static void CheckMapLinks( TidyDocImpl* doc, Node* node )
2859 if (!Level3_Enabled( doc ))
2862 /* Stores the 'HREF' link of an AREA element within a MAP element */
2863 for ( child = node->content; child != NULL; child = child->next )
2865 if ( nodeIsAREA(child) )
2867 /* Checks for 'HREF' attribute */
2868 AttVal* href = attrGetHREF( child );
2869 if ( hasValue(href) &&
2870 !FindLinkA( doc, &doc->root, href->value ) )
2872 TY_(ReportAccessError)( doc, node, IMG_MAP_CLIENT_MISSING_TEXT_LINKS );
2879 /****************************************************
2880 * CheckForStyleAttribute
2882 * Checks all elements within the document to check
2883 * for the use of 'STYLE' attribute.
2884 ****************************************************/
2886 static void CheckForStyleAttribute( TidyDocImpl* doc, Node* node )
2889 if (Level1_Enabled( doc ))
2891 /* Must not contain 'STYLE' attribute */
2892 AttVal* style = attrGetSTYLE( node );
2893 if ( hasValue(style) )
2895 TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ATTR );
2899 /* Recursively check all child nodes.
2901 for ( content = node->content; content != NULL; content = content->next )
2902 CheckForStyleAttribute( doc, content );
2906 /*****************************************************
2907 * CheckForListElements
2909 * Checks document for list elements (<ol>, <ul>, <li>)
2910 *****************************************************/
2912 static void CheckForListElements( TidyDocImpl* doc, Node* node )
2914 if ( nodeIsLI(node) )
2916 doc->access.ListElements++;
2918 else if ( nodeIsOL(node) || nodeIsUL(node) )
2920 doc->access.OtherListElements++;
2923 for ( node = node->content; node != NULL; node = node->next )
2925 CheckForListElements( doc, node );
2930 /******************************************************
2933 * Ensures that lists are properly used. <ol> and <ul>
2934 * must contain <li> within itself, and <li> must not be
2936 ******************************************************/
2938 static void CheckListUsage( TidyDocImpl* doc, Node* node )
2942 if (!Level2_Enabled( doc ))
2945 if ( nodeIsOL(node) )
2946 msgcode = LIST_USAGE_INVALID_OL;
2947 else if ( nodeIsUL(node) )
2948 msgcode = LIST_USAGE_INVALID_UL;
2955 ** b) was not added by Tidy parser
2956 ** IFF OL/UL node is implicit
2958 if ( !nodeIsLI(node->content) ) {
2959 TY_(ReportAccessWarning)( doc, node, msgcode );
2960 } else if ( node->implicit ) { /* if a tidy added node */
2961 TY_(ReportAccessWarning)( doc, node, LIST_USAGE_INVALID_LI );
2964 else if ( nodeIsLI(node) )
2966 /* Check that LI parent
2968 ** b) is either OL or UL
2969 ** IFF the LI parent was added by Tidy
2970 ** ie, if it is marked 'implicit', then
2971 ** emit warnings LIST_USAGE_INVALID_UL or
2972 ** warning LIST_USAGE_INVALID_OL tests
2974 if ( node->parent == NULL ||
2975 ( !nodeIsOL(node->parent) && !nodeIsUL(node->parent) ) )
2977 TY_(ReportAccessWarning)( doc, node, LIST_USAGE_INVALID_LI );
2978 } else if ( node->implicit && node->parent &&
2979 ( nodeIsOL(node->parent) || nodeIsUL(node->parent) ) ) {
2980 /* if tidy added LI node, then */
2981 msgcode = nodeIsUL(node->parent) ?
2982 LIST_USAGE_INVALID_UL : LIST_USAGE_INVALID_OL;
2983 TY_(ReportAccessWarning)( doc, node, msgcode );
2988 /************************************************************
2989 * InitAccessibilityChecks
2991 * Initializes the AccessibilityChecks variables as necessary
2992 ************************************************************/
2994 static void InitAccessibilityChecks( TidyDocImpl* doc, int level123 )
2996 TidyClearMemory( &doc->access, sizeof(doc->access) );
2997 doc->access.PRIORITYCHK = level123;
3000 /************************************************************
3001 * CleanupAccessibilityChecks
3003 * Cleans up the AccessibilityChecks variables as necessary
3004 ************************************************************/
3007 static void FreeAccessibilityChecks( TidyDocImpl* ARG_UNUSED(doc) )
3009 /* free any memory allocated for the lists
3011 Linked List of Links not used. Just search document as
3012 AREA tags are encountered. Same algorithm, but no
3013 data structures necessary.
3018 void *templink = (void *)current;
3020 current = current->next;
3021 TidyDocFree(doc, templink);
3027 /************************************************************
3028 * AccessibilityChecks
3030 * Traverses through the individual nodes of the tree
3031 * and checks attributes and elements for accessibility.
3032 * after the tree structure has been formed.
3033 ************************************************************/
3035 static void AccessibilityCheckNode( TidyDocImpl* doc, Node* node )
3039 /* Check BODY for color contrast */
3040 if ( nodeIsBODY(node) )
3042 CheckColorContrast( doc, node );
3045 /* Checks document for MetaData */
3046 else if ( nodeIsHEAD(node) )
3048 if ( !CheckMetaData( doc, node, no ) )
3049 MetaDataPresent( doc, node );
3052 /* Check the ANCHOR tag */
3053 else if ( nodeIsA(node) )
3055 CheckAnchorAccess( doc, node );
3058 /* Check the IMAGE tag */
3059 else if ( nodeIsIMG(node) )
3061 CheckFlicker( doc, node );
3062 CheckColorAvailable( doc, node );
3063 CheckImage( doc, node );
3066 /* Checks MAP for client-side text links */
3067 else if ( nodeIsMAP(node) )
3069 CheckMapLinks( doc, node );
3072 /* Check the AREA tag */
3073 else if ( nodeIsAREA(node) )
3075 CheckArea( doc, node );
3078 /* Check the APPLET tag */
3079 else if ( nodeIsAPPLET(node) )
3081 CheckDeprecated( doc, node );
3082 ProgrammaticObjects( doc, node );
3083 DynamicContent( doc, node );
3084 AccessibleCompatible( doc, node );
3085 CheckFlicker( doc, node );
3086 CheckColorAvailable( doc, node );
3087 CheckApplet(doc, node );
3090 /* Check the OBJECT tag */
3091 else if ( nodeIsOBJECT(node) )
3093 ProgrammaticObjects( doc, node );
3094 DynamicContent( doc, node );
3095 AccessibleCompatible( doc, node );
3096 CheckFlicker( doc, node );
3097 CheckColorAvailable( doc, node );
3098 CheckObject( doc, node );
3101 /* Check the FRAME tag */
3102 else if ( nodeIsFRAME(node) )
3104 CheckFrame( doc, node );
3107 /* Check the IFRAME tag */
3108 else if ( nodeIsIFRAME(node) )
3110 CheckIFrame( doc, node );
3113 /* Check the SCRIPT tag */
3114 else if ( nodeIsSCRIPT(node) )
3116 DynamicContent( doc, node );
3117 ProgrammaticObjects( doc, node );
3118 AccessibleCompatible( doc, node );
3119 CheckFlicker( doc, node );
3120 CheckColorAvailable( doc, node );
3121 CheckScriptAcc( doc, node );
3124 /* Check the TABLE tag */
3125 else if ( nodeIsTABLE(node) )
3127 CheckColorContrast( doc, node );
3128 CheckTable( doc, node );
3131 /* Check the PRE for ASCII art */
3132 else if ( nodeIsPRE(node) || nodeIsXMP(node) )
3134 CheckASCII( doc, node );
3137 /* Check the LABEL tag */
3138 else if ( nodeIsLABEL(node) )
3140 CheckLabel( doc, node );
3143 /* Check INPUT tag for validity */
3144 else if ( nodeIsINPUT(node) )
3146 CheckColorAvailable( doc, node );
3147 CheckInputLabel( doc, node );
3148 CheckInputAttributes( doc, node );
3151 /* Checks FRAMESET element for NOFRAME section */
3152 else if ( nodeIsFRAMESET(node) )
3154 CheckFrameSet( doc, node );
3157 /* Checks for header elements for valid header increase */
3158 else if ( TY_(nodeIsHeader)(node) )
3160 CheckHeaderNesting( doc, node );
3163 /* Checks P element to ensure that it is not a header */
3164 else if ( nodeIsP(node) )
3166 CheckParagraphHeader( doc, node );
3169 /* Checks HTML elemnt for valid 'LANG' */
3170 else if ( nodeIsHTML(node) )
3172 CheckHTMLAccess( doc, node );
3175 /* Checks BLINK for any blinking text */
3176 else if ( nodeIsBLINK(node) )
3178 CheckBlink( doc, node );
3181 /* Checks MARQUEE for any MARQUEE text */
3182 else if ( nodeIsMARQUEE(node) )
3184 CheckMarquee( doc, node );
3187 /* Checks LINK for 'REL' attribute */
3188 else if ( nodeIsLINK(node) )
3190 CheckLink( doc, node );
3193 /* Checks to see if STYLE is used */
3194 else if ( nodeIsSTYLE(node) )
3196 CheckColorContrast( doc, node );
3197 CheckStyle( doc, node );
3200 /* Checks to see if EMBED is used */
3201 else if ( nodeIsEMBED(node) )
3203 CheckEmbed( doc, node );
3204 ProgrammaticObjects( doc, node );
3205 AccessibleCompatible( doc, node );
3206 CheckFlicker( doc, node );
3209 /* Deprecated HTML if the following tags are found in the document */
3210 else if ( nodeIsBASEFONT(node) ||
3211 nodeIsCENTER(node) ||
3212 nodeIsISINDEX(node) ||
3217 nodeIsSTRIKE(node) ||
3220 CheckDeprecated( doc, node );
3223 /* Checks for 'ABBR' attribute if needed */
3224 else if ( nodeIsTH(node) )
3226 CheckTH( doc, node );
3229 /* Ensures that lists are properly used */
3230 else if ( nodeIsLI(node) || nodeIsOL(node) || nodeIsUL(node) )
3232 CheckListUsage( doc, node );
3235 /* Recursively check all child nodes.
3237 for ( content = node->content; content != NULL; content = content->next )
3239 AccessibilityCheckNode( doc, content );
3244 void TY_(AccessibilityChecks)( TidyDocImpl* doc )
3247 InitAccessibilityChecks( doc, cfg(doc, TidyAccessibilityCheckLevel) );
3249 /* Hello there, ladies and gentlemen... */
3250 TY_(AccessibilityHelloMessage)( doc );
3252 /* Checks all elements for script accessibility */
3253 CheckScriptKeyboardAccessible( doc, &doc->root );
3255 /* Checks entire document for the use of 'STYLE' attribute */
3256 CheckForStyleAttribute( doc, &doc->root );
3258 /* Checks for '!DOCTYPE' */
3259 CheckDocType( doc );
3262 /* Checks to see if stylesheets are used to control the layout */
3263 if ( Level2_Enabled( doc )
3264 && ! CheckMissingStyleSheets( doc, &doc->root ) )
3266 TY_(ReportAccessWarning)( doc, &doc->root, STYLE_SHEET_CONTROL_PRESENTATION );
3269 /* Check to see if any list elements are found within the document */
3270 CheckForListElements( doc, &doc->root );
3272 /* Checks for natural language change */
3273 /* Must contain more than 3 words of text in the document
3275 ** CPR - Not sure what intent is here, but this
3276 ** routine has nothing to do with changes in language.
3277 ** It seems like a bad idea to emit this message for
3278 ** every document with _more_ than 3 words!
3280 if ( WordCount(doc, &doc->root) > 3 )
3282 TY_(ReportAccessWarning)( doc, node, INDICATE_CHANGES_IN_LANGUAGE);
3287 /* Recursively apply all remaining checks to
3288 ** each node in document.
3290 AccessibilityCheckNode( doc, &doc->root );
3293 FreeAccessibilityChecks( doc );
3301 * indent-tabs-mode: nil
3303 * eval: (c-set-offset 'substatement-open 0)