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