~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

TidyLib
tidy/src/access.c

Version: ~ [ 1.0 ] ~

** Warning: Cannot open xref database.

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.