tidy-ext 0.1.7

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