/[pub]/test/html-webhacc/cc.cgi
Suika

Diff of /test/html-webhacc/cc.cgi

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.3 by wakaba, Wed Jun 27 13:30:15 2007 UTC revision 1.35 by wakaba, Sun Feb 10 04:08:04 2008 UTC
# Line 1  Line 1 
1  #!/usr/bin/perl  #!/usr/bin/perl
2  use strict;  use strict;
3    use utf8;
4    
5  use lib qw[/home/httpd/html/www/markup/html/whatpm  use lib qw[/home/httpd/html/www/markup/html/whatpm
6             /home/wakaba/work/manakai/lib             /home/wakaba/work/manakai2/lib];
            /home/wakaba/public_html/-temp/wiki/lib];  
7  use CGI::Carp qw[fatalsToBrowser];  use CGI::Carp qw[fatalsToBrowser];
8  use Scalar::Util qw[refaddr];  use Scalar::Util qw[refaddr];
9    use Time::HiRes qw/time/;
 use SuikaWiki::Input::HTTP; ## TODO: Use some better CGI module  
10    
11  sub htescape ($) {  sub htescape ($) {
12    my $s = $_[0];    my $s = $_[0];
# Line 15  sub htescape ($) { Line 14  sub htescape ($) {
14    $s =~ s/</&lt;/g;    $s =~ s/</&lt;/g;
15    $s =~ s/>/&gt;/g;    $s =~ s/>/&gt;/g;
16    $s =~ s/"/&quot;/g;    $s =~ s/"/&quot;/g;
17    $s =~ s!([\x00-\x09\x0B-\x1F\x7F-\x80])!sprintf '<var>U+%04X</var>', ord $1!ge;    $s =~ s{([\x00-\x09\x0B-\x1F\x7F-\xA0\x{FEFF}\x{FFFC}-\x{FFFF}])}{
18        sprintf '<var>U+%04X</var>', ord $1;
19      }ge;
20    return $s;    return $s;
21  } # htescape  } # htescape
22    
23  my $http = SuikaWiki::Input::HTTP->new;    my @nav;
24      my %time;
25  ## TODO: _charset_    require Message::DOM::DOMImplementation;
26      my $dom = Message::DOM::DOMImplementation->new;
27    my $input_format = $http->parameter ('i') || 'text/html';  {
28    my $inner_html_element = $http->parameter ('e');    use Message::CGI::HTTP;
29    my $input_uri = 'thismessage:/';    my $http = Message::CGI::HTTP->new;
30    
31    my $s = $http->parameter ('s');    if ($http->get_meta_variable ('PATH_INFO') ne '/') {
32    if (length $s > 1000_000) {      print STDOUT "Status: 404 Not Found\nContent-Type: text/plain; charset=us-ascii\n\n400";
     print STDOUT "Status: 400 Document Too Long\nContent-Type: text/plain; charset=us-ascii\n\nToo long";  
33      exit;      exit;
34    }    }
35    
36    my @nav;    binmode STDOUT, ':utf8';
37      $| = 1;
38    
39      load_text_catalog ('en'); ## TODO: conneg
40    
41    print STDOUT qq[Content-Type: text/html; charset=utf-8    print STDOUT qq[Content-Type: text/html; charset=utf-8
42    
43  <!DOCTYPE html>  <!DOCTYPE html>
# Line 43  my $http = SuikaWiki::Input::HTTP->new; Line 47  my $http = SuikaWiki::Input::HTTP->new;
47  <link rel="stylesheet" href="../cc-style.css" type="text/css">  <link rel="stylesheet" href="../cc-style.css" type="text/css">
48  </head>  </head>
49  <body>  <body>
50  <h1>Web Document Conformance Checker (<em>beta</em>)</h1>  <h1><a href="../cc-interface">Web Document Conformance Checker</a>
51    (<em>beta</em>)</h1>
52    ];
53    
54      $| = 0;
55      my $input = get_input_document ($http, $dom);
56      my $char_length = 0;
57    
58  <div id="document-info" section="section">    print qq[
59    <div id="document-info" class="section">
60  <dl>  <dl>
61    <dt>Request URI</dt>
62        <dd><code class="URI" lang="">&lt;<a href="@{[htescape $input->{request_uri}]}">@{[htescape $input->{request_uri}]}</a>&gt;</code></dd>
63  <dt>Document URI</dt>  <dt>Document URI</dt>
64      <dd><code class="URI" lang="">&lt;<a href="@{[htescape $input_uri]}">@{[htescape $input_uri]}</a>&gt;</code></dd>      <dd><code class="URI" lang="">&lt;<a href="@{[htescape $input->{uri}]}" id=anchor-document-uri>@{[htescape $input->{uri}]}</a>&gt;</code>
65  <dt>Internet Media Type</dt>      <script>
66      <dd><code class="MIME" lang="en">@{[htescape $input_format]}</code></dd>        document.title = '<'
67              + document.getElementById ('anchor-document-uri').href + '> \\u2014 '
68              + document.title;
69        </script></dd>
70  ]; # no </dl> yet  ]; # no </dl> yet
71    push @nav, ['#document-info' => 'Information'];    push @nav, ['#document-info' => 'Information'];
72    
73    require Message::DOM::DOMImplementation;  if (defined $input->{s}) {
74    my $dom = Message::DOM::DOMImplementation->____new;    $char_length = length $input->{s};
   my $doc;  
   my $el;  
75    
76    if ($input_format eq 'text/html') {    print STDOUT qq[
77      require Encode;  <dt>Base URI</dt>
78      require Whatpm::HTML;      <dd><code class="URI" lang="">&lt;<a href="@{[htescape $input->{base_uri}]}">@{[htescape $input->{base_uri}]}</a>&gt;</code></dd>
79        <dt>Internet Media Type</dt>
80      $s = Encode::decode ('utf-8', $s);      <dd><code class="MIME" lang="en">@{[htescape $input->{media_type}]}</code>
81        @{[$input->{media_type_overridden} ? '<em>(overridden)</em>' : defined $input->{official_type} ? $input->{media_type} eq $input->{official_type} ? '' : '<em>(sniffed; official type is: <code class=MIME lang=en>'.htescape ($input->{official_type}).'</code>)' : '<em>(sniffed)</em>']}</dd>
     print STDOUT qq[  
82  <dt>Character Encoding</dt>  <dt>Character Encoding</dt>
83      <dd>(none)</dd>      <dd>@{[defined $input->{charset} ? '<code class="charset" lang="en">'.htescape ($input->{charset}).'</code>' : '(none)']}
84        @{[$input->{charset_overridden} ? '<em>(overridden)</em>' : '']}</dd>
85    <dt>Length</dt>
86        <dd>$char_length byte@{[$char_length == 1 ? '' : 's']}</dd>
87  </dl>  </dl>
88  </div>  </div>
89    ];
90    
91  <div id="source-string" class="section">    $input->{id_prefix} = '';
92  <h2>Document Source</h2>    #$input->{nested} = 0;
93      my $result = {conforming_min => 1, conforming_max => 1};
94      check_and_print ($input => $result);
95      print_result_section ($result);
96    } else {
97      print STDOUT qq[</dl></div>];
98      print_result_input_error_section ($input);
99    }
100    
101      print STDOUT qq[
102    <ul class="navigation" id="nav-items">
103    ];
104      for (@nav) {
105        print STDOUT qq[<li><a href="$_->[0]">$_->[1]</a></li>];
106      }
107      print STDOUT qq[
108    </ul>
109    </body>
110    </html>
111  ];  ];
     push @nav, ['#source-string' => 'Source'];  
     print_source_string (\$s);  
     print STDOUT qq[  
 </div>  
112    
113  <div id="parse-errors" class="section">    for (qw/decode parse parse_html parse_xml parse_manifest
114  <h2>Parse Errors</h2>            check check_manifest/) {
115        next unless defined $time{$_};
116        open my $file, '>>', ".cc-$_.txt" or die ".cc-$_.txt: $!";
117        print $file $char_length, "\t", $time{$_}, "\n";
118      }
119    
120    exit;
121    }
122    
123  <ul>  sub add_error ($$$) {
124      my ($layer, $err, $result) = @_;
125      if (defined $err->{level}) {
126        if ($err->{level} eq 's') {
127          $result->{$layer}->{should}++;
128          $result->{$layer}->{score_min} -= 2;
129          $result->{conforming_min} = 0;
130        } elsif ($err->{level} eq 'w' or $err->{level} eq 'g') {
131          $result->{$layer}->{warning}++;
132        } elsif ($err->{level} eq 'u' or $err->{level} eq 'unsupported') {
133          $result->{$layer}->{unsupported}++;
134          $result->{unsupported} = 1;
135        } else {
136          $result->{$layer}->{must}++;
137          $result->{$layer}->{score_max} -= 2;
138          $result->{$layer}->{score_min} -= 2;
139          $result->{conforming_min} = 0;
140          $result->{conforming_max} = 0;
141        }
142      } else {
143        $result->{$layer}->{must}++;
144        $result->{$layer}->{score_max} -= 2;
145        $result->{$layer}->{score_min} -= 2;
146        $result->{conforming_min} = 0;
147        $result->{conforming_max} = 0;
148      }
149    } # add_error
150    
151    sub check_and_print ($$) {
152      my ($input, $result) = @_;
153    
154      print_http_header_section ($input, $result);
155    
156      my $doc;
157      my $el;
158      my $cssom;
159      my $manifest;
160      my @subdoc;
161    
162      if ($input->{media_type} eq 'text/html') {
163        ($doc, $el) = print_syntax_error_html_section ($input, $result);
164        print_source_string_section
165            ($input,
166             \($input->{s}),
167             $input->{charset} || $doc->input_encoding);
168      } elsif ({
169                'text/xml' => 1,
170                'application/atom+xml' => 1,
171                'application/rss+xml' => 1,
172                'application/svg+xml' => 1,
173                'application/xhtml+xml' => 1,
174                'application/xml' => 1,
175               }->{$input->{media_type}}) {
176        ($doc, $el) = print_syntax_error_xml_section ($input, $result);
177        print_source_string_section ($input,
178                                     \($input->{s}),
179                                     $doc->input_encoding);
180      } elsif ($input->{media_type} eq 'text/css') {
181        $cssom = print_syntax_error_css_section ($input, $result);
182        print_source_string_section
183            ($input, \($input->{s}),
184             $cssom->manakai_input_encoding);
185      } elsif ($input->{media_type} eq 'text/cache-manifest') {
186    ## TODO: MUST be text/cache-manifest
187        $manifest = print_syntax_error_manifest_section ($input, $result);
188        print_source_string_section ($input, \($input->{s}),
189                                     'utf-8');
190      } else {
191        ## TODO: Change HTTP status code??
192        print_result_unknown_type_section ($input, $result);
193      }
194    
195      if (defined $doc or defined $el) {
196        $doc->document_uri ($input->{uri});
197        $doc->manakai_entity_base_uri ($input->{base_uri});
198        print_structure_dump_dom_section ($input, $doc, $el);
199        my $elements = print_structure_error_dom_section
200            ($input, $doc, $el, $result, sub {
201              push @subdoc, shift;
202            });
203        print_table_section ($input, $elements->{table}) if @{$elements->{table}};
204        print_listing_section ({
205          id => 'identifiers', label => 'IDs', heading => 'Identifiers',
206        }, $input, $elements->{id}) if keys %{$elements->{id}};
207        print_listing_section ({
208          id => 'terms', label => 'Terms', heading => 'Terms',
209        }, $input, $elements->{term}) if keys %{$elements->{term}};
210        print_listing_section ({
211          id => 'classes', label => 'Classes', heading => 'Classes',
212        }, $input, $elements->{class}) if keys %{$elements->{class}};
213      } elsif (defined $cssom) {
214        print_structure_dump_cssom_section ($input, $cssom);
215        ## TODO: CSSOM validation
216      } elsif (defined $manifest) {
217        print_structure_dump_manifest_section ($input, $manifest);
218        print_structure_error_manifest_section ($input, $manifest, $result);
219      }
220    
221      my $id_prefix = 0;
222      for my $subinput (@subdoc) {
223        $subinput->{id_prefix} = 'subdoc-' . ++$id_prefix;
224        $subinput->{nested} = 1;
225        $subinput->{base_uri} = $subinput->{container_node}->base_uri
226            unless defined $subinput->{base_uri};
227        my $ebaseuri = htescape ($subinput->{base_uri});
228        push @nav, ['#' . $subinput->{id_prefix} => 'Sub #' . $id_prefix];
229        print STDOUT qq[<div id="$subinput->{id_prefix}" class=section>
230          <h2>Subdocument #$id_prefix</h2>
231    
232          <dl>
233          <dt>Internet Media Type</dt>
234            <dd><code class="MIME" lang="en">@{[htescape $subinput->{media_type}]}</code>
235          <dt>Container Node</dt>
236            <dd>@{[get_node_link ($input, $subinput->{container_node})]}</dd>
237          <dt>Base <abbr title="Uniform Resource Identifiers">URI</abbr></dt>
238            <dd><code class=URI>&lt;<a href="$ebaseuri">$ebaseuri</a>></code></dd>
239          </dl>];              
240    
241        $subinput->{id_prefix} .= '-';
242        check_and_print ($subinput => $result);
243    
244        print STDOUT qq[</div>];
245      }
246    } # check_and_print
247    
248    sub print_http_header_section ($$) {
249      my ($input, $result) = @_;
250      return unless defined $input->{header_status_code} or
251          defined $input->{header_status_text} or
252          @{$input->{header_field} or []};
253      
254      push @nav, ['#source-header' => 'HTTP Header'] unless $input->{nested};
255      print STDOUT qq[<div id="$input->{id_prefix}source-header" class="section">
256    <h2>HTTP Header</h2>
257    
258    <p><strong>Note</strong>: Due to the limitation of the
259    network library in use, the content of this section might
260    not be the real header.</p>
261    
262    <table><tbody>
263  ];  ];
264    push @nav, ['#parse-errors' => 'Parse Error'];  
265      if (defined $input->{header_status_code}) {
266        print STDOUT qq[<tr><th scope="row">Status code</th>];
267        print STDOUT qq[<td><code>@{[htescape ($input->{header_status_code})]}</code></td></tr>];
268      }
269      if (defined $input->{header_status_text}) {
270        print STDOUT qq[<tr><th scope="row">Status text</th>];
271        print STDOUT qq[<td><code>@{[htescape ($input->{header_status_text})]}</code></td></tr>];
272      }
273      
274      for (@{$input->{header_field}}) {
275        print STDOUT qq[<tr><th scope="row"><code>@{[htescape ($_->[0])]}</code></th>];
276        print STDOUT qq[<td><code>@{[htescape ($_->[1])]}</code></td></tr>];
277      }
278    
279      print STDOUT qq[</tbody></table></div>];
280    } # print_http_header_section
281    
282    sub print_syntax_error_html_section ($$) {
283      my ($input, $result) = @_;
284      
285      require Encode;
286      require Whatpm::HTML;
287      
288      print STDOUT qq[
289    <div id="$input->{id_prefix}parse-errors" class="section">
290    <h2>Parse Errors</h2>
291    
292    <dl>];
293      push @nav, ['#parse-errors' => 'Parse Error'] unless $input->{nested};
294    
295    my $onerror = sub {    my $onerror = sub {
296      my (%opt) = @_;      my (%opt) = @_;
297        my ($type, $cls, $msg) = get_text ($opt{type}, $opt{level});
298      if ($opt{column} > 0) {      if ($opt{column} > 0) {
299        print STDOUT qq[<li><a href="#line-$opt{line}">Line $opt{line}</a> column $opt{column}: ];        print STDOUT qq[<dt class="$cls"><a href="#$input->{id_prefix}line-$opt{line}">Line $opt{line}</a> column $opt{column}</dt>\n];
300      } else {      } else {
301        $opt{line}--;        $opt{line} = $opt{line} - 1 || 1;
302        print STDOUT qq[<li><a href="#line-$opt{line}">Line $opt{line}</a>: ];        print STDOUT qq[<dt class="$cls"><a href="#$input->{id_prefix}line-$opt{line}">Line $opt{line}</a></dt>\n];
303      }      }
304      print STDOUT qq[@{[htescape $opt{type}]}</li>\n];      $type =~ tr/ /-/;
305        $type =~ s/\|/%7C/g;
306        $msg .= qq[ [<a href="../error-description#@{[htescape ($type)]}">Description</a>]];
307        print STDOUT qq[<dd class="$cls">], get_error_level_label (\%opt);
308        print STDOUT qq[$msg</dd>\n];
309    
310        add_error ('syntax', \%opt => $result);
311    };    };
312    
313    $doc = $dom->create_document;    my $doc = $dom->create_document;
314      my $el;
315      my $inner_html_element = $input->{inner_html_element};
316    if (defined $inner_html_element and length $inner_html_element) {    if (defined $inner_html_element and length $inner_html_element) {
317        $input->{charset} ||= 'windows-1252'; ## TODO: for now.
318        my $time1 = time;
319        my $t = Encode::decode ($input->{charset}, $input->{s});
320        $time{decode} = time - $time1;
321        
322      $el = $doc->create_element_ns      $el = $doc->create_element_ns
323          ('http://www.w3.org/1999/xhtml', [undef, $inner_html_element]);          ('http://www.w3.org/1999/xhtml', [undef, $inner_html_element]);
324      Whatpm::HTML->set_inner_html ($el, $s, $onerror);      $time1 = time;
325        Whatpm::HTML->set_inner_html ($el, $t, $onerror);
326        $time{parse} = time - $time1;
327    } else {    } else {
328      Whatpm::HTML->parse_string ($s => $doc, $onerror);      my $time1 = time;
329        Whatpm::HTML->parse_byte_string
330            ($input->{charset}, $input->{s} => $doc, $onerror);
331        $time{parse_html} = time - $time1;
332    }    }
333      $doc->manakai_charset ($input->{official_charset})
334          if defined $input->{official_charset};
335      
336      print STDOUT qq[</dl></div>];
337    
338      return ($doc, $el);
339    } # print_syntax_error_html_section
340    
341    sub print_syntax_error_xml_section ($$) {
342      my ($input, $result) = @_;
343      
344      require Message::DOM::XMLParserTemp;
345      
346    print STDOUT qq[    print STDOUT qq[
347  </ul>  <div id="$input->{id_prefix}parse-errors" class="section">
 </div>  
 ];  
   } elsif ($input_format eq 'application/xhtml+xml') {  
     require Message::DOM::XMLParserTemp;  
     require Encode;  
       
     my $t = Encode::decode ('utf-8', $s);  
   
     print STDOUT qq[  
 <dt>Character Encoding</dt>  
     <dd>(none)</dd>  
 </dl>  
 </div>  
   
 <div id="source-string" class="section">  
 <h2>Document Source</h2>  
 ];  
     push @nav, ['#source-string' => 'Source'];  
     print_source_string (\$t);  
     print STDOUT qq[  
 </div>  
   
 <div id="parse-errors" class="section">  
348  <h2>Parse Errors</h2>  <h2>Parse Errors</h2>
349    
350  <ul>  <dl>];
351  ];    push @nav, ['#parse-errors' => 'Parse Error'] unless $input->{prefix};
   push @nav, ['#parse-errors' => 'Parse Error'];  
352    
353    my $onerror = sub {    my $onerror = sub {
354      my $err = shift;      my $err = shift;
355      my $line = $err->location->line_number;      my $line = $err->location->line_number;
356      print STDOUT qq[<li><a href="#line-$line">Line $line</a> column ];      print STDOUT qq[<dt><a href="#$input->{id_prefix}line-$line">Line $line</a> column ];
357      print STDOUT $err->location->column_number, ": ";      print STDOUT $err->location->column_number, "</dt><dd>";
358      print STDOUT htescape $err->text, "</li>\n";      print STDOUT htescape $err->text, "</dd>\n";
359    
360        add_error ('syntax', {type => $err->text,
361                    level => [
362                              $err->SEVERITY_FATAL_ERROR => 'm',
363                              $err->SEVERITY_ERROR => 'm',
364                              $err->SEVERITY_WARNING => 's',
365                             ]->[$err->severity]} => $result);
366    
367      return 1;      return 1;
368    };    };
369    
370    open my $fh, '<', \$s;    my $time1 = time;
371    $doc = Message::DOM::XMLParserTemp->parse_byte_stream    open my $fh, '<', \($input->{s});
372        ($fh => $dom, $onerror, charset => 'utf-8');    my $doc = Message::DOM::XMLParserTemp->parse_byte_stream
373          ($fh => $dom, $onerror, charset => $input->{charset});
374      print STDOUT qq[    $time{parse_xml} = time - $time1;
375  </ul>    $doc->manakai_charset ($input->{official_charset})
376  </div>        if defined $input->{official_charset};
377  ];  
378    } else {    print STDOUT qq[</dl></div>];
379      print STDOUT qq[  
380  </dl>    return ($doc, undef);
381    } # print_syntax_error_xml_section
382  <div id="result-summary" class="section">  
383  <p><em>Media type <code class="MIME" lang="en">@{[htescape $input_format]}</code> is not supported!</em></p>  sub get_css_parser () {
384  </div>    our $CSSParser;
385  ];    return $CSSParser if $CSSParser;
386      push @nav, ['#result-summary' => 'Result'];  
387      require Whatpm::CSS::Parser;
388      my $p = Whatpm::CSS::Parser->new;
389    
390    #  if ($parse_mode eq 'q') {
391    #    $p->{unitless_px} = 1;
392    #    $p->{hashless_color} = 1;
393    #  }
394    
395      $p->{prop}->{$_} = 1 for qw/
396        background background-attachment background-color background-image
397        background-position background-position-x background-position-y
398        background-repeat border border-bottom border-bottom-color
399        border-bottom-style border-bottom-width border-collapse border-color
400        border-left border-left-color
401        border-left-style border-left-width border-right border-right-color
402        border-right-style border-right-width
403        border-spacing -manakai-border-spacing-x -manakai-border-spacing-y
404        border-style border-top border-top-color border-top-style border-top-width
405        border-width bottom
406        caption-side clear clip color content counter-increment counter-reset
407        cursor direction display empty-cells float font
408        font-family font-size font-size-adjust font-stretch
409        font-style font-variant font-weight height left
410        letter-spacing line-height
411        list-style list-style-image list-style-position list-style-type
412        margin margin-bottom margin-left margin-right margin-top marker-offset
413        marks max-height max-width min-height min-width opacity -moz-opacity
414        orphans outline outline-color outline-style outline-width overflow
415        overflow-x overflow-y
416        padding padding-bottom padding-left padding-right padding-top
417        page page-break-after page-break-before page-break-inside
418        position quotes right size table-layout
419        text-align text-decoration text-indent text-transform
420        top unicode-bidi vertical-align visibility white-space width widows
421        word-spacing z-index
422      /;
423      $p->{prop_value}->{display}->{$_} = 1 for qw/
424        block clip inline inline-block inline-table list-item none
425        table table-caption table-cell table-column table-column-group
426        table-header-group table-footer-group table-row table-row-group
427        compact marker
428      /;
429      $p->{prop_value}->{position}->{$_} = 1 for qw/
430        absolute fixed relative static
431      /;
432      $p->{prop_value}->{float}->{$_} = 1 for qw/
433        left right none
434      /;
435      $p->{prop_value}->{clear}->{$_} = 1 for qw/
436        left right none both
437      /;
438      $p->{prop_value}->{direction}->{ltr} = 1;
439      $p->{prop_value}->{direction}->{rtl} = 1;
440      $p->{prop_value}->{marks}->{crop} = 1;
441      $p->{prop_value}->{marks}->{cross} = 1;
442      $p->{prop_value}->{'unicode-bidi'}->{$_} = 1 for qw/
443        normal bidi-override embed
444      /;
445      for my $prop_name (qw/overflow overflow-x overflow-y/) {
446        $p->{prop_value}->{$prop_name}->{$_} = 1 for qw/
447          visible hidden scroll auto -webkit-marquee -moz-hidden-unscrollable
448        /;
449      }
450      $p->{prop_value}->{visibility}->{$_} = 1 for qw/
451        visible hidden collapse
452      /;
453      $p->{prop_value}->{'list-style-type'}->{$_} = 1 for qw/
454        disc circle square decimal decimal-leading-zero
455        lower-roman upper-roman lower-greek lower-latin
456        upper-latin armenian georgian lower-alpha upper-alpha none
457        hebrew cjk-ideographic hiragana katakana hiragana-iroha
458        katakana-iroha
459      /;
460      $p->{prop_value}->{'list-style-position'}->{outside} = 1;
461      $p->{prop_value}->{'list-style-position'}->{inside} = 1;
462      $p->{prop_value}->{'page-break-before'}->{$_} = 1 for qw/
463        auto always avoid left right
464      /;
465      $p->{prop_value}->{'page-break-after'}->{$_} = 1 for qw/
466        auto always avoid left right
467      /;
468      $p->{prop_value}->{'page-break-inside'}->{auto} = 1;
469      $p->{prop_value}->{'page-break-inside'}->{avoid} = 1;
470      $p->{prop_value}->{'background-repeat'}->{$_} = 1 for qw/
471        repeat repeat-x repeat-y no-repeat
472      /;
473      $p->{prop_value}->{'background-attachment'}->{scroll} = 1;
474      $p->{prop_value}->{'background-attachment'}->{fixed} = 1;
475      $p->{prop_value}->{'font-size'}->{$_} = 1 for qw/
476        xx-small x-small small medium large x-large xx-large
477        -manakai-xxx-large -webkit-xxx-large
478        larger smaller
479      /;
480      $p->{prop_value}->{'font-style'}->{normal} = 1;
481      $p->{prop_value}->{'font-style'}->{italic} = 1;
482      $p->{prop_value}->{'font-style'}->{oblique} = 1;
483      $p->{prop_value}->{'font-variant'}->{normal} = 1;
484      $p->{prop_value}->{'font-variant'}->{'small-caps'} = 1;
485      $p->{prop_value}->{'font-stretch'}->{$_} = 1 for
486          qw/normal wider narrower ultra-condensed extra-condensed
487            condensed semi-condensed semi-expanded expanded
488            extra-expanded ultra-expanded/;
489      $p->{prop_value}->{'text-align'}->{$_} = 1 for qw/
490        left right center justify begin end
491      /;
492      $p->{prop_value}->{'text-transform'}->{$_} = 1 for qw/
493        capitalize uppercase lowercase none
494      /;
495      $p->{prop_value}->{'white-space'}->{$_} = 1 for qw/
496        normal pre nowrap pre-line pre-wrap
497      /;
498      $p->{prop_value}->{'text-decoration'}->{$_} = 1 for qw/
499        none blink underline overline line-through
500      /;
501      $p->{prop_value}->{'caption-side'}->{$_} = 1 for qw/
502        top bottom left right
503      /;
504      $p->{prop_value}->{'table-layout'}->{auto} = 1;
505      $p->{prop_value}->{'table-layout'}->{fixed} = 1;
506      $p->{prop_value}->{'border-collapse'}->{collapase} = 1;
507      $p->{prop_value}->{'border-collapse'}->{separate} = 1;
508      $p->{prop_value}->{'empty-cells'}->{show} = 1;
509      $p->{prop_value}->{'empty-cells'}->{hide} = 1;
510      $p->{prop_value}->{cursor}->{$_} = 1 for qw/
511        auto crosshair default pointer move e-resize ne-resize nw-resize n-resize
512        se-resize sw-resize s-resize w-resize text wait help progress
513      /;
514      for my $prop (qw/border-top-style border-left-style
515                       border-bottom-style border-right-style outline-style/) {
516        $p->{prop_value}->{$prop}->{$_} = 1 for qw/
517          none hidden dotted dashed solid double groove ridge inset outset
518        /;
519    }    }
520      for my $prop (qw/color background-color
521                       border-bottom-color border-left-color border-right-color
522                       border-top-color border-color/) {
523        $p->{prop_value}->{$prop}->{transparent} = 1;
524        $p->{prop_value}->{$prop}->{flavor} = 1;
525        $p->{prop_value}->{$prop}->{'-manakai-default'} = 1;
526      }
527      $p->{prop_value}->{'outline-color'}->{invert} = 1;
528      $p->{prop_value}->{'outline-color'}->{'-manakai-invert-or-currentcolor'} = 1;
529      $p->{pseudo_class}->{$_} = 1 for qw/
530        active checked disabled empty enabled first-child first-of-type
531        focus hover indeterminate last-child last-of-type link only-child
532        only-of-type root target visited
533        lang nth-child nth-last-child nth-of-type nth-last-of-type not
534        -manakai-contains -manakai-current
535      /;
536      $p->{pseudo_element}->{$_} = 1 for qw/
537        after before first-letter first-line
538      /;
539    
540      return $CSSParser = $p;
541    } # get_css_parser
542    
543    if (defined $doc or defined $el) {  sub print_syntax_error_css_section ($$) {
544      print STDOUT qq[    my ($input, $result) = @_;
 <div id="document-tree" class="section">  
 <h2>Document Tree</h2>  
 ];  
     push @nav, ['#document-tree' => 'Tree'];  
545    
546      print_document_tree ($el || $doc);    print STDOUT qq[
547    <div id="$input->{id_prefix}parse-errors" class="section">
548      print STDOUT qq[  <h2>Parse Errors</h2>
 </div>  
549    
550  <div id="document-errors" class="section">  <dl>];
551  <h2>Document Errors</h2>    push @nav, ['#parse-errors' => 'Parse Error'] unless $input->{nested};
552    
553  <ul>    my $p = get_css_parser ();
554  ];    $p->{onerror} = sub {
555      push @nav, ['#document-errors' => 'Document Error'];      my (%opt) = @_;
556        my ($type, $cls, $msg) = get_text ($opt{type}, $opt{level});
557        if ($opt{token}) {
558          print STDOUT qq[<dt class="$cls"><a href="#$input->{id_prefix}line-$opt{token}->{line}">Line $opt{token}->{line}</a> column $opt{token}->{column}];
559        } else {
560          print STDOUT qq[<dt class="$cls">Unknown location];
561        }
562        if (defined $opt{value}) {
563          print STDOUT qq[ (<code>@{[htescape ($opt{value})]}</code>)];
564        } elsif (defined $opt{token}) {
565          print STDOUT qq[ (<code>@{[htescape (Whatpm::CSS::Tokenizer->serialize_token ($opt{token}))]}</code>)];
566        }
567        $type =~ tr/ /-/;
568        $type =~ s/\|/%7C/g;
569        $msg .= qq[ [<a href="../error-description#@{[htescape ($type)]}">Description</a>]];
570        print STDOUT qq[<dd class="$cls">], get_error_level_label (\%opt);
571        print STDOUT qq[$msg</dd>\n];
572    
573      require Whatpm::ContentChecker;      add_error ('syntax', \%opt => $result);
574      my $onerror = sub {    };
575        my %opt = @_;    $p->{href} = $input->{uri};
576        print STDOUT qq[<li><a href="#node-@{[refaddr $opt{node}]}">],    $p->{base_uri} = $input->{base_uri};
           htescape get_node_path ($opt{node}),  
           "</a>: ", htescape $opt{type}, "</li>\n";  
     };  
577    
578      if ($el) {    my $s = \$input->{s};
579        Whatpm::ContentChecker->check_element ($el, $onerror);    my $charset;
580      unless ($input->{is_char_string}) {
581        require Encode;
582        if (defined $input->{charset}) {## TODO: IANA->Perl
583          $charset = $input->{charset};
584          $s = \(Encode::decode ($input->{charset}, $$s));
585      } else {      } else {
586        Whatpm::ContentChecker->check_document ($doc, $onerror);        ## TODO: charset detection
587          $s = \(Encode::decode ($charset = 'utf-8', $$s));
588      }      }
   
     print STDOUT qq[  
 </ul>  
 </div>  
 ];  
589    }    }
590      
591      my $cssom = $p->parse_char_string ($$s);
592      $cssom->manakai_input_encoding ($charset) if defined $charset;
593    
594    ## TODO: Show result    print STDOUT qq[</dl></div>];
595    
596      return $cssom;
597    } # print_syntax_error_css_section
598    
599    sub print_syntax_error_manifest_section ($$) {
600      my ($input, $result) = @_;
601    
602      require Whatpm::CacheManifest;
603    
604    print STDOUT qq[    print STDOUT qq[
605  <ul class="navigation" id="nav-items">  <div id="$input->{id_prefix}parse-errors" class="section">
606  ];  <h2>Parse Errors</h2>
   for (@nav) {  
     print STDOUT qq[<li><a href="$_->[0]">$_->[1]</a></li>];  
   }  
   print STDOUT qq[  
 </ul>  
 </body>  
 </html>  
 ];  
607    
608  exit;  <dl>];
609      push @nav, ['#parse-errors' => 'Parse Error'] unless $input->{nested};
610    
611  sub print_source_string ($) {    my $onerror = sub {
612    my $s = $_[0];      my (%opt) = @_;
613    my $i = 1;      my ($type, $cls, $msg) = get_text ($opt{type}, $opt{level});
614    print STDOUT qq[<ol lang="">\n];      print STDOUT qq[<dt class="$cls">], get_error_label ($input, \%opt),
615    while ($$s =~ /\G([^\x0A]*?)\x0D?\x0A/gc) {          qq[</dt>];
616      print STDOUT qq[<li id="line-$i">], htescape $1, "</li>\n";      $type =~ tr/ /-/;
617      $i++;      $type =~ s/\|/%7C/g;
618        $msg .= qq[ [<a href="../error-description#@{[htescape ($type)]}">Description</a>]];
619        print STDOUT qq[<dd class="$cls">], get_error_level_label (\%opt);
620        print STDOUT qq[$msg</dd>\n];
621    
622        add_error ('syntax', \%opt => $result);
623      };
624    
625      my $time1 = time;
626      my $manifest = Whatpm::CacheManifest->parse_byte_string
627          ($input->{s}, $input->{uri}, $input->{base_uri}, $onerror);
628      $time{parse_manifest} = time - $time1;
629    
630      print STDOUT qq[</dl></div>];
631    
632      return $manifest;
633    } # print_syntax_error_manifest_section
634    
635    sub print_source_string_section ($$$) {
636      my $input = shift;
637      my $s;
638      unless ($input->{is_char_string}) {
639        require Encode;
640        my $enc = Encode::find_encoding ($_[1]); ## TODO: charset name -> Perl name
641        return unless $enc;
642    
643        $s = \($enc->decode (${$_[0]}));
644      } else {
645        $s = $_[0];
646    }    }
647    if ($$s =~ /\G([^\x0A]+)/gc) {  
648      print STDOUT qq[<li id="line-$i">], htescape $1, "</li>\n";    my $i = 1;                            
649      push @nav, ['#source-string' => 'Source'] unless $input->{nested};
650      print STDOUT qq[<div id="$input->{id_prefix}source-string" class="section">
651    <h2>Document Source</h2>
652    <ol lang="">\n];
653      if (length $$s) {
654        while ($$s =~ /\G([^\x0A]*?)\x0D?\x0A/gc) {
655          print STDOUT qq[<li id="$input->{id_prefix}line-$i">], htescape $1,
656              "</li>\n";
657          $i++;
658        }
659        if ($$s =~ /\G([^\x0A]+)/gc) {
660          print STDOUT qq[<li id="$input->{id_prefix}line-$i">], htescape $1,
661              "</li>\n";
662        }
663      } else {
664        print STDOUT q[<li id="$input->{id_prefix}line-1"></li>];
665    }    }
666    print STDOUT "</ol>";    print STDOUT "</ol></div>";
667  } # print_input_string  } # print_input_string_section
668    
669    sub print_document_tree ($$) {
670      my ($input, $node) = @_;
671    
 sub print_document_tree ($) {  
   my $node = shift;  
672    my $r = '<ol class="xoxo">';    my $r = '<ol class="xoxo">';
673    
674    my @node = ($node);    my @node = ($node);
# Line 247  sub print_document_tree ($) { Line 679  sub print_document_tree ($) {
679        next;        next;
680      }      }
681    
682      my $node_id = 'node-'.refaddr $child;      my $node_id = $input->{id_prefix} . 'node-'.refaddr $child;
683      my $nt = $child->node_type;      my $nt = $child->node_type;
684      if ($nt == $child->ELEMENT_NODE) {      if ($nt == $child->ELEMENT_NODE) {
685        $r .= qq'<li id="$node_id"><code>' . htescape ($child->tag_name) .        my $child_nsuri = $child->namespace_uri;
686          $r .= qq[<li id="$node_id" class="tree-element"><code title="@{[defined $child_nsuri ? $child_nsuri : '']}">] . htescape ($child->tag_name) .
687            '</code>'; ## ISSUE: case            '</code>'; ## ISSUE: case
688    
689        if ($child->has_attributes) {        if ($child->has_attributes) {
690          $r .= '<ul class="attributes">';          $r .= '<ul class="attributes">';
691          for my $attr (sort {$a->[0] cmp $b->[0]} map { [$_->name, $_->value, 'node-'.refaddr $_] }          for my $attr (sort {$a->[0] cmp $b->[0]} map { [$_->name, $_->value, $_->namespace_uri, 'node-'.refaddr $_] }
692                        @{$child->attributes}) {                        @{$child->attributes}) {
693            $r .= qq'<li id="$attr->[2]"><code>' . htescape ($attr->[0]) . '</code> = '; ## ISSUE: case?            $r .= qq[<li id="$input->{id_prefix}$attr->[3]" class="tree-attribute"><code title="@{[defined $attr->[2] ? htescape ($attr->[2]) : '']}">] . htescape ($attr->[0]) . '</code> = '; ## ISSUE: case?
694            $r .= '<q>' . htescape ($attr->[1]) . '</q></li>'; ## TODO: children            $r .= '<q>' . htescape ($attr->[1]) . '</q></li>'; ## TODO: children
695          }          }
696          $r .= '</ul>';          $r .= '</ul>';
697        }        }
698    
699        if ($node->has_child_nodes) {        if ($child->has_child_nodes) {
700          $r .= '<ol class="children">';          $r .= '<ol class="children">';
701          unshift @node, @{$child->child_nodes}, '</ol>';          unshift @node, @{$child->child_nodes}, '</ol></li>';
702          } else {
703            $r .= '</li>';
704        }        }
705      } elsif ($nt == $child->TEXT_NODE) {      } elsif ($nt == $child->TEXT_NODE) {
706        $r .= qq'<li id="$node_id"><q>' . htescape ($child->data) . '</q></li>';        $r .= qq'<li id="$node_id" class="tree-text"><q lang="">' . htescape ($child->data) . '</q></li>';
707      } elsif ($nt == $child->CDATA_SECTION_NODE) {      } elsif ($nt == $child->CDATA_SECTION_NODE) {
708        $r .= qq'<li id="$node_id"><code>&lt;[CDATA[</code><q>' . htescape ($child->data) . '</q><code>]]&gt;</code></li>';        $r .= qq'<li id="$node_id" class="tree-cdata"><code>&lt;[CDATA[</code><q lang="">' . htescape ($child->data) . '</q><code>]]&gt;</code></li>';
709      } elsif ($nt == $child->COMMENT_NODE) {      } elsif ($nt == $child->COMMENT_NODE) {
710        $r .= qq'<li id="$node_id"><code>&lt;!--</code><q>' . htescape ($child->data) . '</q><code>--&gt;</code></li>';        $r .= qq'<li id="$node_id" class="tree-comment"><code>&lt;!--</code><q lang="">' . htescape ($child->data) . '</q><code>--&gt;</code></li>';
711      } elsif ($nt == $child->DOCUMENT_NODE) {      } elsif ($nt == $child->DOCUMENT_NODE) {
712        $r .= qq'<li id="$node_id">Document</li>';        $r .= qq'<li id="$node_id" class="tree-document">Document';
713          $r .= qq[<ul class="attributes">];
714          my $cp = $child->manakai_charset;
715          if (defined $cp) {
716            $r .= qq[<li><code>charset</code> parameter = <code>];
717            $r .= htescape ($cp) . qq[</code></li>];
718          }
719          $r .= qq[<li><code>inputEncoding</code> = ];
720          my $ie = $child->input_encoding;
721          if (defined $ie) {
722            $r .= qq[<code>@{[htescape ($ie)]}</code>];
723            if ($child->manakai_has_bom) {
724              $r .= qq[ (with <code class=charname><abbr>BOM</abbr></code>)];
725            }
726          } else {
727            $r .= qq[(<code>null</code>)];
728          }
729          $r .= qq[<li>@{[scalar get_text ('manakaiIsHTML:'.($child->manakai_is_html?1:0))]}</li>];
730          $r .= qq[<li>@{[scalar get_text ('manakaiCompatMode:'.$child->manakai_compat_mode)]}</li>];
731          unless ($child->manakai_is_html) {
732            $r .= qq[<li>XML version = <code>@{[htescape ($child->xml_version)]}</code></li>];
733            if (defined $child->xml_encoding) {
734              $r .= qq[<li>XML encoding = <code>@{[htescape ($child->xml_encoding)]}</code></li>];
735            } else {
736              $r .= qq[<li>XML encoding = (null)</li>];
737            }
738            $r .= qq[<li>XML standalone = @{[$child->xml_standalone ? 'true' : 'false']}</li>];
739          }
740          $r .= qq[</ul>];
741        if ($child->has_child_nodes) {        if ($child->has_child_nodes) {
742          $r .= '<ol>';          $r .= '<ol class="children">';
743          unshift @node, @{$child->child_nodes}, '</ol>';          unshift @node, @{$child->child_nodes}, '</ol></li>';
744        }        }
745      } elsif ($nt == $child->DOCUMENT_TYPE_NODE) {      } elsif ($nt == $child->DOCUMENT_TYPE_NODE) {
746        $r .= qq'<li id="$node_id"><code>&lt;!DOCTYPE&gt;</code><ul>';        $r .= qq'<li id="$node_id" class="tree-doctype"><code>&lt;!DOCTYPE&gt;</code><ul class="attributes">';
747        $r .= '<li>Name = <q>@{[htescape ($child->name)]}</q></li>';        $r .= qq[<li class="tree-doctype-name">Name = <q>@{[htescape ($child->name)]}</q></li>];
748        $r .= '<li>Public identifier = <q>@{[htescape ($child->public_id)]}</q></li>';        $r .= qq[<li class="tree-doctype-publicid">Public identifier = <q>@{[htescape ($child->public_id)]}</q></li>];
749        $r .= '<li>System identifier = <q>@{[htescape ($child->system_id)]}</q></li>';        $r .= qq[<li class="tree-doctype-systemid">System identifier = <q>@{[htescape ($child->system_id)]}</q></li>];
750        $r .= '</ul></li>';        $r .= '</ul></li>';
751      } elsif ($nt == $child->PROCESSING_INSTRUCTION_NODE) {      } elsif ($nt == $child->PROCESSING_INSTRUCTION_NODE) {
752        $r .= qq'<li id="$node_id"><code>&lt;?@{[htescape ($child->target)]}?&gt;</code>';        $r .= qq'<li id="$node_id" class="tree-id"><code>&lt;?@{[htescape ($child->target)]}</code> <q>@{[htescape ($child->data)]}</q><code>?&gt;</code></li>';
       $r .= '<ul><li>@{[htescape ($child->data)]}</li></ul></li>';  
753      } else {      } else {
754        $r .= qq'<li id="$node_id">@{[$child->node_type]} @{[htescape ($child->node_name)]}</li>'; # error        $r .= qq'<li id="$node_id" class="tree-unknown">@{[$child->node_type]} @{[htescape ($child->node_name)]}</li>'; # error
755      }      }
756    }    }
757    
# Line 297  sub print_document_tree ($) { Line 759  sub print_document_tree ($) {
759    print STDOUT $r;    print STDOUT $r;
760  } # print_document_tree  } # print_document_tree
761    
762    sub print_structure_dump_dom_section ($$$) {
763      my ($input, $doc, $el) = @_;
764    
765      print STDOUT qq[
766    <div id="$input->{id_prefix}document-tree" class="section">
767    <h2>Document Tree</h2>
768    ];
769      push @nav, [qq[#$input->{id_prefix}document-tree] => 'Tree']
770          unless $input->{nested};
771    
772      print_document_tree ($input, $el || $doc);
773    
774      print STDOUT qq[</div>];
775    } # print_structure_dump_dom_section
776    
777    sub print_structure_dump_cssom_section ($$) {
778      my ($input, $cssom) = @_;
779    
780      print STDOUT qq[
781    <div id="$input->{id_prefix}document-tree" class="section">
782    <h2>Document Tree</h2>
783    ];
784      push @nav, [qq[#$input->{id_prefix}document-tree] => 'Tree']
785          unless $input->{nested};
786    
787      ## TODO:
788      print STDOUT "<pre>".htescape ($cssom->css_text)."</pre>";
789    
790      print STDOUT qq[</div>];
791    } # print_structure_dump_cssom_section
792    
793    sub print_structure_dump_manifest_section ($$) {
794      my ($input, $manifest) = @_;
795    
796      print STDOUT qq[
797    <div id="$input->{id_prefix}dump-manifest" class="section">
798    <h2>Cache Manifest</h2>
799    ];
800      push @nav, [qq[#$input->{id_prefix}dump-manifest] => 'Cache Manifest']
801          unless $input->{nested};
802    
803      print STDOUT qq[<dl><dt>Explicit entries</dt>];
804      for my $uri (@{$manifest->[0]}) {
805        my $euri = htescape ($uri);
806        print STDOUT qq[<dd><code class=uri>&lt;<a href="$euri">$euri</a>></code></dd>];
807      }
808    
809      print STDOUT qq[<dt>Fallback entries</dt><dd>
810          <table><thead><tr><th scope=row>Oppotunistic Caching Namespace</th>
811          <th scope=row>Fallback Entry</tr><tbody>];
812      for my $uri (sort {$a cmp $b} keys %{$manifest->[1]}) {
813        my $euri = htescape ($uri);
814        my $euri2 = htescape ($manifest->[1]->{$uri});
815        print STDOUT qq[<tr><td><code class=uri>&lt;<a href="$euri">$euri</a>></code></td>
816            <td><code class=uri>&lt;<a href="$euri2">$euri2</a>></code></td>];
817      }
818    
819      print STDOUT qq[</table><dt>Online whitelist</dt>];
820      for my $uri (@{$manifest->[2]}) {
821        my $euri = htescape ($uri);
822        print STDOUT qq[<dd><code class=uri>&lt;<a href="$euri">$euri</a>></code></dd>];
823      }
824    
825      print STDOUT qq[</dl></div>];
826    } # print_structure_dump_manifest_section
827    
828    sub print_structure_error_dom_section ($$$$$) {
829      my ($input, $doc, $el, $result, $onsubdoc) = @_;
830    
831      print STDOUT qq[<div id="$input->{id_prefix}document-errors" class="section">
832    <h2>Document Errors</h2>
833    
834    <dl>];
835      push @nav, [qq[#$input->{id_prefix}document-errors] => 'Document Error']
836          unless $input->{nested};
837    
838      require Whatpm::ContentChecker;
839      my $onerror = sub {
840        my %opt = @_;
841        my ($type, $cls, $msg) = get_text ($opt{type}, $opt{level}, $opt{node});
842        $type =~ tr/ /-/;
843        $type =~ s/\|/%7C/g;
844        $msg .= qq[ [<a href="../error-description#@{[htescape ($type)]}">Description</a>]];
845        print STDOUT qq[<dt class="$cls">] . get_error_label ($input, \%opt) .
846            qq[</dt>\n<dd class="$cls">], get_error_level_label (\%opt);
847        print STDOUT $msg, "</dd>\n";
848        add_error ('structure', \%opt => $result);
849      };
850    
851      my $elements;
852      my $time1 = time;
853      if ($el) {
854        $elements = Whatpm::ContentChecker->check_element
855            ($el, $onerror, $onsubdoc);
856      } else {
857        $elements = Whatpm::ContentChecker->check_document
858            ($doc, $onerror, $onsubdoc);
859      }
860      $time{check} = time - $time1;
861    
862      print STDOUT qq[</dl></div>];
863    
864      return $elements;
865    } # print_structure_error_dom_section
866    
867    sub print_structure_error_manifest_section ($$$) {
868      my ($input, $manifest, $result) = @_;
869    
870      print STDOUT qq[<div id="$input->{id_prefix}document-errors" class="section">
871    <h2>Document Errors</h2>
872    
873    <dl>];
874      push @nav, [qq[#$input->{id_prefix}document-errors] => 'Document Error']
875          unless $input->{nested};
876    
877      require Whatpm::CacheManifest;
878      Whatpm::CacheManifest->check_manifest ($manifest, sub {
879        my %opt = @_;
880        my ($type, $cls, $msg) = get_text ($opt{type}, $opt{level}, $opt{node});
881        $type =~ tr/ /-/;
882        $type =~ s/\|/%7C/g;
883        $msg .= qq[ [<a href="../error-description#@{[htescape ($type)]}">Description</a>]];
884        print STDOUT qq[<dt class="$cls">] . get_error_label ($input, \%opt) .
885            qq[</dt>\n<dd class="$cls">], $msg, "</dd>\n";
886        add_error ('structure', \%opt => $result);
887      });
888    
889      print STDOUT qq[</div>];
890    } # print_structure_error_manifest_section
891    
892    sub print_table_section ($$) {
893      my ($input, $tables) = @_;
894      
895      push @nav, [qq[#$input->{id_prefix}tables] => 'Tables']
896          unless $input->{nested};
897      print STDOUT qq[
898    <div id="$input->{id_prefix}tables" class="section">
899    <h2>Tables</h2>
900    
901    <!--[if IE]><script type="text/javascript" src="../excanvas.js"></script><![endif]-->
902    <script src="../table-script.js" type="text/javascript"></script>
903    <noscript>
904    <p><em>Structure of tables are visualized here if scripting is enabled.</em></p>
905    </noscript>
906    ];
907      
908      require JSON;
909      
910      my $i = 0;
911      for my $table_el (@$tables) {
912        $i++;
913        print STDOUT qq[<div class="section" id="$input->{id_prefix}table-$i"><h3>] .
914            get_node_link ($input, $table_el) . q[</h3>];
915    
916        ## TODO: Make |ContentChecker| return |form_table| result
917        ## so that this script don't have to run the algorithm twice.
918        my $table = Whatpm::HTMLTable->form_table ($table_el);
919        
920        for (@{$table->{column_group}}, @{$table->{column}}, $table->{caption}) {
921          next unless $_;
922          delete $_->{element};
923        }
924        
925        for (@{$table->{row_group}}) {
926          next unless $_;
927          next unless $_->{element};
928          $_->{type} = $_->{element}->manakai_local_name;
929          delete $_->{element};
930        }
931        
932        for (@{$table->{cell}}) {
933          next unless $_;
934          for (@{$_}) {
935            next unless $_;
936            for (@$_) {
937              $_->{id} = refaddr $_->{element} if defined $_->{element};
938              delete $_->{element};
939              $_->{is_header} = $_->{is_header} ? 1 : 0;
940            }
941          }
942        }
943            
944        print STDOUT '</div><script type="text/javascript">tableToCanvas (';
945        print STDOUT JSON::objToJson ($table);
946        print STDOUT qq[, document.getElementById ('$input->{id_prefix}table-$i')];
947        print STDOUT qq[, '$input->{id_prefix}');</script>];
948      }
949      
950      print STDOUT qq[</div>];
951    } # print_table_section
952    
953    sub print_listing_section ($$$) {
954      my ($opt, $input, $ids) = @_;
955      
956      push @nav, ['#' . $input->{id_prefix} . $opt->{id} => $opt->{label}]
957          unless $input->{nested};
958      print STDOUT qq[
959    <div id="$input->{id_prefix}$opt->{id}" class="section">
960    <h2>$opt->{heading}</h2>
961    
962    <dl>
963    ];
964      for my $id (sort {$a cmp $b} keys %$ids) {
965        print STDOUT qq[<dt><code>@{[htescape $id]}</code></dt>];
966        for (@{$ids->{$id}}) {
967          print STDOUT qq[<dd>].get_node_link ($input, $_).qq[</dd>];
968        }
969      }
970      print STDOUT qq[</dl></div>];
971    } # print_listing_section
972    
973    sub print_result_section ($) {
974      my $result = shift;
975    
976      print STDOUT qq[
977    <div id="result-summary" class="section">
978    <h2>Result</h2>];
979    
980      if ($result->{unsupported} and $result->{conforming_max}) {  
981        print STDOUT qq[<p class=uncertain id=result-para>The conformance
982            checker cannot decide whether the document is conforming or
983            not, since the document contains one or more unsupported
984            features.  The document might or might not be conforming.</p>];
985      } elsif ($result->{conforming_min}) {
986        print STDOUT qq[<p class=PASS id=result-para>No conformance-error is
987            found in this document.</p>];
988      } elsif ($result->{conforming_max}) {
989        print STDOUT qq[<p class=SEE-RESULT id=result-para>This document
990            is <strong>likely <em>non</em>-conforming</strong>, but in rare case
991            it might be conforming.</p>];
992      } else {
993        print STDOUT qq[<p class=FAIL id=result-para>This document is
994            <strong><em>non</em>-conforming</strong>.</p>];
995      }
996    
997      print STDOUT qq[<table>
998    <colgroup><col><colgroup><col><col><col><colgroup><col>
999    <thead>
1000    <tr><th scope=col></th>
1001    <th scope=col><a href="../error-description#level-m"><em class=rfc2119>MUST</em>‐level
1002    Errors</a></th>
1003    <th scope=col><a href="../error-description#level-s"><em class=rfc2119>SHOULD</em>‐level
1004    Errors</a></th>
1005    <th scope=col><a href="../error-description#level-w">Warnings</a></th>
1006    <th scope=col>Score</th></tr></thead><tbody>];
1007    
1008      my $must_error = 0;
1009      my $should_error = 0;
1010      my $warning = 0;
1011      my $score_min = 0;
1012      my $score_max = 0;
1013      my $score_base = 20;
1014      my $score_unit = $score_base / 100;
1015      for (
1016        [Transfer => 'transfer', ''],
1017        [Character => 'char', ''],
1018        [Syntax => 'syntax', '#parse-errors'],
1019        [Structure => 'structure', '#document-errors'],
1020      ) {
1021        $must_error += ($result->{$_->[1]}->{must} += 0);
1022        $should_error += ($result->{$_->[1]}->{should} += 0);
1023        $warning += ($result->{$_->[1]}->{warning} += 0);
1024        $score_min += (($result->{$_->[1]}->{score_min} *= $score_unit) += $score_base);
1025        $score_max += (($result->{$_->[1]}->{score_max} *= $score_unit) += $score_base);
1026    
1027        my $uncertain = $result->{$_->[1]}->{unsupported} ? '?' : '';
1028        my $label = $_->[0];
1029        if ($result->{$_->[1]}->{must} or
1030            $result->{$_->[1]}->{should} or
1031            $result->{$_->[1]}->{warning} or
1032            $result->{$_->[1]}->{unsupported}) {
1033          $label = qq[<a href="$_->[2]">$label</a>];
1034        }
1035    
1036        print STDOUT qq[<tr class="@{[$uncertain ? 'uncertain' : '']}"><th scope=row>$label</th><td class="@{[$result->{$_->[1]}->{must} ? 'FAIL' : '']}">$result->{$_->[1]}->{must}$uncertain</td><td class="@{[$result->{$_->[1]}->{should} ? 'SEE-RESULT' : '']}">$result->{$_->[1]}->{should}$uncertain</td><td>$result->{$_->[1]}->{warning}$uncertain</td>];
1037        if ($uncertain) {
1038          print qq[<td class="@{[$result->{$_->[1]}->{must} ? 'FAIL' : $result->{$_->[1]}->{should} ? 'SEE-RESULT' : '']}">&#x2212;&#x221E;..$result->{$_->[1]}->{score_max}</td>];
1039        } elsif ($result->{$_->[1]}->{score_min} != $result->{$_->[1]}->{score_max}) {
1040          print qq[<td class="@{[$result->{$_->[1]}->{must} ? 'FAIL' : 'SEE-RESULT']}">$result->{$_->[1]}->{score_min}..$result->{$_->[1]}->{score_max}</td></tr>];
1041        } else {
1042          print qq[<td class="@{[$result->{$_->[1]}->{must} ? 'FAIL' : '']}">$result->{$_->[1]}->{score_min}</td></tr>];
1043        }
1044      }
1045    
1046      $score_max += $score_base;
1047    
1048      print STDOUT qq[
1049    <tr class=uncertain><th scope=row>Semantics</th><td>0?</td><td>0?</td><td>0?</td><td>&#x2212;&#x221E;..$score_base</td></tr>
1050    </tbody>
1051    <tfoot><tr class=uncertain><th scope=row>Total</th>
1052    <td class="@{[$must_error ? 'FAIL' : '']}">$must_error?</td>
1053    <td class="@{[$should_error ? 'SEE-RESULT' : '']}">$should_error?</td>
1054    <td>$warning?</td>
1055    <td class="@{[$must_error ? 'FAIL' : $should_error ? 'SEE-RESULT' : '']}"><strong>&#x2212;&#x221E;..$score_max</strong></td></tr></tfoot>
1056    </table>
1057    
1058    <p><strong>Important</strong>: This conformance checking service
1059    is <em>under development</em>.  The result above might be <em>wrong</em>.</p>
1060    </div>];
1061      push @nav, ['#result-summary' => 'Result'];
1062    } # print_result_section
1063    
1064    sub print_result_unknown_type_section ($$) {
1065      my ($input, $result) = @_;
1066    
1067      my $euri = htescape ($input->{uri});
1068      print STDOUT qq[
1069    <div id="$input->{id_prefix}parse-errors" class="section">
1070    <h2>Errors</h2>
1071    
1072    <dl>
1073    <dt class=unsupported><code>&lt;<a href="$euri">$euri</a>&gt;</code></dt>
1074        <dd class=unsupported><strong><a href="../error-description#level-u">Not
1075            supported</a></strong>:
1076        Media type
1077        <code class="MIME" lang="en">@{[htescape $input->{media_type}]}</code>
1078        is not supported.</dd>
1079    </dl>
1080    </div>
1081    ];
1082      push @nav, [qq[#$input->{id_prefix}parse-errors] => 'Errors']
1083          unless $input->{nested};
1084      add_error (char => {level => 'u'} => $result);
1085      add_error (syntax => {level => 'u'} => $result);
1086      add_error (structure => {level => 'u'} => $result);
1087    } # print_result_unknown_type_section
1088    
1089    sub print_result_input_error_section ($) {
1090      my $input = shift;
1091      print STDOUT qq[<div class="section" id="result-summary">
1092    <p><em><strong>Input Error</strong>: @{[htescape ($input->{error_status_text})]}</em></p>
1093    </div>];
1094      push @nav, ['#result-summary' => 'Result'];
1095    } # print_result_input_error_section
1096    
1097    sub get_error_label ($$) {
1098      my ($input, $err) = @_;
1099    
1100      my $r = '';
1101    
1102      if (defined $err->{line}) {
1103        if ($err->{column} > 0) {
1104          $r = qq[<a href="#$input->{id_prefix}line-$err->{line}">Line $err->{line}</a> column $err->{column}];
1105        } else {
1106          $err->{line} = $err->{line} - 1 || 1;
1107          $r = qq[<a href="#$input->{id_prefix}line-$err->{line}">Line $err->{line}</a>];
1108        }
1109      }
1110    
1111      if (defined $err->{node}) {
1112        $r .= ' ' if length $r;
1113        $r = get_node_link ($input, $err->{node});
1114      }
1115    
1116      if (defined $err->{index}) {
1117        $r .= ' ' if length $r;
1118        $r .= 'Index ' . (0+$err->{index});
1119      }
1120    
1121      if (defined $err->{value}) {
1122        $r .= ' ' if length $r;
1123        $r .= '<q><code>' . htescape ($err->{value}) . '</code></q>';
1124      }
1125    
1126      return $r;
1127    } # get_error_label
1128    
1129    sub get_error_level_label ($) {
1130      my $err = shift;
1131    
1132      my $r = '';
1133    
1134      if (not defined $err->{level} or $err->{level} eq 'm') {
1135        $r = qq[<strong><a href="../error-description#level-m"><em class=rfc2119>MUST</em>‐level
1136            error</a></strong>: ];
1137      } elsif ($err->{level} eq 's') {
1138        $r = qq[<strong><a href="../error-description#level-s"><em class=rfc2119>SHOULD</em>‐level
1139            error</a></strong>: ];
1140      } elsif ($err->{level} eq 'w') {
1141        $r = qq[<strong><a href="../error-description#level-w">Warning</a></strong>:
1142            ];
1143      } elsif ($err->{level} eq 'u' or $err->{level} eq 'unsupported') {
1144        $r = qq[<strong><a href="../error-description#level-u">Not
1145            supported</a></strong>: ];
1146      } else {
1147        my $elevel = htescape ($err->{level});
1148        $r = qq[<strong><a href="../error-description#level-$elevel">$elevel</a></strong>:
1149            ];
1150      }
1151    
1152      return $r;
1153    } # get_error_level_label
1154    
1155  sub get_node_path ($) {  sub get_node_path ($) {
1156    my $node = shift;    my $node = shift;
1157    my @r;    my @r;
# Line 312  sub get_node_path ($) { Line 1167  sub get_node_path ($) {
1167        $rs = '"' . $node->data . '"';        $rs = '"' . $node->data . '"';
1168        $node = $node->parent_node;        $node = $node->parent_node;
1169      } elsif ($node->node_type == 9) {      } elsif ($node->node_type == 9) {
1170          @r = ('') unless @r;
1171        $rs = '';        $rs = '';
1172        $node = $node->parent_node;        $node = $node->parent_node;
1173      } else {      } else {
# Line 323  sub get_node_path ($) { Line 1179  sub get_node_path ($) {
1179    return join '/', @r;    return join '/', @r;
1180  } # get_node_path  } # get_node_path
1181    
1182    sub get_node_link ($$) {
1183      return qq[<a href="#$_[0]->{id_prefix}node-@{[refaddr $_[1]]}">] .
1184          htescape (get_node_path ($_[1])) . qq[</a>];
1185    } # get_node_link
1186    
1187    {
1188      my $Msg = {};
1189    
1190    sub load_text_catalog ($) {
1191      my $lang = shift; # MUST be a canonical lang name
1192      open my $file, '<:utf8', "cc-msg.$lang.txt"
1193          or die "$0: cc-msg.$lang.txt: $!";
1194      while (<$file>) {
1195        if (s/^([^;]+);([^;]*);//) {
1196          my ($type, $cls, $msg) = ($1, $2, $_);
1197          $msg =~ tr/\x0D\x0A//d;
1198          $Msg->{$type} = [$cls, $msg];
1199        }
1200      }
1201    } # load_text_catalog
1202    
1203    sub get_text ($) {
1204      my ($type, $level, $node) = @_;
1205      $type = $level . ':' . $type if defined $level;
1206      $level = 'm' unless defined $level;
1207      my @arg;
1208      {
1209        if (defined $Msg->{$type}) {
1210          my $msg = $Msg->{$type}->[1];
1211          $msg =~ s{<var>\$([0-9]+)</var>}{
1212            defined $arg[$1] ? htescape ($arg[$1]) : '(undef)';
1213          }ge;
1214          $msg =~ s{<var>{\@([A-Za-z0-9:_.-]+)}</var>}{
1215            UNIVERSAL::can ($node, 'get_attribute_ns')
1216                ? htescape ($node->get_attribute_ns (undef, $1)) : ''
1217          }ge;
1218          $msg =~ s{<var>{\@}</var>}{
1219            UNIVERSAL::can ($node, 'value') ? htescape ($node->value) : ''
1220          }ge;
1221          $msg =~ s{<var>{local-name}</var>}{
1222            UNIVERSAL::can ($node, 'manakai_local_name')
1223              ? htescape ($node->manakai_local_name) : ''
1224          }ge;
1225          $msg =~ s{<var>{element-local-name}</var>}{
1226            (UNIVERSAL::can ($node, 'owner_element') and
1227             $node->owner_element)
1228              ? htescape ($node->owner_element->manakai_local_name)
1229              : ''
1230          }ge;
1231          return ($type, 'level-' . $level . ' ' . $Msg->{$type}->[0], $msg);
1232        } elsif ($type =~ s/:([^:]*)$//) {
1233          unshift @arg, $1;
1234          redo;
1235        }
1236      }
1237      return ($type, 'level-'.$level, htescape ($_[0]));
1238    } # get_text
1239    
1240    }
1241    
1242    sub get_input_document ($$) {
1243      my ($http, $dom) = @_;
1244    
1245      my $request_uri = $http->get_parameter ('uri');
1246      my $r = {};
1247      if (defined $request_uri and length $request_uri) {
1248        my $uri = $dom->create_uri_reference ($request_uri);
1249        unless ({
1250                 http => 1,
1251                }->{lc $uri->uri_scheme}) {
1252          return {uri => $request_uri, request_uri => $request_uri,
1253                  error_status_text => 'URI scheme not allowed'};
1254        }
1255    
1256        require Message::Util::HostPermit;
1257        my $host_permit = new Message::Util::HostPermit;
1258        $host_permit->add_rule (<<EOH);
1259    Allow host=suika port=80
1260    Deny host=suika
1261    Allow host=suika.fam.cx port=80
1262    Deny host=suika.fam.cx
1263    Deny host=localhost
1264    Deny host=*.localdomain
1265    Deny ipv4=0.0.0.0/8
1266    Deny ipv4=10.0.0.0/8
1267    Deny ipv4=127.0.0.0/8
1268    Deny ipv4=169.254.0.0/16
1269    Deny ipv4=172.0.0.0/11
1270    Deny ipv4=192.0.2.0/24
1271    Deny ipv4=192.88.99.0/24
1272    Deny ipv4=192.168.0.0/16
1273    Deny ipv4=198.18.0.0/15
1274    Deny ipv4=224.0.0.0/4
1275    Deny ipv4=255.255.255.255/32
1276    Deny ipv6=0::0/0
1277    Allow host=*
1278    EOH
1279        unless ($host_permit->check ($uri->uri_host, $uri->uri_port || 80)) {
1280          return {uri => $request_uri, request_uri => $request_uri,
1281                  error_status_text => 'Connection to the host is forbidden'};
1282        }
1283    
1284        require LWP::UserAgent;
1285        my $ua = WDCC::LWPUA->new;
1286        $ua->{wdcc_dom} = $dom;
1287        $ua->{wdcc_host_permit} = $host_permit;
1288        $ua->agent ('Mozilla'); ## TODO: for now.
1289        $ua->parse_head (0);
1290        $ua->protocols_allowed ([qw/http/]);
1291        $ua->max_size (1000_000);
1292        my $req = HTTP::Request->new (GET => $request_uri);
1293        $req->header ('Accept-Encoding' => 'identity, *; q=0');
1294        my $res = $ua->request ($req);
1295        ## TODO: 401 sets |is_success| true.
1296        if ($res->is_success or $http->get_parameter ('error-page')) {
1297          $r->{base_uri} = $res->base; ## NOTE: It does check |Content-Base|, |Content-Location|, and <base>. ## TODO: Use our own code!
1298          $r->{uri} = $res->request->uri;
1299          $r->{request_uri} = $request_uri;
1300    
1301          ## TODO: More strict parsing...
1302          my $ct = $res->header ('Content-Type');
1303          if (defined $ct and $ct =~ /;\s*charset\s*=\s*"?([^\s;"]+)"?/i) {
1304            $r->{charset} = lc $1;
1305            $r->{charset} =~ tr/\\//d;
1306            $r->{official_charset} = $r->{charset};
1307          }
1308    
1309          my $input_charset = $http->get_parameter ('charset');
1310          if (defined $input_charset and length $input_charset) {
1311            $r->{charset_overridden}
1312                = (not defined $r->{charset} or $r->{charset} ne $input_charset);
1313            $r->{charset} = $input_charset;
1314          }
1315    
1316          ## TODO: Support for HTTP Content-Encoding
1317    
1318          $r->{s} = ''.$res->content;
1319    
1320          require Whatpm::ContentType;
1321          ($r->{official_type}, $r->{media_type})
1322              = Whatpm::ContentType->get_sniffed_type
1323                  (get_file_head => sub {
1324                     return substr $r->{s}, 0, shift;
1325                   },
1326                   http_content_type_byte => $ct,
1327                   has_http_content_encoding =>
1328                       defined $res->header ('Content-Encoding'),
1329                   supported_image_types => {});
1330        } else {
1331          $r->{uri} = $res->request->uri;
1332          $r->{request_uri} = $request_uri;
1333          $r->{error_status_text} = $res->status_line;
1334        }
1335    
1336        $r->{header_field} = [];
1337        $res->scan (sub {
1338          push @{$r->{header_field}}, [$_[0], $_[1]];
1339        });
1340        $r->{header_status_code} = $res->code;
1341        $r->{header_status_text} = $res->message;
1342      } else {
1343        $r->{s} = ''.$http->get_parameter ('s');
1344        $r->{uri} = q<thismessage:/>;
1345        $r->{request_uri} = q<thismessage:/>;
1346        $r->{base_uri} = q<thismessage:/>;
1347        $r->{charset} = ''.$http->get_parameter ('_charset_');
1348        $r->{charset} =~ s/\s+//g;
1349        $r->{charset} = 'utf-8' if $r->{charset} eq '';
1350        $r->{official_charset} = $r->{charset};
1351        $r->{header_field} = [];
1352    
1353        require Whatpm::ContentType;
1354        ($r->{official_type}, $r->{media_type})
1355            = Whatpm::ContentType->get_sniffed_type
1356                (get_file_head => sub {
1357                   return substr $r->{s}, 0, shift;
1358                 },
1359                 http_content_type_byte => undef,
1360                 has_http_content_encoding => 0,
1361                 supported_image_types => {});
1362      }
1363    
1364      my $input_format = $http->get_parameter ('i');
1365      if (defined $input_format and length $input_format) {
1366        $r->{media_type_overridden}
1367            = (not defined $r->{media_type} or $input_format ne $r->{media_type});
1368        $r->{media_type} = $input_format;
1369      }
1370      if (defined $r->{s} and not defined $r->{media_type}) {
1371        $r->{media_type} = 'text/html';
1372        $r->{media_type_overridden} = 1;
1373      }
1374    
1375      if ($r->{media_type} eq 'text/xml') {
1376        unless (defined $r->{charset}) {
1377          $r->{charset} = 'us-ascii';
1378          $r->{official_charset} = $r->{charset};
1379        } elsif ($r->{charset_overridden} and $r->{charset} eq 'us-ascii') {
1380          $r->{charset_overridden} = 0;
1381        }
1382      }
1383    
1384      if (length $r->{s} > 1000_000) {
1385        $r->{error_status_text} = 'Entity-body too large';
1386        delete $r->{s};
1387        return $r;
1388      }
1389    
1390      $r->{inner_html_element} = $http->get_parameter ('e');
1391    
1392      return $r;
1393    } # get_input_document
1394    
1395    package WDCC::LWPUA;
1396    BEGIN { push our @ISA, 'LWP::UserAgent'; }
1397    
1398    sub redirect_ok {
1399      my $ua = shift;
1400      unless ($ua->SUPER::redirect_ok (@_)) {
1401        return 0;
1402      }
1403    
1404      my $uris = $_[1]->header ('Location');
1405      return 0 unless $uris;
1406      my $uri = $ua->{wdcc_dom}->create_uri_reference ($uris);
1407      unless ({
1408               http => 1,
1409              }->{lc $uri->uri_scheme}) {
1410        return 0;
1411      }
1412      unless ($ua->{wdcc_host_permit}->check ($uri->uri_host, $uri->uri_port || 80)) {
1413        return 0;
1414      }
1415      return 1;
1416    } # redirect_ok
1417    
1418  =head1 AUTHOR  =head1 AUTHOR
1419    
1420  Wakaba <w@suika.fam.cx>.  Wakaba <w@suika.fam.cx>.
1421    
1422  =head1 LICENSE  =head1 LICENSE
1423    
1424  Copyright 2007 Wakaba <w@suika.fam.cx>  Copyright 2007-2008 Wakaba <w@suika.fam.cx>
1425    
1426  This library is free software; you can redistribute it  This library is free software; you can redistribute it
1427  and/or modify it under the same terms as Perl itself.  and/or modify it under the same terms as Perl itself.

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.35

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24