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