File Coverage

blib/lib/HTML/ParseBrowser.pm
Criterion Covered Total %
statement 169 183 92.3
branch 97 116 83.6
condition 26 47 55.3
subroutine 7 8 87.5
pod 2 2 100.0
total 301 356 84.5


line stmt bran cond sub pod time code
1             package HTML::ParseBrowser;
2             $HTML::ParseBrowser::VERSION = '1.13';
3 1     1   572 use 5.006;
  1         3  
4 1     1   6 use strict;
  1         2  
  1         23  
5 1     1   4 use warnings;
  1         7  
  1         29  
6              
7 1     1   2 use vars qw($AUTOLOAD);
  1         1  
  1         1746  
8              
9             my %lang =
10             (
11             'en' => 'English',
12             'de' => 'German',
13             'fr' => 'French',
14             'es' => 'Spanish',
15             'it' => 'Italian',
16             'da' => 'Danish',
17             'ja' => 'Japanese',
18             'ru' => 'Russian',
19             );
20             my $langRE = join('|', keys %lang);
21              
22             my %name_map =
23             (
24             'Mozilla' => 'Netscape',
25             'Gecko' => 'Mozilla',
26             'Netscape6' => 'Netscape',
27             'MSIE' => 'Internet Explorer',
28             );
29              
30             sub new {
31 1     1 1 374 my $class = shift;
32 1         1 my $browser = {};
33 1   33     7 bless $browser, ref $class || $class;
34 1         2 $browser->Parse(shift);
35 1         6 return $browser;
36             }
37              
38             sub Parse {
39 47     47 1 46700 my $browser = shift;
40 47         50 my $ua_string = shift;
41 47         53 my $useragent = $ua_string;
42 47         35 my $version;
43 47         35 delete $browser->{$_} for keys %{$browser};
  47         394  
44 47 100       108 return undef unless $useragent;
45 46 50       85 return undef if $useragent eq '-';
46 46         66 $browser->{user_agent} = $useragent;
47 46         233 $useragent =~ s/Opera (?=\d)/Opera\//i;
48              
49 46         126 while ($useragent =~ s/\[(\w+)\]//) {
50 2   33     2 push @{$browser->{languages}}, $lang{$1} || $1;
  2         10  
51 2         3 push @{$browser->{langs}}, $1;
  2         7  
52             }
53              
54 46         217 while ($useragent =~ /\((.*?)\)/) {
55 73 100       175 $browser->{detail} .= '; ' if defined($browser->{detail});
56 73         166 $browser->{detail} .= $1;
57 73         402 $useragent =~ s/\((.*?)\)//;
58             }
59 46 100       90 if (defined($browser->{detail})) {
60 45         293 $browser->{properties} = [split /;\s+/, $browser->{detail}];
61             }
62              
63 46         280 $browser->{useragents} = [grep /\//, split /\s+/, $useragent];
64              
65 46 100       343 if ($ua_string =~ /(iPhone|iPad|iPod).*?OS\s+(\d+_\d(_\d)?)/) {
    100          
    100          
    100          
    100          
    100          
66 7         13 $browser->{name} = 'Safari';
67 7         11 $browser->{os} = $browser->{ostype} = 'iOS';
68 7         27 ($browser->{osvers} = $2) =~ s/_/./g;
69 7 50       48 if ($useragent =~ m!(Version|CriOS)/((\d+)(\.(\d+)[\.0-9]*)?)!) {
70 7 100       27 if ($1 eq 'CriOS') {
71 1         3 $browser->{name} = 'Chrome';
72             }
73 7         15 $browser->{version}->{v} = $2;
74 7         12 $browser->{version}->{major} = $3;
75 7 50 33     42 $browser->{version}->{minor} = $5 if defined($5) && $5 ne '';
76             }
77             }
78             elsif ($ua_string =~ m!\((BlackBerry|BB10).*Version/([0-9\.]+)!) {
79 2         5 my $version_string = $2;
80 2         4 $browser->{name} = $browser->{ostype} = 'BlackBerry';
81 2         5 $browser->{version}->{v} = $version_string;
82 2 50       8 if ($version_string =~ m!^([0-9]+)(\.([0-9]+).*)?!) {
83 2         7 $browser->{version}->{major} = $browser->{osvers} = $1;
84 2         7 $browser->{os} = "BlackBerry $1";
85 2 50 33     16 $browser->{version}->{minor} = $3 if defined($3) && $3 ne '';
86             }
87             }
88             elsif ($ua_string =~ m!Mozilla/5.0 \(.*?Windows.*?; rv:((\d+)\.(\d+))\) like Gecko!) {
89 1         3 $browser->{name} = 'MSIE';
90 1         3 $browser->{version}->{v} = $1;
91 1         2 $browser->{version}->{major} = $2;
92 1         2 $browser->{version}->{minor} = $3;
93             } elsif ($useragent =~ m!OPR/((\d+)\.(\d+)\.\d+\.\d+)!) {
94 1         2 $browser->{name} = 'Opera';
95 1         3 $browser->{version}->{v} = $1;
96 1         3 $browser->{version}->{major} = $2;
97 1         2 $browser->{version}->{minor} = $3;
98             } elsif ($useragent =~ m!\bVersion/((\d+)\.(\d+)\S*) Safari/!) {
99 4         9 $browser->{name} = 'Safari';
100 4         10 $browser->{version}->{v} = $1;
101 4         10 $browser->{version}->{major} = $2;
102 4         7 $browser->{version}->{minor} = $3;
103             } elsif ($useragent =~ m!Opera/.*Version/((\d+)\.(\d+)\S*)$!) {
104 2         4 $browser->{name} = 'Opera';
105 2         5 $browser->{version}->{v} = $1;
106 2         4 $browser->{version}->{major} = $2;
107 2         4 $browser->{version}->{minor} = $3;
108             } else {
109 29         52 my $seenchrome = 0;
110 29         29 for (@{$browser->{useragents}}) {
  29         57  
111 71         141 my ($br, $ver) = split /\//;
112 71 50       126 $br = 'Chrome' if $br eq 'CriOS';
113 71 100 66     125 if ($br ne 'Safari' || not $seenchrome) {
114 66         68 $browser->{name} = $br;
115 66         80 $browser->{version}->{v} = $ver;
116 66 100       170 if ($ver =~ m!^v?(\d+)\.(\d+)!) {
117 58         146 ($browser->{version}->{major}, $browser->{version}->{minor}) = ($1, $2);
118             }
119             }
120 71 100       123 $seenchrome = 1 if lc($br) eq 'chrome';
121 71 100       95 last if lc($br) eq 'iron';
122 70 100       97 last if lc($br) eq 'lynx';
123             # last if lc($br) eq 'chrome';
124 69 100       120 last if lc($br) eq 'opera';
125             }
126             }
127              
128 46         45 for (@{$browser->{properties}}) {
  46         93  
129 204 100       349 /compatible/i and next;
130              
131 195 100 100     791 unless (defined($browser->{name}) && (lc($browser->{name}) eq 'webtv' || lc($browser->{name}) eq 'opera')) {
      33        
132 176 100       242 /^MSIE (.*)$/ and do {
133 6         12 $browser->{name} = 'MSIE';
134 6         11 $browser->{version}->{v} = $1;
135             ($browser->{version}->{major},
136 6         26 $browser->{version}->{minor}) = split /\./, $1, 2;
137             };
138             }
139              
140 195 50       243 m!^Edge/(([0-9]+)\.([0-9]+))! and do {
141 0         0 $browser->{name} = 'Edge';
142 0         0 $browser->{version}->{v} = $1;
143 0         0 $browser->{version}->{major} = $2;
144 0         0 $browser->{version}->{minor} = $3;
145             };
146              
147 195 100       226 if (m!^AOL ([0-9].*)!) {
148 1         3 $browser->{name} = 'AOL';
149 1         2 $browser->{version}->{v} = $1;
150 1         6 ($browser->{version}->{major}, $browser->{version}->{minor}) = split /\./, $browser->{version}->{v};
151             }
152              
153 195 100       217 /^Konqueror\/([-0-9.a-z]+)/ and do {
154 1         2 $browser->{name} = 'Konqueror';
155 1         3 $browser->{version}->{v} = $1;
156 1         6 ($browser->{version}->{major}, $browser->{version}->{minor}) = split /\./, $browser->{version}->{v};
157             };
158              
159 195 0 33     259 /\bCamino\/([0-9.]+)/ and do {
160 0         0 $browser->{name} = 'Camino';
161 0         0 $browser->{version}->{v} = $1;
162 0         0 ($browser->{version}->{major}, $browser->{version}->{minor}) = split /\./, $browser->{version}->{v}, 2;
163             } and last;
164              
165 195 100       212 if (m!^Opera Mini/([0-9.]+)!) {
166 1         3 $browser->{name} = 'Opera Mini';
167 1         2 $browser->{version}->{v} = $1;
168 1         6 ($browser->{version}->{major}, $browser->{version}->{minor}) = split /\./, $browser->{version}->{v};
169             }
170              
171 195 100       267 if (/^Win/) {
172 24         35 $browser->{os} = $_;
173 24 100       66 $browser->{ostype} = 'Windows' . (/phone/i ? ' Phone' : '');
174 24 100 66     137 if (/Windows NT\s*((\d+)(\.\d+)?)/ || /^WinNT((\d+)(\.\d+)?)/) {
    100          
    50          
175 18         24 $browser->{ostype} = 'Windows NT';
176 18         22 $version = $1;
177 18 100 66     124 if ($version >= 10) {
    100          
    100          
    100          
    50          
    100          
    50          
    0          
178 1         3 $browser->{osvers} = '10';
179             }
180             elsif ($version >= 6.3 && $version < 7) {
181 1         2 $browser->{osvers} = '8.1';
182             } elsif ($version >= 6.2) {
183 1         3 $browser->{osvers} = '8';
184             } elsif ($version >= 6.1) {
185 5         9 $browser->{osvers} = '7';
186             } elsif ($version >= 6.06) {
187 0         0 $browser->{osvers} = 'Server 2008';
188             } elsif ($version >= 6.0) {
189 5         8 $browser->{osvers} = 'Vista';
190             } elsif ($version >= 5.1) {
191 5         9 $browser->{osvers} = 'XP';
192             } elsif ($version >= 5.0) {
193 0         0 $browser->{osvers} = '2000';
194             } else {
195 0         0 $browser->{osvers} = $version;
196             }
197             }
198             elsif (/Windows (?:Phone )?(\d+(\.\d+)?)/) {
199 1         2 $browser->{osvers} = $1;
200             } elsif (/Win(\w\w)/i) {
201 5         9 $browser->{osvers} = $1;
202             }
203             }
204              
205 195 100       254 if (/^Mac/) {
206 6         11 $browser->{os} = $_;
207 6         7 $browser->{ostype} = 'Macintosh';
208 6         18 (undef, $browser->{osvers}) = split /[ _]/, $_, 2;
209             }
210              
211 195 50       235 if (/^PPC$/) {
212 0         0 $browser->{osarc} = 'PPC';
213             }
214              
215             # TODO: parsing of version and osarc doesn't always get it right. See Danish Opera test
216 195 100 100     434 if (/Android\s([\.0-9]+)/ && lc($browser->{name}) ne 'edge') {
    100          
217 3         4 $browser->{os} = 'Android';
218 3         5 $browser->{ostype} = 'Linux';
219 3         5 $browser->{osvers} = $1;
220             } elsif (/^Linux/) {
221 8         10 my $lstr = $_;
222 8         16 $browser->{os} = 'Linux';
223 8         11 $browser->{ostype} = 'Linux';
224 8 100       50 if ($lstr =~ s/(i386|mips|amd64|sparc64|ppc|i686|i586|armv51|x86|x86-64|x86_64|ppc64|x64|x64_64)\b//) {
225 4         9 $browser->{osarc} = $1;
226             }
227 8 50       19 if ($lstr =~ / (\d+\.\S+)/) {
228 0         0 $browser->{osvers} = $1;
229             }
230             }
231              
232 195 100       314 if (/^(SunOS|Solaris)/i) {
233 1         3 $browser->{os} = $_;
234 1         2 $browser->{ostype} = 'Solaris';
235 1 50       7 if (/(sun4[a-z]|i86pc)/) {
236 1         3 $browser->{osarc} = $1;
237             }
238 1 50       6 if (/^SunOS\s*([0-9\.]+)/) {
239 1         2 $browser->{osvers} = $1;
240             }
241             }
242              
243 195 100 100     1113 if (/^($langRE)-/ || /^($langRE)$/) {
244 21         39 my $langCode = $1;
245 21         16 push(@{$browser->{languages}}, $lang{$langCode});
  21         77  
246 21         17 push(@{$browser->{langs}}, $langCode);
  21         43  
247             }
248             }
249              
250 46 100 66     168 if (defined($browser->{name}) && exists $name_map{ $browser->{name} }) {
251 7         15 $browser->{name} = $name_map{ $browser->{name} };
252             }
253              
254 46   33     59 $browser->{name} ||= $useragent;
255              
256 46 100       82 if ($browser->{name} eq 'Konqueror') {
257 1   50     3 $browser->{ostype} ||= 'Linux';
258             }
259              
260 46         31 my %langs_in;
261              
262 46         29 for (@{$browser->{langs}}) {
  46         85  
263 23         48 $langs_in{$_}++;
264             }
265              
266 46 100       81 if (int(keys %langs_in) > 0) {
267 23         80 ($browser->{lang}) = sort {$langs_in{$a} <=> $langs_in{$b}} keys %langs_in;
  0         0  
268 23   33     59 $browser->{language} = $lang{$browser->{lang}} || $browser->{lang};
269             # delete $browser->{language} unless $browser->{language};
270             }
271 46         93 return $browser;
272             }
273              
274       0     sub DESTROY {
275             }
276              
277             sub AUTOLOAD {
278 246     246   546 my $self = shift;
279 246         424 my $method = lc($AUTOLOAD);
280 246         928 $method =~ s/^.*\:\://;
281              
282 246 100       593 if (exists($self->{$method})) {
    50          
283 169         495 return $self->{$method};
284             } elsif (exists($self->{version}->{$method})) {
285 77         232 return $self->{version}->{$method};
286             }
287              
288 0           return undef;
289             }
290              
291             __END__