OSDN Git Service

Initial commit
[wordring-tm/wordring-tm.git] / third_party / tidy-html5-master / src / access.c
1 /* access.c -- carry out accessibility checks
2
3   Copyright University of Toronto
4   Portions (c) 1998-2009 (W3C) MIT, ERCIM, Keio University
5   See tidy.h for the copyright notice.
6   
7 */
8
9 /*********************************************************************
10 * AccessibilityChecks
11 *
12 * Carries out processes for all accessibility checks.  Traverses
13 * through all the content within the tree and evaluates the tags for
14 * accessibility.
15 *
16 * To perform the following checks, 'AccessibilityChecks' must be
17 * called AFTER the tree structure has been formed.
18 *
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.)
23 *
24 * Copyright University of Toronto
25 * Programmed by: Mike Lam and Chris Ridpath
26 * Modifications by : Terry Teague (TRT)
27 *
28 * Reference document: http://www.w3.org/TR/WAI-WEBCONTENT/
29 *********************************************************************/
30
31
32 #include "tidy-int.h"
33
34 #if SUPPORT_ACCESSIBILITY_CHECKS
35
36 #include "access.h"
37 #include "message.h"
38 #include "tags.h"
39 #include "attrs.h"
40 #include "tmbstr.h"
41
42
43 /* 
44     The accessibility checks to perform depending on user's desire.
45
46     1. priority 1
47     2. priority 1 & 2
48     3. priority 1, 2, & 3
49 */
50
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"};
55
56 #define N_IMAGE_EXTS (sizeof(imageExtensions)/sizeof(ctmbstr))
57
58 /* List of possible sound file types */
59 static const ctmbstr soundExtensions[] =
60 {".wav", ".au", ".aiff", ".snd", ".ra", ".rm"};
61
62 static const int soundExtErrCodes[] = 
63 {
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,
69     AUDIO_MISSING_TEXT_RM
70 };
71
72 #define N_AUDIO_EXTS (sizeof(soundExtensions)/sizeof(ctmbstr))
73
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"};
79
80 #define N_MEDIA_EXTS (sizeof(mediaExtensions)/sizeof(ctmbstr))
81
82 /* List of possible frame sources */
83 static const ctmbstr frameExtensions[] =
84 {".htm", ".html", ".shtm", ".shtml", ".cfm", ".cfml",
85 ".asp", ".cgi", ".pl", ".smil"};
86
87 #define N_FRAME_EXTS (sizeof(frameExtensions)/sizeof(ctmbstr))
88
89 /* List of possible colour values */
90 static const int colorValues[][3] =
91 {
92   {  0,  0,  0},
93   {128,128,128},
94   {192,192,192},
95   {255,255,255},
96   {192,  0,  0},
97   {255,  0,  0},
98   {128,  0,128},
99   {255,  0,255},
100   {  0,128,  0},
101   {  0,255,  0},
102   {128,128,  0},
103   {255,255,  0},  
104   {  0,  0,128},
105   {  0,  0,255},
106   {  0,128,128},
107   {  0,255,255}
108 };
109
110 #define N_COLOR_VALS (sizeof(colorValues)/(sizeof(int[3]))
111
112 /* These arrays are used to convert color names to their RGB values */
113 static const ctmbstr colorNames[] =
114 {
115   "black",
116   "silver",
117   "grey",
118   "white",
119   "maroon",
120   "red",
121   "purple",
122   "fuchsia",
123   "green",
124   "lime",
125   "olive",
126   "yellow", 
127   "navy",
128   "blue",
129   "teal",
130   "aqua"
131 };
132
133 #define N_COLOR_NAMES (sizeof(colorNames)/sizeof(ctmbstr))
134 #define N_COLORS N_COLOR_NAMES
135
136
137 /* function prototypes */
138 static void InitAccessibilityChecks( TidyDocImpl* doc, int level123 );
139 static void FreeAccessibilityChecks( TidyDocImpl* doc );
140
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 );
144
145 /*
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);
150 */
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 );
155
156 /*
157     GetFileExtension takes a path and returns the extension
158     portion of the path (if any).
159 */
160
161 static void GetFileExtension( ctmbstr path, tmbchar *ext, uint maxExt )
162 {
163     int i = TY_(tmbstrlen)(path) - 1;
164     
165     ext[0] = '\0';
166     
167     do {
168         if ( path[i] == '/' || path[i] == '\\' )
169             break;
170         else if ( path[i] == '.' )
171         {
172             TY_(tmbstrncpy)( ext, path+i, maxExt );
173             break;
174         }
175     } while ( --i > 0 );
176 }
177
178 /************************************************************************
179 * IsImage
180 *
181 * Checks if the given filename is an image file.
182 * Returns 'yes' if it is, 'no' if it's not.
183 ************************************************************************/
184
185 static Bool IsImage( ctmbstr iType )
186 {
187     uint i;
188
189     /* Get the file extension */
190     tmbchar ext[20];
191     GetFileExtension( iType, ext, sizeof(ext) );
192
193     /* Compare it to the array of known image file extensions */
194     for (i = 0; i < N_IMAGE_EXTS; i++)
195     {
196         if ( TY_(tmbstrcasecmp)(ext, imageExtensions[i]) == 0 )
197             return yes;
198     }
199     
200     return no;
201 }
202
203
204 /***********************************************************************
205 * IsSoundFile
206 *
207 * Checks if the given filename is a sound file.
208 * Returns 'yes' if it is, 'no' if it's not.
209 ***********************************************************************/
210
211 static int IsSoundFile( ctmbstr sType )
212 {
213     uint i;
214     tmbchar ext[ 20 ];
215     GetFileExtension( sType, ext, sizeof(ext) );
216
217     for (i = 0; i < N_AUDIO_EXTS; i++)
218     {
219         if ( TY_(tmbstrcasecmp)(ext, soundExtensions[i]) == 0 )
220             return soundExtErrCodes[i];
221     }
222     return 0;
223 }
224
225
226 /***********************************************************************
227 * IsValidSrcExtension
228 *
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"
232 *
233 * Returns yes if it is, returns no otherwise.
234 ***********************************************************************/
235
236 static Bool IsValidSrcExtension( ctmbstr sType )
237 {
238     uint i;
239     tmbchar ext[20];
240     GetFileExtension( sType, ext, sizeof(ext) );
241
242     for (i = 0; i < N_FRAME_EXTS; i++)
243     {
244         if ( TY_(tmbstrcasecmp)(ext, frameExtensions[i]) == 0 )
245             return yes;
246     }
247     return no;
248 }
249
250
251 /*********************************************************************
252 * IsValidMediaExtension
253 *
254 * Checks to warn the user that syncronized text equivalents are 
255 * required if multimedia is used.
256 *********************************************************************/
257
258 static Bool IsValidMediaExtension( ctmbstr sType )
259 {
260     uint i;
261     tmbchar ext[20];
262     GetFileExtension( sType, ext, sizeof(ext) );
263
264     for (i = 0; i < N_MEDIA_EXTS; i++)
265     {
266         if ( TY_(tmbstrcasecmp)(ext, mediaExtensions[i]) == 0 )
267             return yes;
268     }
269     return no;
270 }
271
272
273 /************************************************************************
274 * IsWhitespace
275 *
276 * Checks if the given string is all whitespace.
277 * Returns 'yes' if it is, 'no' if it's not.
278 ************************************************************************/
279
280 static Bool IsWhitespace( ctmbstr pString )
281 {
282     Bool isWht = yes;
283     ctmbstr cp;
284
285     for ( cp = pString; isWht && cp && *cp; ++cp )
286     {
287         isWht = TY_(IsWhite)( *cp );
288     }
289     return isWht;
290 }
291
292 static Bool hasValue( AttVal* av )
293 {
294     return ( av && ! IsWhitespace(av->value) );
295 }
296
297 /***********************************************************************
298 * IsPlaceholderAlt
299 *  
300 * Checks to see if there is an image and photo place holder contained
301 * in the ALT text.
302 *
303 * Returns 'yes' if there is, 'no' if not.
304 ***********************************************************************/
305
306 static Bool IsPlaceholderAlt( ctmbstr txt )
307 {
308     return ( strstr(txt, "image") != NULL || 
309              strstr(txt, "photo") != NULL );
310 }
311
312
313 /***********************************************************************
314 * IsPlaceholderTitle
315 *  
316 * Checks to see if there is an TITLE place holder contained
317 * in the 'ALT' text.
318 *
319 * Returns 'yes' if there is, 'no' if not.
320
321 static Bool IsPlaceHolderTitle( ctmbstr txt )
322 {
323     return ( strstr(txt, "title") != NULL );
324 }
325 ***********************************************************************/
326
327
328 /***********************************************************************
329 * IsPlaceHolderObject
330 *  
331 * Checks to see if there is an OBJECT place holder contained
332 * in the 'ALT' text.
333 *
334 * Returns 'yes' if there is, 'no' if not.
335 ***********************************************************************/
336
337 static Bool IsPlaceHolderObject( ctmbstr txt )
338 {
339     return ( strstr(txt, "object") != NULL );
340 }
341
342
343 /**********************************************************
344 * EndsWithBytes
345 *
346 * Checks to see if the ALT text ends with 'bytes'
347 * Returns 'yes', if true, 'no' otherwise.
348 **********************************************************/
349
350 static Bool EndsWithBytes( ctmbstr txt )
351 {
352     uint len = TY_(tmbstrlen)( txt );
353     return ( len >= 5 && TY_(tmbstrcmp)(txt+len-5, "bytes") == 0 );
354 }
355
356
357 /*******************************************************
358 * textFromOneNode
359 *
360 * Returns a list of characters contained within one
361 * text node.
362 *******************************************************/
363
364 static ctmbstr textFromOneNode( TidyDocImpl* doc, Node* node )
365 {
366     uint i;
367     uint x = 0;
368     tmbstr txt = doc->access.text;
369     
370     if ( node )
371     {
372         /* Copy contents of a text node */
373         for (i = node->start; i < node->end; ++i, ++x )
374         {
375             txt[x] = doc->lexer->lexbuf[i];
376
377             /* Check buffer overflow */
378             if ( x >= sizeof(doc->access.text)-1 )
379                 break;
380         }
381     }
382
383     txt[x] = '\0';
384     return txt;
385 }
386
387
388 /*********************************************************
389 * getTextNode
390 *
391 * Locates text nodes within a container element.
392 * Retrieves text that are found contained within 
393 * text nodes, and concatenates the text.
394 *********************************************************/
395     
396 static void getTextNode( TidyDocImpl* doc, Node* node )
397 {
398     tmbstr txtnod = doc->access.textNode;       
399     
400     /* 
401        Continues to traverse through container element until it no
402        longer contains any more contents 
403     */
404
405     /* If the tag of the node is NULL, then grab the text within the node */
406     if ( TY_(nodeIsText)(node) )
407     {
408         uint i;
409
410         /* Retrieves each character found within the text node */
411         for (i = node->start; i < node->end; i++)
412         {
413             /* The text must not exceed buffer */
414             if ( doc->access.counter >= TEXTBUF_SIZE-1 )
415                 return;
416
417             txtnod[ doc->access.counter++ ] = doc->lexer->lexbuf[i];
418         }
419
420         /* Traverses through the contents within a container element */
421         for ( node = node->content; node != NULL; node = node->next )
422             getTextNode( doc, node );
423     }   
424 }
425
426
427 /**********************************************************
428 * getTextNodeClear
429 *
430 * Clears the current 'textNode' and reloads it with new
431 * text.  The textNode must be cleared before use.
432 **********************************************************/
433
434 static tmbstr getTextNodeClear( TidyDocImpl* doc, Node* node )
435 {
436     /* Clears list */
437     TidyClearMemory( doc->access.textNode, TEXTBUF_SIZE );
438     doc->access.counter = 0;
439
440     getTextNode( doc, node->content );
441     return doc->access.textNode;
442 }
443
444 /**********************************************************
445 * LevelX_Enabled
446 *
447 * Tell whether access "X" is enabled.
448 **********************************************************/
449
450 static Bool Level1_Enabled( TidyDocImpl* doc )
451 {
452    return doc->access.PRIORITYCHK == 1 ||
453           doc->access.PRIORITYCHK == 2 ||
454           doc->access.PRIORITYCHK == 3;
455 }
456 static Bool Level2_Enabled( TidyDocImpl* doc )
457 {
458     return doc->access.PRIORITYCHK == 2 ||
459            doc->access.PRIORITYCHK == 3;
460 }
461 static Bool Level3_Enabled( TidyDocImpl* doc )
462 {
463     return doc->access.PRIORITYCHK == 3;
464 }
465
466 /********************************************************
467 * CheckColorAvailable
468 *
469 * Verify that information conveyed with color is 
470 * available without color.
471 ********************************************************/
472
473 static void CheckColorAvailable( TidyDocImpl* doc, Node* node )
474 {
475     if (Level1_Enabled( doc ))
476     {
477         if ( nodeIsIMG(node) )
478             TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_IMAGE );
479
480         else if ( nodeIsAPPLET(node) )
481             TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_APPLET );
482
483         else if ( nodeIsOBJECT(node) )
484             TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_OBJECT );
485
486         else if ( nodeIsSCRIPT(node) )
487             TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_SCRIPT );
488
489         else if ( nodeIsINPUT(node) )
490             TY_(ReportAccessWarning)( doc, node, INFORMATION_NOT_CONVEYED_INPUT );
491     }
492 }
493
494 /*********************************************************************
495 * CheckColorContrast
496 *
497 * Checks elements for color contrast.  Must have valid contrast for
498 * valid visibility.
499 *
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 *********************************************************************/
509
510 static void CheckColorContrast( TidyDocImpl* doc, Node* node )
511 {
512     int rgbBG[3] = {255,255,255};   /* Black text on white BG */
513
514     if (Level3_Enabled( doc ))
515     {
516         Bool gotBG = yes;
517         AttVal* av;
518
519         /* Check for 'BGCOLOR' first to compare with other color attributes */
520         for ( av = node->attributes; av; av = av->next )
521         {            
522             if ( attrIsBGCOLOR(av) )
523             {
524                 if ( hasValue(av) )
525                     gotBG = GetRgb( av->value, rgbBG );
526             }
527         }
528         
529         /* 
530            Search for COLOR attributes to compare with background color
531            Must have valid colour contrast
532         */
533         for ( av = node->attributes; gotBG && av != NULL; av = av->next )
534         {
535             uint errcode = 0;
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;
544
545             if ( errcode && hasValue(av) )
546             {
547                 int rgbFG[3] = {0, 0, 0};  /* Black text */
548
549                 if ( GetRgb(av->value, rgbFG) &&
550                      !CompareColors(rgbBG, rgbFG) )
551                 {
552                     TY_(ReportAccessWarning)( doc, node, errcode );
553                 }
554             }
555         }
556     }
557 }
558
559
560 /**************************************************************
561 * CompareColors
562 *
563 * Compares two RGB colors for good contrast.
564 **************************************************************/
565 static int minmax( int i1, int i2 )
566 {
567    return MAX(i1, i2) - MIN(i1,i2);
568 }
569 static int brightness( const int rgb[3] )
570 {
571    return ((rgb[0]*299) + (rgb[1]*587) + (rgb[2]*114)) / 1000;
572 }
573
574 static Bool CompareColors( const int rgbBG[3], const int rgbFG[3] )
575 {
576     int brightBG = brightness( rgbBG );
577     int brightFG = brightness( rgbFG );
578
579     int diffBright = minmax( brightBG, brightFG );
580
581     int diffColor = minmax( rgbBG[0], rgbFG[0] )
582                   + minmax( rgbBG[1], rgbFG[1] )
583                   + minmax( rgbBG[2], rgbFG[2] );
584
585     return ( diffBright > 180 &&
586              diffColor > 500 );
587 }
588
589
590 /*********************************************************************
591 * GetRgb
592 *
593 * Gets the red, green and blue values for this attribute for the 
594 * background.
595 *
596 * Example: If attribute is BGCOLOR="#121005" then red = 18, green = 16,
597 * blue = 5.
598 *********************************************************************/
599
600 static Bool GetRgb( ctmbstr color, int rgb[] )
601 {
602     uint x;
603
604     /* Check if we have a color name */
605     for (x = 0; x < N_COLORS; x++)
606     {
607         if ( strstr(colorNames[x], color) != NULL )
608         {
609             rgb[0] = colorValues[x][0];
610             rgb[1] = colorValues[x][1];
611             rgb[2] = colorValues[x][2];
612             return yes;
613         }
614     }
615
616     /*
617        No color name so must be hex values 
618        Is this a number in hexadecimal format?
619     */
620     
621     /* Must be 7 characters in the RGB value (including '#') */
622     if ( TY_(tmbstrlen)(color) == 7 && color[0] == '#' )
623     {
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]);
627         return yes;
628     }
629     return no;
630
631
632
633
634 /*******************************************************************
635 * ctox
636 *
637 * Converts a character to a number.
638 * Example: if given character is 'A' then returns 10.
639 *
640 * Returns the number that the character represents. Returns -1 if not a
641 * valid number.
642 *******************************************************************/
643
644 static int ctox( tmbchar ch )
645 {
646     if ( ch >= '0' && ch <= '9' )
647     {
648          return ch - '0';
649     }
650     else if ( ch >= 'a' && ch <= 'f' )
651     {
652         return ch - 'a' + 10;
653     }
654     else if ( ch >= 'A' && ch <= 'F' )
655     {
656         return ch - 'A' + 10;
657     }
658     return -1;
659 }
660
661
662 /***********************************************************
663 * CheckImage
664 *
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 ***********************************************************/
670
671 static void CheckImage( TidyDocImpl* doc, Node* node )
672 {
673     Bool HasAlt = no;
674     Bool HasIsMap = no;
675     Bool HasLongDesc = no;
676     Bool HasDLINK = no;
677     Bool HasValidHeight = no;
678     Bool HasValidWidthBullet = no;
679     Bool HasValidWidthHR = no; 
680     Bool HasTriggeredMissingLongDesc = no;
681
682     AttVal* av;
683                 
684     if (Level1_Enabled( doc ))
685     {
686         /* Checks all image attributes for invalid values within attributes */
687         for (av = node->attributes; av != NULL; av = av->next)
688         {
689             /* 
690                Checks for valid ALT attribute.
691                The length of the alt text must be less than 150 characters 
692                long.
693             */
694             if ( attrIsALT(av) )
695             {
696                 if (av->value != NULL) 
697                 {
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))
703                     {
704                         HasAlt = yes;
705                     }
706
707                     else if (TY_(tmbstrlen)(av->value) > 150)
708                     {
709                         HasAlt = yes;
710                         TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_TOO_LONG );
711                     }
712
713                     else if (IsImage (av->value) == yes)
714                     {
715                         HasAlt = yes;
716                         TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_FILENAME);
717                     }
718             
719                     else if (IsPlaceholderAlt (av->value) == yes)
720                     {
721                         HasAlt = yes;
722                         TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_PLACEHOLDER);
723                     }
724
725                     else if (EndsWithBytes (av->value) == yes)
726                     {
727                         HasAlt = yes;
728                         TY_(ReportAccessWarning)( doc, node, IMG_ALT_SUSPICIOUS_FILE_SIZE);
729                     }
730                 }
731             }
732
733             /* 
734                Checks for width values of 'bullets' and 'horizontal
735                rules' for validity.
736
737                Valid pixel width for 'bullets' must be < 30, and > 150 for
738                horizontal rules.
739             */
740             else if ( attrIsWIDTH(av) )
741             {
742                 /* Longdesc attribute needed if width attribute is not present. */
743                 if ( hasValue(av) )
744                 {
745                     int width = atoi( av->value );
746                     if ( width < 30 )
747                         HasValidWidthBullet = yes;
748
749                     if ( width > 150 )
750                         HasValidWidthHR = yes;
751                 }
752             }
753
754             /* 
755                Checks for height values of 'bullets' and horizontal
756                rules for validity.
757
758                Valid pixel height for 'bullets' and horizontal rules 
759                mustt be < 30.
760             */
761             else if ( attrIsHEIGHT(av) )
762             {
763                 /* Longdesc attribute needed if height attribute not present. */
764                 if ( hasValue(av) && atoi(av->value) < 30 )
765                     HasValidHeight = yes;
766             }
767
768             /* 
769                Checks for longdesc and determines validity.  
770                The length of the 'longdesc' must be > 1
771             */
772             else if ( attrIsLONGDESC(av) )
773             {
774                 if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
775                     HasLongDesc = yes;
776               }
777
778             /* 
779                Checks for 'USEMAP' attribute.  Ensures that
780                text links are provided for client-side image maps
781             */
782             else if ( attrIsUSEMAP(av) )
783             {
784                 if ( hasValue(av) )
785                     doc->access.HasUseMap = yes;
786             }    
787
788             else if ( attrIsISMAP(av) )
789             {
790                 HasIsMap = yes;
791             }
792         }    
793         
794         
795         /* 
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
799             the letter 'd'.
800         */
801         if ( nodeIsA(node->next) )
802         {
803             node = node->next;
804             
805             /* 
806                 Node following the anchor must be a text node
807                 for dLINK to exist 
808             */
809
810             if (node->content != NULL && (node->content)->tag == NULL)
811             {
812                 /* Number of characters found within the text node */
813                 ctmbstr word = textFromOneNode( doc, node->content);
814                     
815                 if ((TY_(tmbstrcmp)(word,"d") == 0)||
816                     (TY_(tmbstrcmp)(word,"D") == 0))
817                 {
818                     HasDLINK = yes;
819                 }
820             }
821         }
822                     
823         /*
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.
827         */
828         
829         if ( node->next && !node->next->tag )
830         {
831             node = node->next;
832
833             if ( nodeIsA(node->next) )
834             {
835                 node = node->next;
836
837                 /* 
838                     Node following the ANCHOR must be a text node
839                     for dLINK to exist 
840                 */
841                 if (node->content != NULL && node->content->tag == NULL)
842                 {
843                     /* Number of characters found within the text node */
844                     ctmbstr word = textFromOneNode( doc, node->content );
845
846                     if ((TY_(tmbstrcmp)(word, "d") == 0)||
847                         (TY_(tmbstrcmp)(word, "D") == 0))
848                     {
849                         HasDLINK = yes;
850                     }
851                 }
852             }
853         }
854
855         if ((HasAlt == no)&&
856             (HasValidWidthBullet == yes)&&
857             (HasValidHeight == yes))
858         {
859         }
860
861         if ((HasAlt == no)&&
862             (HasValidWidthHR == yes)&&
863             (HasValidHeight == yes))
864         {
865         }
866
867         if (HasAlt == no)
868         {
869             TY_(ReportAccessError)( doc, node, IMG_MISSING_ALT);
870         }
871
872         if ((HasLongDesc == no)&&
873             (HasValidHeight ==yes)&&
874             ((HasValidWidthHR == yes)||
875              (HasValidWidthBullet == yes)))
876         {
877             HasTriggeredMissingLongDesc = yes;
878         }
879
880         if (HasTriggeredMissingLongDesc == no)
881         {
882             if ((HasDLINK == yes)&&
883                 (HasLongDesc == no))
884             {
885                 TY_(ReportAccessWarning)( doc, node, IMG_MISSING_LONGDESC);
886             }
887
888             if ((HasLongDesc == yes)&&
889                 (HasDLINK == no))
890             {
891                 TY_(ReportAccessWarning)( doc, node, IMG_MISSING_DLINK);
892             }
893
894             if ((HasLongDesc == no)&&
895                 (HasDLINK == no))
896             {
897                 TY_(ReportAccessWarning)( doc, node, IMG_MISSING_LONGDESC_DLINK);
898             }
899         }
900
901         if (HasIsMap == yes)
902         {
903             TY_(ReportAccessError)( doc, node, IMAGE_MAP_SERVER_SIDE_REQUIRES_CONVERSION);
904
905             TY_(ReportAccessWarning)( doc, node, IMG_MAP_SERVER_REQUIRES_TEXT_LINKS);
906         }
907     }
908 }
909
910
911 /***********************************************************
912 * CheckApplet
913 *
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 ***********************************************************/
921
922 static void CheckApplet( TidyDocImpl* doc, Node* node )
923 {
924     Bool HasAlt = no;
925     Bool HasDescription = no;
926
927     AttVal* av;
928         
929     if (Level1_Enabled( doc ))
930     {
931         /* Checks for attributes within the APPLET element */
932         for (av = node->attributes; av != NULL; av = av->next)
933         {
934             /*
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.
938             */
939
940             if ( attrIsALT(av) )
941             {
942                 if (av->value != NULL)
943                 {
944                     HasAlt = yes;
945                 }
946             }
947         }
948
949         if (HasAlt == no)
950         {
951             /* Must have alternate text representation for that element */
952             if (node->content != NULL) 
953             {
954                 ctmbstr word = NULL;
955
956                 if ( node->content->tag == NULL )
957                     word = textFromOneNode( doc, node->content);
958
959                 if ( node->content->content != NULL &&
960                      node->content->content->tag == NULL )
961                 {
962                     word = textFromOneNode( doc, node->content->content);
963                 }
964                 
965                 if ( word != NULL && !IsWhitespace(word) )
966                     HasDescription = yes;
967             }
968         }
969
970         if ( !HasDescription && !HasAlt )
971         {
972             TY_(ReportAccessError)( doc, node, APPLET_MISSING_ALT );
973         }
974     }
975 }
976
977
978 /*******************************************************************
979 * CheckObject
980 *
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 
984 * representation.
985 *******************************************************************/
986
987 static void CheckObject( TidyDocImpl* doc, Node* node )
988 {
989     Bool HasAlt = no;
990     Bool HasDescription = no;
991
992     if (Level1_Enabled( doc ))
993     {
994         if ( node->content != NULL)
995         {
996             if ( node->content->type != TextNode )
997             {
998                 Node* tnode = node->content;
999                 AttVal* av;
1000
1001                 for ( av=tnode->attributes; av; av = av->next )
1002                 {
1003                     if ( attrIsALT(av) )
1004                     {
1005                         HasAlt = yes;
1006                         break;
1007                     }
1008                 }
1009             }
1010
1011             /* Must have alternate text representation for that element */
1012             if ( !HasAlt )
1013             {
1014                 ctmbstr word = NULL;
1015
1016                 if ( TY_(nodeIsText)(node->content) )
1017                     word = textFromOneNode( doc, node->content );
1018
1019                 if ( word == NULL &&
1020                      TY_(nodeIsText)(node->content->content) )
1021                 {
1022                     word = textFromOneNode( doc, node->content->content );
1023                 }
1024                     
1025                 if ( word != NULL && !IsWhitespace(word) )
1026                     HasDescription = yes;
1027             }
1028         }
1029
1030         if ( !HasAlt && !HasDescription )
1031         {
1032             TY_(ReportAccessError)( doc, node, OBJECT_MISSING_ALT );
1033         }
1034     }
1035 }
1036
1037
1038 /***************************************************************
1039 * CheckMissingStyleSheets
1040 *
1041 * Ensures that stylesheets are used to control the presentation.
1042 ***************************************************************/
1043
1044 static Bool CheckMissingStyleSheets( TidyDocImpl* doc, Node* node )
1045 {
1046     AttVal* av;
1047     Node* content;
1048     Bool sspresent = no;
1049
1050     for ( content = node->content;
1051           !sspresent && content != NULL;
1052           content = content->next )
1053     {
1054         sspresent = ( nodeIsLINK(content)  ||
1055                       nodeIsSTYLE(content) ||
1056                       nodeIsFONT(content)  ||
1057                       nodeIsBASEFONT(content) );
1058
1059         for ( av = content->attributes;
1060               !sspresent && av != NULL;
1061               av = av->next )
1062         {
1063             sspresent = ( attrIsSTYLE(av) || attrIsTEXT(av)  ||
1064                           attrIsVLINK(av) || attrIsALINK(av) ||
1065                           attrIsLINK(av) );
1066
1067             if ( !sspresent && attrIsREL(av) )
1068             {
1069                 sspresent = AttrValueIs(av, "stylesheet");
1070             }
1071         }
1072
1073         if ( ! sspresent )
1074             sspresent = CheckMissingStyleSheets( doc, content );
1075     }
1076     return sspresent;
1077 }
1078
1079
1080 /*******************************************************************
1081 * CheckFrame
1082 *
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 *******************************************************************/
1088
1089 static void CheckFrame( TidyDocImpl* doc, Node* node )
1090 {
1091     Bool HasTitle = no;
1092     AttVal* av;
1093
1094     doc->access.numFrames++;
1095
1096     if (Level1_Enabled( doc ))
1097     {
1098         /* Checks for attributes within the FRAME element */
1099         for (av = node->attributes; av != NULL; av = av->next)
1100         {
1101             /* Checks if 'LONGDESC' value is valid only if present */
1102             if ( attrIsLONGDESC(av) )
1103             {
1104                 if ( hasValue(av) && TY_(tmbstrlen)(av->value) > 1 )
1105                 {
1106                     doc->access.HasCheckedLongDesc++;
1107                 }
1108             }
1109
1110             /* Checks for valid 'SRC' value within the frame element */
1111             else if ( attrIsSRC(av) )
1112             {
1113                 if ( hasValue(av) && !IsValidSrcExtension(av->value) )
1114                 {
1115                     TY_(ReportAccessError)( doc, node, FRAME_SRC_INVALID );
1116                 }
1117             }
1118
1119             /* Checks for valid 'TITLE' value within frame element */
1120             else if ( attrIsTITLE(av) )
1121             {
1122                 if ( hasValue(av) )
1123                     HasTitle = yes;
1124
1125                 if ( !HasTitle )
1126                 {
1127                     if ( av->value == NULL || TY_(tmbstrlen)(av->value) == 0 )
1128                     {
1129                         HasTitle = yes;
1130                         TY_(ReportAccessError)( doc, node, FRAME_TITLE_INVALID_NULL);
1131                     }
1132                     else
1133                     {
1134                         if ( IsWhitespace(av->value) && TY_(tmbstrlen)(av->value) > 0 )
1135                         {
1136                             HasTitle = yes;
1137                             TY_(ReportAccessError)( doc, node, FRAME_TITLE_INVALID_SPACES );
1138                         }
1139                     }
1140                 }
1141             }
1142         }
1143
1144         if ( !HasTitle )
1145         {
1146             TY_(ReportAccessError)( doc, node, FRAME_MISSING_TITLE);
1147         }
1148
1149         if ( doc->access.numFrames==3 && doc->access.HasCheckedLongDesc<3 )
1150         {
1151             doc->access.numFrames = 0;
1152             TY_(ReportAccessWarning)( doc, node, FRAME_MISSING_LONGDESC );
1153         }
1154     }
1155 }
1156
1157
1158 /****************************************************************
1159 * CheckIFrame
1160 *
1161 * Checks if 'SRC' value is valid.  Must end in appropriate
1162 * file extension.
1163 ****************************************************************/
1164
1165 static void CheckIFrame( TidyDocImpl* doc, Node* node )
1166 {
1167     if (Level1_Enabled( doc ))
1168     {
1169         /* Checks for valid 'SRC' value within the IFRAME element */
1170         AttVal* av = attrGetSRC( node );
1171         if ( hasValue(av) )
1172         {
1173             if ( !IsValidSrcExtension(av->value) )
1174                 TY_(ReportAccessError)( doc, node, FRAME_SRC_INVALID );
1175         }
1176     }
1177 }
1178
1179
1180 /**********************************************************************
1181 * CheckAnchorAccess
1182 *
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 **********************************************************************/
1188
1189 static void CheckAnchorAccess( TidyDocImpl* doc, Node* node )
1190 {
1191     AttVal* av;
1192     Bool HasDescription = no;
1193     Bool HasTriggeredLink = no;
1194
1195     /* Checks for attributes within the ANCHOR element */
1196     for ( av = node->attributes; av != NULL; av = av->next )
1197     {
1198         if (Level1_Enabled( doc ))
1199         {
1200             /* Must be of valid sound file type */
1201             if ( attrIsHREF(av) )
1202             {
1203                 if ( hasValue(av) )
1204                 {
1205                     tmbchar ext[ 20 ];
1206                     GetFileExtension (av->value, ext, sizeof(ext) );
1207
1208                     /* Checks to see if multimedia is used */
1209                     if ( IsValidMediaExtension(av->value) )
1210                     {
1211                         TY_(ReportAccessError)( doc, node, MULTIMEDIA_REQUIRES_TEXT );
1212                     }
1213             
1214                     /* 
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.
1218                     */
1219                     if ( TY_(tmbstrlen)(ext) < 6 && TY_(tmbstrlen)(ext) > 0 )
1220                     {
1221                         int errcode = IsSoundFile( av->value );
1222                         if ( errcode )
1223                         {
1224                             if (node->next != NULL)
1225                             {
1226                                 if (node->next->tag == NULL)
1227                                 {
1228                                     ctmbstr word = textFromOneNode( doc, node->next);
1229                                 
1230                                     /* Must contain at least one letter in the text */
1231                                     if (IsWhitespace (word) == no)
1232                                     {
1233                                         HasDescription = yes;
1234                                     }
1235                                 }
1236                             }
1237
1238                             /* Must contain text description of sound file */
1239                             if ( !HasDescription )
1240                             {
1241                                 TY_(ReportAccessError)( doc, node, errcode );
1242                             }
1243                         }
1244                     }
1245                 }
1246             }
1247         }
1248
1249         if (Level2_Enabled( doc ))
1250         {
1251             /* Checks 'TARGET' attribute for validity if it exists */
1252             if ( attrIsTARGET(av) )
1253             {
1254                 if (AttrValueIs(av, "_new"))
1255                 {
1256                     TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
1257                 }
1258                 else if (AttrValueIs(av, "_blank"))
1259                 {
1260                     TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
1261                 }
1262             }
1263         }
1264     }
1265     
1266     if (Level2_Enabled( doc ))
1267     {
1268         if ((node->content != NULL)&&
1269             (node->content->tag == NULL))
1270         {
1271             ctmbstr word = textFromOneNode( doc, node->content);
1272
1273             if ((word != NULL)&&
1274                 (IsWhitespace (word) == no))
1275             {
1276                 if (TY_(tmbstrcmp) (word, "more") == 0)
1277                 {
1278                     HasTriggeredLink = yes;
1279                 }
1280
1281                 if (TY_(tmbstrcmp) (word, "click here") == 0)
1282                 {
1283                     TY_(ReportAccessWarning)( doc, node, LINK_TEXT_NOT_MEANINGFUL_CLICK_HERE);
1284                 }
1285
1286                 if (HasTriggeredLink == no)
1287                 {
1288                     if (TY_(tmbstrlen)(word) < 6)
1289                     {
1290                         TY_(ReportAccessWarning)( doc, node, LINK_TEXT_NOT_MEANINGFUL);
1291                     }
1292                 }
1293
1294                 if (TY_(tmbstrlen)(word) > 60)
1295                 {
1296                     TY_(ReportAccessWarning)( doc, node, LINK_TEXT_TOO_LONG);
1297                 }
1298
1299             }
1300         }
1301         
1302         if (node->content == NULL)
1303         {
1304             TY_(ReportAccessWarning)( doc, node, LINK_TEXT_MISSING);
1305         }
1306     }
1307 }
1308
1309
1310 /************************************************************
1311 * CheckArea
1312 *
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' 
1317 * or '_blank'.
1318 ************************************************************/
1319
1320 static void CheckArea( TidyDocImpl* doc, Node* node )
1321 {
1322     Bool HasAlt = no;
1323     AttVal* av;
1324
1325     /* Checks all attributes within the AREA element */
1326     for (av = node->attributes; av != NULL; av = av->next)
1327     {
1328         if (Level1_Enabled( doc ))
1329         {
1330             /*
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.
1334             */
1335                 
1336             if ( attrIsALT(av) )
1337             {
1338                 /* The check for validity */
1339                 if (av->value != NULL) 
1340                 {
1341                     HasAlt = yes;
1342                 }
1343             }
1344         }
1345
1346         if (Level2_Enabled( doc ))
1347         {
1348             if ( attrIsTARGET(av) )
1349             {
1350                 if (AttrValueIs(av, "_new"))
1351                 {
1352                     TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_NEW);
1353                 }
1354                 else if (AttrValueIs(av, "_blank"))
1355                 {
1356                     TY_(ReportAccessWarning)( doc, node, NEW_WINDOWS_REQUIRE_WARNING_BLANK);
1357                 }
1358             }
1359         }
1360     }
1361
1362     if (Level1_Enabled( doc ))
1363     {
1364         /* AREA must contain alt text */
1365         if (HasAlt == no)
1366         {
1367             TY_(ReportAccessError)( doc, node, AREA_MISSING_ALT);
1368         }    
1369     }
1370 }
1371
1372
1373 /***************************************************
1374 * CheckScript
1375 *
1376 * Checks the SCRIPT element to ensure that a
1377 * NOSCRIPT section follows the SCRIPT.  
1378 ***************************************************/
1379
1380 static void CheckScriptAcc( TidyDocImpl* doc, Node* node )
1381 {
1382     if (Level1_Enabled( doc ))
1383     {
1384         /* NOSCRIPT element must appear immediately following SCRIPT element */
1385         if ( node->next == NULL || !nodeIsNOSCRIPT(node->next) )
1386         {
1387             TY_(ReportAccessError)( doc, node, SCRIPT_MISSING_NOSCRIPT);
1388         }
1389     }
1390 }
1391
1392
1393 /**********************************************************
1394 * CheckRows
1395 *
1396 * Check to see that each table has a row of headers if
1397 * a column of columns doesn't exist. 
1398 **********************************************************/
1399
1400 static void CheckRows( TidyDocImpl* doc, Node* node )
1401 {
1402     int numTR = 0;
1403     int numValidTH = 0;
1404     
1405     doc->access.CheckedHeaders++;
1406
1407     for (; node != NULL; node = node->next )
1408     {
1409         numTR++;
1410         if ( nodeIsTH(node->content) )
1411         {
1412             doc->access.HasTH = yes;            
1413             if ( TY_(nodeIsText)(node->content->content) )
1414             {
1415                 ctmbstr word = textFromOneNode( doc, node->content->content);
1416                 if ( !IsWhitespace(word) )
1417                     numValidTH++;
1418             }
1419         }
1420     }
1421
1422     if (numTR == numValidTH)
1423         doc->access.HasValidRowHeaders = yes;
1424
1425     if ( numTR >= 2 &&
1426          numTR > numValidTH &&
1427          numValidTH >= 2 &&
1428          doc->access.HasTH == yes )
1429         doc->access.HasInvalidRowHeader = yes;
1430 }
1431
1432
1433 /**********************************************************
1434 * CheckColumns
1435 *
1436 * Check to see that each table has a column of headers if
1437 * a row of columns doesn't exist.  
1438 **********************************************************/
1439
1440 static void CheckColumns( TidyDocImpl* doc, Node* node )
1441 {
1442     Node* tnode;
1443     int numTH = 0;
1444     Bool isMissingHeader = no;
1445
1446     doc->access.CheckedHeaders++;
1447
1448     /* Table must have row of headers if headers for columns don't exist */
1449     if ( nodeIsTH(node->content) )
1450     {
1451         doc->access.HasTH = yes;
1452
1453         for ( tnode = node->content; tnode; tnode = tnode->next )
1454         {
1455             if ( nodeIsTH(tnode) )
1456             {
1457                 if ( TY_(nodeIsText)(tnode->content) )
1458                 {
1459                     ctmbstr word = textFromOneNode( doc, tnode->content);
1460                     if ( !IsWhitespace(word) )
1461                         numTH++;
1462                 }
1463             }
1464             else
1465             {
1466                 isMissingHeader = yes;
1467             }
1468         }
1469     }
1470
1471     if ( !isMissingHeader && numTH > 0 )
1472         doc->access.HasValidColumnHeaders = yes;
1473
1474     if ( isMissingHeader && numTH >= 2 )
1475         doc->access.HasInvalidColumnHeader = yes;
1476 }
1477
1478
1479 /*****************************************************
1480 * CheckTH
1481 *
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 *****************************************************/
1486
1487 static void CheckTH( TidyDocImpl* doc, Node* node )
1488 {
1489     Bool HasAbbr = no;
1490     ctmbstr word = NULL;
1491     AttVal* av;
1492
1493     if (Level3_Enabled( doc ))
1494     {
1495         /* Checks TH element for 'ABBR' attribute */
1496         for (av = node->attributes; av != NULL; av = av->next)
1497         {
1498             if ( attrIsABBR(av) )
1499             {
1500                 /* Value must not be NULL and must be less than 15 characters */
1501                 if ((av->value != NULL)&&
1502                     (IsWhitespace (av->value) == no))
1503                 {
1504                     HasAbbr = yes;
1505                 }
1506
1507                 if ((av->value == NULL)||
1508                     (TY_(tmbstrlen)(av->value) == 0))
1509                 {
1510                     HasAbbr = yes;
1511                     TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_NULL);
1512                 }
1513                 
1514                 if ((IsWhitespace (av->value) == yes)&&
1515                     (TY_(tmbstrlen)(av->value) > 0))
1516                 {
1517                     HasAbbr = yes;
1518                     TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR_SPACES);
1519                 }
1520             }
1521         }
1522
1523         /* If the header is greater than 15 characters, an abbreviation is needed */
1524         word = textFromOneNode( doc, node->content);
1525
1526         if ((word != NULL)&&
1527             (IsWhitespace (word) == no))
1528         {
1529             /* Must have 'ABBR' attribute if header is > 15 characters */
1530             if ((TY_(tmbstrlen)(word) > 15)&&
1531                 (HasAbbr == no))
1532             {
1533                 TY_(ReportAccessWarning)( doc, node, TABLE_MAY_REQUIRE_HEADER_ABBR);
1534             }
1535         }
1536     }
1537 }
1538
1539
1540 /*****************************************************************
1541 * CheckMultiHeaders
1542 *
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 *****************************************************************/
1548
1549 static void CheckMultiHeaders( TidyDocImpl* doc, Node* node )
1550 {
1551     Node* TNode;
1552     Node* temp;
1553     
1554     Bool validColSpanRows = yes;
1555     Bool validColSpanColumns = yes;
1556
1557     int flag = 0;
1558
1559     if (Level1_Enabled( doc ))
1560     {
1561         if (node->content != NULL)
1562         {
1563             TNode = node->content;
1564
1565             /* 
1566                Checks for column of multiple headers found 
1567                within a data table. 
1568             */
1569             while (TNode != NULL)
1570             {
1571                 if ( nodeIsTR(TNode) )
1572                 {
1573                     flag = 0; /* Issue #168 - access test 5-2-1-2 */
1574                     if (TNode->content != NULL)
1575                     {
1576                         temp = TNode->content;
1577
1578                         /* The number of TH elements found within TR element */
1579                         if (flag == 0)
1580                         {
1581                             while (temp != NULL)
1582                             {
1583                                 /* 
1584                                    Must contain at least one TH element 
1585                                    within in the TR element 
1586                                 */
1587                                 if ( nodeIsTH(temp) )
1588                                 {
1589                                     AttVal* av;
1590                                     for (av = temp->attributes; av != NULL; av = av->next)
1591                                     {
1592                                         if ( attrIsCOLSPAN(av)
1593                                              && (atoi(av->value) > 1) )
1594                                             validColSpanColumns = no;
1595
1596                                         if ( attrIsROWSPAN(av)
1597                                              && (atoi(av->value) > 1) )
1598                                             validColSpanRows = no;
1599                                     }
1600                                 }
1601
1602                                 temp = temp->next;
1603                             }    
1604
1605                             flag = 1;
1606                         }
1607                     }
1608                 }
1609             
1610                 TNode = TNode->next;
1611             }
1612
1613             /* Displays HTML 4 Table Algorithm when multiple column of headers used */
1614             if (validColSpanRows == no)
1615             {
1616                 TY_(ReportAccessWarning)( doc, node, DATA_TABLE_REQUIRE_MARKUP_ROW_HEADERS );
1617                 TY_(DisplayHTMLTableAlgorithm)( doc );
1618             }
1619
1620             if (validColSpanColumns == no)
1621             {
1622                 TY_(ReportAccessWarning)( doc, node, DATA_TABLE_REQUIRE_MARKUP_COLUMN_HEADERS );
1623                 TY_(DisplayHTMLTableAlgorithm)( doc );
1624             }
1625         }
1626     }
1627 }
1628
1629
1630 /****************************************************
1631 * CheckTable
1632 *
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 ****************************************************/
1637
1638 static void CheckTable( TidyDocImpl* doc, Node* node )
1639 {
1640     Node* TNode;
1641     Node* temp;
1642
1643     tmbstr word = NULL;
1644
1645     int numTR = 0;
1646
1647     Bool HasSummary = no;
1648     Bool HasCaption = no;
1649
1650     if (Level3_Enabled( doc ))
1651     {
1652         AttVal* av;
1653         /* Table must have a 'SUMMARY' describing the purpose of the table */
1654         for (av = node->attributes; av != NULL; av = av->next)
1655         {
1656             if ( attrIsSUMMARY(av) )
1657             {
1658                 if ( hasValue(av) )
1659                 {
1660                     HasSummary = yes;
1661
1662                     if (AttrContains(av, "summary") && 
1663                         AttrContains(av, "table"))
1664                     {
1665                         TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_PLACEHOLDER );
1666                     }
1667                 }
1668
1669                 if ( av->value == NULL || TY_(tmbstrlen)(av->value) == 0 )
1670                 {
1671                     HasSummary = yes;
1672                     TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_NULL );
1673                 }
1674                 else if ( IsWhitespace(av->value) && TY_(tmbstrlen)(av->value) > 0 )
1675                 {
1676                     HasSummary = yes;
1677                     TY_(ReportAccessError)( doc, node, TABLE_SUMMARY_INVALID_SPACES );
1678                 }
1679             }
1680         }
1681
1682         /* TABLE must have content. */
1683         if (node->content == NULL)
1684         {
1685             TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS);
1686         
1687             return;
1688         }
1689     }
1690
1691     if (Level1_Enabled( doc ))
1692     {
1693         /* Checks for multiple headers */
1694         CheckMultiHeaders( doc, node );
1695     }
1696     
1697     if (Level2_Enabled( doc ))
1698     {
1699         /* Table must have a CAPTION describing the purpose of the table */
1700         if ( nodeIsCAPTION(node->content) )
1701         {
1702             TNode = node->content;
1703
1704             if (TNode->content && TNode->content->tag == NULL)
1705             {
1706                 word = getTextNodeClear( doc, TNode);
1707             }
1708
1709             if ( !IsWhitespace(word) )
1710             {
1711                 HasCaption = yes;
1712             }
1713         }
1714
1715         if (HasCaption == no)
1716         {
1717             TY_(ReportAccessError)( doc, node, TABLE_MISSING_CAPTION);
1718         }
1719     }
1720
1721     
1722     if (node->content != NULL)
1723     {
1724         if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
1725         {
1726             CheckColumns( doc, node->content->next );
1727         }
1728         else if ( nodeIsTR(node->content) )
1729         {
1730             CheckColumns( doc, node->content );
1731         }
1732     }
1733     
1734     if ( ! doc->access.HasValidColumnHeaders )
1735     {
1736         if (node->content != NULL)
1737         {
1738             if ( nodeIsCAPTION(node->content) && nodeIsTR(node->content->next) )
1739             {
1740                 CheckRows( doc, node->content->next);
1741             }
1742             else if ( nodeIsTR(node->content) )
1743             {
1744                 CheckRows( doc, node->content);
1745             }
1746         }
1747     }
1748     
1749     
1750     if (Level3_Enabled( doc ))
1751     {
1752         /* Suppress warning for missing 'SUMMARY for HTML 2.0 and HTML 3.2 */
1753         if (HasSummary == no)
1754         {
1755             TY_(ReportAccessError)( doc, node, TABLE_MISSING_SUMMARY);
1756         }
1757     }
1758
1759     if (Level2_Enabled( doc ))
1760     {
1761         if (node->content != NULL)
1762         {
1763             temp = node->content;
1764
1765             while (temp != NULL)
1766             {
1767                 if ( nodeIsTR(temp) )
1768                 {
1769                     numTR++;
1770                 }
1771
1772                 temp = temp->next;
1773             }
1774
1775             if (numTR == 1)
1776             {
1777                 TY_(ReportAccessWarning)( doc, node, LAYOUT_TABLES_LINEARIZE_PROPERLY);
1778             }
1779         }
1780     
1781         if ( doc->access.HasTH )
1782         {
1783             TY_(ReportAccessWarning)( doc, node, LAYOUT_TABLE_INVALID_MARKUP);
1784         }
1785     }
1786
1787     if (Level1_Enabled( doc ))
1788     {
1789         if ( doc->access.CheckedHeaders == 2 )
1790         {
1791             if ( !doc->access.HasValidRowHeaders &&
1792                  !doc->access.HasValidColumnHeaders &&
1793                  !doc->access.HasInvalidRowHeader &&
1794                  !doc->access.HasInvalidColumnHeader  )
1795             {
1796                 TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS);
1797             }
1798
1799             if ( !doc->access.HasValidRowHeaders && 
1800                  doc->access.HasInvalidRowHeader )
1801             {
1802                 TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS_ROW);
1803             }
1804
1805             if ( !doc->access.HasValidColumnHeaders &&
1806                  doc->access.HasInvalidColumnHeader )
1807             {
1808                 TY_(ReportAccessError)( doc, node, DATA_TABLE_MISSING_HEADERS_COLUMN);
1809             }
1810         }
1811     }
1812 }
1813
1814
1815 /***************************************************
1816 * CheckASCII
1817
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 ***************************************************/
1822
1823 static void CheckASCII( TidyDocImpl* doc, Node* node )
1824 {
1825     Node* temp1;
1826     Node* temp2;
1827
1828     tmbstr skipOver = NULL;
1829     Bool IsAscii = no;
1830     int HasSkipOverLink = 0;
1831         
1832     uint i, x;
1833     int newLines = -1;
1834     tmbchar compareLetter;
1835     int matchingCount = 0;
1836     AttVal* av;
1837     
1838     if (Level1_Enabled( doc ) && node->content)
1839     {
1840         /* 
1841            Checks the text within the PRE and XMP tags to see if ascii 
1842            art is present 
1843         */
1844         for (i = node->content->start + 1; i < node->content->end; i++)
1845         {
1846             matchingCount = 0;
1847
1848             /* Counts the number of lines of text */
1849             if (doc->lexer->lexbuf[i] == '\n')
1850             {
1851                 newLines++;
1852             }
1853             
1854             compareLetter = doc->lexer->lexbuf[i];
1855
1856             /* Counts consecutive character matches */
1857             for (x = i; x < i + 5; x++)
1858             {
1859                 if (doc->lexer->lexbuf[x] == compareLetter)
1860                 {
1861                     matchingCount++;
1862                 }
1863
1864                 else
1865                 {
1866                     break;
1867                 }
1868             }
1869
1870             /* Must have at least 5 consecutive character matches */
1871             if (matchingCount >= 5)
1872             {
1873                 break;
1874             }
1875         }
1876
1877         /* 
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 
1880         */
1881         if (newLines >= 6 || matchingCount >= 5)
1882         {
1883             IsAscii = yes;
1884         }
1885
1886         /* Checks for skip over link if ASCII art is present */
1887         if (IsAscii == yes)
1888         {
1889             if (node->prev != NULL && node->prev->prev != NULL)
1890             {
1891                 temp1 = node->prev->prev;
1892
1893                 /* Checks for 'HREF' attribute */
1894                 for (av = temp1->attributes; av != NULL; av = av->next)
1895                 {
1896                     if ( attrIsHREF(av) && hasValue(av) )
1897                     {
1898                         skipOver = av->value;
1899                         HasSkipOverLink++;
1900                     }
1901                 }
1902             }
1903         }
1904     }
1905
1906     if (Level2_Enabled( doc ))
1907     {
1908         /* 
1909            Checks for A element following PRE to ensure proper skipover link
1910            only if there is an A element preceding PRE.
1911         */
1912         if (HasSkipOverLink == 1)
1913         {
1914             if ( nodeIsA(node->next) )
1915             {
1916                 temp2 = node->next;
1917                 
1918                 /* Checks for 'NAME' attribute */
1919                 for (av = temp2->attributes; av != NULL; av = av->next)
1920                 {
1921                     if ( attrIsNAME(av) && hasValue(av) )
1922                     {
1923                         /* 
1924                            Value within the 'HREF' attribute must be the same
1925                            as the value within the 'NAME' attribute for valid
1926                            skipover.
1927                         */
1928                         if ( strstr(skipOver, av->value) != NULL )
1929                         {
1930                             HasSkipOverLink++;
1931                         }
1932                     }
1933                 }
1934             }
1935         }
1936
1937         if (IsAscii == yes)
1938         {
1939             TY_(ReportAccessError)( doc, node, ASCII_REQUIRES_DESCRIPTION);
1940             if (Level3_Enabled( doc ) && (HasSkipOverLink < 2))
1941                 TY_(ReportAccessError)( doc, node, SKIPOVER_ASCII_ART);
1942         }
1943
1944     }
1945 }
1946
1947
1948 /***********************************************************
1949 * CheckFormControls
1950 *
1951 * <form> must have valid 'FOR' attribute, and <label> must
1952 * have valid 'ID' attribute for valid form control.
1953 ***********************************************************/
1954
1955 static void CheckFormControls( TidyDocImpl* doc, Node* node )
1956 {
1957     if ( !doc->access.HasValidFor &&
1958          doc->access.HasValidId )
1959     {
1960         TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY_FOR);
1961     }    
1962
1963     if ( !doc->access.HasValidId &&
1964          doc->access.HasValidFor )
1965     {
1966         TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY_ID);
1967     }
1968
1969     if ( !doc->access.HasValidId &&
1970          !doc->access.HasValidFor )
1971     {
1972         TY_(ReportAccessError)( doc, node, ASSOCIATE_LABELS_EXPLICITLY);
1973     }
1974 }
1975
1976
1977 /************************************************************
1978 * CheckLabel
1979 *
1980 * Check for valid 'FOR' attribute within the LABEL element
1981 ************************************************************/
1982
1983 static void CheckLabel( TidyDocImpl* doc, Node* node )
1984 {
1985     if (Level2_Enabled( doc ))
1986     {    
1987         /* Checks for valid 'FOR' attribute */
1988         AttVal* av = attrGetFOR( node );
1989         if ( hasValue(av) )
1990             doc->access.HasValidFor = yes;
1991
1992         if ( ++doc->access.ForID == 2 )
1993         {
1994             doc->access.ForID = 0;
1995             CheckFormControls( doc, node );
1996         }
1997     }
1998 }
1999
2000
2001 /************************************************************
2002 * CheckInputLabel
2003
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 ************************************************************/
2009
2010 static void CheckInputLabel( TidyDocImpl* doc, Node* node )
2011 {
2012     if (Level2_Enabled( doc ))
2013     {
2014         AttVal* av;
2015
2016         /* Checks attributes within the INPUT element */
2017         for (av = node->attributes; av != NULL; av = av->next)
2018         {
2019             /* Must have valid 'ID' value */
2020             if ( attrIsID(av) && hasValue(av) )
2021                 doc->access.HasValidId = yes;
2022         }
2023
2024         if ( ++doc->access.ForID == 2 )
2025         {
2026             doc->access.ForID = 0;
2027             CheckFormControls( doc, node );
2028         }
2029     }
2030 }
2031
2032
2033 /***************************************************************
2034 * CheckInputAttributes 
2035 *
2036 * INPUT element must have a valid 'ALT' attribute if the
2037 * 'VALUE' attribute is present.
2038 ***************************************************************/
2039
2040 static void CheckInputAttributes( TidyDocImpl* doc, Node* node )
2041 {
2042     Bool HasAlt = no;
2043     Bool MustHaveAlt = no;
2044     AttVal* av;
2045
2046     /* Checks attributes within the INPUT element */
2047     for (av = node->attributes; av != NULL; av = av->next)
2048     {
2049         /* 'VALUE' must be found if the 'TYPE' is 'text' or 'checkbox' */
2050         if ( attrIsTYPE(av) && hasValue(av) )
2051         {
2052             if (Level1_Enabled( doc ))
2053             {
2054                 if (AttrValueIs(av, "image"))
2055                 {
2056                     MustHaveAlt = yes;
2057                 }
2058             }
2059
2060         }
2061
2062         if ( attrIsALT(av) && hasValue(av) )
2063         {
2064             HasAlt = yes;
2065         }
2066     }
2067
2068     if ( MustHaveAlt && !HasAlt )
2069     {
2070         TY_(ReportAccessError)( doc, node, IMG_BUTTON_MISSING_ALT );
2071     }
2072
2073 }
2074
2075
2076 /***************************************************************
2077 * CheckFrameSet
2078 *
2079 * Frameset must have valid NOFRAME section.  Must contain some 
2080 * text but must not contain information telling user to update 
2081 * browsers, 
2082 ***************************************************************/
2083
2084 static void CheckFrameSet( TidyDocImpl* doc, Node* node )
2085 {
2086     Node* temp;
2087     Bool HasNoFrames = no;
2088
2089     if (Level1_Enabled( doc ))
2090     {
2091         if ( doc->badAccess & BA_INVALID_LINK_NOFRAMES )
2092         {
2093            TY_(ReportAccessError)( doc, node, NOFRAMES_INVALID_LINK);
2094            doc->badAccess &= ~BA_INVALID_LINK_NOFRAMES; /* emit only once */
2095         }
2096         for ( temp = node->content; temp != NULL ; temp = temp->next )
2097         {
2098             if ( nodeIsNOFRAMES(temp) )
2099             {
2100                 HasNoFrames = yes;
2101
2102                 if ( temp->content && nodeIsP(temp->content->content) )
2103                 {
2104                     Node* para = temp->content->content;
2105                     if ( TY_(nodeIsText)(para->content) )
2106                     {
2107                         ctmbstr word = textFromOneNode( doc, para->content );
2108                         if ( word && strstr(word, "browser") != NULL )
2109                             TY_(ReportAccessError)( doc, para, NOFRAMES_INVALID_CONTENT );
2110                     }
2111                 }
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);
2117             }
2118         }
2119
2120         if (HasNoFrames == no)
2121             TY_(ReportAccessError)( doc, node, FRAME_MISSING_NOFRAMES);
2122     }
2123 }
2124
2125
2126 /***********************************************************
2127 * CheckHeaderNesting
2128 *
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 ***********************************************************/
2134
2135 static void CheckHeaderNesting( TidyDocImpl* doc, Node* node )
2136 {
2137     Node* temp;
2138     uint i;
2139     int numWords = 1;
2140
2141     Bool IsValidIncrease = no;
2142     Bool NeedsDescription = no;
2143
2144     if (Level2_Enabled( doc ))
2145     {
2146         /* 
2147            Text within header element cannot contain more than 20 words without
2148            a separate description
2149         */
2150         if (node->content != NULL && node->content->tag == NULL)
2151         {
2152             ctmbstr word = textFromOneNode( doc, node->content);
2153
2154             for (i = 0; i < TY_(tmbstrlen)(word); i++)
2155             {
2156                 if (word[i] == ' ')
2157                 {
2158                     numWords++;
2159                 }
2160             }
2161
2162             if (numWords > 20)
2163             {
2164                 NeedsDescription = yes;
2165             }
2166         }
2167
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
2170         */
2171         if ( TY_(nodeIsHeader)(node) )
2172         {
2173             uint level = TY_(nodeHeaderLevel)( node );
2174             IsValidIncrease = yes;
2175
2176             for ( temp = node->next; temp != NULL; temp = temp->next )
2177             {
2178                 uint nested = TY_(nodeHeaderLevel)( temp );
2179                 if ( nested >= level )
2180                 {
2181                     IsValidIncrease = ( nested <= level + 1 );
2182                     break;
2183                 }
2184             }
2185         }
2186
2187         if ( !IsValidIncrease )
2188             TY_(ReportAccessWarning)( doc, node, HEADERS_IMPROPERLY_NESTED );
2189     
2190         if ( NeedsDescription )
2191             TY_(ReportAccessWarning)( doc, node, HEADER_USED_FORMAT_TEXT );    
2192     }
2193 }
2194
2195
2196 /*************************************************************
2197 * CheckParagraphHeader
2198 *
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 *************************************************************/
2203
2204 static void CheckParagraphHeader( TidyDocImpl* doc, Node* node )
2205 {
2206     Bool IsNotHeader = no;
2207     Node* temp;
2208
2209     if (Level2_Enabled( doc ))
2210     {
2211         /* Cannot contain text formatting elements */
2212         if (node->content != NULL)   
2213         {                     
2214             if (node->content->tag != NULL)
2215             {
2216                 temp = node->content;
2217
2218                 while (temp != NULL)
2219                 {
2220                     if (temp->tag == NULL)
2221                     {
2222                         IsNotHeader = yes;
2223                         break;
2224                     }
2225                         
2226                     temp = temp->next;
2227                 }
2228             }
2229
2230             if ( !IsNotHeader )
2231             {
2232                 if ( nodeIsSTRONG(node->content) )
2233                 {
2234                     TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_BOLD);
2235                 }
2236
2237                 if ( nodeIsU(node->content) )
2238                 {
2239                     TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_UNDERLINE);
2240                 }
2241
2242                 if ( nodeIsEM(node->content) )
2243                 {
2244                     TY_(ReportAccessWarning)( doc, node, POTENTIAL_HEADER_ITALICS);
2245                 }
2246             }
2247         }
2248     }
2249 }
2250
2251
2252 /****************************************************************
2253 * CheckEmbed
2254 *
2255 * Checks to see if 'SRC' is a multimedia type.  Must have 
2256 * syncronized captions if used.
2257 ****************************************************************/
2258
2259 static void CheckEmbed( TidyDocImpl* doc, Node* node )
2260 {
2261     if (Level1_Enabled( doc ))
2262     {
2263         AttVal* av = attrGetSRC( node );
2264         if ( hasValue(av) && IsValidMediaExtension(av->value) )
2265         {
2266              TY_(ReportAccessError)( doc, node, MULTIMEDIA_REQUIRES_TEXT );
2267         }
2268     }
2269 }
2270
2271
2272 /*********************************************************************
2273 * CheckHTMLAccess
2274 *
2275 * Checks HTML element for valid 'LANG' attribute.  Must be a valid
2276 * language.  ie. 'fr' or 'en'
2277 ********************************************************************/
2278
2279 static void CheckHTMLAccess( TidyDocImpl* doc, Node* node )
2280 {
2281     Bool ValidLang = no;
2282
2283     if (Level3_Enabled( doc ))
2284     {
2285         AttVal* av = attrGetLANG( node );
2286         if ( av )
2287         {
2288             ValidLang = yes;
2289             if ( !hasValue(av) )
2290                 TY_(ReportAccessError)( doc, node, LANGUAGE_INVALID );
2291         }
2292         if ( !ValidLang )
2293             TY_(ReportAccessError)( doc, node, LANGUAGE_NOT_IDENTIFIED );
2294     }
2295 }
2296
2297
2298 /********************************************************
2299 * CheckBlink
2300 *
2301 * Document must not contain the BLINK element.  
2302 * It is invalid HTML/XHTML.
2303 *********************************************************/
2304
2305 static void CheckBlink( TidyDocImpl* doc, Node* node )
2306 {
2307     
2308     if (Level2_Enabled( doc ))
2309     {
2310         /* Checks to see if text is found within the BLINK element. */
2311         if ( TY_(nodeIsText)(node->content) )
2312         {
2313             ctmbstr word = textFromOneNode( doc, node->content );
2314             if ( !IsWhitespace(word) )
2315             {
2316                 TY_(ReportAccessError)( doc, node, REMOVE_BLINK_MARQUEE );
2317             }
2318         }
2319     }
2320 }
2321
2322
2323 /********************************************************
2324 * CheckMarquee
2325 *
2326 * Document must not contain the MARQUEE element.
2327 * It is invalid HTML/XHTML.
2328 ********************************************************/
2329
2330
2331 static void CheckMarquee( TidyDocImpl* doc, Node* node )
2332 {
2333     if (Level2_Enabled( doc ))
2334     {
2335         /* Checks to see if there is text in between the MARQUEE element */
2336         if ( TY_(nodeIsText)(node) )
2337         {
2338             ctmbstr word = textFromOneNode( doc, node->content);
2339             if ( !IsWhitespace(word) )
2340             {
2341                 TY_(ReportAccessError)( doc, node, REMOVE_BLINK_MARQUEE );
2342             }
2343         }
2344     }
2345 }
2346
2347
2348 /**********************************************************
2349 * CheckLink
2350 *
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 **********************************************************/
2355
2356 static void CheckLink( TidyDocImpl* doc, Node* node )
2357 {
2358     Bool HasRel = no;
2359     Bool HasType = no;
2360
2361     if (Level1_Enabled( doc ))
2362     {
2363         AttVal* av;
2364         /* Check for valid 'REL' and 'TYPE' attribute */
2365         for (av = node->attributes; av != NULL; av = av->next)
2366         {
2367             if ( attrIsREL(av) && hasValue(av) )
2368             {
2369                 if (AttrContains(av, "stylesheet"))
2370                     HasRel = yes;
2371             }
2372
2373             if ( attrIsTYPE(av) && hasValue(av) )
2374             {
2375                 HasType = yes;
2376             }
2377         }
2378
2379         if (HasRel && HasType)
2380             TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_LINK );
2381     }
2382 }
2383
2384
2385 /*******************************************************
2386 * CheckStyle
2387 *
2388 * Document must not contain STYLE element.  HTML/XHTML 
2389 * document is unreadable when style sheets are applied.
2390 *******************************************************/
2391
2392 static void CheckStyle( TidyDocImpl* doc, Node* node )
2393 {
2394     if (Level1_Enabled( doc ))
2395     {
2396         TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ELEMENT );
2397     }
2398 }
2399
2400
2401 /*************************************************************
2402 * DynamicContent
2403 *
2404 * Verify that equivalents of dynamic content are updated and 
2405 * available as often as the dynamic content.
2406 *************************************************************/
2407
2408
2409 static void DynamicContent( TidyDocImpl* doc, Node* node )
2410 {
2411     if (Level1_Enabled( doc ))
2412     {
2413         uint msgcode = 0;
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;
2420
2421         if ( msgcode )
2422             TY_(ReportAccessWarning)( doc, node, msgcode );
2423     }
2424 }
2425
2426
2427 /*************************************************************
2428 * ProgrammaticObjects
2429 *
2430 * Verify that the page is usable when programmatic objects 
2431 * are disabled.
2432 *************************************************************/
2433
2434 static void ProgrammaticObjects( TidyDocImpl* doc, Node* node )
2435 {
2436     if (Level1_Enabled( doc ))
2437     {
2438         int msgcode = 0;
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;
2447
2448         if ( msgcode )
2449             TY_(ReportAccessWarning)( doc, node, msgcode );
2450     }
2451 }
2452
2453
2454 /*************************************************************
2455 * AccessibleCompatible
2456 *
2457 * Verify that programmatic objects are directly accessible.
2458 *************************************************************/
2459
2460 static void AccessibleCompatible( TidyDocImpl* doc, Node* node )
2461 {
2462     if (Level1_Enabled( doc ))
2463     {
2464         int msgcode = 0;
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;
2473
2474         if ( msgcode )
2475             TY_(ReportAccessWarning)( doc, node, msgcode );
2476     }
2477 }
2478
2479
2480 /********************************************************
2481 * WordCount
2482 *
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.
2486 *
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 ********************************************************/
2492 #if 0
2493 static int WordCount( TidyDocImpl* doc, Node* node )
2494 {
2495     int wc = 0;
2496
2497     if (Level1_Enabled( doc ))
2498     {
2499         /* Count the number of words found within a text node */
2500         if ( TY_(nodeIsText)( node ) )
2501         {
2502             tmbchar ch;
2503             ctmbstr word = textFromOneNode( doc, node );
2504             if ( !IsWhitespace(word) )
2505             {
2506                 ++wc;
2507                 while ( (ch = *word++) && wc < 5 )
2508                 {
2509                     if ( ch == ' ')
2510                         ++wc;
2511                 }
2512             }
2513         }
2514
2515         for ( node = node->content; wc < 5 && node; node = node->next )
2516         {
2517             wc += WordCount( doc, node );
2518         }
2519     }
2520     return wc;
2521 }
2522 #endif
2523
2524 /**************************************************
2525 * CheckFlicker
2526 *
2527 * Verify that the page does not cause flicker.
2528 **************************************************/
2529
2530 static void CheckFlicker( TidyDocImpl* doc, Node* node )
2531 {
2532     if (Level1_Enabled( doc ))
2533     {
2534         int msgcode = 0;
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;
2543
2544         /* Checks for animated gif within the <img> tag. */
2545         else if ( nodeIsIMG(node) )
2546         {
2547             AttVal* av = attrGetSRC( node );
2548             if ( hasValue(av) )
2549             {
2550                 tmbchar ext[20];
2551                 GetFileExtension( av->value, ext, sizeof(ext) );
2552                 if ( TY_(tmbstrcasecmp)(ext, ".gif") == 0 )
2553                     msgcode = REMOVE_FLICKER_ANIMATED_GIF;
2554             }
2555         }            
2556
2557         if ( msgcode )
2558             TY_(ReportAccessWarning)( doc, node, msgcode );
2559     }
2560 }
2561
2562
2563 /**********************************************************
2564 * CheckDeprecated
2565 *
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 **********************************************************/
2570
2571 static void CheckDeprecated( TidyDocImpl* doc, Node* node )
2572 {
2573     if (Level2_Enabled( doc ))
2574     {
2575         int msgcode = 0;
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;
2596
2597         if ( msgcode )
2598             TY_(ReportAccessError)( doc, node, msgcode );
2599     }
2600 }
2601
2602
2603 /************************************************************
2604 * CheckScriptKeyboardAccessible
2605 *
2606 * Elements must have a device independent event handler if 
2607 * they have any of the following device dependent event 
2608 * handlers. 
2609 ************************************************************/
2610
2611 static void CheckScriptKeyboardAccessible( TidyDocImpl* doc, Node* node )
2612 {
2613     Node* content;
2614     int HasOnMouseDown = 0;
2615     int HasOnMouseUp = 0;
2616     int HasOnClick = 0;
2617     int HasOnMouseOut = 0;
2618     int HasOnMouseOver = 0;
2619     int HasOnMouseMove = 0;
2620
2621     if (Level2_Enabled( doc ))
2622     {
2623         AttVal* av;
2624         /* Checks all elements for their attributes */
2625         for (av = node->attributes; av != NULL; av = av->next)
2626         {
2627             /* Must also have 'ONKEYDOWN' attribute with 'ONMOUSEDOWN' */
2628             if ( attrIsOnMOUSEDOWN(av) )
2629                 HasOnMouseDown++;
2630
2631             /* Must also have 'ONKEYUP' attribute with 'ONMOUSEUP' */
2632             if ( attrIsOnMOUSEUP(av) )
2633                 HasOnMouseUp++;
2634
2635             /* Must also have 'ONKEYPRESS' attribute with 'ONCLICK' */
2636             if ( attrIsOnCLICK(av) )
2637                 HasOnClick++;
2638
2639             /* Must also have 'ONBLUR' attribute with 'ONMOUSEOUT' */
2640             if ( attrIsOnMOUSEOUT(av) )
2641                 HasOnMouseOut++;
2642
2643             if ( attrIsOnMOUSEOVER(av) )
2644                 HasOnMouseOver++;
2645
2646             if ( attrIsOnMOUSEMOVE(av) )
2647                 HasOnMouseMove++;
2648
2649             if ( attrIsOnKEYDOWN(av) )
2650                 HasOnMouseDown++;
2651
2652             if ( attrIsOnKEYUP(av) )
2653                 HasOnMouseUp++;
2654
2655             if ( attrIsOnKEYPRESS(av) )
2656                 HasOnClick++;
2657
2658             if ( attrIsOnBLUR(av) )
2659                 HasOnMouseOut++;
2660         }
2661
2662         if ( HasOnMouseDown == 1 )
2663             TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_DOWN);
2664
2665         if ( HasOnMouseUp == 1 )
2666             TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_UP);
2667
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);
2672
2673         if ( HasOnMouseOver == 1 )
2674             TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_OVER);
2675
2676         if ( HasOnMouseMove == 1 )
2677             TY_(ReportAccessError)( doc, node, SCRIPT_NOT_KEYBOARD_ACCESSIBLE_ON_MOUSE_MOVE);
2678
2679         /* Recursively check all child nodes.
2680          */
2681         for ( content = node->content; content != NULL; content = content->next )
2682             CheckScriptKeyboardAccessible( doc, content );
2683     }
2684 }
2685
2686
2687 /**********************************************************
2688 * CheckMetaData
2689 *
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 **********************************************************/
2695
2696
2697 static Bool CheckMetaData( TidyDocImpl* doc, Node* node, Bool HasMetaData )
2698 {
2699     Bool HasHttpEquiv = no;
2700     Bool HasContent = no;
2701     Bool ContainsAttr = no;
2702
2703     if (Level2_Enabled( doc ))
2704     {
2705         if ( nodeIsMETA(node) )
2706         {
2707             AttVal* av;
2708             for (av = node->attributes; av != NULL; av = av->next)
2709             {
2710                 if ( attrIsHTTP_EQUIV(av) && hasValue(av) )
2711                 {
2712                     ContainsAttr = yes;
2713
2714                     /* Must not have an auto-refresh */
2715                     if (AttrValueIs(av, "refresh"))
2716                     {
2717                         HasHttpEquiv = yes;
2718                         TY_(ReportAccessError)( doc, node, REMOVE_AUTO_REFRESH );
2719                     }
2720                 }
2721
2722                 if ( attrIsCONTENT(av) && hasValue(av) )
2723                 {
2724                     ContainsAttr = yes;
2725
2726                     /* If the value is not an integer, then it must not be a URL */
2727                     if ( TY_(tmbstrncmp)(av->value, "http:", 5) == 0)
2728                     {
2729                         HasContent = yes;
2730                         TY_(ReportAccessError)( doc, node, REMOVE_AUTO_REDIRECT);
2731                     }
2732                 }
2733             }
2734         
2735             if ( HasContent || HasHttpEquiv )
2736             {
2737                 HasMetaData = yes;
2738                 TY_(ReportAccessError)( doc, node, METADATA_MISSING_REDIRECT_AUTOREFRESH);
2739             }
2740             else
2741             {
2742                 if ( ContainsAttr && !HasContent && !HasHttpEquiv )
2743                     HasMetaData = yes;                    
2744             }
2745         }
2746
2747         if ( !HasMetaData && 
2748              nodeIsADDRESS(node) &&
2749              nodeIsA(node->content) )
2750         {
2751             HasMetaData = yes;
2752         }
2753             
2754         if ( !HasMetaData &&
2755              !nodeIsTITLE(node) &&
2756              TY_(nodeIsText)(node->content) )
2757         {
2758             ctmbstr word = textFromOneNode( doc, node->content );
2759             if ( !IsWhitespace(word) )
2760                 HasMetaData = yes;
2761         }
2762
2763         if( !HasMetaData && nodeIsLINK(node) )
2764         {
2765             AttVal* av = attrGetREL(node);
2766             if( !AttrContains(av, "stylesheet") )
2767                 HasMetaData = yes;
2768         }
2769             
2770         /* Check for MetaData */
2771         for ( node = node->content; node; node = node->next )
2772         {
2773             HasMetaData = CheckMetaData( doc, node, HasMetaData );
2774         }
2775     }
2776     return HasMetaData;
2777 }
2778
2779
2780 /*******************************************************
2781 * MetaDataPresent
2782 *
2783 * Determines if MetaData is present in document
2784 *******************************************************/
2785
2786 static void MetaDataPresent( TidyDocImpl* doc, Node* node )
2787 {
2788     if (Level2_Enabled( doc ))
2789     {
2790         TY_(ReportAccessError)( doc, node, METADATA_MISSING );
2791     }
2792 }
2793
2794
2795 /*****************************************************
2796 * CheckDocType
2797 *
2798 * Checks that every HTML/XHTML document contains a 
2799 * '!DOCTYPE' before the root node. ie.  <HTML>
2800 *****************************************************/
2801
2802 static void CheckDocType( TidyDocImpl* doc )
2803 {
2804     if (Level2_Enabled( doc ))
2805     {
2806         Node* DTnode = TY_(FindDocType)(doc);
2807
2808         /* If the doctype has been added by tidy, DTnode->end will be 0. */
2809         if (DTnode && DTnode->end != 0)
2810         {
2811             ctmbstr word = textFromOneNode( doc, DTnode);
2812             if ((strstr (word, "HTML PUBLIC") == NULL) &&
2813                 (strstr (word, "html PUBLIC") == NULL))
2814                 DTnode = NULL;
2815         }
2816         if (!DTnode)
2817            TY_(ReportAccessError)( doc, &doc->root, DOCTYPE_MISSING);
2818     }
2819 }
2820
2821
2822
2823 /********************************************************
2824 * CheckMapLinks
2825 *
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 ********************************************************/
2830
2831 static Bool urlMatch( ctmbstr url1, ctmbstr url2 )
2832 {
2833   /* TODO: Make host part case-insensitive and
2834   ** remainder case-sensitive.
2835   */
2836   return ( TY_(tmbstrcmp)( url1, url2 ) == 0 );
2837 }
2838
2839 static Bool FindLinkA( TidyDocImpl* doc, Node* node, ctmbstr url )
2840 {
2841   Bool found = no;
2842   for ( node = node->content; !found && node; node = node->next )
2843   {
2844     if ( nodeIsA(node) )
2845     {
2846       AttVal* href = attrGetHREF( node );
2847       found = ( hasValue(href) && urlMatch(url, href->value) );
2848     }
2849     else
2850         found = FindLinkA( doc, node, url );
2851   }
2852   return found;
2853 }
2854
2855 static void CheckMapLinks( TidyDocImpl* doc, Node* node )
2856 {
2857     Node* child;
2858
2859     if (!Level3_Enabled( doc ))
2860         return;
2861
2862     /* Stores the 'HREF' link of an AREA element within a MAP element */
2863     for ( child = node->content; child != NULL; child = child->next )
2864     {
2865         if ( nodeIsAREA(child) )
2866         {
2867             /* Checks for 'HREF' attribute */                
2868             AttVal* href = attrGetHREF( child );
2869             if ( hasValue(href) &&
2870                  !FindLinkA( doc, &doc->root, href->value ) )
2871             {
2872                 TY_(ReportAccessError)( doc, node, IMG_MAP_CLIENT_MISSING_TEXT_LINKS );
2873             }
2874         }
2875     }
2876 }
2877
2878
2879 /****************************************************
2880 * CheckForStyleAttribute
2881 *
2882 * Checks all elements within the document to check 
2883 * for the use of 'STYLE' attribute.
2884 ****************************************************/
2885
2886 static void CheckForStyleAttribute( TidyDocImpl* doc, Node* node )
2887 {
2888     Node* content;
2889     if (Level1_Enabled( doc ))
2890     {
2891         /* Must not contain 'STYLE' attribute */
2892         AttVal* style = attrGetSTYLE( node );
2893         if ( hasValue(style) )
2894         {
2895             TY_(ReportAccessWarning)( doc, node, STYLESHEETS_REQUIRE_TESTING_STYLE_ATTR );
2896         }
2897     }
2898
2899     /* Recursively check all child nodes.
2900     */
2901     for ( content = node->content; content != NULL; content = content->next )
2902         CheckForStyleAttribute( doc, content );
2903 }
2904
2905
2906 /*****************************************************
2907 * CheckForListElements
2908 *
2909 * Checks document for list elements (<ol>, <ul>, <li>)
2910 *****************************************************/
2911
2912 static void CheckForListElements( TidyDocImpl* doc, Node* node )
2913 {
2914     if ( nodeIsLI(node) )
2915     {
2916         doc->access.ListElements++;
2917     }
2918     else if ( nodeIsOL(node) || nodeIsUL(node) )
2919     {
2920         doc->access.OtherListElements++;
2921     }
2922
2923     for ( node = node->content; node != NULL; node = node->next )
2924     {
2925         CheckForListElements( doc, node );
2926     }
2927 }
2928
2929
2930 /******************************************************
2931 * CheckListUsage
2932 *
2933 * Ensures that lists are properly used.  <ol> and <ul>
2934 * must contain <li> within itself, and <li> must not be
2935 * by itself.
2936 ******************************************************/
2937
2938 static void CheckListUsage( TidyDocImpl* doc, Node* node )
2939 {
2940     int msgcode = 0;
2941
2942     if (!Level2_Enabled( doc ))
2943         return;
2944
2945     if ( nodeIsOL(node) )
2946         msgcode = LIST_USAGE_INVALID_OL;
2947     else if ( nodeIsUL(node) )
2948         msgcode = LIST_USAGE_INVALID_UL;
2949
2950     if ( msgcode )
2951     {
2952        /*
2953        ** Check that OL/UL
2954        ** a) has LI child,
2955        ** b) was not added by Tidy parser
2956        ** IFF OL/UL node is implicit
2957        */
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 );
2962        }
2963     }
2964     else if ( nodeIsLI(node) )
2965     {
2966         /* Check that LI parent 
2967         ** a) exists,
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 
2973         */
2974         if ( node->parent == NULL ||
2975              ( !nodeIsOL(node->parent) && !nodeIsUL(node->parent) ) )
2976         {
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 );
2984         }
2985     }
2986 }
2987
2988 /************************************************************
2989 * InitAccessibilityChecks
2990 *
2991 * Initializes the AccessibilityChecks variables as necessary
2992 ************************************************************/
2993
2994 static void InitAccessibilityChecks( TidyDocImpl* doc, int level123 )
2995 {
2996     TidyClearMemory( &doc->access, sizeof(doc->access) );
2997     doc->access.PRIORITYCHK = level123;
2998 }
2999
3000 /************************************************************
3001 * CleanupAccessibilityChecks
3002 *
3003 * Cleans up the AccessibilityChecks variables as necessary
3004 ************************************************************/
3005
3006
3007 static void FreeAccessibilityChecks( TidyDocImpl* ARG_UNUSED(doc) )
3008 {
3009     /* free any memory allocated for the lists
3010
3011     Linked List of Links not used.  Just search document as 
3012     AREA tags are encountered.  Same algorithm, but no
3013     data structures necessary.
3014
3015     current = start;
3016     while (current)
3017     {
3018         void    *templink = (void *)current;
3019         
3020         current = current->next;
3021         TidyDocFree(doc, templink);
3022     }
3023     start = NULL;
3024     */
3025 }
3026
3027 /************************************************************
3028 * AccessibilityChecks
3029 *
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 ************************************************************/
3034
3035 static void AccessibilityCheckNode( TidyDocImpl* doc, Node* node )
3036 {
3037     Node* content;
3038     
3039     /* Check BODY for color contrast */
3040     if ( nodeIsBODY(node) )
3041     {
3042         CheckColorContrast( doc, node );
3043     }
3044
3045     /* Checks document for MetaData */
3046     else if ( nodeIsHEAD(node) )
3047     {
3048         if ( !CheckMetaData( doc, node, no ) )
3049           MetaDataPresent( doc, node );
3050     }
3051     
3052     /* Check the ANCHOR tag */
3053     else if ( nodeIsA(node) )
3054     {
3055         CheckAnchorAccess( doc, node );
3056     }
3057
3058     /* Check the IMAGE tag */
3059     else if ( nodeIsIMG(node) )
3060     {
3061         CheckFlicker( doc, node );
3062         CheckColorAvailable( doc, node );
3063         CheckImage( doc, node );
3064     }
3065
3066         /* Checks MAP for client-side text links */
3067     else if ( nodeIsMAP(node) )
3068     {
3069         CheckMapLinks( doc, node );
3070     }
3071
3072     /* Check the AREA tag */
3073     else if ( nodeIsAREA(node) )
3074     {
3075         CheckArea( doc, node );
3076     }
3077
3078     /* Check the APPLET tag */
3079     else if ( nodeIsAPPLET(node) )
3080     {
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 );
3088     }
3089     
3090     /* Check the OBJECT tag */
3091     else if ( nodeIsOBJECT(node) )
3092     {
3093         ProgrammaticObjects( doc, node );
3094         DynamicContent( doc, node );
3095         AccessibleCompatible( doc, node );
3096         CheckFlicker( doc, node );
3097         CheckColorAvailable( doc, node );
3098         CheckObject( doc, node );
3099     }
3100     
3101     /* Check the FRAME tag */
3102     else if ( nodeIsFRAME(node) )
3103     {
3104         CheckFrame( doc, node );
3105     }
3106     
3107     /* Check the IFRAME tag */
3108     else if ( nodeIsIFRAME(node) )
3109     {
3110         CheckIFrame( doc, node );
3111     }
3112     
3113     /* Check the SCRIPT tag */
3114     else if ( nodeIsSCRIPT(node) )
3115     {
3116         DynamicContent( doc, node );
3117         ProgrammaticObjects( doc, node );
3118         AccessibleCompatible( doc, node );
3119         CheckFlicker( doc, node );
3120         CheckColorAvailable( doc, node );
3121         CheckScriptAcc( doc, node );
3122     }
3123
3124     /* Check the TABLE tag */
3125     else if ( nodeIsTABLE(node) )
3126     {
3127         CheckColorContrast( doc, node );
3128         CheckTable( doc, node );
3129     }
3130
3131     /* Check the PRE for ASCII art */
3132     else if ( nodeIsPRE(node) || nodeIsXMP(node) )
3133     {
3134         CheckASCII( doc, node );
3135     }
3136
3137     /* Check the LABEL tag */
3138     else if ( nodeIsLABEL(node) )
3139     {
3140         CheckLabel( doc, node );
3141     }
3142
3143     /* Check INPUT tag for validity */
3144     else if ( nodeIsINPUT(node) )
3145     {
3146         CheckColorAvailable( doc, node );
3147         CheckInputLabel( doc, node );
3148         CheckInputAttributes( doc, node );
3149     }
3150
3151     /* Checks FRAMESET element for NOFRAME section */
3152     else if ( nodeIsFRAMESET(node) )
3153     {
3154         CheckFrameSet( doc, node );
3155     }
3156     
3157     /* Checks for header elements for valid header increase */
3158     else if ( TY_(nodeIsHeader)(node) )
3159     {
3160         CheckHeaderNesting( doc, node );
3161     }
3162
3163     /* Checks P element to ensure that it is not a header */
3164     else if ( nodeIsP(node) )
3165     {
3166         CheckParagraphHeader( doc, node );
3167     }
3168
3169     /* Checks HTML elemnt for valid 'LANG' */
3170     else if ( nodeIsHTML(node) )
3171     {
3172         CheckHTMLAccess( doc, node );
3173     }
3174
3175     /* Checks BLINK for any blinking text */
3176     else if ( nodeIsBLINK(node) )
3177     {
3178         CheckBlink( doc, node );
3179     }
3180
3181     /* Checks MARQUEE for any MARQUEE text */
3182     else if ( nodeIsMARQUEE(node) )
3183     {
3184         CheckMarquee( doc, node );
3185     }
3186
3187     /* Checks LINK for 'REL' attribute */
3188     else if ( nodeIsLINK(node) )
3189     {
3190         CheckLink( doc, node );
3191     }
3192
3193     /* Checks to see if STYLE is used */
3194     else if ( nodeIsSTYLE(node) )
3195     {
3196         CheckColorContrast( doc, node );
3197         CheckStyle( doc, node );
3198     }
3199
3200     /* Checks to see if EMBED is used */
3201     else if ( nodeIsEMBED(node) )
3202     {
3203         CheckEmbed( doc, node );
3204         ProgrammaticObjects( doc, node );
3205         AccessibleCompatible( doc, node );
3206         CheckFlicker( doc, node );
3207     }
3208
3209     /* Deprecated HTML if the following tags are found in the document */
3210     else if ( nodeIsBASEFONT(node) ||
3211               nodeIsCENTER(node)   ||
3212               nodeIsISINDEX(node)  ||
3213               nodeIsU(node)        ||
3214               nodeIsFONT(node)     ||
3215               nodeIsDIR(node)      ||
3216               nodeIsS(node)        ||
3217               nodeIsSTRIKE(node)   ||
3218               nodeIsMENU(node) )
3219     {
3220         CheckDeprecated( doc, node );
3221     }
3222
3223     /* Checks for 'ABBR' attribute if needed */
3224     else if ( nodeIsTH(node) )
3225     {
3226         CheckTH( doc, node );
3227     }
3228
3229     /* Ensures that lists are properly used */
3230     else if ( nodeIsLI(node) || nodeIsOL(node) || nodeIsUL(node) )
3231     {
3232         CheckListUsage( doc, node );
3233     }
3234
3235     /* Recursively check all child nodes.
3236     */
3237     for ( content = node->content; content != NULL; content = content->next )
3238     {
3239         AccessibilityCheckNode( doc, content );
3240     }
3241 }
3242
3243
3244 void TY_(AccessibilityChecks)( TidyDocImpl* doc )
3245 {
3246     /* Initialize */
3247     InitAccessibilityChecks( doc, cfg(doc, TidyAccessibilityCheckLevel) );
3248
3249     /* Hello there, ladies and gentlemen... */
3250     TY_(AccessibilityHelloMessage)( doc );
3251
3252     /* Checks all elements for script accessibility */
3253     CheckScriptKeyboardAccessible( doc, &doc->root );
3254
3255     /* Checks entire document for the use of 'STYLE' attribute */
3256     CheckForStyleAttribute( doc, &doc->root );
3257
3258     /* Checks for '!DOCTYPE' */
3259     CheckDocType( doc );
3260
3261     
3262     /* Checks to see if stylesheets are used to control the layout */
3263     if ( Level2_Enabled( doc )
3264          && ! CheckMissingStyleSheets( doc, &doc->root ) )
3265     {
3266         TY_(ReportAccessWarning)( doc, &doc->root, STYLE_SHEET_CONTROL_PRESENTATION );
3267     }
3268
3269     /* Check to see if any list elements are found within the document */
3270     CheckForListElements( doc, &doc->root );
3271
3272     /* Checks for natural language change */
3273     /* Must contain more than 3 words of text in the document
3274     **
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!
3279
3280     if ( WordCount(doc, &doc->root) > 3 )
3281     {
3282         TY_(ReportAccessWarning)( doc, node, INDICATE_CHANGES_IN_LANGUAGE);
3283     }
3284     */
3285
3286
3287     /* Recursively apply all remaining checks to 
3288     ** each node in document.
3289     */
3290     AccessibilityCheckNode( doc, &doc->root );
3291
3292     /* Cleanup */
3293     FreeAccessibilityChecks( doc );
3294 }
3295
3296 #endif
3297
3298 /*
3299  * local variables:
3300  * mode: c
3301  * indent-tabs-mode: nil
3302  * c-basic-offset: 4
3303  * eval: (c-set-offset 'substatement-open 0)
3304  * end:
3305  */