File Coverage

blib/lib/Mojo/DOM/Role/Restrict.pm
Criterion Covered Total %
statement 80 86 93.0
branch 45 66 68.1
condition 29 30 96.6
subroutine 15 15 100.0
pod 3 5 60.0
total 172 202 85.1


line stmt bran cond sub pod time code
1             package Mojo::DOM::Role::Restrict;
2 9     9   78387 use strict; use warnings; our $VERSION = 0.03;
  9     9   30  
  9         287  
  9         49  
  9         22  
  9         528  
3 9     9   605 use Mojo::Base -role;
  9         406895  
  9         69  
4 9     9   4648 use Mojo::Util qw(xml_escape);
  9         19  
  9         7995  
5              
6 26 100   26 0 10994 sub to_string { $_[1] ? ${$_[0]}->render : $_[0]->render; }
  11         53  
7              
8 15     15 0 57 sub render { _render($_[0]->tree, $_[0]->xml, $_[0]->restrict_spec) }
9              
10             around parse => sub {
11             my ($orig, $self) = (shift, shift);
12             $self->restrict_spec($_[1] || $self->restrict_spec || {
13             '*' => { '*' => 1 }
14             });
15             return $self->$orig(@_);
16             };
17              
18             sub restrict_spec {
19 70 100   70 1 633 if ( $_[1] ) {
20 33   100     52 $_[1]->{$_} && ! ref $_[1]->{$_} && do { $_[1]->{$_} = { '*' => 1 } } for (keys %{$_[1]});
  33   66     347  
  24         90  
21 33         81 ${$_[0]}->{restrict_spec} = $_[1];
  33         630  
22             }
23 70         114 ${$_[0]}->{restrict_spec};
  70         199  
24             }
25              
26 9     9 1 4850 sub valid { _valid($_[0]->tree, $_[0]->restrict_spec($_[1])) }
27              
28 12 50   12 1 9418 sub restrict { _restrict($_[0]->tree, $_[0]->restrict_spec($_[1])) && $_[0] }
29              
30             # copy, paste and edit via Mojo::DOM::HTML::_render
31              
32             my %EMPTY = map { $_ => 1 } qw(area base br col embed hr img input keygen link menuitem meta param source track wbr);
33              
34             sub _render {
35 143     143   260 my ($tree, $xml, $spec) = @_;
36            
37             # Tag
38 143         229 my $type = $tree->[0];
39 143 100       289 if ($type eq 'tag') {
40              
41             # Start tag
42 90         125 my ($tag, $attrs) = _valid_tag($spec, $tree->[1], {%{$tree->[2]}});
  90         276  
43            
44 90 100       302 return '' unless $tag;
45            
46 76         147 my $result = "<$tag";
47              
48             # Attributes
49 76         106 for (sort keys %{$attrs}) {
  76         227  
50 78         428 my ($key, $value) = _valid_attribute($spec, $tag, $_, $attrs->{$_});
51 78 0       446 $result .= defined $value
    50          
    100          
52             ? qq{ $key="} . xml_escape($value) . '"'
53             : $xml
54             ? qq{ $key="$key"}
55             : " $key"
56             if $key;
57             }
58              
59             # No children
60 76 0       236 return $xml ? "$result />" : $EMPTY{$tag} ? "$result>" : "$result>" unless $tree->[4];
    0          
    50          
61              
62             # Children
63 9     9   78 no warnings 'recursion';
  9         22  
  9         10107  
64 76         187 $result .= '>' . join '', map { _render($_, $xml, $spec) } @$tree[4 .. $#$tree];
  113         441  
65              
66             # End tag
67 76         571 return "$result";
68             }
69              
70             # Text (escaped)
71 53 100       142 return xml_escape $tree->[1] if $type eq 'text';
72              
73             # Raw text
74 17 100       40 return $tree->[1] if $type eq 'raw';
75              
76             # Root
77 15 50       137 return join '', map { _render($_, $xml, $spec) } @$tree[1 .. $#$tree] if $type eq 'root';
  15         66  
78              
79             # DOCTYPE
80 0 0       0 return '[1] . '>' if $type eq 'doctype';
81              
82             # Comment
83 0 0       0 return '' if $type eq 'comment';
84              
85             # CDATA
86 0 0       0 return '[1] . ']]>' if $type eq 'cdata';
87              
88             # Processing instruction
89 0 0       0 return '[1] . '?>' if $type eq 'pi';
90              
91             # Everything else
92 0         0 return '';
93             }
94              
95             sub _valid_tag {
96 204     204   378 my ($spec, $tag, $attrs) = @_;
97 204   100     712 my $valid = $spec->{$tag} // $spec->{'*'};
98             return ref $valid && $valid->{validate_tag}
99 204 100 100     979 ? $valid->{validate_tag}($tag, $attrs)
    100          
100             : $valid
101             ? ($tag, $attrs)
102             : 0;
103             }
104              
105             sub _valid_attribute {
106 154     154   347 my ($spec, $tag, $attr, $value) = @_;
107 154   100     846 my $valid = $spec->{$tag}->{$attr} // $spec->{$tag}->{'*'} // $spec->{'*'}->{$attr} // $spec->{'*'}->{'*'};
      100        
      100        
108 154 100 100     926 return ref $valid
    100          
109             ? $valid->($attr, $value)
110             : ($valid and $valid =~ m/1/ || $value =~ m/$valid/)
111             ? ( $attr, $value )
112             : 0;
113             }
114              
115             sub _valid {
116 58     58   108 my ($tree, $spec) = @_;
117 58 100       136 if ($tree->[0] eq 'tag') {
    100          
118 38         56 my ($tag, $attrs) = _valid_tag($spec, $tree->[1], {%{$tree->[2]}});
  38         99  
119 38 100       106 return 0 unless $tag;
120             _valid_attribute($spec, $tag, $_, $attrs->{$_}) or return 0
121 34   100     45 for (sort keys %{$attrs});
  34         97  
122 32 50       74 if ($tree->[4]) {
123 32   100     109 _valid($_, $spec) or return 0 for ( @$tree[4 .. $#$tree] );
124             }
125             } elsif ($tree->[0] eq 'root') {
126 9   100     39 _valid($_, $spec) or return 0 for ( @$tree[1 .. $#$tree] );
127             }
128 36         143 return 1;
129             }
130              
131             sub _restrict {
132 120     120   222 my ($tree, $spec) = @_;
133 120 100       277 if ($tree->[0] eq 'tag') {
    100          
134 76         142 my ($tag, $attrs) = _valid_tag($spec, $tree->[1], $tree->[2]);
135 76 100       232 return 0 unless $tag;
136 64         140 $tree->[1] = $tag;
137 64         88 for (sort keys %{$attrs}) {
  64         222  
138 64         166 my ($key, $value) = _valid_attribute($spec, $tag, $_, delete $attrs->{$_});
139 64 100       338 $attrs->{$key} = $value if $key;
140             }
141 64 50       144 if ($tree->[4]) {
142 64         87 my $i = 4;
143 12         64 _restrict($_, $spec) ? $i++ : splice(@{$tree}, $i, 1)
144 64 100       234 for ( @$tree[$i .. $#$tree] );
145             }
146             } elsif ($tree->[0] eq 'root') {
147 12         23 my $i = 1;
148 0         0 _restrict($_, $spec) ? $i++ : splice(@{$tree}, $i, 1)
149 12 50       67 for ( @$tree[$i .. $#$tree] );
150             }
151 108         260 return 1;
152             }
153              
154             1;
155              
156             # TODO diff
157              
158             __END__