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