File Coverage

blib/lib/HTML/ParseBrowser.pm
Criterion Covered Total %
statement 169 183 92.3
branch 97 116 83.6
condition 27 47 57.4
subroutine 7 8 87.5
pod 2 2 100.0
total 302 356 84.8


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