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