| blib/lib/Plack/Middleware/HTMLLint.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 25 | 27 | 92.5 |
| branch | n/a | ||
| condition | n/a | ||
| subroutine | 9 | 9 | 100.0 |
| pod | n/a | ||
| total | 34 | 36 | 94.4 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package Plack::Middleware::HTMLLint; | ||||||
| 2 | 2 | 2 | 17223 | use 5.008_001; | |||
| 2 | 5 | ||||||
| 2 | 63 | ||||||
| 3 | 2 | 2 | 8 | use strict; | |||
| 2 | 2 | ||||||
| 2 | 121 | ||||||
| 4 | 2 | 2 | 10 | use warnings; | |||
| 2 | 5 | ||||||
| 2 | 103 | ||||||
| 5 | |||||||
| 6 | our $VERSION = '0.03'; | ||||||
| 7 | |||||||
| 8 | 2 | 2 | 472 | use parent qw/ Plack::Middleware /; | |||
| 2 | 259 | ||||||
| 2 | 12 | ||||||
| 9 | |||||||
| 10 | use constant +{ | ||||||
| 11 | 2 | 197 | PSGI_STATUS => 0, | ||||
| 12 | PSGI_HEADER => 1, | ||||||
| 13 | PSGI_BODY => 2, | ||||||
| 14 | 2 | 2 | 13749 | }; | |||
| 2 | 4 | ||||||
| 15 | |||||||
| 16 | use constant +{ | ||||||
| 17 | 2 | 124 | SYNTAX_HTML5 => 'html5', | ||||
| 18 | SYNTAX_HTML4 => 'html4', | ||||||
| 19 | SYNTAX_XHTML => 'xhtml', | ||||||
| 20 | 2 | 2 | 12 | }; | |||
| 2 | 2 | ||||||
| 21 | |||||||
| 22 | 2 | 2 | 10 | use Plack::Util; | |||
| 2 | 2 | ||||||
| 2 | 46 | ||||||
| 23 | 2 | 2 | 9 | use Plack::Util::Accessor qw/error2html/; | |||
| 2 | 3 | ||||||
| 2 | 11 | ||||||
| 24 | 2 | 2 | 638 | use HTML::Lint; | |||
| 0 | |||||||
| 0 | |||||||
| 25 | use HTML::Escape qw/escape_html/; | ||||||
| 26 | |||||||
| 27 | sub prepare_app { | ||||||
| 28 | my $self = shift; | ||||||
| 29 | unless ($self->error2html) { | ||||||
| 30 | $self->error2html(sub { | ||||||
| 31 | my @errors = @_; | ||||||
| 32 | |||||||
| 33 | my @error_html; | ||||||
| 34 | push @error_html => ' '; |
||||||
| 35 | push @error_html => 'HTML Error'; |
||||||
| 36 | push @error_html => '
|
||||||
| 37 | foreach my $error (@errors) { | ||||||
| 38 | push @error_html => ' |
||||||
| 39 | push @error_html => ' |
||||||
| 40 | } | ||||||
| 41 | push @error_html => ''; | ||||||
| 42 | |||||||
| 43 | push @error_html => ''; | ||||||
| 44 | |||||||
| 45 | return join '', @error_html; | ||||||
| 46 | }); | ||||||
| 47 | } | ||||||
| 48 | } | ||||||
| 49 | |||||||
| 50 | sub call { | ||||||
| 51 | my($self, $env) = @_; | ||||||
| 52 | |||||||
| 53 | return $self->response_cb($self->app->($env), sub { | ||||||
| 54 | my $res = shift; | ||||||
| 55 | my $content_type = Plack::Util::header_get($res->[PSGI_HEADER], 'Content-Type') || ''; | ||||||
| 56 | |||||||
| 57 | if ($content_type =~ m{^(?:text/x?html|application/xhtml\+xml)\b}io) {# HTML/XHTML | ||||||
| 58 | my $do_lint = sub { | ||||||
| 59 | my $content = shift; | ||||||
| 60 | |||||||
| 61 | my $syntax = ($content =~ /^$/imo) ? SYNTAX_HTML5: | ||||||
| 62 | ($content_type =~ m{^(?:text/xhtml|application/xhtml\+xml)\b}io) ? SYNTAX_XHTML: | ||||||
| 63 | SYNTAX_HTML4; | ||||||
| 64 | |||||||
| 65 | if (my @errors = $self->html_lint($syntax => $content)) { | ||||||
| 66 | return $self->error2html->(@errors); | ||||||
| 67 | } | ||||||
| 68 | else { | ||||||
| 69 | return ''; | ||||||
| 70 | } | ||||||
| 71 | }; | ||||||
| 72 | |||||||
| 73 | if ($res->[PSGI_BODY]) { | ||||||
| 74 | my $content = ''; | ||||||
| 75 | Plack::Util::foreach($res->[PSGI_BODY] => sub { $content .= $_[0] }); | ||||||
| 76 | if (my $error_html = $do_lint->($content)) { | ||||||
| 77 | unless ($content =~ s{]*)>}{$error_html}i) { | ||||||
| 78 | ## fallback | ||||||
| 79 | $content .= $error_html; | ||||||
| 80 | } | ||||||
| 81 | $res->[PSGI_BODY] = [$content]; | ||||||
| 82 | } | ||||||
| 83 | } | ||||||
| 84 | else { | ||||||
| 85 | # XXX: It has become increasingly complex not to block the stream as possible. | ||||||
| 86 | my $buffer = ''; | ||||||
| 87 | my $html_last_buffer = ''; | ||||||
| 88 | my $end_of_html_body = 0; | ||||||
| 89 | my $do_lint_finished = 0; | ||||||
| 90 | return sub { | ||||||
| 91 | my $body_chunk = shift; | ||||||
| 92 | if (defined $body_chunk) { | ||||||
| 93 | $buffer .= $body_chunk; | ||||||
| 94 | if ($end_of_html_body || $body_chunk =~ m{}io) { | ||||||
| 95 | $end_of_html_body = 1; | ||||||
| 96 | $html_last_buffer .= $body_chunk; | ||||||
| 97 | return ''; | ||||||
| 98 | } | ||||||
| 99 | else { | ||||||
| 100 | return $body_chunk; | ||||||
| 101 | } | ||||||
| 102 | } | ||||||
| 103 | else { | ||||||
| 104 | if ($do_lint_finished) { | ||||||
| 105 | return; | ||||||
| 106 | } | ||||||
| 107 | else { | ||||||
| 108 | my $error_html = $do_lint->($buffer); | ||||||
| 109 | if ($error_html) { | ||||||
| 110 | unless ($html_last_buffer =~ s{}{$error_html |
}i) {