File Coverage

blib/lib/HTTP/BrowserDetect.pm
Criterion Covered Total %
statement 946 1043 90.7
branch 788 898 87.7
condition 328 411 79.8
subroutine 76 79 96.2
pod 46 47 97.8
total 2184 2478 88.1


line stmt bran cond sub pod time code
1 5     5   1362668 use strict;
  5         17  
  5         203  
2 5     5   27 use warnings;
  5         8  
  5         308  
3              
4 5     5   92 use 5.006;
  5         17  
5              
6             package HTTP::BrowserDetect;
7              
8             our $VERSION = '3.45';
9              
10             # Operating Systems
11             our @OS_TESTS = qw(
12             windows mac os2
13             unix linux vms
14             bsd amiga brew
15             bb10 rimtabletos
16             chromeos ios
17             firefoxos
18             );
19              
20             # More precise Windows
21             our @WINDOWS_TESTS = qw(
22             win16 win3x win31
23             win95 win98 winnt
24             winme win32 win2k
25             winxp win2k3 winvista
26             win7 win8 win8_0
27             win8_1 win10 win10_0
28             wince winphone winphone7
29             winphone7_5 winphone8 winphone8_1
30             winphone10
31             );
32              
33             # More precise Mac
34             our @MAC_TESTS = qw(
35             macosx mac68k macppc
36             );
37              
38             # More precise Unix
39             our @UNIX_TESTS = qw(
40             sun sun4 sun5
41             suni86 irix irix5
42             irix6 hpux hpux9
43             hpux10 aix aix1
44             aix2 aix3 aix4
45             sco unixware mpras
46             reliant dec sinix
47             );
48              
49             # More precise BSDs
50             our @BSD_TESTS = qw(
51             freebsd
52             );
53              
54             # Gaming devices
55             our @GAMING_TESTS = qw(
56             ps3gameos pspgameos
57             );
58              
59             # Device related tests
60             our @DEVICE_TESTS = qw(
61             android audrey blackberry dsi iopener ipad
62             iphone ipod kindle kindlefire n3ds palm ps3 psp wap webos
63             mobile tablet
64             );
65              
66             # Browsers
67             our @BROWSER_TESTS = qw(
68             mosaic netscape firefox
69             chrome safari ie
70             opera lynx links
71             elinks neoplanet neoplanet2
72             avantgo emacs mozilla
73             konqueror realplayer netfront
74             mobile_safari obigo aol
75             lotusnotes staroffice icab
76             webtv browsex silk
77             applecoremedia galeon seamonkey
78             epiphany ucbrowser dalvik
79             edge pubsub adm
80             brave imagesearcherpro polaris
81             edgelegacy samsung
82             instagram
83             yandex_browser
84             );
85              
86             our @IE_TESTS = qw(
87             ie3 ie4 ie4up
88             ie5 ie5up ie55
89             ie55up ie6 ie7
90             ie8 ie9 ie10
91             ie11
92             ie_compat_mode
93             );
94              
95             our @OPERA_TESTS = qw(
96             opera3 opera4 opera5
97             opera6 opera7
98             );
99              
100             our @AOL_TESTS = qw(
101             aol3 aol4
102             aol5 aol6
103             );
104              
105             our @NETSCAPE_TESTS = qw(
106             nav2 nav3 nav4
107             nav4up nav45 nav45up
108             nav6 nav6up navgold
109             );
110              
111             # Firefox variants
112             our @FIREFOX_TESTS = qw(
113             firebird iceweasel phoenix
114             namoroka
115             fxios
116             );
117              
118             # Engine tests
119             our @ENGINE_TESTS = qw(
120             gecko trident webkit
121             presto khtml edgehtml
122             blink
123             );
124              
125             # These bot names get turned into methods. Crazy, right? (I don't even think
126             # this is documented anymore.) We'll leave this in place for back compat, but
127             # we won't add any new methods moving forward.
128              
129             my @OLD_ROBOT_TESTS = qw(
130             ahrefs
131             altavista
132             amazonbot
133             apache
134             askjeeves
135             baidu
136             bingbot
137             curl
138             facebook
139             getright
140             golib
141             google
142             googleadsbot
143             googleadsense
144             googlebotimage
145             googlebotnews
146             googlebotvideo
147             googlefavicon
148             googlemobile
149             indy
150             infoseek
151             ipsagent
152             java
153             linkchecker
154             linkexchange
155             lwp
156             lycos
157             malware
158             mj12bot
159             msn
160             msnmobile
161             msoffice
162             nutch
163             phplib
164             puf
165             pythonurllib
166             rubylib
167             slurp
168             specialarchiver
169             wget
170             yahoo
171             yandex
172             yandeximages
173             headlesschrome
174             );
175              
176             our @ROBOT_TESTS = (
177             [ 'Applebot', 'apple' ],
178             [ 'baiduspider', 'baidu' ],
179             [ 'bitlybot', 'bitly' ],
180             [ 'developers.google.com//web/snippet', 'google-plus-snippet' ],
181             [ 'Discordbot', 'discordbot' ],
182             [ 'embedly', 'embedly' ],
183             [ 'facebookexternalhit', 'facebook' ],
184             [ 'flipboard', 'flipboard' ],
185             [ 'Google Page Speed', 'google-page-speed' ],
186             [ 'ltx71', 'ltx71' ],
187             [ 'linkedinbot', 'linkedin' ],
188             [ 'nuzzel', 'nuzzel' ],
189             [ 'outbrain', 'outbrain' ],
190             [ 'pinterest/0.', 'pinterest' ],
191             [ 'pinterestbot', 'pinterest' ],
192             [ 'Pro-Sitemaps', 'pro-sitemaps' ],
193             [ 'quora link preview', 'quora-link-preview' ],
194             [ 'Qwantify', 'qwantify' ],
195             [ 'redditbot', 'reddit', ],
196             [ 'researchscan', 'researchscan' ],
197             [ 'rogerbot', 'rogerbot' ],
198             [ 'ShowyouBot', 'showyou' ],
199             [ 'SkypeUriPreview', 'skype-uri-preview' ],
200             [ 'slackbot', 'slack' ],
201             [ 'Swiftbot', 'swiftbot' ],
202             [ 'tumblr', 'tumblr' ],
203             [ 'twitterbot', 'twitter' ],
204             [ 'vkShare', 'vkshare' ],
205             [ 'W3C_Validator', 'w3c-validator' ],
206             [ 'WhatsApp', 'whatsapp' ],
207             [ 'Amazonbot', 'amazonbot' ],
208             );
209              
210             our @MISC_TESTS = qw(
211             dotnet x11
212             webview
213             meta_app
214             );
215              
216             our @ALL_TESTS = (
217             @OS_TESTS, @WINDOWS_TESTS,
218             @MAC_TESTS, @UNIX_TESTS,
219             @BSD_TESTS, @GAMING_TESTS,
220             @DEVICE_TESTS, @BROWSER_TESTS,
221             @IE_TESTS, @OPERA_TESTS,
222             @AOL_TESTS, @NETSCAPE_TESTS,
223             @FIREFOX_TESTS, @ENGINE_TESTS,
224             @OLD_ROBOT_TESTS, @MISC_TESTS,
225             );
226              
227             # https://support.google.com/webmasters/answer/1061943?hl=en
228              
229             my %ROBOT_NAMES = (
230             ahrefs => 'Ahrefs',
231             altavista => 'AltaVista',
232             amazonbot => 'Amazonbot',
233             'apache-http-client' => 'Apache HttpClient',
234             apple => 'Apple',
235             'archive-org' => 'Internet Archive',
236             askjeeves => 'AskJeeves',
237             baidu => 'Baidu',
238             baiduspider => 'Baidu Spider',
239             bingbot => 'Bingbot',
240             bitly => 'Bitly',
241             curl => 'curl',
242             discordbot => 'Discord',
243             embedly => 'Embedly',
244             facebook => 'Facebook',
245             facebookexternalhit => 'Facebook',
246             flipboard => 'Flipboard',
247             getright => 'GetRight',
248             golib => 'Go language http library',
249             google => 'Googlebot',
250             'google-adsbot' => 'Google AdsBot',
251             'google-adsense' => 'Google AdSense',
252             'googlebot' => 'Googlebot',
253             'googlebot-image' => 'Googlebot Images',
254             'googlebot-mobile' => 'Googlebot Mobile',
255             'googlebot-news' => 'Googlebot News',
256             'googlebot-video' => 'Googlebot Video',
257             'google-favicon' => 'Google Favicon',
258             'google-mobile' => 'Googlebot Mobile',
259             'google-page-speed' => 'Google Page Speed',
260             'google-plus-snippet' => 'Google+ Snippet',
261             'indy-library' => 'Indy Library',
262             infoseek => 'InfoSeek',
263             java => 'Java',
264             'libwww-perl' => 'LWP::UserAgent',
265             linkchecker => 'LinkChecker',
266             linkedin => 'LinkedIn',
267             linkexchange => 'LinkExchange',
268             ltx71 => 'ltx71',
269             lycos => 'Lycos',
270             malware => 'Malware / hack attempt',
271             'microsoft-office' => 'Microsoft Office',
272             mj12bot => 'Majestic-12 DSearch',
273             msn => 'MSN',
274             'msn-mobile' => 'MSN Mobile',
275             nutch => 'Apache Nutch',
276             nuzzel => 'Nuzzel',
277             outbrain => 'Outbrain',
278             phplib => 'PHP http library',
279             pinterest => 'Pinterest',
280             'pro-sitemaps' => 'Pro Sitemap Service',
281             puf => 'puf',
282             'python-urllib' => 'Python-urllib',
283             'quora-link-preview' => 'Quora Link Preview',
284             qwantify => 'Qwantify',
285             researchscan => 'Researchscan RWTH Aachen',
286             reddit => 'Reddit',
287             robot => 'robot',
288             rogerbot => 'Moz',
289             'ruby-http-library' => 'Ruby http library',
290             showyou => 'Showyou',
291             'skype-uri-preview' => 'Skype URI Preview',
292             slack => 'slack',
293             swiftbot => 'Swiftbot',
294             tumblr => 'Tumblr',
295             twitter => 'Twitter',
296             unknown => 'Unknown Bot',
297             'verisign-ips-agent' => 'Verisign ips-agent',
298             vkshare => 'VK Share',
299             'w3c-validator' => 'W3C Validator',
300             wget => 'Wget',
301             whatsapp => 'WhatsApp',
302             yahoo => 'Yahoo',
303             'yahoo-slurp' => 'Yahoo! Slurp',
304             yandex => 'Yandex',
305             'yandex-images' => 'YandexImages',
306             'headless-chrome' => 'HeadlessChrome',
307             );
308              
309             my %ROBOT_IDS = (
310             ahrefs => 'ahrefs',
311             altavista => 'altavista',
312             apache => 'apache-http-client',
313             askjeeves => 'askjeeves',
314             baidu => 'baidu',
315             baiduspider => 'baidu',
316             bingbot => 'bingbot',
317             curl => 'curl',
318             facebook => 'facebook',
319             getright => 'getright',
320             golib => 'golib',
321             google => 'googlebot',
322             googleadsbot => 'google-adsbot',
323             googleadsense => 'google-adsense',
324             googlebotimage => 'googlebot-image',
325             googlebotnews => 'googlebot-news',
326             googlebotvideo => 'googlebot-video',
327             googlefavicon => 'google-favicon',
328             googlemobile => 'googlebot-mobile',
329             indy => 'indy-library',
330             infoseek => 'infoseek',
331             ipsagent => 'verisign-ips-agent',
332             java => 'java',
333             linkchecker => 'linkchecker',
334             linkexchange => 'linkexchange',
335             ltx71 => 'ltx71',
336             lwp => 'libwww-perl',
337             lycos => 'lycos',
338             malware => 'malware',
339             mj12bot => 'mj12bot',
340             msn => 'msn',
341             msnmobile => 'msn-mobile',
342             msoffice => 'microsoft-office',
343             nutch => 'nutch',
344             phplib => 'phplib',
345             puf => 'puf',
346             pythonurllib => 'python-urllib',
347             robot => 'robot',
348             rubylib => 'ruby-http-library',
349             researchscan => 'researchscan',
350             slurp => 'yahoo-slurp',
351             specialarchiver => 'archive-org',
352             wget => 'wget',
353             yahoo => 'yahoo',
354             yandex => 'yandex',
355             yandeximages => 'yandex-images',
356             headlesschrome => 'headless-chrome',
357             amazonbot => 'amazonbot',
358             );
359              
360             my %BROWSER_NAMES = (
361             adm => 'Android Download Manager',
362             aol => 'AOL Browser',
363             applecoremedia => 'AppleCoreMedia',
364             blackberry => 'BlackBerry',
365             brave => 'Brave',
366             browsex => 'BrowseX',
367             chrome => 'Chrome',
368             curl => 'curl',
369             dalvik => 'Dalvik',
370             dsi => 'Nintendo DSi',
371             edge => 'Edge',
372             elinks => 'ELinks',
373             epiphany => 'Epiphany',
374             firefox => 'Firefox',
375             galeon => 'Galeon',
376             icab => 'iCab',
377             iceweasel => 'IceWeasel',
378             ie => 'MSIE',
379             imagesearcherpro => 'ImageSearcherPro',
380             instagram => 'Instagram',
381             konqueror => 'Konqueror',
382             links => 'Links',
383             lotusnotes => 'Lotus Notes',
384             lynx => 'Lynx',
385             mobile_safari => 'Mobile Safari',
386             mosaic => 'Mosaic',
387             mozilla => 'Mozilla',
388             n3ds => 'Nintendo 3DS',
389             netfront => 'NetFront',
390             netscape => 'Netscape',
391             obigo => 'Obigo',
392             opera => 'Opera',
393             polaris => 'Polaris',
394             pubsub => 'Safari RSS Reader',
395             puf => 'puf',
396             realplayer => 'RealPlayer',
397             safari => 'Safari',
398             samsung => 'Samsung',
399             seamonkey => 'SeaMonkey',
400             silk => 'Silk',
401             staroffice => 'StarOffice',
402             ucbrowser => 'UCBrowser',
403             webtv => 'WebTV',
404             yandex_browser => 'Yandex Browser',
405             );
406              
407             # Device names
408             my %DEVICE_NAMES = (
409             android => 'Android',
410             audrey => 'Audrey',
411             blackberry => 'BlackBerry',
412             dsi => 'Nintendo DSi',
413             iopener => 'iopener',
414             ipad => 'iPad',
415             iphone => 'iPhone',
416             ipod => 'iPod',
417             kindle => 'Amazon Kindle',
418             n3ds => 'Nintendo 3DS',
419             palm => 'Palm',
420             ps3 => 'Sony PlayStation 3',
421             psp => 'Sony PlayStation Portable',
422             wap => 'WAP capable phone',
423             webos => 'webOS',
424             );
425              
426             my %OS_NAMES = (
427             amiga => 'Amiga',
428             android => 'Android',
429             bb10 => 'BlackBerry 10',
430             brew => 'Brew',
431             chromeos => 'Chrome OS',
432             firefoxos => 'Firefox OS',
433             ios => 'iOS',
434             linux => 'Linux',
435             mac => 'Mac',
436             macosx => 'Mac OS X',
437             os2 => 'OS/2',
438             ps3gameos => 'Playstation 3 GameOS',
439             pspgameos => 'Playstation Portable GameOS',
440             rimtabletos => 'RIM Tablet OS',
441             unix => 'Unix',
442             vms => 'VMS',
443             win2k => 'Win2k',
444             win2k3 => 'Win2k3',
445             win3x => 'Win3x',
446             win7 => 'Win7',
447             win8 => 'Win8',
448             win8_0 => 'Win8.0',
449             win8_1 => 'Win8.1',
450             win10 => 'Win10',
451             win10_0 => 'Win10.0',
452             win95 => 'Win95',
453             win98 => 'Win98',
454             winme => 'WinME',
455             winnt => 'WinNT',
456             winphone => 'Windows Phone',
457             winvista => 'WinVista',
458             winxp => 'WinXP',
459             );
460              
461             # Safari build -> version map for versions prior to 3.0
462             # (since then, version appears in the user-agent string)
463              
464             my %safari_build_to_version = qw(
465             48 0.8
466             51 0.8.1
467             60 0.8.2
468             73 0.9
469             74 1.0b2v74
470             85 1.0
471             85.7 1.0.2
472             85.8 1.0.3
473             100 1.1
474             100.1 1.1.1
475             125 1.2
476             125.1 1.2.1
477             125.7 1.2.2
478             125.9 1.2.3
479             125.11 1.2.4
480             312 1.3
481             312.3 1.3.1
482             312.5 1.3.2
483             412 2.0
484             412.5 2.0.1
485             416.12 2.0.2
486             417.8 2.0.3
487             419.3 2.0.4
488             );
489              
490             #######################################################################################################
491             # BROWSER OBJECT
492              
493             my $default = undef;
494              
495             sub new {
496 7301     7301 1 16227892 my ( $class, $user_agent ) = @_;
497              
498 7301         25581 my $self = {};
499 7301         22089 bless $self, $class;
500              
501 7301 100       30486 unless ( defined $user_agent ) {
502 2         7 $user_agent = $ENV{'HTTP_USER_AGENT'};
503             }
504              
505 7301 100       39022 $self->{user_agent} = defined $user_agent ? $user_agent : q{};
506 7301         38298 $self->_init_core;
507              
508 7301         135891 return $self;
509             }
510              
511             ### Accessors for computed-on-demand test attributes
512              
513             foreach my $test ( @ENGINE_TESTS, @MISC_TESTS ) {
514             ## no critic (TestingAndDebugging::ProhibitNoStrict)
515 5     5   42 no strict 'refs';
  5         10  
  5         706  
516             *{$test} = sub {
517 29339     29339   3052251 my ($self) = @_;
518 29339   100     158122 return $self->{tests}->{$test} || 0;
519             };
520             }
521              
522             foreach my $test (
523             @OS_TESTS, @WINDOWS_TESTS, @MAC_TESTS, @UNIX_TESTS,
524             @BSD_TESTS, @GAMING_TESTS
525             ) {
526             ## no critic (TestingAndDebugging::ProhibitNoStrict)
527 5     5   47 no strict 'refs';
  5         9  
  5         654  
528             *{$test} = sub {
529 50360     50360   14003694 my ($self) = @_;
530 50360 100       153843 $self->_init_os() unless $self->{os_tests};
531 50360   100     225740 return $self->{os_tests}->{$test} || 0;
532             };
533             }
534              
535             foreach my $test ( @BROWSER_TESTS, @FIREFOX_TESTS ) {
536             ## no critic (TestingAndDebugging::ProhibitNoStrict)
537 5     5   33 no strict 'refs';
  5         8  
  5         618  
538             *{$test} = sub {
539 53527     53527   9225019 my ($self) = @_;
540 53527   100     277333 return $self->{browser_tests}->{$test} || 0;
541             };
542             }
543              
544             foreach my $test (@OLD_ROBOT_TESTS) {
545             ## no critic (TestingAndDebugging::ProhibitNoStrict)
546 5     5   40 no strict 'refs';
  5         11  
  5         758  
547              
548             # For the 'robot' test, we return undef instead of 0 if it's
549             # false, to match os() and browser() and the like.
550             my $false_result = ( $test eq 'robot' ? undef : 0 );
551              
552             *{$test} = sub {
553 31996     31996   7564782 my ($self) = @_;
554 31996 100       122346 $self->_init_robots() unless $self->{robot_tests};
555 31996   66     165423 return $self->{robot_tests}->{$test} || $false_result;
556             };
557             }
558              
559             foreach my $test (
560             @NETSCAPE_TESTS, @IE_TESTS, @AOL_TESTS,
561             @OPERA_TESTS
562             ) {
563             ## no critic (TestingAndDebugging::ProhibitNoStrict)
564 5     5   55 no strict 'refs';
  5         9  
  5         603  
565             *{$test} = sub {
566 24353     24353   6750945 my ($self) = @_;
567 24353 100       90118 $self->_init_version() unless $self->{version_tests};
568 24353   100     121888 return $self->{version_tests}->{$test} || 0;
569             };
570             }
571              
572             foreach my $test (@DEVICE_TESTS) {
573             ## no critic (TestingAndDebugging::ProhibitNoStrict)
574 5     5   40 no strict 'refs';
  5         9  
  5         110896  
575             *{$test} = sub {
576 21073     21073   3836796 my ($self) = @_;
577 21073 100       100989 $self->_init_device() unless $self->{device_tests};
578 21073   100     124764 return $self->{device_tests}->{$test} || 0;
579             };
580             }
581              
582             sub user_agent {
583 1     1 1 8 my ( $self, $user_agent ) = @_;
584 1 50       4 if ( defined($user_agent) ) {
585 0         0 die
586             'Calling HTTP::BrowserDetect::user_agent() with an argument is no longer allowed; please use new().';
587             }
588             else {
589 1         10 return $self->{user_agent};
590             }
591             }
592              
593             ### Code for initializing various pieces of the object. Since it would
594             ### be needlessly slow if we examined every user agent for every piece
595             ### of information we might possibly need, we only initialize things
596             ### when they're actually queried about. Specifically:
597             ###
598             ### _init_core() sets up:
599             ### $self->{browser_tests}
600             ### $self->{browser}
601             ### $self->{browser_string}
602             ### $self->{tests}
603             ### $self->{engine_version}
604             ### $self->{realplayer_version}
605             ###
606             ### _init_version() sets up:
607             ### $self->{version_tests}
608             ### $self->{major}
609             ### $self->{minor}
610             ### $self->{beta}
611             ### $self->{realplayer_version}
612             ###
613             ### _init_os() sets up:
614             ### $self->{os}
615             ### $self->{os_string}
616             ### $self->{os_tests}
617             ### $self->{os_version}
618             ###
619             ### _init_os_version() sets up:
620             ### $self->{os_version}
621             ###
622             ### _init_device() sets up:
623             ### $self->{device_tests}
624             ### $self->{device}
625             ### $self->{device_string}
626             ###
627             ### _init_robots() sets up:
628             ### $self->{robot}
629             ### $self->{robot_tests}
630             ### $self->{robot_string}
631             ### $self->{robot_version}
632             ###
633              
634             # Private method -- Set up the basics (browser and misc attributes)
635             # for a new user-agent string
636             sub _init_core {
637 7301     7301   27041 my ( $self, $new_ua ) = @_;
638              
639 7301         30710 my $ua = lc $self->{user_agent};
640              
641             # any UA via Google Translate gets this appended
642 7301         27822 $ua =~ s{,gzip\(gfe\)\z}{};
643              
644             # These get filled in immediately
645 7301         25235 $self->{tests} = {};
646 7301         22484 $self->{browser_tests} = {};
647              
648 7301         20700 my $tests = $self->{tests};
649 7301         16650 my $browser_tests = $self->{browser_tests};
650 7301         18830 my $browser = undef;
651 7301         20916 my $browser_string = undef;
652              
653             # Detect engine
654 7301         22104 $self->{engine_version} = undef;
655              
656 7301 100 100     139754 if ( $ua =~ m{edge/([\d\.]+)} ) {
    100 66        
    100          
    100          
    100          
    100          
657 30         107 $tests->{edgehtml} = 1;
658 30         171 $self->{engine_version} = $1;
659             }
660             elsif ( $ua =~ /trident\/([\w\.\d]*)/ ) {
661 944         3772 $tests->{trident} = 1;
662 944         4893 $self->{engine_version} = $1;
663             }
664             elsif ( index( $ua, 'gecko' ) != -1 && index( $ua, 'like gecko' ) == -1 )
665             {
666 732         3539 $tests->{gecko} = 1;
667 732 100       8494 if ( $ua =~ /\([^)]*rv:([\w.\d]*)/ ) {
668 660         3178 $self->{engine_version} = $1;
669             }
670             }
671             elsif ( $ua =~ m{applewebkit/([\d.\+]+)} && not $tests->{edgehtml} ) {
672              
673             # Chrome 28+ uses Blink (fork of WebKit). We detect Chrome 38+ as Blink.
674             # Chrome versions before 38 are considered WebKit for compatibility.
675 2670         13967 my $webkit_version = $1;
676 2670         7253 my $is_blink = 0;
677              
678             # Check if this is Chrome/Chromium and extract version
679 2670 100       28532 if ( $ua =~ m{(?:chrome|chromium|crios)/(\d+)} ) {
680 1608         4801 my $chrome_version = $1;
681 1608 100       8977 if ( $chrome_version >= 38 ) {
682 582         1639 $is_blink = 1;
683             }
684             }
685              
686 2670 100       12346 if ($is_blink) {
687 582         1947 $tests->{blink} = 1;
688 582         2184 $self->{engine_version} = $webkit_version;
689             }
690             else {
691 2088         6663 $tests->{webkit} = 1;
692 2088         7701 $self->{engine_version} = $webkit_version;
693             }
694             }
695             elsif ( $ua =~ m{presto/([\d.]+)} ) {
696 138         513 $tests->{presto} = 1;
697 138         658 $self->{engine_version} = $1;
698             }
699             elsif ( $ua =~ m{khtml/([\d.]+)} ) {
700 12         56 $tests->{khtml} = 1;
701 12         70 $self->{engine_version} = $1;
702             }
703              
704             # Detect browser
705              
706 7301 100 100     362024 if ( index( $ua, 'galeon' ) != -1 ) {
    100 100        
    100 66        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100 100        
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    50          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
707              
708             # Needs to go above firefox
709              
710 12         28 $browser = 'galeon';
711 12         33 $browser_tests->{galeon} = 1;
712             }
713             elsif ( index( $ua, 'epiphany' ) != -1 ) {
714              
715             # Needs to go above firefox + mozilla
716              
717 6         15 $browser = 'epiphany';
718 6         26 $browser_tests->{epiphany} = 1;
719             }
720             elsif ( $ua =~ m{(?:edg|edga|edgios)/[\d.]+} ) {
721 24         72 $browser = 'edge';
722 24         65 $browser_string = 'Edge';
723              
724 24         82 $browser_tests->{edge} = 1;
725             }
726             elsif ( $ua =~ m{edge/[\d.]+} ) {
727 30         69 $browser = 'edge';
728 30         66 $browser_string = 'Edge';
729              
730 30         89 $browser_tests->{edgelegacy} = 1;
731             }
732             elsif (
733             $ua =~ m{
734             (firebird|iceweasel|phoenix|namoroka|firefox|fxios)
735             \/
736             ( [^.]* ) # Major version number is everything before first dot
737             \. # The first dot
738             ( [\d]* ) # Minor version number is digits after first dot
739             }xo
740             && index( $ua, 'not firefox' ) == -1
741             ) # Hack for Yahoo Slurp
742             {
743             # Browser is Firefox, possibly under an alternate name
744              
745 636         1860 $browser = 'firefox';
746 636 100       4156 $browser_string = ucfirst( $1 eq 'fxios' ? $browser : $1 );
747              
748 636         2263 $browser_tests->{$1} = 1;
749 636         1643 $browser_tests->{firefox} = 1;
750             }
751             elsif ( $self->{user_agent} =~ m{ \[ FB (?: AN | _IAB ) }x ) {
752 78   50     819 $browser_string = $self->_match(
753             [ 'Facebook', '[FB_IAB/FB4A', '[FBAN/FBIOS' ],
754             [ 'Facebook Lite', '[FBAN/EMA' ],
755             [
756             'Facebook Messenger', '[FB_IAB/Orca-Android',
757             '[FB_IAB/MESSENGER', '[FBAN/MessengerForiOS'
758             ],
759             [
760             'Facebook Messenger Lite', '[FBAN/mLite',
761             '[FBAN/MessengerLiteForiOS'
762             ],
763             ) || 'Meta App';
764 78         443 $browser = lc $browser_string;
765 78         294 $browser =~ tr/ /_/;
766 78         261 $tests->{meta_app} = 1;
767             }
768             elsif ( $ua =~ m{opera|opr\/} ) {
769              
770             # Browser is Opera
771              
772 264         726 $browser = 'opera';
773 264         908 $browser_tests->{opera} = 1;
774             }
775             elsif ( $tests->{trident}
776             || index( $ua, 'msie' ) != -1
777             || index( $ua, 'microsoft internet explorer' ) != -1 ) {
778              
779             # Browser is MSIE (possibly AOL branded)
780              
781 1730         5021 $browser = 'ie';
782 1730         5429 $browser_tests->{ie} = 1;
783              
784 1730 100 100     15705 if ( index( $ua, 'aol' ) != -1
785             || index( $ua, 'america online browser' ) != -1 ) {
786 42         101 $browser_string = 'AOL Browser';
787 42         118 $browser_tests->{aol} = 1;
788             }
789              
790             # Disabled for now -- need to figure out how to deal with version numbers
791             #elsif ( index ( $ua, 'acoobrowser' ) != -1 ) {
792             # $browser_string = 'Acoo Browser';
793             #}
794             #elsif ( index( $ua, 'avant' ) != -1 ) {
795             # $browser_string = 'Avant Browser';
796             #}
797             #elsif ( index( $ua, 'crazy browser' ) != -1 ) {
798             # $browser_string = 'Crazy Browser';
799             #}
800             #elsif ( index( $ua, 'deepnet explorer' ) != -1 ) {
801             # $browser_string = 'Deepnet Explorer';
802             #}
803             #elsif ( index( $ua, 'maxthon' ) != -1 ) {
804             # $browser_string = 'Maxthon';
805             #}
806             }
807             elsif ( 0 < index $self->{user_agent}, ' Instagram ' ) {
808 12         28 $browser = 'instagram';
809 12         54 $browser_tests->{$browser} = 1;
810             }
811             elsif ( index( $ua, 'brave' ) != -1 ) {
812              
813             # Has to go above Chrome, it includes 'like Chrome/'
814              
815 12         32 $browser = 'brave';
816 12         43 $browser_tests->{$browser} = 1;
817             }
818             elsif ( index( $ua, 'silk' ) != -1 ) {
819              
820             # Has to go above Chrome, it includes 'like Chrome/'
821              
822 72         205 $browser = 'silk';
823 72         312 $browser_tests->{$browser} = 1;
824             }
825             elsif ( index( $ua, 'samsungbrowser' ) != -1 ) {
826              
827             # Has to go above Chrome, it includes 'Chrome/'
828 6         17 $browser = 'samsung';
829 6         25 $browser_tests->{$browser} = 1;
830             }
831             elsif ( index( $ua, 'ucbrowser' ) != -1 ) {
832              
833             # Has to go above Safari, Mozilla and Chrome
834              
835 336         1075 $browser = 'ucbrowser';
836 336         1289 $browser_tests->{$browser} = 1;
837             }
838             elsif ( 0 < index $self->{user_agent}, ' YaBrowser/' ) {
839 36         110 $browser = 'yandex_browser';
840 36         144 $browser_tests->{$browser} = 1;
841             }
842             elsif (index( $ua, 'chrome/' ) != -1
843             || index( $ua, 'crios' ) != -1 ) {
844              
845             # Browser is Chrome
846              
847 1386         3964 $browser = 'chrome';
848 1386         3957 $browser_tests->{chrome} = 1;
849              
850 1386 100       6625 if ( index( $ua, 'chromium' ) != -1 ) {
851 12         44 $browser_string = 'Chromium';
852             }
853             }
854             elsif (index( $ua, 'blackberry' ) != -1
855             || index( $ua, 'bb10' ) != -1
856             || index( $ua, 'rim tablet os' ) != -1 ) {
857              
858             # Has to go above Safari
859 42         152 $browser = 'blackberry'; # test gets set during device check
860             }
861             elsif (( index( $ua, 'safari' ) != -1 )
862             || ( index( $ua, 'applewebkit' ) != -1 ) ) {
863              
864             # Browser is Safari
865              
866 960         3345 $browser_tests->{safari} = 1;
867 960         2506 $browser = 'safari';
868 960 100 100     7325 if ( index( $ua, ' mobile safari/' ) != -1
869             || index( $ua, 'mobilesafari' ) != -1 ) {
870 276         754 $browser_string = 'Mobile Safari';
871 276         941 $browser_tests->{mobile_safari} = 1;
872             }
873 960 100       4379 if ( index( $ua, 'puffin' ) != -1 ) {
874 18         56 $browser_string = 'Puffin';
875             }
876             }
877             elsif ( index( $ua, 'neoplanet' ) != -1 ) {
878              
879             # Browser is Neoplanet
880              
881 0         0 $browser = 'ie';
882 0         0 $browser_tests->{$browser} = 1;
883 0         0 $browser_tests->{neoplanet} = 1;
884 0 0       0 $browser_tests->{neoplanet2} = 1 if ( index( $ua, '2.' ) != -1 );
885             }
886              
887             ## The following browsers all need to be tested for *before*
888             ## Mozilla, otherwise we'll think they are Mozilla because they
889             ## look very much like it.
890             elsif ( index( $ua, 'webtv' ) != -1 ) {
891 0         0 $browser = 'webtv';
892 0         0 $browser_tests->{$browser} = 1;
893             }
894             elsif ( index( $ua, 'nintendo 3ds' ) != -1 ) {
895 6         17 $browser = 'n3ds'; # Test gets set during device check
896             }
897             elsif ( index( $ua, 'nintendo dsi' ) != -1 ) {
898 0         0 $browser = 'dsi'; # Test gets set during device check
899             }
900             elsif (index( $ua, 'playstation 3' ) != -1
901             || index( $ua, 'playstation portable' ) != -1
902             || index( $ua, 'netfront' ) != -1 ) {
903 42         112 $browser = 'netfront';
904 42         191 $browser_tests->{$browser} = 1;
905             }
906             elsif ( index( $ua, 'browsex' ) != -1 ) {
907 6         18 $browser = 'browsex';
908 6         28 $browser_tests->{$browser} = 1;
909             }
910             elsif ( index( $ua, 'polaris' ) != -1 ) {
911 18         48 $browser = 'polaris';
912 18         70 $browser_tests->{$browser} = 1;
913             }
914              
915             ## At this point if it looks like Mozilla but we haven't found
916             ## anything else for it to be, it's probably Mozilla.
917             elsif (index( $ua, 'mozilla' ) != -1
918             && index( $ua, 'compatible' ) == -1 ) {
919              
920             # Browser is a Gecko-powered Netscape (i.e. Mozilla) version
921              
922 168         529 $browser = 'mozilla';
923 168 100 100     1611 if ( index( $ua, 'netscape' ) != -1
    100          
924             || !$tests->{gecko} ) {
925 108         340 $browser = 'netscape';
926             }
927             elsif ( index( $ua, 'seamonkey' ) != -1 ) {
928 6         11 $browser = 'seamonkey';
929             }
930 168         636 $browser_tests->{$browser} = 1;
931 168         432 $browser_tests->{netscape} = 1;
932 168         520 $browser_tests->{mozilla} = ( $tests->{gecko} );
933             }
934              
935             ## Long series of unlikely browsers (ones that don't look like Mozilla)
936             elsif ( index( $ua, 'staroffice' ) != -1 ) {
937 12         36 $browser = 'staroffice';
938 12         38 $browser_tests->{$browser} = 1;
939             }
940             elsif ( index( $ua, 'icab' ) != -1 ) {
941 6         18 $browser = 'icab';
942 6         17 $browser_tests->{$browser} = 1;
943             }
944             elsif ( index( $ua, 'lotus-notes' ) != -1 ) {
945 6         14 $browser = 'lotusnotes';
946 6         17 $browser_tests->{$browser} = 1;
947             }
948             elsif ( index( $ua, 'konqueror' ) != -1 ) {
949 24         112 $browser = 'konqueror';
950 24         109 $browser_tests->{$browser} = 1;
951             }
952             elsif ( index( $ua, 'lynx' ) != -1 ) {
953 6         16 $browser = 'lynx';
954 6         20 $browser_tests->{$browser} = 1;
955             }
956             elsif ( index( $ua, 'elinks' ) != -1 ) {
957 6         11 $browser = 'elinks';
958 6         19 $browser_tests->{$browser} = 1;
959             }
960             elsif ( index( $ua, 'links' ) != -1 ) {
961 12         49 $browser = 'links';
962 12         41 $browser_tests->{$browser} = 1;
963             }
964             elsif ( index( $ua, 'mosaic' ) != -1 ) {
965 0         0 $browser = 'mosaic';
966 0         0 $browser_tests->{$browser} = 1;
967             }
968             elsif ( index( $ua, 'emacs' ) != -1 ) {
969 6         13 $browser = 'emacs';
970 6         17 $browser_tests->{$browser} = 1;
971             }
972             elsif ( index( $ua, 'obigo' ) != -1 ) {
973 30         75 $browser = 'obigo';
974 30         96 $browser_tests->{$browser} = 1;
975             }
976             elsif ( index( $ua, 'teleca' ) != -1 ) {
977 54         203 $browser = 'obigo';
978 54         133 $browser_string = 'Teleca';
979 54         205 $browser_tests->{$browser} = 1;
980             }
981             elsif ( index( $ua, 'libcurl' ) != -1 || $ua =~ /^curl/ ) {
982 36         137 $browser = 'curl'; # Test gets set during robot check
983             }
984             elsif ( index( $ua, 'puf/' ) != -1 ) {
985 6         19 $browser = 'puf'; # Test gets set during robot check
986             }
987             elsif ( index( $ua, 'applecoremedia/' ) != -1 ) {
988 6         24 $browser = 'applecoremedia';
989 6         26 $browser_tests->{$browser} = 1;
990             }
991             elsif ( index( $ua, 'androiddownloadmanager' ) != -1 ) {
992 6         22 $browser = 'adm';
993 6         21 $browser_tests->{$browser} = 1;
994             }
995             elsif ( index( $ua, 'dalvik' ) != -1 ) {
996 12         47 $browser = 'dalvik';
997 12         55 $browser_tests->{$browser} = 1;
998             }
999             elsif ( index( $ua, 'apple-pubsub' ) != -1 ) {
1000 6         27 $browser = 'pubsub';
1001 6         24 $browser_tests->{$browser} = 1;
1002             }
1003             elsif ( index( $ua, 'imagesearcherpro' ) != -1 ) {
1004 6         22 $browser = 'imagesearcherpro';
1005 6         24 $browser_tests->{$browser} = 1;
1006             }
1007              
1008 7301         29026 $self->{browser} = $browser;
1009 7301 100 100     57281 $self->{browser_string} = $browser_string || $BROWSER_NAMES{$browser}
1010             if defined $browser;
1011              
1012             # Other random tests
1013              
1014 7301 100       31728 $tests->{x11} = 1 if index( $ua, 'x11' ) != -1;
1015 7301 100       28723 $tests->{dotnet} = 1 if index( $ua, '.net clr' ) != -1;
1016              
1017 7301 100       26586 if ( index( $ua, 'realplayer' ) != -1 ) {
1018              
1019             # Hack for Realplayer -- fix the version and 'real' browser
1020              
1021 18         96 $self->_init_version; # Set appropriate tests for whatever the 'real'
1022             # browser is.
1023              
1024             # Now set the browser to Realplayer.
1025 18         70 $self->{browser} = 'realplayer';
1026 18         54 $self->{browser_string} = 'RealPlayer';
1027 18         50 $browser_tests->{realplayer} = 1;
1028              
1029             # Now override the version with the Realplayer version (but leave
1030             # alone the tests we already set, which might have been based on the
1031             # 'real' browser's version).
1032 18         74 $self->{realplayer_version} = undef;
1033              
1034 18 100       147 if ( $ua =~ /realplayer\/([\d+\.]+)/ ) {
    50          
1035 12         45 $self->{realplayer_version} = $1;
1036             ( $self->{major}, $self->{minor} )
1037 12         71 = split( /\./, $self->{realplayer_version} );
1038 12 50       74 $self->{minor} = ".$self->{minor}" if defined $self->{minor};
1039             }
1040             elsif ( $ua =~ /realplayer\s(\w+)/ ) {
1041 6         20 $self->{realplayer_version} = $1;
1042             }
1043             }
1044              
1045 7301 100       32445 if ( index( $ua, '(r1 ' ) != -1 ) {
1046              
1047             # Realplayer plugin -- don't override browser but do set property
1048 18         48 $browser_tests->{realplayer} = 1;
1049             }
1050              
1051             # Details: https://developer.chrome.com/multidevice/user-agent#webview_user_agent
1052             # WebView is identified by the '; wv)' marker in the user agent string.
1053             # Prior to this fix, we incorrectly checked for Chrome version >= 30, which
1054             # caused false positives for regular Chrome browsers on Android.
1055 7301 100 100     29953 if ( $self->android && index( $ua, '; wv)' ) != -1 ) {
1056 42         153 $tests->{webview} = 1;
1057             }
1058              
1059             }
1060              
1061             # match strings or regexps against user_agent
1062             # pass specs as list of [ 'thing to return', …tests… ]
1063             sub _match {
1064 78     78   330 my ( $self, @specs ) = @_;
1065 78         239 for my $spec (@specs) {
1066 156         357 my ( $ret, @tests ) = @{$spec};
  156         564  
1067 156         373 for my $m (@tests) {
1068 264 100 66     1722 return $ret if !ref $m && 0 < index $self->{user_agent}, $m;
1069 186 50 33     710 return $ret if 'Regexp' eq ref $m && $self->{user_agent} =~ $m;
1070             }
1071             }
1072             }
1073              
1074             # Any of these fragments within a user-agent generally indicates it's
1075             # a robot.
1076             my @ROBOT_FRAGMENTS = qw(
1077             agent
1078             analy
1079             appender
1080             babya
1081             checker
1082             bot
1083             copy
1084             crawl
1085             explorador
1086             fetch
1087             find
1088             ia_archive
1089             index
1090             netcraft
1091             reap
1092             resolver
1093             sleuth
1094             scan
1095             service
1096             spider
1097             thumbtack-thunderdome
1098             tiscali
1099             validator
1100             verif
1101             webcapture
1102             worm
1103             zyborg
1104             );
1105              
1106             my %ROBOT_FRAGMENT_EXCEPTIONS = (
1107             bot => ['cubot'],
1108             );
1109              
1110             sub _init_robots {
1111 8855     8855   18350 my $self = shift;
1112              
1113 8855         36968 my $ua = lc $self->{user_agent};
1114 8855         23453 my $tests = $self->{tests};
1115 8855         20913 my $browser_tests = $self->{browser_tests};
1116              
1117 8855         36842 my $robot_tests = $self->{robot_tests} = {};
1118 8855         30473 my $id;
1119             my $r;
1120              
1121 8855         0 my $robot_fragment; # The text that indicates it's a robot (we'll
1122             # use this later to detect robot version, and
1123             # maybe robot_string)
1124              
1125 8855 100 66     795742 if ( index( $ua, 'libwww-perl' ) != -1 || index( $ua, 'lwp-' ) != -1 ) {
    100 100        
    100 100        
    100 100        
    50 100        
    100 66        
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    50          
    100          
    50          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
1126 18         58 $r = 'lwp';
1127 18         75 $robot_tests->{lib} = 1;
1128 18 50       97 $robot_fragment = (
1129             ( index( $ua, 'libwww-perl' ) != -1 ) ? 'libwww-perl' : 'lwp-' );
1130             }
1131             elsif ( index( $ua, 'slurp' ) != -1 ) {
1132 12         28 $r = 'slurp';
1133 12         43 $robot_tests->{yahoo} = 1;
1134             }
1135             elsif (index( $ua, 'yahoo' ) != -1
1136             && index( $ua, 'jp.co.yahoo' ) == -1 ) {
1137 6         17 $r = 'yahoo';
1138             }
1139             elsif ( index( $ua, 'msnbot-mobile' ) != -1 ) {
1140 6         16 $r = 'msnmobile';
1141 6         14 $robot_tests->{msn} = 1;
1142 6         15 $robot_fragment = 'msnbot';
1143             }
1144             elsif ( index( $ua, 'bingbot-mobile' ) != -1 ) {
1145 0         0 $r = 'bingbot';
1146 0         0 $robot_tests->{bingbot} = 1;
1147 0         0 $robot_fragment = 'bingbot';
1148             }
1149             elsif ( index( $ua, 'msnbot' ) != -1 ) {
1150 6         15 $r = 'msn';
1151 6         17 $robot_fragment = 'msnbot';
1152             }
1153             elsif (index( $ua, 'binglocalsearch' ) != -1
1154             || index( $ua, 'bingbot' ) != -1
1155             || index( $ua, 'bingpreview' ) != -1 ) {
1156 30         112 $r = 'bingbot';
1157 30         98 $robot_tests->{bingbot} = 1;
1158 30         80 $robot_fragment = 'bingbot';
1159             }
1160             elsif ( index( $ua, 'microsoft office existence discovery' ) != -1 ) {
1161 6         13 $r = 'msoffice';
1162 6         14 $robot_fragment = 'office';
1163             }
1164             elsif ( index( $ua, 'ahrefsbot' ) != -1 ) {
1165 6         13 $r = 'ahrefs';
1166             }
1167             elsif ( index( $ua, 'altavista' ) != -1 ) {
1168 0         0 $r = 'altavista';
1169             }
1170             elsif ( index( $ua, 'apache-httpclient' ) != -1 ) {
1171 12         35 $r = 'apache';
1172             }
1173             elsif ( $ua =~ m{\( *\) *\{ *\: *\; *} ) {
1174              
1175             # Shellcode for spawning a process, i.e. (){:;} with some kind of whitespace interleaved
1176 6         9 $r = 'malware';
1177             }
1178             elsif ( index( $ua, 'ask jeeves/teoma' ) != -1 ) {
1179 0         0 $r = 'askjeeves';
1180 0         0 $robot_fragment = 'teoma';
1181             }
1182             elsif ( index( $ua, 'baiduspider' ) != -1 ) {
1183 18         43 $r = 'baidu';
1184             }
1185             elsif ( index( $ua, 'libcurl' ) != -1 || $ua =~ /^curl/ ) {
1186 36         136 $r = 'curl';
1187 36         162 $robot_tests->{lib} = 1;
1188             }
1189             elsif ( index( $ua, 'facebookexternalhit' ) != -1 ) {
1190 6         25 $r = 'facebook';
1191             }
1192             elsif ( index( $ua, 'getright' ) != -1 ) {
1193 6         13 $r = 'getright';
1194             }
1195             elsif ( index( $ua, 'adsbot-google' ) != -1 ) {
1196 12         25 $r = 'googleadsbot';
1197 12         26 $robot_tests->{google} = 1;
1198 12         20 $robot_fragment = 'adsbot-google';
1199             }
1200             elsif ( index( $ua, 'mediapartners-google' ) != -1 ) {
1201 6         13 $r = 'googleadsense';
1202 6         16 $robot_tests->{google} = 1;
1203 6         10 $robot_fragment = 'mediapartners-google';
1204             }
1205             elsif ( index( $ua, 'google favicon' ) != -1 ) {
1206 6         15 $r = 'googlefavicon';
1207 6         23 $robot_tests->{google} = 1;
1208 6         15 $robot_fragment = 'favicon';
1209             }
1210             elsif ( index( $ua, 'googlebot-image' ) != -1 ) {
1211 6         19 $r = 'googlebotimage';
1212 6         19 $robot_tests->{google} = 1;
1213 6         40 $robot_fragment = 'googlebot-image';
1214             }
1215             elsif ( index( $ua, 'googlebot-news' ) != -1 ) {
1216 6         21 $r = 'googlebotnews';
1217 6         24 $robot_tests->{google} = 1;
1218 6         21 $robot_fragment = 'googlebot-news';
1219             }
1220             elsif ( index( $ua, 'googlebot-video' ) != -1 ) {
1221 6         16 $r = 'googlebotvideo';
1222 6         19 $robot_tests->{google} = 1;
1223 6         14 $robot_fragment = 'googlebot-video';
1224             }
1225             elsif ( index( $ua, 'googlebot-mobile' ) != -1 ) {
1226 18         54 $r = 'googlemobile';
1227 18         59 $robot_tests->{google} = 1;
1228 18         48 $robot_fragment = 'googlebot-mobile';
1229             }
1230             elsif ( index( $ua, 'googlebot' ) != -1 ) {
1231 42         104 $r = 'google';
1232             }
1233             elsif ( $ua =~ m{go.*package http} ) {
1234 6         16 $r = 'golib';
1235 6         13 $robot_tests->{lib} = 1;
1236 6         16 $robot_fragment = 'package';
1237             }
1238             elsif ( $ua =~ m{^http_request} ) {
1239 6         15 $r = 'phplib';
1240 6         16 $robot_tests->{lib} = 1;
1241 6         15 $robot_fragment = 'http_request';
1242             }
1243             elsif ( $ua =~ m{^http_request} ) {
1244 0         0 $r = 'phplib';
1245 0         0 $robot_tests->{lib} = 1;
1246             }
1247             elsif ( index( $ua, 'indy library' ) != -1 ) {
1248 6         19 $r = 'indy';
1249 6         20 $robot_tests->{lib} = 1;
1250             }
1251             elsif ( index( $ua, 'infoseek' ) != -1 ) {
1252 0         0 $r = 'infoseek';
1253             }
1254             elsif ( index( $ua, 'ips-agent' ) != -1 ) {
1255 6         38 $r = 'ipsagent';
1256 6         17 $robot_fragment = 'ips-agent';
1257             }
1258             elsif ( index( $ua, 'lecodechecker' ) != -1 ) {
1259 0         0 $r = 'linkexchange';
1260 0         0 $robot_fragment = 'lecodechecker';
1261             }
1262             elsif ( index( $ua, 'linkchecker' ) != -1 ) {
1263 12         41 $r = 'linkchecker';
1264             }
1265             elsif ( index( $ua, 'lycos' ) != -1 ) {
1266 0         0 $r = 'lycos';
1267             }
1268             elsif ( index( $ua, 'mechanize' ) != -1 ) {
1269 12         29 $r = 'rubylib';
1270 12         31 $robot_tests->{lib} = 1;
1271 12         28 $robot_fragment = 'mechanize';
1272             }
1273             elsif ( index( $ua, 'mj12bot/' ) != -1 ) {
1274 6         14 $r = 'mj12bot';
1275             }
1276             elsif ( index( $ua, 'nutch' ) != -1 ) {
1277 18         57 $r = 'nutch';
1278             }
1279             elsif ( index( $ua, 'puf/' ) != -1 ) {
1280 6         16 $r = 'puf';
1281 6         20 $robot_tests->{lib} = 1;
1282             }
1283             elsif ( index( $ua, 'scooter' ) != -1 ) {
1284 0         0 $r = 'scooter';
1285             }
1286             elsif ( index( $ua, 'special_archiver' ) != -1 ) {
1287 6         22 $r = 'specialarchiver';
1288 6         16 $robot_fragment = 'special_archiver';
1289             }
1290             elsif ( index( $ua, 'wget' ) == 0 ) {
1291 12         29 $r = 'wget';
1292             }
1293             elsif ( index( $ua, 'yandexbot' ) != -1 ) {
1294 6         30 $r = 'yandex';
1295             }
1296             elsif ( index( $ua, 'yandeximages' ) != -1 ) {
1297 6         32 $r = 'yandeximages';
1298             }
1299             elsif ( index( $ua, 'headlesschrome' ) != -1 ) {
1300 6         31 $r = 'headlesschrome';
1301             }
1302             elsif ( $ua =~ m{^java} && !$self->{browser} ) {
1303 42         165 $r = 'java';
1304 42         158 $robot_tests->{lib} = 1;
1305             }
1306             elsif ( index( $ua, 'jdk' ) != -1 ) {
1307 0         0 $r = 'java';
1308 0         0 $robot_tests->{lib} = 1;
1309 0         0 $robot_fragment = 'jdk';
1310             }
1311             elsif ( index( $ua, 'jakarta commons-httpclient' ) != -1 ) {
1312 6         22 $r = 'java';
1313 6         18 $robot_tests->{lib} = 1;
1314 6         17 $robot_fragment = 'jakarta';
1315             }
1316             elsif ( index( $ua, 'google-http-java-client' ) != -1 ) {
1317 6         17 $r = 'java';
1318 6         21 $robot_tests->{lib} = 1;
1319 6         14 $robot_fragment = 'google';
1320             }
1321             elsif ( index( $ua, 'python-urllib' ) != -1 ) {
1322 12         35 $r = 'pythonurllib';
1323 12         42 $robot_tests->{lib} = 1;
1324 12         53 $robot_fragment = 'python-urllib';
1325             }
1326             elsif ( index( $ua, 'researchscan.comsys.rwth-aachen.de' ) != -1 ) {
1327 6         32 $r = 'researchscan';
1328             }
1329              
1330             # These @ROBOT_TESTS were added in 3.15. Some of them may need more
1331             # individualized treatment, but get them identified as bots for now.
1332              
1333             # XXX
1334             else {
1335             TEST:
1336 8393         35685 for my $set (@ROBOT_TESTS) {
1337 259343         522274 my $match = lc $set->[0];
1338              
1339 259343 100       630725 if ( index( $ua, lc($match) ) != -1 ) {
1340 54         120 $id = $set->[1];
1341 54         118 $r = $id;
1342 54         131 $robot_fragment = lc $match;
1343 54         201 last TEST;
1344             }
1345             }
1346             }
1347              
1348 8855 100 100     74599 if ( $browser_tests->{applecoremedia}
      100        
1349             || $browser_tests->{dalvik}
1350             || $browser_tests->{adm} ) {
1351 30         125 $robot_tests->{lib} = 1;
1352             }
1353              
1354 8855 100       85604 if ($r) {
    50          
    100          
    100          
1355              
1356             # Got a named robot
1357 516         1565 $robot_tests->{$r} = 1;
1358 516 100       1573 if ( !$id ) {
1359 462         1595 $id = $ROBOT_IDS{$r};
1360             }
1361              
1362 516 50       1627 if ( !exists $robot_tests->{robot_id} ) {
1363 516         1740 $robot_tests->{robot_id} = $id;
1364             }
1365              
1366             # This isn't all keyed on ids (yet)
1367 516   33     3004 $self->{robot_string} = $ROBOT_NAMES{$id} || $ROBOT_NAMES{$r};
1368 516         1255 $robot_tests->{robot} = $r;
1369 516 100       1501 $robot_fragment = $r if !defined $robot_fragment;
1370             }
1371             elsif ( $ua =~ /seek (?! mo (?: toolbar )? \s+ \d+\.\d+ )/x ) {
1372              
1373             # Store the fragment for later, to determine full name
1374 0         0 $robot_fragment = 'seek';
1375 0         0 $robot_tests->{robot} = 'unknown';
1376             }
1377             elsif ( $ua =~ /search (?! [\w\s]* toolbar \b | bar \b | erpro \b )/x ) {
1378              
1379             # Store the fragment for later, to determine full name
1380 42         234 $robot_fragment = 'search';
1381 42         191 $robot_tests->{robot} = 'unknown';
1382             }
1383             elsif ( $self->{user_agent} =~ /([\w \/\.\-]+)[ \;\(\)]*\+https?\:/i ) {
1384              
1385             # Something followed by +http
1386 246         1628 $self->{robot_string} = $1;
1387 246         4049 $self->{robot_string} =~ s/^ *(.+?)[ \;\(\)]*?( *\/[\d\.]+ *)?$/$1/;
1388 246         2721 $robot_fragment = $1;
1389 246         879 $robot_tests->{robot} = 'unknown';
1390             }
1391             else {
1392             # See if we have a simple fragment
1393             FRAGMENT:
1394 8051         26243 for my $fragment (@ROBOT_FRAGMENTS) {
1395 212256 100       467180 if ( $ROBOT_FRAGMENT_EXCEPTIONS{$fragment} ) {
1396 8009         13779 for my $exception (
1397 8009 50       31602 @{ $ROBOT_FRAGMENT_EXCEPTIONS{$fragment} || [] } ) {
1398 8009 100       30458 if ( index( $ua, $exception ) != -1 ) {
1399 8         22 next FRAGMENT;
1400             }
1401             }
1402             }
1403              
1404 212248 100       470818 if ( index( $ua, $fragment ) != -1 ) {
1405 311         807 $robot_fragment = $fragment;
1406 311         1128 $robot_tests->{robot} = 'unknown';
1407 311         805 last;
1408             }
1409             }
1410             }
1411              
1412 8855 100 100     41267 if ( exists $robot_tests->{robot} && $robot_tests->{robot} eq 'unknown' )
1413             {
1414 599         2170 $robot_tests->{robot_id} = 'unknown';
1415             }
1416              
1417 8855 100       27360 if ( defined $robot_fragment ) {
1418              
1419             # Examine what surrounds the fragment; that leads us to the
1420             # version and the string (if we haven't explicitly set one).
1421              
1422 1115 100       157185 if (
1423             $self->{user_agent} =~ m{\s* # Beginning whitespace
1424             ([\w .:,\-\@\/]* # Words before fragment
1425             $robot_fragment # Match the fragment
1426             [\w .:,\-\@\/]*) # Words after fragment
1427             }ix
1428             ) {
1429 1043         5760 my $full_string = $1;
1430 1043         10343 $full_string =~ s/ *$//; # Trim whitespace at end
1431 1043 100 100     9356 if (
      66        
1432             $self->{user_agent} eq $full_string
1433             && $self->{user_agent} =~ m{\/.*\/}
1434             && $self->{user_agent} =~ m{
1435             ([\w]* # Words before fragment
1436             $robot_fragment # Match the fragment
1437             (\/[\d\.]+)? # Version
1438             [\w]*) # Beta stuff
1439             }ix
1440             ) {
1441             # We matched the whole string, but it seems to
1442             # make more sense as whitespace-separated
1443             # 'thing/ver' tokens
1444 36         148 $full_string = $1;
1445             }
1446              
1447             # Figure out robot version based on the string
1448 1043 100 66     15344 if ( $full_string
1449             and $full_string =~ s/[\/ \.v]*(\d+)(\.\d+)?([\.\w]*)$// ) {
1450 678         5772 $self->{robot_version} = [ $1, $2, $3 ];
1451             }
1452             else {
1453 365         1434 $self->{robot_version} = undef;
1454             }
1455              
1456             # Set robot_string, if we don't already have an explicitly set
1457             # one
1458 1043 100       4390 if ( !defined $self->{robot_string} ) {
1459 353         1294 $self->{robot_string} = $full_string;
1460             }
1461             }
1462             }
1463              
1464 8855 100       34802 if ( !exists( $self->{robot_version} ) ) {
1465 6242         32617 $self->{robot_version} = undef;
1466             }
1467             }
1468              
1469             ### OS tests, only run on demand
1470              
1471             sub _init_os {
1472 7296     7296   16718 my $self = shift;
1473              
1474 7296         20997 my $tests = $self->{tests};
1475 7296         22822 my $browser_tests = $self->{browser_tests};
1476 7296         32077 my $ua = lc $self->{user_agent};
1477              
1478 7296         28348 my $os_tests = $self->{os_tests} = {};
1479 7296         17260 my $os = undef;
1480 7296         15040 my $os_string = undef;
1481              
1482             # Windows
1483              
1484 7296 50       32626 if ( index( $ua, '16bit' ) != -1 ) {
1485 0         0 $os = 'windows';
1486 0         0 $os_string = '16-bit Windows';
1487 0         0 $os_tests->{win16} = $os_tests->{windows} = 1;
1488             }
1489              
1490 7296 100       26961 if ( index( $ua, 'win' ) != -1 ) {
1491 2964 100 66     111625 if ( index( $ua, 'win16' ) != -1
    100 66        
    100 100        
    100 100        
    100 100        
    100          
    100          
1492             || index( $ua, 'windows 3' ) != -1
1493             || index( $ua, 'windows 16-bit' ) != -1 ) {
1494 12         38 $os_tests->{win16} = 1;
1495 12         33 $os_tests->{win3x} = 1;
1496 12         26 $os = 'windows';
1497 12 100       42 if ( index( $ua, 'windows 3.1' ) != -1 ) {
1498 6         17 $os_tests->{win31} = 1;
1499 6         14 $os_string = 'Win3x'; # FIXME bug compatibility
1500             }
1501             else {
1502 6         15 $os_string = 'Win3x';
1503             }
1504             }
1505             elsif (index( $ua, 'win95' ) != -1
1506             || index( $ua, 'windows 95' ) != -1 ) {
1507 72         174 $os = 'windows';
1508 72         148 $os_string = 'Win95';
1509 72         278 $os_tests->{win95} = $os_tests->{win32} = 1;
1510             }
1511             elsif (
1512             index( $ua, 'win 9x 4.90' ) != -1 # whatever
1513             || index( $ua, 'windows me' ) != -1
1514             ) {
1515 30         111 $os = 'windows';
1516 30         69 $os_string = 'WinME';
1517 30         157 $os_tests->{winme} = $os_tests->{win32} = 1;
1518             }
1519             elsif (index( $ua, 'win98' ) != -1
1520             || index( $ua, 'windows 98' ) != -1 ) {
1521 24         53 $os = 'windows';
1522 24         48 $os_string = 'Win98';
1523 24         252 $os_tests->{win98} = $os_tests->{win32} = 1;
1524             }
1525             elsif ( index( $ua, 'windows 2000' ) != -1 ) {
1526 12         31 $os = 'windows';
1527 12         30 $os_string = 'Win2k';
1528 12         48 $os_tests->{win2k} = $os_tests->{winnt} = $os_tests->{win32} = 1;
1529             }
1530             elsif ( index( $ua, 'windows ce' ) != -1 ) {
1531 12         31 $os = 'windows';
1532 12         25 $os_string = 'WinCE';
1533 12         46 $os_tests->{wince} = 1;
1534             }
1535             elsif ( index( $ua, 'windows phone' ) != -1 ) {
1536 54         151 $os = 'winphone';
1537 54         200 $os_tests->{winphone} = 1;
1538              
1539 54 100       534 if ( index( $ua, 'windows phone os 7.0' ) != -1 ) {
    100          
    100          
    100          
    50          
1540 6         18 $os_tests->{winphone7} = 1;
1541             }
1542             elsif ( index( $ua, 'windows phone os 7.5' ) != -1 ) {
1543 12         76 $os_tests->{winphone7_5} = 1;
1544             }
1545             elsif ( index( $ua, 'windows phone 8.0' ) != -1 ) {
1546 12         44 $os_tests->{winphone8} = 1;
1547             }
1548             elsif ( index( $ua, 'windows phone 8.1' ) != -1 ) {
1549 18         92 $os_tests->{winphone8_1} = 1;
1550             }
1551             elsif ( index( $ua, 'windows phone 10.0' ) != -1 ) {
1552 6         21 $os_tests->{winphone10} = 1;
1553             }
1554             }
1555             }
1556              
1557 7296 100       27778 if ( index( $ua, 'nt' ) != -1 ) {
1558 3804 100 66     68837 if ( index( $ua, 'nt 5.0' ) != -1
    100 100        
    100 100        
    100 66        
    100          
    100          
    100          
    100          
    100          
1559             || index( $ua, 'nt5' ) != -1 ) {
1560 90         293 $os = 'windows';
1561 90         215 $os_string = 'Win2k';
1562 90         520 $os_tests->{win2k} = $os_tests->{winnt} = $os_tests->{win32} = 1;
1563             }
1564             elsif ( index( $ua, 'nt 5.1' ) != -1 ) {
1565 984         2575 $os = 'windows';
1566 984         2398 $os_string = 'WinXP';
1567 984         4977 $os_tests->{winxp} = $os_tests->{winnt} = $os_tests->{win32} = 1;
1568             }
1569             elsif ( index( $ua, 'nt 5.2' ) != -1 ) {
1570 126         316 $os = 'windows';
1571 126         282 $os_string = 'Win2k3';
1572 126         688 $os_tests->{win2k3} = $os_tests->{winnt} = $os_tests->{win32} = 1;
1573             }
1574             elsif ( index( $ua, 'nt 6.0' ) != -1 ) {
1575 204         492 $os = 'windows';
1576 204         474 $os_string = 'WinVista';
1577             $os_tests->{winvista} = $os_tests->{winnt} = $os_tests->{win32}
1578 204         1079 = 1;
1579             }
1580             elsif ( index( $ua, 'nt 6.1' ) != -1 ) {
1581 966         2898 $os = 'windows';
1582 966         2557 $os_string = 'Win7';
1583 966         5236 $os_tests->{win7} = $os_tests->{winnt} = $os_tests->{win32} = 1;
1584             }
1585             elsif ( index( $ua, 'nt 6.2' ) != -1 ) {
1586 96         239 $os = 'windows';
1587 96         227 $os_string = 'Win8.0';
1588             $os_tests->{win8_0} = $os_tests->{win8} = $os_tests->{winnt}
1589 96         576 = $os_tests->{win32} = 1;
1590             }
1591             elsif ( index( $ua, 'nt 6.3' ) != -1 ) {
1592 78         263 $os = 'windows';
1593 78         246 $os_string = 'Win8.1';
1594             $os_tests->{win8_1} = $os_tests->{win8} = $os_tests->{winnt}
1595 78         610 = $os_tests->{win32} = 1;
1596             }
1597             elsif ( index( $ua, 'nt 10.0' ) != -1 ) {
1598 36         80 $os = 'windows';
1599 36         82 $os_string = 'Win10.0';
1600             $os_tests->{win10_0} = $os_tests->{win10} = $os_tests->{winnt}
1601 36         213 = $os_tests->{win32} = 1;
1602             }
1603             elsif (index( $ua, 'winnt' ) != -1
1604             || index( $ua, 'windows nt' ) != -1
1605             || index( $ua, 'nt4' ) != -1
1606             || index( $ua, 'nt3' ) != -1 ) {
1607 54         160 $os = 'windows';
1608 54         107 $os_string = 'WinNT';
1609 54         300 $os_tests->{winnt} = $os_tests->{win32} = 1;
1610             }
1611             }
1612              
1613 7296 100 100     188142 if ($os) {
    100 100        
    100 66        
    100 33        
    100 33        
    100 33        
    100 33        
    100 33        
    100 33        
    50 33        
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    100          
    50          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1614              
1615             # windows, set through some path above
1616 2850         9464 $os_tests->{windows} = 1;
1617 2850 50       18976 $os_tests->{win32} = 1 if index( $ua, 'win32' ) != -1;
1618             }
1619             elsif ( index( $ua, 'macintosh' ) != -1 || index( $ua, 'mac_' ) != -1 ) {
1620              
1621             # Mac operating systems
1622 462         1645 $os_tests->{mac} = 1;
1623 462 100       2067 if ( index( $ua, 'mac os x' ) != -1 ) {
1624 438         1063 $os = 'macosx';
1625 438         1443 $os_tests->{$os} = 1;
1626             }
1627             else {
1628 24         183 $os = 'mac';
1629             }
1630 462 50 33     5882 if ( index( $ua, '68k' ) != -1 || index( $ua, '68000' ) != -1 ) {
    100 100        
1631 0         0 $os_tests->{mac68k} = 1;
1632             }
1633             elsif ( index( $ua, 'ppc' ) != -1 || index( $ua, 'powerpc' ) != -1 ) {
1634 126         317 $os_tests->{macppc} = 1;
1635             }
1636             }
1637             elsif ( $ua =~ m{\b(ipad|iphone|ipod)\b} ) {
1638              
1639             # iOS
1640 420         1094 $os = 'ios';
1641 420         2492 $os_tests->{$os} = 1;
1642             }
1643             elsif ( index( $ua, 'android' ) != -1 ) {
1644              
1645             # Android
1646 1050         2945 $os = 'android'; # Test gets set in the device testing
1647             }
1648             elsif ( index( $ua, 'inux' ) != -1 ) {
1649              
1650             # Linux
1651 498         1464 $os = 'linux';
1652 498         2391 $os_tests->{linux} = $os_tests->{unix} = 1;
1653             }
1654             elsif ( $tests->{x11} && index( $ua, 'cros' ) != -1 ) {
1655              
1656             # ChromeOS
1657 42         127 $os = 'chromeos';
1658 42         145 $os_tests->{chromeos} = 1;
1659             }
1660             ## Long series of unlikely OSs
1661             elsif ( index( $ua, 'amiga' ) != -1 ) {
1662 6         15 $os = 'amiga';
1663 6         23 $os_tests->{$os} = 1;
1664             }
1665             elsif ( index( $ua, 'os/2' ) != -1 ) {
1666 6         13 $os = 'os2';
1667 6         15 $os_tests->{$os} = 1;
1668             }
1669             elsif ( index( $ua, 'solaris' ) != -1 ) {
1670 6         13 $os = 'unix';
1671 6         10 $os_string = 'Solaris';
1672 6         17 $os_tests->{sun} = $os_tests->{unix} = 1;
1673             }
1674             elsif ( index( $ua, 'samsung' ) == -1 && index( $ua, 'sun' ) != -1 ) {
1675 0         0 $os = 'unix';
1676 0         0 $os_string = 'SunOS';
1677 0         0 $os_tests->{sun} = $os_tests->{unix} = 1;
1678 0 0       0 $os_tests->{suni86} = 1 if index( $ua, 'i86' ) != -1;
1679 0 0       0 $os_tests->{sun4} = 1 if index( $ua, 'sunos 4' ) != -1;
1680 0 0       0 $os_tests->{sun5} = 1 if index( $ua, 'sunos 5' ) != -1;
1681             }
1682             elsif ( index( $ua, 'irix' ) != -1 ) {
1683 0         0 $os = 'unix';
1684 0         0 $os_string = 'Irix';
1685 0         0 $os_tests->{irix} = $os_tests->{unix} = 1;
1686 0 0       0 $os_tests->{irix5} = 1 if ( index( $ua, 'irix5' ) != -1 );
1687 0 0       0 $os_tests->{irix6} = 1 if ( index( $ua, 'irix6' ) != -1 );
1688             }
1689             elsif ( index( $ua, 'hp-ux' ) != -1 ) {
1690 0         0 $os = 'unix';
1691 0         0 $os_string = 'HP-UX';
1692 0         0 $os_tests->{hpux} = $os_tests->{unix} = 1;
1693 0 0       0 $os_tests->{hpux9} = 1 if index( $ua, '09.' ) != -1;
1694 0 0       0 $os_tests->{hpux10} = 1 if index( $ua, '10.' ) != -1;
1695             }
1696             elsif ( index( $ua, 'aix' ) != -1 ) {
1697 0         0 $os = 'unix';
1698 0         0 $os_string = 'AIX';
1699 0         0 $os_tests->{aix} = $os_tests->{unix} = 1;
1700 0 0       0 $os_tests->{aix1} = 1 if ( index( $ua, 'aix 1' ) != -1 );
1701 0 0       0 $os_tests->{aix2} = 1 if ( index( $ua, 'aix 2' ) != -1 );
1702 0 0       0 $os_tests->{aix3} = 1 if ( index( $ua, 'aix 3' ) != -1 );
1703 0 0       0 $os_tests->{aix4} = 1 if ( index( $ua, 'aix 4' ) != -1 );
1704             }
1705             elsif ( $ua =~ m{\bsco\b} || index( $ua, 'unix_sv' ) != -1 ) {
1706 0         0 $os = 'unix';
1707 0         0 $os_string = 'SCO Unix';
1708 0         0 $os_tests->{sco} = $os_tests->{unix} = 1;
1709             }
1710             elsif ( index( $ua, 'unix_system_v' ) != -1 ) {
1711 0         0 $os = 'unix';
1712 0         0 $os_string = 'System V Unix';
1713 0         0 $os_tests->{unixware} = $os_tests->{unix} = 1;
1714             }
1715             elsif ( $ua =~ m{\bncr\b} ) {
1716 0         0 $os = 'unix';
1717 0         0 $os_string = 'NCR Unix';
1718 0         0 $os_tests->{mpras} = $os_tests->{unix} = 1;
1719             }
1720             elsif ( index( $ua, 'reliantunix' ) != -1 ) {
1721 0         0 $os = 'unix';
1722 0         0 $os_string = 'Reliant Unix';
1723 0         0 $os_tests->{reliant} = $os_tests->{unix} = 1;
1724             }
1725             elsif (index( $ua, 'dec' ) != -1
1726             || index( $ua, 'osf1' ) != -1
1727             || index( $ua, 'declpha' ) != -1
1728             || index( $ua, 'alphaserver' ) != -1
1729             || index( $ua, 'ultrix' ) != -1
1730             || index( $ua, 'alphastation' ) != -1 ) {
1731 0         0 $os = 'unix';
1732 0         0 $os_tests->{dec} = $os_tests->{unix} = 1;
1733             }
1734             elsif ( index( $ua, 'sinix' ) != -1 ) {
1735 0         0 $os = 'unix';
1736 0         0 $os_tests->{sinix} = $os_tests->{unix} = 1;
1737             }
1738             elsif ( index( $ua, 'bsd' ) != -1 ) {
1739 30         103 $os = 'unix';
1740 30 50       521 if ( $self->{user_agent} =~ m{(\w*bsd\w*)}i ) {
1741 30         136 $os_string = $1;
1742             }
1743 30         163 $os_tests->{bsd} = $os_tests->{unix} = 1;
1744 30 50       171 $os_tests->{freebsd} = 1 if index( $ua, 'freebsd' ) != -1;
1745             }
1746             elsif ( $tests->{x11} ) {
1747              
1748             # Some Unix we didn't identify
1749 12         33 $os = 'unix';
1750 12         39 $os_tests->{unix} = 1;
1751             }
1752             elsif ( index( $ua, 'vax' ) != -1 || index( $ua, 'openvms' ) != -1 ) {
1753              
1754 0         0 $os = 'vms';
1755 0         0 $os_tests->{vms} = 1;
1756             }
1757             elsif ( index( $ua, 'bb10' ) != -1 ) {
1758 12         30 $os = 'bb10';
1759 12         34 $os_tests->{bb10} = 1;
1760             }
1761             elsif ( index( $ua, 'rim tablet os' ) != -1 ) {
1762 6         19 $os = 'rimtabletos';
1763 6         21 $os_tests->{rimtabletos} = 1;
1764             }
1765             elsif ( index( $ua, 'playstation 3' ) != -1 ) {
1766 6         13 $os = 'ps3gameos';
1767 6         18 $os_tests->{ps3gameos} = 1;
1768             }
1769             elsif ( index( $ua, 'playstation portable' ) != -1 ) {
1770 6         14 $os = 'pspgameos';
1771 6         15 $os_tests->{pspgameos} = 1;
1772             }
1773             elsif ( index( $ua, 'windows' ) != -1 ) {
1774              
1775             # Windows again, the super generic version
1776 54         194 $os_tests->{windows} = 1;
1777             }
1778             elsif ( index( $ua, 'win32' ) != -1 ) {
1779 18         124 $os_tests->{win32} = $os_tests->{windows} = 1;
1780             }
1781             elsif ( $self->{user_agent} =~ m{(brew)|(\bbmp\b)}i ) {
1782 144         584 $os = 'brew';
1783 144 100       811 if ($1) {
1784 102         207 $os_string = 'Brew';
1785             }
1786             else {
1787 42         127 $os_string = 'Brew MP';
1788             }
1789 144         502 $os_tests->{brew} = 1;
1790             }
1791             else {
1792 1668         5332 $os = undef;
1793             }
1794              
1795             # To deal with FirefoxOS we seem to have to load-on-demand devices
1796             # also, by calling ->mobile and ->tablet. We have to be careful;
1797             # if we ever created a loop back from _init_devices to _init_os
1798             # we'd run forever.
1799 7296 100 100     29217 if ( !$os
      66        
      100        
      100        
1800             && $browser_tests->{firefox}
1801             && index( $ua, 'fennec' ) == -1
1802             && ( $self->mobile || $self->tablet ) ) {
1803 12         24 $os = 'firefoxos';
1804 12         33 $os_tests->{firefoxos} = 1;
1805             }
1806              
1807 7296         24495 $self->{os} = $os;
1808 7296 100 100     41106 if ( $os and !$os_string ) {
1809 2592         9182 $os_string = $OS_NAMES{$os};
1810             }
1811 7296         24805 $self->{os_string} = $os_string;
1812             }
1813              
1814             sub _init_os_version {
1815 2556     2556   7895 my ($self) = @_;
1816              
1817 2556         10337 my $os = $self->os;
1818 2556         9057 my $os_string = $self->os_string;
1819 2556         12435 my $ua = lc $self->{user_agent};
1820              
1821 2556         6029 my $os_version = undef;
1822              
1823 2556 100       32324 if ( !defined $os ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1824              
1825             # Nothing is going to work if we have no OS. Skip everything.
1826             }
1827             elsif ( $os eq 'winphone' ) {
1828 26 50       359 if ( $ua =~ m{windows phone (?:os )?(\d+)(\.?\d*)([\.\d]*)} ) {
1829 26         213 $os_version = [ $1, $2, $3 ];
1830             }
1831             }
1832             elsif ( $os eq 'macosx' ) {
1833 194 100       2067 if ( $ua =~ m{os x (\d+)[\._](\d+)[\._]?(\d*)} ) {
1834 157 100       1883 $os_version = [ $1, ".$2", length($3) ? ".$3" : q{} ];
1835             }
1836             }
1837             elsif ( $os eq 'ios' ) {
1838 174 100       1945 if ( $ua =~ m{ os (\d+)[\._ ](\d+)[\._ ]?(\d*)} ) {
1839 172 100       1909 $os_version = [ $1, ".$2", length($3) ? ".$3" : q{} ];
1840             }
1841             }
1842             elsif ( $os eq 'chromeos' ) {
1843 19 50       268 if ( $ua =~ m{ cros \S* (\d+)(\.?\d*)([\.\d]*)} ) {
1844 19         157 $os_version = [ $1, $2, $3 ];
1845             }
1846             }
1847             elsif ( $os eq 'android' ) {
1848 491 100       5570 if ( $ua =~ m{android (\d+)(\.?\d*)([\w\-\.]*)[\;\)]} ) {
1849 473         3639 $os_version = [ $1, $2, $3 ];
1850             }
1851             }
1852             elsif ( $os eq 'firefoxos' ) {
1853 6 50       74 if ( $ua =~ m{firefox/(\d+)(\.?\d*)([\.\d]*)} ) {
1854 6         38 $os_version = [ $1, $2, $3 ];
1855             }
1856             }
1857             elsif ( $os eq 'brew' ) {
1858 71 100       948 if ( $ua =~ m{(brew|\bbmp) (\d+)(\.?\d*)([\.\d]*)} ) {
1859 52         445 $os_version = [ $2, $3, $4 ];
1860             }
1861             }
1862              
1863             # Set the version. It might be set to undef, in which case we know
1864             # not to go through this next time.
1865 2556         11389 $self->{os_version} = $os_version;
1866             }
1867              
1868             ### Version determination, only run on demand
1869              
1870             sub _init_version {
1871 7247     7247   20825 my ($self) = @_;
1872              
1873 7247         30154 my $ua = lc $self->{user_agent};
1874 7247         17564 my $tests = $self->{tests};
1875 7247         15619 my $browser_tests = $self->{browser_tests};
1876 7247         22219 my $browser = $self->{browser};
1877              
1878 7247         20604 $self->{version_tests} = {};
1879 7247         19935 my $version_tests = $self->{version_tests};
1880              
1881 7247         18239 my ( $major, $minor, $beta );
1882              
1883             ### First figure out version numbers. First, we test if we're
1884             ### using a browser that needs some special method to determine
1885             ### the version.
1886              
1887 7247 100 100     197005 if ( defined $browser && $browser eq 'opera' ) {
    100 100        
    100 100        
    100 66        
    100 100        
    100 66        
    100 66        
    100 66        
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1888              
1889             # Opera has a 'compatible; ' section, but lies sometimes. It needs
1890             # special handling.
1891              
1892             # http://dev.opera.com/articles/view/opera-ua-string-changes/
1893             # http://my.opera.com/community/openweb/idopera/
1894             # Opera/9.80 (S60; SymbOS; Opera Mobi/320; U; sv) Presto/2.4.15 Version/10.00
1895             # Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 OPR/15.0.1147.100
1896              
1897 263 100       4714 if ( $ua =~ m{\AOpera.*\sVersion/(\d*)\.(\d*)\z}i ) {
    100          
    50          
1898 90         373 $major = $1;
1899 90         305 $minor = $2;
1900             }
1901             elsif ( $ua =~ m{\bOPR/(\d+)\.(\d+)}i ) {
1902 48         238 $major = $1;
1903 48         194 $minor = $2;
1904             }
1905             elsif ( $ua =~ m{Opera[ /](\d+).(\d+)}i ) {
1906 125         566 $major = $1;
1907 125         403 $minor = $2;
1908             }
1909             }
1910             elsif ( !defined $browser
1911             && $ua
1912             =~ m{\b compatible; \s* [\w\-]* [/\s] ( [0-9]+ ) (?: .([0-9]+) (\S*) )? ;}x
1913             ) {
1914             # MSIE and some others use a 'compatible' format
1915             # Only match when browser is not yet identified
1916 204         1381 ( $major, $minor, $beta ) = ( $1, $2, $3 );
1917             }
1918             elsif ( !$browser ) {
1919              
1920             # Nothing else is going to work if $browser isn't defined; skip the
1921             # specific approaches and go straight to the generic ones.
1922             }
1923             elsif ( $browser_tests->{edge} ) {
1924 24         203 ( $major, $minor, $beta )
1925             = $ua =~ m{Edge/(\d+)(?:\.(\d+))?([\.\d]+)?}i;
1926 24 50       454 ( $major, $minor, $beta )
1927             = $ua =~ m{(?:Edg|EdgA|EdgiOS)/(\d+)(?:\.(\d+))?([\.\d]+)?}i
1928             unless defined $major;
1929             }
1930             elsif ( $browser_tests->{safari} ) {
1931              
1932             # Safari Version
1933              
1934 956 100       14466 if (
    100          
1935             0
1936             && $ua =~ m{ # Disabled for bug compatibility
1937             version/
1938             ( \d+ ) # Major version number is everything before first dot
1939             \. # First dot
1940             ( \d+ )? # Minor version number follows dot
1941             }x
1942             ) {
1943             # Safari starting with version 3.0 provides its own public version
1944             ( $major, $minor ) = ( $1, $2, undef );
1945             }
1946 0         0 elsif ( $ua =~ m{ safari/ ( \d+ (?: \.\d+ )* ) }x ) {
1947 890 50       10571 if ( my ( $safari_build, $safari_minor ) = split /\./, $1 ) {
1948 890         3600 $major = int( $safari_build / 100 );
1949 890         3221 $minor = int( $safari_build % 100 );
1950 890 100       4534 $beta = ".$safari_minor" if defined $safari_minor;
1951             }
1952             }
1953             elsif ( $ua =~ m{applewebkit\/([\d\.]{1,})}xi ) {
1954 54 50       513 if ( my ( $safari_build, $safari_minor ) = split /\./, $1 ) {
1955 54         216 $major = int( $safari_build / 100 );
1956 54         152 $minor = int( $safari_build % 100 );
1957 54 100       275 $beta = ".$safari_minor" if $safari_minor;
1958             }
1959             }
1960             }
1961             elsif ( $browser_tests->{fxios} ) {
1962 6         63 ( $major, $minor ) = $ua =~ m{ \b fxios/ (\d+) [.] (\d+) }x;
1963             }
1964             elsif ( $tests->{meta_app} ) {
1965              
1966             # init. in order to avoid guessing downstream
1967 78         405 ( $major, $minor, $beta ) = (q{}) x 3;
1968              
1969             # get version only from FBAV/ part
1970 78 100       761 if ( $ua =~ m{ \b fbav/ ([^;]*) }x ) {
1971 60         438 ( $major, $minor, $beta ) = split /[.]/, $1, 3;
1972 60 50       285 if ($beta) {
1973              
1974             # "minor" forcibly gets a "." prepended at the end of _init_version
1975             # while "beta" does not - yet it is documented to include the "."
1976 60         292 $beta = q{.} . $beta;
1977             }
1978             }
1979             }
1980             elsif ( $browser_tests->{instagram} ) {
1981 12         49 ( $major, $minor, $beta ) = (q{}) x 3; # don't guess downstream
1982 12 50       110 if ( $self->{user_agent} =~ m{ \b Instagram [ ]+ ([0-9.]+) [ ] }x ) {
1983 12         69 ( $major, $minor, $beta ) = split /[.]/, $1, 3;
1984 12 50       40 if ($beta) {
1985 12         44 $beta = q{.} . $beta;
1986             }
1987             }
1988             }
1989             elsif ( $browser_tests->{ie} ) {
1990              
1991             # MSIE
1992              
1993 1708 100       21284 if ( $ua =~ m{\b msie \s ( [0-9\.]+ ) ( [a-z]+ [a-z0-9]* )? ;}x ) {
    100          
    100          
1994              
1995             # Internet Explorer
1996 1618         11565 ( $major, $minor, $beta ) = split /\./, $1;
1997 1618 100       8626 $beta = $2 if $2; # Capture version suffix like "2.0d"
1998             }
1999             elsif ( $ua =~ m{\b rv: ( [0-9\.]+ ) \b}x ) {
2000              
2001             # MSIE masking as Gecko really well ;)
2002 72         601 ( $major, $minor, $beta ) = split /\./, $1;
2003             }
2004             elsif ( $ua
2005             =~ m{\b compatible; \s* [\w\-]* [/\s] ( [0-9]+ ) (?: .([0-9]+) (\S*) )? ;}x
2006             ) {
2007             # Fallback to compatible format for IE
2008 6         33 ( $major, $minor, $beta ) = ( $1, $2, $3 );
2009             }
2010             }
2011             elsif ( $browser eq 'n3ds' ) {
2012 6 50       61 if ( $ua =~ m{Nintendo 3DS;.*\sVersion/(\d*)\.(\d*)}i ) {
2013 6         19 $major = $1;
2014 6         14 $minor = $2;
2015             }
2016             }
2017             elsif ( $browser eq 'browsex' ) {
2018 6 50       79 if ( $ua =~ m{BrowseX \((\d+)\.(\d+)([\d.]*)}i ) {
2019 6         21 $major = $1;
2020 6         14 $minor = $2;
2021 6         22 $beta = $3;
2022             }
2023             }
2024             elsif ( $ua =~ m{netscape6/(\d+)\.(\d+)([\d.]*)} ) {
2025              
2026             # Other cases get handled below, we just need this to skip the '6'
2027 6         21 $major = $1;
2028 6         17 $minor = $2;
2029 6         22 $beta = $3;
2030             }
2031             elsif ( $browser eq 'brave' ) {
2032              
2033             # Note: since 0.7.10, Brave has changed the branding
2034             # of GitHub's 'Electron' (http://electron.atom.io/) to 'Brave'.
2035             # This means the browser string has both 'brave/' (the browser)
2036             # and 'Brave/' (re-branded Electron) in it.
2037             # The generic section below looks at $self->{browser_string}, which is 'Brave'
2038             # (Electron) and not $self->{browser} which is 'brave'.
2039             # Caveat parser.
2040 12 50       131 if ( $ua =~ m{brave/(\d+)\.(\d+)([\d.]*)} ) {
2041 12         53 $major = $1;
2042 12         37 $minor = $2;
2043 12         44 $beta = $3;
2044             }
2045             }
2046             elsif ($browser eq 'chrome'
2047             && $ua =~ m{crios/(\d+)\.(\d+)([\d.]*)} ) {
2048 18         69 $major = $1;
2049 18         44 $minor = $2;
2050 18         54 $beta = $3;
2051             }
2052             elsif ($browser eq 'pubsub'
2053             && $ua =~ m{apple-pubsub/(\d+)\.?(\d+)?([\d.]*)} ) {
2054 6         53 $major = $1;
2055 6         20 $minor = $2;
2056 6         22 $beta = $3;
2057             }
2058             elsif ($browser eq 'obigo'
2059             && $self->{user_agent} =~ m{(obigo[\w\-]*|teleca)[\/ ]\w(\d+)(\w*)}i )
2060             {
2061 78         331 $major = $2;
2062 78         208 $minor = q{};
2063 78         386 $beta = $3;
2064             }
2065             elsif ($browser eq 'polaris'
2066             && $ua =~ m{polaris[ \/](\d+)\.?(\d+)?([\d\.]*)} ) {
2067 18         61 $major = $1;
2068 18         56 $minor = $2;
2069 18         55 $beta = $3;
2070             }
2071             elsif ($browser eq 'ucbrowser'
2072             && $ua =~ m{ucbrowser[\/ ]*(\d+)\.?(\d+)?([\d\.]*)} ) {
2073 333         1712 $major = $1;
2074 333         1025 $minor = $2;
2075 333         1478 $beta = $3;
2076             }
2077             elsif ( $browser eq 'samsung' && $ua =~ m{samsungbrowser/(\d+)\.(\d+)\s} )
2078             {
2079 6         38 $major = $1;
2080 6         18 $minor = $2;
2081             }
2082             elsif ( $browser_tests->{yandex_browser} ) {
2083 36         201 ( $major, $minor, $beta ) = (q{}) x 3; # don't guess downstream
2084 36 50       500 if ( $self->{user_agent} =~ m{ \b YaBrowser / ([0-9.]+) [ ] }x ) {
2085 36         305 ( $major, $minor, $beta ) = split /[.]/, $1, 3;
2086 36 50       207 if ($beta) {
2087 36         126 $beta = q{.} . $beta;
2088             }
2089             }
2090             }
2091              
2092             # If we didn't match a browser-specific test, we look for
2093             # '$browser/x.y.z'
2094 7247 100 100     32967 if ( !defined $major && defined $self->{browser_string} ) {
2095 2520         13974 my $version_index = index( $ua, lc "$self->{browser_string}/" );
2096 2520 100       10019 if ( $version_index != -1 ) {
2097 2322         11740 my $version_str
2098             = substr( $ua, $version_index + length($browser) );
2099 2322 100       22894 if ( $version_str =~ m{/(\d+)\.(\d+)([\w.]*)} ) {
2100 2310         11503 $major = $1;
2101 2310         7292 $minor = $2;
2102 2310         9323 $beta = $3;
2103             }
2104             }
2105             }
2106              
2107             # If that didn't work, we try 'Version/x.y.z'
2108 7247 100       22343 if ( !defined $major ) {
2109 1185 100       4400 if ( $ua =~ m{version/(\d+)\.(\d+)([\w.]*)} ) {
2110 24         87 $major = $1;
2111 24         94 $minor = $2;
2112 24         106 $beta = $3;
2113             }
2114             }
2115              
2116             # If that didn't work, we start guessing. Just grab
2117             # anything after a word and a slash.
2118 7247 100       19103 if ( !defined $major ) {
2119              
2120 1161         11754 ( $major, $minor, $beta ) = (
2121             $ua =~ m{
2122             \S+ # Greedily catch anything leading up to forward slash.
2123             \/ # Version starts with a slash
2124             [A-Za-z]* # Eat any letters before the major version
2125             ( [0-9]+ ) # Major version number is everything before the first dot
2126             \. # The first dot
2127             ([\d]* ) # Minor version number is every digit after the first dot
2128             # Throw away remaining numbers and dots
2129             ( [^\s]* ) # Beta version string is up to next space
2130             }x
2131             );
2132             }
2133              
2134             # If that didn't work, try even more generic.
2135 7247 100       22065 if ( !defined $major ) {
2136              
2137 324 50       1907 if ( $ua =~ /[A-Za-z]+\/(\d+)\;/ ) {
2138 0         0 $major = $1;
2139 0         0 $minor = 0;
2140             }
2141             }
2142              
2143             # If that didn't work, give up.
2144 7247 100       19020 $major = 0 if !$major;
2145 7247 100       18253 $minor = 0 if !$minor;
2146 7247 100 100     34765 $beta = undef if ( defined $beta && $beta eq q{} );
2147              
2148             # Now set version tests
2149              
2150 7247 100       23539 if ( $browser_tests->{netscape} ) {
2151              
2152             # Netscape browsers
2153 166 100       828 $version_tests->{nav2} = 1 if $major == 2;
2154 166 100       641 $version_tests->{nav3} = 1 if $major == 3;
2155 166 100       639 $version_tests->{nav4} = 1 if $major == 4;
2156 166 100       686 $version_tests->{nav4up} = 1 if $major >= 4;
2157 166 100 100     828 $version_tests->{nav45} = 1 if $major == 4 && $minor == 5;
2158 166 100 100     1509 $version_tests->{nav45up} = 1
      100        
2159             if ( $major == 4 && ".$minor" >= .5 )
2160             || $major >= 5;
2161 166 100 100     907 $version_tests->{navgold} = 1
2162             if defined $beta && ( index( $beta, 'gold' ) != -1 );
2163 166 100 100     992 $version_tests->{nav6} = 1
2164             if ( $major == 5 || $major == 6 ); # go figure
2165 166 100       625 $version_tests->{nav6up} = 1 if $major >= 5;
2166              
2167 166 100       662 if ( $browser eq 'seamonkey' ) {
2168              
2169             # Ugh, seamonkey versions started back at 1.
2170 6         13 $version_tests->{nav2} = 0;
2171 6         12 $version_tests->{nav4up} = 1;
2172 6         17 $version_tests->{nav45up} = 1;
2173 6         11 $version_tests->{nav6} = 1;
2174 6         12 $version_tests->{nav6up} = 1;
2175             }
2176             }
2177              
2178 7247 100       21404 if ( $browser_tests->{ie} ) {
2179 1708 100       8405 $version_tests->{ie3} = 1 if ( $major == 3 );
2180 1708 100       5824 $version_tests->{ie4} = 1 if ( $major == 4 );
2181 1708 100       10303 $version_tests->{ie4up} = 1 if ( $major >= 4 );
2182 1708 100       5829 $version_tests->{ie5} = 1 if ( $major == 5 );
2183 1708 100       7600 $version_tests->{ie5up} = 1 if ( $major >= 5 );
2184 1708 100 100     7625 $version_tests->{ie55} = 1 if ( $major == 5 && $minor == 5 );
2185 1708 100 100     19354 $version_tests->{ie55up} = 1 if ( ".$minor" >= .5 || $major >= 6 );
2186 1708 100       6686 $version_tests->{ie6} = 1 if ( $major == 6 );
2187 1708 100       6899 $version_tests->{ie7} = 1 if ( $major == 7 );
2188 1708 100       7580 $version_tests->{ie8} = 1 if ( $major == 8 );
2189 1708 100       4849 $version_tests->{ie9} = 1 if ( $major == 9 );
2190 1708 100       7888 $version_tests->{ie10} = 1 if ( $major == 10 );
2191 1708 100       5507 $version_tests->{ie11} = 1 if ( $major == 11 );
2192              
2193             $version_tests->{ie_compat_mode}
2194             = ( $version_tests->{ie7}
2195             && $tests->{trident}
2196 1708   100     11142 && defined $self->engine_version
2197             && $self->engine_version >= 4 );
2198             }
2199              
2200 7247 100       23078 if ( $browser_tests->{aol} ) {
2201             $version_tests->{aol3} = 1
2202             if ( index( $ua, 'aol 3.0' ) != -1
2203 41 100 66     302 || $version_tests->{ie3} );
2204             $version_tests->{aol4} = 1
2205             if ( index( $ua, 'aol 4.0' ) != -1 )
2206 41 50 33     289 || $version_tests->{ie4};
2207 41 50       138 $version_tests->{aol5} = 1 if index( $ua, 'aol 5.0' ) != -1;
2208 41 100       144 $version_tests->{aol6} = 1 if index( $ua, 'aol 6.0' ) != -1;
2209 41 50       155 $version_tests->{aoltv} = 1 if index( $ua, 'navio' ) != -1;
2210             }
2211              
2212 7247 100       25249 if ( $browser_tests->{opera} ) {
2213 263 100 66     2452 $version_tests->{opera3} = 1
2214             if index( $ua, 'opera 3' ) != -1 || index( $ua, 'opera/3' ) != -1;
2215 263 50 66     2444 $version_tests->{opera4} = 1
      33        
2216             if ( index( $ua, 'opera 4' ) != -1 )
2217             || ( index( $ua, 'opera/4' ) != -1
2218             && ( index( $ua, 'nintendo dsi' ) == -1 ) );
2219 263 50 33     1984 $version_tests->{opera5} = 1
2220             if ( index( $ua, 'opera 5' ) != -1 )
2221             || ( index( $ua, 'opera/5' ) != -1 );
2222 263 100 66     2492 $version_tests->{opera6} = 1
2223             if ( index( $ua, 'opera 6' ) != -1 )
2224             || ( index( $ua, 'opera/6' ) != -1 );
2225 263 100 100     1932 $version_tests->{opera7} = 1
2226             if ( index( $ua, 'opera 7' ) != -1 )
2227             || ( index( $ua, 'opera/7' ) != -1 );
2228              
2229             }
2230              
2231 7247         18209 $minor = ".$minor";
2232              
2233 7247         35496 $self->{major} = $major;
2234 7247         21457 $self->{minor} = $minor;
2235 7247         35831 $self->{beta} = $beta;
2236             }
2237              
2238             ### Device tests, only run on demand
2239              
2240             sub _init_device {
2241 8315     8315   27842 my ($self) = @_;
2242              
2243 8315         32240 my $ua = lc $self->{user_agent};
2244 8315         20306 my $browser_tests = $self->{browser_tests};
2245 8315         18395 my $tests = $self->{tests};
2246              
2247 8315         18798 my ( $device, $device_string );
2248 8315         32149 my $device_tests = $self->{device_tests} = {};
2249              
2250 8315 100 100     470813 if ( index( $ua, 'windows phone' ) != -1 ) {
    100 100        
    100 100        
    100 100        
    100 100        
    100 66        
    100 100        
    100 66        
    50 100        
    50 66        
    50 100        
    50 66        
    100 66        
    100 66        
    100 66        
    100 100        
    100 66        
      66        
      66        
      66        
      66        
2251 55         270 $device = 'winphone';
2252              
2253             # Test is set in _init_os()
2254             }
2255             elsif (index( $ua, 'android' ) != -1
2256             || index( $ua, 'silk-accelerated' ) != -1 ) {
2257              
2258             # Silk-accelerated indicates a 1st generation Kindle Fire,
2259             # which may not have other indications of being an Android
2260             # device.
2261 1056         2799 $device = 'android';
2262 1056         4037 $device_tests->{$device} = 1;
2263             }
2264             elsif (index( $ua, 'blackberry' ) != -1
2265             || index( $ua, 'bb10' ) != -1
2266             || index( $ua, 'rim tablet os' ) != -1 ) {
2267 42         91 $device = 'blackberry';
2268 42         120 $device_tests->{$device} = 1;
2269             }
2270             elsif ( index( $ua, 'ipod' ) != -1 ) {
2271 18         54 $device = 'ipod';
2272 18         62 $device_tests->{$device} = 1;
2273             }
2274             elsif ( index( $ua, 'ipad' ) != -1 ) {
2275 162         422 $device = 'ipad';
2276 162         753 $device_tests->{$device} = 1;
2277             }
2278             elsif ( index( $ua, 'iphone' ) != -1 ) {
2279 240         531 $device = 'iphone';
2280 240         1117 $device_tests->{$device} = 1;
2281             }
2282             elsif ( index( $ua, 'webos' ) != -1 ) {
2283 6         19 $device = 'webos';
2284 6         85 $device_tests->{$device} = 1;
2285             }
2286             elsif ( index( $ua, 'kindle' ) != -1 ) {
2287 12         30 $device = 'kindle';
2288 12         40 $device_tests->{$device} = 1;
2289             }
2290             elsif ( index( $ua, 'audrey' ) != -1 ) {
2291 0         0 $device = 'audrey';
2292 0         0 $device_tests->{$device} = 1;
2293             }
2294             elsif ( index( $ua, 'i-opener' ) != -1 ) {
2295 0         0 $device = 'iopener';
2296 0         0 $device_tests->{$device} = 1;
2297             }
2298             elsif ( index( $ua, 'avantgo' ) != -1 ) {
2299 0         0 $device = 'avantgo';
2300 0         0 $device_tests->{$device} = 1;
2301 0         0 $device_tests->{palm} = 1;
2302             }
2303             elsif ( index( $ua, 'palmos' ) != -1 ) {
2304 0         0 $device = 'palm';
2305 0         0 $device_tests->{$device} = 1;
2306             }
2307             elsif ( index( $ua, 'playstation 3' ) != -1 ) {
2308 6         12 $device = 'ps3';
2309 6         29 $device_tests->{$device} = 1;
2310             }
2311             elsif ( index( $ua, 'playstation portable' ) != -1 ) {
2312 6         13 $device = 'psp';
2313 6         16 $device_tests->{$device} = 1;
2314             }
2315             elsif ( index( $ua, 'nintendo dsi' ) != -1 ) {
2316 6         15 $device = 'dsi';
2317 6         44 $device_tests->{$device} = 1;
2318             }
2319             elsif ( index( $ua, 'nintendo 3ds' ) != -1 ) {
2320 6         17 $device = 'n3ds';
2321 6         16 $device_tests->{$device} = 1;
2322             }
2323             elsif (
2324             $browser_tests->{obigo}
2325             || $browser_tests->{ucbrowser}
2326             || index( $ua, 'up.browser' ) != -1
2327             || ( index( $ua, 'nokia' ) != -1
2328             && index( $ua, 'windows phone' ) == -1 )
2329             || index( $ua, 'alcatel' ) != -1
2330             || $ua =~ m{\bbrew\b}
2331             || $ua =~ m{\bbmp\b}
2332             || index( $ua, 'ericsson' ) != -1
2333             || index( $ua, 'sie-' ) == 0
2334             || index( $ua, 'wmlib' ) != -1
2335             || index( $ua, ' wap' ) != -1
2336             || index( $ua, 'wap ' ) != -1
2337             || index( $ua, 'wap/' ) != -1
2338             || index( $ua, '-wap' ) != -1
2339             || index( $ua, 'wap-' ) != -1
2340             || index( $ua, 'wap' ) == 0
2341             || index( $ua, 'wapper' ) != -1
2342             || index( $ua, 'zetor' ) != -1
2343             ) {
2344 582         1798 $device = 'wap';
2345 582         2531 $device_tests->{$device} = 1;
2346             }
2347              
2348             $device_tests->{tablet} = (
2349             index( $ua, 'ipad' ) != -1
2350             || ( $browser_tests->{ie}
2351             && index( $ua, 'windows phone' ) == -1
2352             && index( $ua, 'arm' ) != -1 )
2353             || ( index( $ua, 'android' ) != -1
2354             && index( $ua, 'mobile' ) == -1
2355             && index( $ua, 'safari' ) != -1 )
2356 8315   66     475708 || ( $browser_tests->{firefox} && index( $ua, 'tablet' ) != -1 )
2357             || index( $ua, 'an10bg3' ) != -1
2358             || index( $ua, 'an10bg3dt' ) != -1
2359             || index( $ua, 'an10g2' ) != -1
2360             || index( $ua, 'an7bg3' ) != -1
2361             || index( $ua, 'an7dg3' ) != -1
2362             || index( $ua, 'an7dg3childpad' ) != -1
2363             || index( $ua, 'an7dg3st' ) != -1
2364             || index( $ua, 'an7fg3' ) != -1
2365             || index( $ua, 'an7g3' ) != -1
2366             || index( $ua, 'an8cg3' ) != -1
2367             || index( $ua, 'an8g3' ) != -1
2368             || index( $ua, 'an9g3' ) != -1
2369             || index( $ua, 'flyer' ) != -1
2370             || index( $ua, 'hp-tablet' ) != -1
2371             || index( $ua, 'jetstream' ) != -1
2372             || index( $ua, 'kindle' ) != -1
2373             || index( $ua, 'novo7' ) != -1
2374             || index( $ua, 'opera tablet' ) != -1
2375             || index( $ua, 'rim tablet' ) != -1
2376             || index( $ua, 'transformer' ) != -1
2377             || index( $ua, 'xoom' ) != -1
2378             );
2379              
2380 8315 100       33784 if ( !$device_tests->{tablet} ) {
2381             $device_tests->{mobile} = (
2382             ( $browser_tests->{firefox} && index( $ua, 'mobile' ) != -1 )
2383             || ( $browser_tests->{ie}
2384             && index( $ua, 'windows phone' ) == -1
2385             && index( $ua, 'arm' ) != -1 )
2386             || index( $ua, 'windows phone' ) != -1
2387             || index( $ua, 'up.browser' ) != -1
2388             || index( $ua, 'nokia' ) != -1
2389             || index( $ua, 'alcatel' ) != -1
2390             || index( $ua, 'ericsson' ) != -1
2391             || index( $ua, 'sie-' ) == 0
2392             || index( $ua, 'wmlib' ) != -1
2393             || index( $ua, ' wap' ) != -1
2394             || index( $ua, 'wap ' ) != -1
2395             || index( $ua, 'wap/' ) != -1
2396             || index( $ua, '-wap' ) != -1
2397             || index( $ua, 'wap-' ) != -1
2398             || index( $ua, 'wap' ) == 0
2399             || index( $ua, 'wapper' ) != -1
2400             || index( $ua, 'blackberry' ) != -1
2401             || index( $ua, 'mobile' ) != -1
2402             || index( $ua, 'palm' ) != -1
2403             || index( $ua, 'smartphone' ) != -1
2404             || index( $ua, 'windows ce' ) != -1
2405             || index( $ua, 'palmsource' ) != -1
2406             || index( $ua, 'iphone' ) != -1
2407             || index( $ua, 'ipod' ) != -1
2408             || index( $ua, 'ipad' ) != -1
2409             || ( index( $ua, 'opera mini' ) != -1
2410             && index( $ua, 'tablet' ) == -1 )
2411             || index( $ua, 'htc_' ) != -1
2412             || index( $ua, 'symbian' ) != -1
2413             || index( $ua, 'webos' ) != -1
2414             || index( $ua, 'samsung' ) != -1
2415             || index( $ua, 'zetor' ) != -1
2416             || index( $ua, 'android' ) != -1
2417             || index( $ua, 'symbos' ) != -1
2418             || index( $ua, 'opera mobi' ) != -1
2419             || index( $ua, 'fennec' ) != -1
2420             || $ua =~ m{\bbrew\b}
2421             || index( $ua, 'obigo' ) != -1
2422             || index( $ua, 'teleca' ) != -1
2423             || index( $ua, 'polaris' ) != -1
2424             || index( $ua, 'opera tablet' ) != -1
2425             || index( $ua, 'rim tablet' ) != -1
2426             || ( index( $ua, 'bb10' ) != -1
2427             && index( $ua, 'mobile' ) != -1 )
2428             || $device_tests->{psp}
2429             || $device_tests->{dsi}
2430 7977   100     603468 || $device_tests->{'n3ds'}
2431             );
2432             }
2433              
2434 8315 100 100     353555 if ( $browser_tests->{ucbrowser}
    100 100        
    100 100        
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
2435             && $self->{user_agent}
2436             =~ m{ucweb/2.0\s*\(([^\;\)]*\;){3,4}\s*([^\;\)]*?)\s*\)}i ) {
2437 276         1321 $device_string = $2;
2438             }
2439             elsif ( $ua =~ /^(\bmot-[^ \/]+)/ ) {
2440 12         86 $device_string = substr $self->{user_agent}, 0, length $1;
2441 12         80 $device_string =~ s/^MOT-/Motorola /i;
2442             }
2443             elsif ( ( $browser_tests->{obigo} || index( $ua, 'brew' ) != -1 )
2444             && $self->{user_agent} =~ m{\d+x\d+ ([\d\w\- ]+?)( \S+\/\S+)*$}i ) {
2445 108         535 $device_string = $1;
2446             }
2447             elsif (
2448             $ua =~ /windows phone os [^\)]+ iemobile\/[^;]+; ([^;]+; [^;\)]+)/g )
2449             {
2450             # windows phone 7.x
2451             $device_string = substr $self->{user_agent},
2452 18         125 pos($ua) - length $1, length $1;
2453 18         101 $device_string =~ s/; / /;
2454             }
2455             elsif ( $ua
2456             =~ /windows phone [^\)]+ iemobile\/[^;]+; arm; touch; ([^;]+; [^;\)]+)/g
2457             ) {
2458             # windows phone 8.0
2459             $device_string = substr $self->{user_agent},
2460 12         98 pos($ua) - length $1, length $1;
2461 12         67 $device_string =~ s/; / /;
2462             }
2463             elsif (
2464             $ua =~ /windows phone 8[^\)]+ iemobile\/[^;]+; ([^;]+; [^;\)]+)/g ) {
2465              
2466             # windows phone 8.1
2467             $device_string = substr $self->{user_agent},
2468 18         118 pos($ua) - length $1, length $1;
2469 18         106 $device_string =~ s/; / /;
2470             }
2471             elsif ( $ua =~ /bb10; ([^;\)]+)/g ) {
2472             $device_string = 'BlackBerry ' . substr $self->{user_agent},
2473 12         63 pos($ua) - length $1, length $1;
2474 12         49 $device_string =~ s/Kbd/Q10/;
2475             }
2476             elsif ( $ua =~ /blackberry ([\w.]+)/ ) {
2477 6         25 $device_string = "BlackBerry $1";
2478             }
2479             elsif ( $ua =~ /blackberry(\d+)\// ) {
2480 18         86 $device_string = "BlackBerry $1";
2481             }
2482             elsif ( $ua =~ /silk-accelerated/ ) {
2483              
2484             # Only first generation Kindle Fires have that string
2485 12         31 $device_string = 'Kindle Fire';
2486 12         30 $device_tests->{kindlefire} = 1;
2487             }
2488             elsif ( $self->{user_agent} =~ /android .*\; ([^;]*) build/i ) {
2489 978         4126 my $model = $1;
2490 978 100 100     9736 if ( $model =~ m{^KF} || $model =~ m{kindle fire}i ) {
    100          
2491              
2492             # We might hit this even if tablet() is false, if we have
2493             # a Kindle Fire masquerading as a mobile device.
2494 54         144 $device_string = 'Kindle Fire';
2495 54         175 $device_tests->{kindlefire} = 1;
2496             }
2497             elsif ( $device_tests->{tablet} ) {
2498 84         295 $device_string = "Android tablet ($model)";
2499             }
2500             else {
2501 840         2841 $device_string = "Android ($model)";
2502             }
2503             }
2504             elsif ( $self->{user_agent}
2505             =~ /\b((alcatel|huawei|lg|nokia|samsung|sonyericsson)[\w\-]*)\//i ) {
2506 114         529 $device_string = $1;
2507             }
2508             elsif ( $self->{user_agent} =~ /CrKey/ ) {
2509 6         24 $device = 'chromecast';
2510 6         15 $device_string = 'Chromecast';
2511             }
2512             elsif ($device) {
2513 613         3335 $device_string = $DEVICE_NAMES{$device};
2514             }
2515             else {
2516 6112         14446 $device_string = undef;
2517             }
2518              
2519 8315 100       24145 if ($device) {
2520 2203         9346 $self->{device} = $device;
2521             }
2522             else {
2523             $self->{device}
2524 6112         19757 = undef; # Means we cache the fact that we found nothing
2525             }
2526              
2527 8315 100       27136 if ($device_string) {
2528 2196         13176 $self->{device_string} = $device_string;
2529             }
2530             }
2531              
2532             ### Now a big block of public accessors for tests and information
2533              
2534             sub browser {
2535 1733     1733 1 1615696 my ($self) = @_;
2536 1733 50       8316 return undef unless defined $self->{user_agent};
2537 1733         10735 return $self->{browser};
2538             }
2539              
2540             sub browser_string {
2541 1702     1702 1 1223173 my ($self) = @_;
2542 1702 50       9041 return undef unless defined $self->{user_agent};
2543 1702         9424 return $self->{browser_string};
2544             }
2545              
2546             sub robot {
2547 2270     2270 1 3258695 my $self = shift;
2548              
2549 2270 100       21089 $self->_init_robots unless exists( $self->{robot_string} );
2550 2270         12088 return $self->{robot_tests}->{robot};
2551             }
2552              
2553             sub robot_string {
2554 944     944 1 239759 my $self = shift;
2555              
2556 944 100       5746 $self->_init_robots unless exists( $self->{robot_string} );
2557 944         4349 return $self->{robot_string};
2558             }
2559              
2560             sub robot_name {
2561 144     144 0 150089 my $self = shift;
2562 144         693 return $self->robot_string;
2563             }
2564              
2565             sub robot_id {
2566 558     558 1 2103 my $self = shift;
2567             return
2568             $self->{robot_tests}->{robot_id} ? $self->{robot_tests}->{robot_id}
2569 558 0       2630 : $self->robot ? $ROBOT_IDS{ $self->robot }
    50          
2570             : undef;
2571             }
2572              
2573             sub _robot_version {
2574 425     425   942 my ($self) = @_;
2575 425 50       1496 $self->_init_robots unless exists( $self->{robot_string} );
2576 425 50       1223 if ( $self->{robot_version} ) {
2577 425         668 return @{ $self->{robot_version} };
  425         2402  
2578             }
2579             else {
2580 0         0 return ( undef, undef, undef );
2581             }
2582             }
2583              
2584             sub robot_version {
2585 110     110 1 110871 my ($self) = @_;
2586 110         481 my ( $major, $minor, $beta ) = $self->_robot_version;
2587 110 50       381 if ( defined $major ) {
2588 110 100       413 if ( defined $minor ) {
2589 109         1034 return "$major$minor";
2590             }
2591             else {
2592 1         8 return $major;
2593             }
2594             }
2595             else {
2596 0         0 return undef;
2597             }
2598             }
2599              
2600             sub robot_major {
2601 104     104 1 110304 my ($self) = @_;
2602 104         404 my ( $major, $minor, $beta ) = $self->_robot_version;
2603 104         665 return $major;
2604             }
2605              
2606             sub robot_minor {
2607 103     103 1 97545 my ($self) = @_;
2608 103         433 my ( $major, $minor, $beta ) = $self->_robot_version;
2609 103         698 return $minor;
2610             }
2611              
2612             sub robot_beta {
2613 108     108 1 113327 my ($self) = @_;
2614 108         594 my ( $major, $minor, $beta ) = $self->_robot_version;
2615 108         639 return $beta;
2616             }
2617              
2618             sub os {
2619 4225     4225 1 1116699 my ($self) = @_;
2620              
2621 4225 50       17723 return undef unless defined $self->{user_agent};
2622 4225 100       20094 $self->_init_os unless $self->{os_tests};
2623 4225         18441 return $self->{os};
2624             }
2625              
2626             sub os_string {
2627 4206     4206 1 1068006 my ($self) = @_;
2628              
2629 4206 50       15246 return undef unless defined $self->{user_agent};
2630 4206 100       14189 $self->_init_os unless $self->{os_tests};
2631 4206         16640 return $self->{os_string};
2632             }
2633              
2634             sub _os_version {
2635 4011     4011   9674 my ($self) = @_;
2636 4011 100       27438 $self->_init_os_version if !exists( $self->{os_version} );
2637 4011 100       11547 if ( $self->{os_version} ) {
2638 1975         3822 return @{ $self->{os_version} };
  1975         12632  
2639             }
2640             else {
2641 2036         8330 return ( undef, undef, undef );
2642             }
2643             }
2644              
2645             sub os_version {
2646 999     999 1 448449 my ($self) = @_;
2647 999         4653 my ( $major, $minor, $beta ) = $self->_os_version;
2648 999 100       6701 return defined $major ? "$major$minor" : undef;
2649             }
2650              
2651             sub os_major {
2652 988     988 1 514074 my ($self) = @_;
2653 988         4653 my ( $major, $minor, $beta ) = $self->_os_version;
2654 988         4688 return $major;
2655             }
2656              
2657             sub os_minor {
2658 987     987 1 422463 my ($self) = @_;
2659 987         4613 my ( $major, $minor, $beta ) = $self->_os_version;
2660 987         4481 return $minor;
2661             }
2662              
2663             sub os_beta {
2664 1037     1037 1 470848 my ($self) = @_;
2665 1037         5039 my ( $major, $minor, $beta ) = $self->_os_version;
2666 1037         4538 return $beta;
2667             }
2668              
2669             sub _realplayer_version {
2670 0     0   0 my ($self) = @_;
2671              
2672 0 0       0 $self->_init_version unless $self->{version_tests};
2673 0   0     0 return $self->{realplayer_version} || 0;
2674             }
2675              
2676             sub realplayer_browser {
2677 755     755 1 164485 my ($self) = @_;
2678 755   100     10546 return defined( $self->{browser} ) && $self->{browser} eq 'realplayer';
2679             }
2680              
2681             sub gecko_version {
2682 696     696 1 142686 my ($self) = @_;
2683              
2684 696 100       3249 if ( $self->gecko ) {
2685 68         267 return $self->{engine_version};
2686             }
2687             else {
2688 628         2855 return undef;
2689             }
2690             }
2691              
2692             sub version {
2693 902     902 1 372033 my ($self) = @_;
2694 902 100       5388 $self->_init_version() unless $self->{version_tests};
2695              
2696 902 50       11735 return defined $self->{major} ? "$self->{major}$self->{minor}" : undef;
2697             }
2698              
2699             sub major {
2700 1211     1211 1 326121 my ($self) = @_;
2701 1211 100       5658 $self->_init_version() unless $self->{version_tests};
2702              
2703 1211         4200 my ($version) = $self->{major};
2704 1211         5597 return $version;
2705             }
2706              
2707             sub minor {
2708 941     941 1 357900 my ($self) = @_;
2709 941 100       4050 $self->_init_version() unless $self->{version_tests};
2710              
2711 941         2796 my ($version) = $self->{minor};
2712 941         3571 return $version;
2713             }
2714              
2715             sub public_version {
2716 883     883 1 317070 my ($self) = @_;
2717 883         4149 my ( $major, $minor ) = $self->_public;
2718              
2719 883   100     4242 $minor ||= q{};
2720 883 100       6032 return defined $major ? "$major$minor" : undef;
2721             }
2722              
2723             sub public_major {
2724 832     832 1 309530 my ($self) = @_;
2725 832         3863 my ( $major, $minor ) = $self->_public;
2726              
2727 832         3318 return $major;
2728             }
2729              
2730             sub public_minor {
2731 906     906 1 300356 my ($self) = @_;
2732 906         5725 my ( $major, $minor ) = $self->_public;
2733              
2734 906         4069 return $minor;
2735             }
2736              
2737             sub public_beta {
2738 0     0 1 0 my ($self) = @_;
2739 0         0 my ( $major, $minor, $beta ) = $self->_public;
2740              
2741 0         0 return $beta;
2742             }
2743              
2744             sub browser_version {
2745 22     22 1 20849 my ($self) = @_;
2746 22         189 my ( $major, $minor ) = $self->_public;
2747 22   100     109 $minor ||= q{};
2748              
2749 22 100       219 return defined $major ? "$major$minor" : undef;
2750             }
2751              
2752             sub browser_major {
2753 912     912 1 994894 my ($self) = @_;
2754 912         4589 my ( $major, $minor ) = $self->_public;
2755              
2756 912         6449 return $major;
2757             }
2758              
2759             sub browser_minor {
2760 912     912 1 924026 my ($self) = @_;
2761 912         3980 my ( $major, $minor ) = $self->_public;
2762              
2763 912         9672 return $minor;
2764             }
2765              
2766             sub browser_beta {
2767 369     369 1 418944 my ($self) = @_;
2768 369         1849 my ( $major, $minor, $beta ) = $self->_public;
2769              
2770 369         2769 return $beta;
2771             }
2772              
2773             sub _public {
2774 4836     4836   13352 my ($self) = @_;
2775              
2776             # Return Public version of Safari. See RT #48727.
2777 4836 100       17679 if ( $self->safari ) {
2778 640         3033 my $ua = lc $self->{user_agent};
2779              
2780             # Safari starting with version 3.0 provides its own public version
2781 640 100       5801 if (
2782             $ua =~ m{
2783             version/
2784             ( \d+ ) # Major version number is everything before first dot
2785             ( \. \d+ )? # Minor version number is first dot and following digits
2786             }x
2787             ) {
2788 467         3685 return ( $1, $2, undef );
2789             }
2790              
2791             # Safari before version 3.0 had only build numbers; use a lookup table
2792             # provided by Apple to convert to version numbers
2793              
2794 173 100       1456 if ( $ua =~ m{ safari/ ( \d+ (?: \.\d+ )* ) }x ) {
2795 140         687 my $build = $1;
2796 140         509 my $version = $safari_build_to_version{$build};
2797 140 100       653 unless ($version) {
2798              
2799             # if exact build -> version mapping doesn't exist, find next
2800             # lower build
2801              
2802 95         1571 for my $maybe_build (
2803 7335         13139 sort { $self->_cmp_versions( $b, $a ) }
2804             keys %safari_build_to_version
2805             ) {
2806 478 100       1011 $version = $safari_build_to_version{$maybe_build}, last
2807             if $self->_cmp_versions( $build, $maybe_build ) >= 0;
2808             }
2809              
2810             # Special case for specific worm that uses a malformed user agent
2811 95 100       687 return ( '1', '.2', undef ) if $ua =~ m{safari/12x};
2812             }
2813              
2814 135 100       512 return ( undef, undef, undef ) unless defined $version;
2815 130         626 my ( $major, $minor ) = split /\./, $version;
2816 130         311 my $beta;
2817 130 100       648 $minor =~ s/(\D.*)// and $beta = $1;
2818 130         311 $minor = ( '.' . $minor );
2819 130 100       874 return ( $major, $minor, ( $beta ? 1 : undef ) );
2820             }
2821             }
2822              
2823 4229 100       24727 $self->_init_version() unless $self->{version_tests};
2824 4229         25127 return ( $self->{major}, $self->{minor}, $self->{beta} );
2825             }
2826              
2827             sub _cmp_versions {
2828 7813     7813   14418 my ( $self, $a, $b ) = @_;
2829              
2830 7813         14222 my @a = split /\./, $a;
2831 7813         13306 my @b = split /\./, $b;
2832              
2833 7813         16677 while (@b) {
2834 8931 100 100     37275 return -1 if @a == 0 || $a[0] < $b[0];
2835 5053 100 66     19826 return 1 if @b == 0 || $b[0] < $a[0];
2836 1357         2132 shift @a;
2837 1357         3006 shift @b;
2838             }
2839              
2840 239         725 return @a <=> @b;
2841             }
2842              
2843             sub engine {
2844 1606     1606 1 1061443 my ($self) = @_;
2845              
2846             return
2847 1606 100       7380 !$self->engine_string ? undef
    100          
2848             : $self->engine_string eq 'MSIE' ? 'ie'
2849             : lc( $self->engine_string );
2850             }
2851              
2852             sub engine_string {
2853 5726     5726 1 947428 my ($self) = @_;
2854              
2855 5726 100       19085 if ( $self->gecko ) {
2856 720         4054 return 'Gecko';
2857             }
2858              
2859 5006 100       16209 if ( $self->trident ) {
2860 1019         5704 return 'Trident';
2861             }
2862              
2863 3987 100       12380 if ( $self->ie ) {
2864 595         3632 return 'MSIE';
2865             }
2866              
2867 3392 100       10528 if ( $self->edgelegacy ) {
2868 35         195 return 'EdgeHTML';
2869             }
2870              
2871 3357 100       10597 if ( $self->blink ) {
2872 583         3698 return 'Blink';
2873             }
2874              
2875 2774 100       7759 if ( $self->webkit ) {
2876 2187         13337 return 'WebKit';
2877             }
2878              
2879 587 100       1725 if ( $self->presto ) {
2880 151         936 return 'Presto';
2881             }
2882              
2883 436 100       1551 if ( $self->netfront ) {
2884 48         245 return 'NetFront';
2885             }
2886              
2887 388 100       1446 if ( $self->khtml ) {
2888 16         105 return 'KHTML';
2889             }
2890              
2891 372         1528 return undef;
2892             }
2893              
2894             sub engine_version {
2895 1583     1583 1 830379 my ($self) = @_;
2896              
2897             return $self->{engine_version}
2898 1583 100 100     27388 && $self->{engine_version} =~ m{^(\d+(\.\d+)?)} ? $1 : undef;
2899             }
2900              
2901             sub engine_major {
2902 2199     2199 1 1067436 my ($self) = @_;
2903              
2904             return $self->{engine_version}
2905 2199 100 100     30844 && $self->{engine_version} =~ m{^(\d+)} ? $1 : undef;
2906             }
2907              
2908             sub engine_minor {
2909 2103     2103 1 989134 my ($self) = @_;
2910              
2911             return $self->{engine_version}
2912 2103 100 100     30645 && $self->{engine_version} =~ m{^\d+(\.\d+)} ? $1 : undef;
2913             }
2914              
2915             sub engine_beta {
2916 1432     1432 1 868534 my ($self) = @_;
2917              
2918             return $self->{engine_version}
2919 1432 100 100     26020 && $self->{engine_version} =~ m{^\d+\.\d+([\.\d\+]*)} ? $1 : undef;
2920             }
2921              
2922             sub beta {
2923 728     728 1 168592 my ($self) = @_;
2924              
2925 728 100       3717 $self->_init_version unless $self->{version_tests};
2926              
2927 728         2443 my ($version) = $self->{beta};
2928 728         2477 return $version;
2929             }
2930              
2931             sub language {
2932 1081     1081 1 496934 my ($self) = @_;
2933              
2934 1081         5543 my $parsed = $self->_language_country();
2935 1081         7831 return $parsed->{'language'};
2936             }
2937              
2938             sub country {
2939 723     723 1 166455 my ($self) = @_;
2940              
2941 723         4069 my $parsed = $self->_language_country();
2942 723         3699 return $parsed->{'country'};
2943             }
2944              
2945             sub device {
2946 4163     4163 1 1249501 my ($self) = @_;
2947              
2948 4163 50       17531 $self->_init_device if !exists( $self->{device} );
2949 4163         16354 return $self->{device};
2950             }
2951              
2952             sub device_string {
2953 1811     1811 1 539173 my ($self) = @_;
2954              
2955 1811 100       10330 $self->_init_device if !exists( $self->{device_string} );
2956 1811         9274 return $self->{device_string};
2957             }
2958              
2959             sub device_name {
2960 685     685 1 162543 my ($self) = @_;
2961 685         3049 return $self->device_string;
2962             }
2963              
2964             sub _language_country {
2965 1804     1804   5265 my ($self) = @_;
2966              
2967 1804 100       7181 if ( $self->safari ) {
2968 268 100 66     1198 if ( $self->major == 1
2969             && $self->{user_agent} =~ m/\s ( [a-z]{2} ) \)/xms ) {
2970 7         59 return { language => uc $1 };
2971             }
2972 261 100       2604 if ( $self->{user_agent} =~ m/\s ([a-z]{2})-([A-Za-z]{2})/xms ) {
2973 151         1724 return { language => uc $1, country => uc $2 };
2974             }
2975             }
2976              
2977 1646 100 100     6269 if ( $self->aol
2978             && $self->{user_agent} =~ m/;([A-Z]{2})_([A-Z]{2})\)/ ) {
2979 1         11 return { language => $1, country => $2 };
2980             }
2981              
2982 1645 100 100     7700 if ( $self->meta_app
2983             && $self->{user_agent}
2984             =~ m{ ;FBLC/ ([a-z]{2}) (?: [_-] ([A-Z]{2}) )? }x ) {
2985 12 100       147 return { language => uc $1, $2 ? ( country => $2 ) : () };
2986             }
2987              
2988 1633 100 66     6465 if ( $self->instagram
2989             && $self->{user_agent}
2990             =~ m{ (?: [(] | ;[ ] ) ([a-z]{2}) [_-] ([A-Z]{2}) [;)] }x ) {
2991 4 50       43 return { language => uc $1, $2 ? ( country => $2 ) : () };
2992             }
2993              
2994 1629 100       12495 if ( $self->{user_agent} =~ m/\b([a-z]{2})-([A-Za-z]{2})\b/xms ) {
2995 348         4396 return { language => uc $1, country => uc $2 };
2996             }
2997              
2998 1281 100       5847 if ( $self->{user_agent} =~ m/\[([a-z]{2})\]/xms ) {
2999 27         196 return { language => uc $1 };
3000             }
3001              
3002 1254 100       9253 if ( $self->{user_agent} =~ m/\(([^)]+)\)/xms ) {
3003 1119         7623 my @parts = split( /;/, $1 );
3004 1119         2958 foreach my $part (@parts) {
3005              
3006             # 'wv' for WebView is not language code. Details here: https://developer.chrome.com/multidevice/user-agent#webview_user_agent
3007 4709 100 66     20922 if ( $part =~ /^\s*([a-z]{2})\s*$/
      100        
3008             && !( $self->webview && $1 eq 'wv' ) ) {
3009 125         1162 return { language => uc $1 };
3010             }
3011             }
3012             }
3013              
3014 1129         7378 return { language => undef, country => undef };
3015             }
3016              
3017             sub browser_properties {
3018 1942     1942 1 805206 my ($self) = @_;
3019              
3020 1942         4606 my @browser_properties;
3021              
3022 1942         5881 my ( $test, $value );
3023              
3024 1942         5302 while ( ( $test, $value ) = each %{ $self->{tests} } ) {
  3604         19317  
3025 1662 50       8972 push @browser_properties, $test if $value;
3026             }
3027 1942         4354 while ( ( $test, $value ) = each %{ $self->{browser_tests} } ) {
  3678         15453  
3028 1736 100       7720 push @browser_properties, $test if $value;
3029             }
3030              
3031 1942 50       10220 $self->_init_device unless $self->{device_tests};
3032 1942 100       9884 $self->_init_os unless $self->{os_tests};
3033 1942 100       8846 $self->_init_robots unless $self->{robot_tests};
3034 1942 100       7364 $self->_init_version unless $self->{version_tests};
3035              
3036 1942         4845 while ( ( $test, $value ) = each %{ $self->{device_tests} } ) {
  6319         22989  
3037 4377 100       13643 push @browser_properties, $test if $value;
3038             }
3039 1942         4382 while ( ( $test, $value ) = each %{ $self->{os_tests} } ) {
  5787         18809  
3040 3845 50       10992 push @browser_properties, $test if $value;
3041             }
3042 1942         4063 while ( ( $test, $value ) = each %{ $self->{robot_tests} } ) {
  2727         13092  
3043 785 50       2204 push @browser_properties, $test if $value;
3044             }
3045 1942         4181 while ( ( $test, $value ) = each %{ $self->{version_tests} } ) {
  4335         14283  
3046 2393 100       6316 push @browser_properties, $test if $value;
3047             }
3048              
3049             # devices are a property too but it's not stored in %tests
3050             # so I explicitly test for it and add it
3051 1942 100       9373 push @browser_properties, 'device' if ( $self->device() );
3052              
3053 1942         15163 @browser_properties = sort @browser_properties;
3054 1942         10182 return @browser_properties;
3055             }
3056              
3057             sub lib {
3058 30     30 1 31087 my $self = shift;
3059 30 100       165 $self->_init_robots() unless $self->{robot_tests};
3060 30         155 return $self->{robot_tests}->{lib};
3061             }
3062              
3063             sub all_robot_ids {
3064 2     2 1 116 my $self = shift;
3065 2         40 return keys %ROBOT_NAMES;
3066             }
3067              
3068             # The list of U2F supported browsers is expected to increase:
3069             # https://www.bit-tech.net/news/tech/software/w3c-adopts-fidos-webauthn-standard/1/
3070              
3071             sub u2f {
3072 0     0 1 0 my $self = shift;
3073              
3074             # Chrome version 41 and up can U2F
3075 0 0 0     0 return 1
      0        
3076             if $self->chrome
3077             && $self->browser_major
3078             && $self->browser_major >= 41;
3079              
3080             # Opera versions 40 and >= 42 can U2F
3081 0 0 0     0 return 1
      0        
      0        
3082             if $self->opera
3083             && $self->browser_major
3084             && ( $self->browser_major == 40
3085             || $self->browser_major >= 42 );
3086              
3087 0         0 return undef;
3088             }
3089              
3090             # These method are only used by the test suite.
3091             sub _all_tests {
3092 1     1   7544296 return @ALL_TESTS;
3093             }
3094              
3095             sub _robot_names {
3096 1     1   68 return %ROBOT_NAMES;
3097             }
3098              
3099             sub _robot_tests {
3100 2     2   7502999 return @ROBOT_TESTS;
3101             }
3102              
3103             sub _robot_ids {
3104 1     1   37 return %ROBOT_IDS;
3105             }
3106              
3107             1;
3108              
3109             # ABSTRACT: Determine Web browser, version, and platform from an HTTP user agent string
3110              
3111             __END__
3112              
3113             =pod
3114              
3115             =encoding UTF-8
3116              
3117             =head1 NAME
3118              
3119             HTTP::BrowserDetect - Determine Web browser, version, and platform from an HTTP user agent string
3120              
3121             =head1 VERSION
3122              
3123             version 3.45
3124              
3125             =head1 SYNOPSIS
3126              
3127             use HTTP::BrowserDetect ();
3128              
3129             my $user_agent_string
3130             = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36';
3131             my $ua = HTTP::BrowserDetect->new($user_agent_string);
3132              
3133             # Print general information
3134             print 'Browser: ' . $ua->browser_string . "\n" if $ua->browser_string;
3135             print 'Version: ' . $ua->browser_version . $ua->browser_beta . "\n" if $ua->browser_version;
3136             print 'OS: ' . $ua->os_string . "\n" if $ua->os_string;
3137              
3138             # Detect operating system
3139             if ( $ua->windows ) {
3140             if ( $ua->winnt ) {
3141             # do something
3142             }
3143             if ( $ua->win95 ) {
3144             # do something
3145             }
3146             }
3147             print "Mac\n" if $ua->macosx;
3148              
3149             # Detect browser vendor and version
3150             print "Safari\n" if $ua->safari;
3151             print "MSIE\n" if $ua->ie;
3152             print "Mobile\n" if $ua->mobile;
3153             if ( $ua->browser_major(4) ) {
3154             if ( $ua->browser_minor > .5 ) {
3155             # ...;
3156             }
3157             }
3158             if ( $ua->browser_version > 4.5 ) {
3159             # ...;
3160             }
3161              
3162             =head1 DESCRIPTION
3163              
3164             The HTTP::BrowserDetect object does a number of tests on an HTTP user agent
3165             string. The results of these tests are available via methods of the object.
3166              
3167             For an online demonstration of this module's parsing, you can check out
3168             L<https://www.browserdetect.org/>
3169              
3170             This module was originally based upon the JavaScript browser detection
3171             code available at
3172             L<http://www.mozilla.org/docs/web-developer/sniffer/browser_type.html>.
3173              
3174             =head1 CONSTRUCTOR AND STARTUP
3175              
3176             =head2 new()
3177              
3178             HTTP::BrowserDetect->new( $user_agent_string )
3179              
3180             The constructor may be called with a user agent string specified. Otherwise, it
3181             will use the value specified by $ENV{'HTTP_USER_AGENT'}, which is set by the
3182             web server when calling a CGI script.
3183              
3184             =head1 SUBROUTINES/METHODS
3185              
3186             =head1 Browser Information
3187              
3188             =head2 browser()
3189              
3190             Returns the browser, as one of the following values:
3191              
3192             chrome, firefox, ie, opera, safari, adm, applecoremedia, blackberry,
3193             brave, browsex, dalvik, elinks, links, lynx, emacs, epiphany, galeon,
3194             konqueror, icab, lotusnotes, mosaic, mozilla, netfront, netscape,
3195             n3ds, dsi, obigo, polaris, pubsub, realplayer, seamonkey, silk,
3196             staroffice, ucbrowser, webtv, samsung
3197              
3198             If the browser could not be identified (either because unrecognized
3199             or because it is a robot), returns C<undef>.
3200              
3201             =head2 browser_string()
3202              
3203             Returns a human formatted version of the browser name. These names are
3204             subject to change and are meant for display purposes. This may include
3205             information additional to what's in browser() (e.g. distinguishing
3206             Firefox from Iceweasel).
3207              
3208             If the user agent could not be identified, or if it was identified as
3209             a robot instead, returns C<undef>.
3210              
3211             =head1 Browser Version
3212              
3213             Please note that that the version(), major() and minor() methods have been
3214             deprecated as of release 1.78 of this module. They should be replaced
3215             with browser_version(), browser_major(), browser_minor(), and browser_beta().
3216              
3217             The reasoning behind this is that version() method will, in the case of Safari,
3218             return the Safari/XXX numbers even when Version/XXX numbers are present in the
3219             UserAgent string (i.e. it will return incorrect versions for Safari in
3220             some cases).
3221              
3222             =head2 browser_version()
3223              
3224             Returns the browser version (major and minor) as a string. For
3225             example, for Chrome 36.0.1985.67, this returns "36.0".
3226              
3227             =head2 browser_major()
3228              
3229             Returns the major part of the version as a string. For example, for
3230             Chrome 36.0.1985.67, this returns "36".
3231              
3232             Returns undef if no version information can be detected.
3233              
3234             =head2 browser_minor()
3235              
3236             Returns the minor part of the version as a string. This includes the
3237             decimal point; for example, for Chrome 36.0.1985.67, this returns
3238             ".0".
3239              
3240             Returns undef if no version information can be detected.
3241              
3242             =head2 browser_beta()
3243              
3244             Returns any part of the version after the major and minor version, as
3245             a string. For example, for Chrome 36.0.1985.67, this returns
3246             ".1985.67". The beta part of the string can contain any type of
3247             alphanumeric characters.
3248              
3249             Returns undef if no version information can be detected. Returns an
3250             empty string if version information is detected but it contains only
3251             a major and minor version with nothing following.
3252              
3253             =head1 Operating System
3254              
3255             =head2 os()
3256              
3257             Returns one of the following strings, or C<undef>:
3258              
3259             windows, winphone, mac, macosx, linux, android, ios, os2, unix, vms,
3260             chromeos, firefoxos, ps3, psp, rimtabletos, blackberry, amiga, brew
3261              
3262             =head2 os_string()
3263              
3264             Returns a human formatted version of the OS name. These names are
3265             subject to change and are really meant for display purposes. This may
3266             include information additional to what's in os() (e.g. distinguishing
3267             various editions of Windows from one another) (although for a way to
3268             do that that's more suitable for use in program logic, see below under
3269             "OS related properties").
3270              
3271             Returns C<undef> if no OS information could be detected.
3272              
3273             =head2 os_version(), os_major(), os_minor(), os_beta()
3274              
3275             Returns version information for the OS, if any could be detected. The
3276             format is the same as for the browser_version() functions.
3277              
3278             =head1 Mobile Devices
3279              
3280             =head2 mobile()
3281              
3282             Returns true if the browser appears to belong to a mobile phone or
3283             similar device (i.e. one small enough that the mobile version of a
3284             page is probably preferable over the desktop version).
3285              
3286             In previous versions, tablet devices sometimes had mobile() return
3287             true. They are now mutually exclusive.
3288              
3289             =head2 tablet()
3290              
3291             Returns true if the browser appears to belong to a tablet device.
3292              
3293             =head2 device()
3294              
3295             Returns the type of mobile / tablet hardware, if it can be detected.
3296              
3297             Currently returns one of: android, audrey, avantgo, blackberry, dsi, iopener, ipad,
3298             iphone, ipod, kindle, n3ds, palm, ps3, psp, wap, webos, winphone.
3299              
3300             Returns C<undef> if this is not a tablet/mobile device or no hardware
3301             information can be detected.
3302              
3303             =head2 device_string()
3304              
3305             Returns a human formatted version of the hardware device name. These names are
3306             subject to change and are really meant for display purposes. You should use
3307             the device() method in your logic. This may include additional
3308             information (such as the model of phone if it is detectable).
3309              
3310             Returns C<undef> if this is not a portable device or if no device name
3311             can be detected.
3312              
3313             =head1 Robots
3314              
3315             =head2 robot()
3316              
3317             If the user agent appears to be a robot, spider, crawler, or other
3318             automated Web client, this returns one of the following values:
3319              
3320             lwp, slurp, yahoo, bingbot, msnmobile, msn, msoffice, ahrefs,
3321             altavista, apache, askjeeves, baidu, curl, facebook, getright,
3322             googleadsbot, googleadsense, googlebotimage, googlebotnews,
3323             googlebotvideo, googlefavicon, googlemobile, google, golib, indy,
3324             infoseek, ipsagent, linkchecker, linkexchange, lycos, malware,
3325             mj12bot, nutch, phplib, puf, pythonurllib, rubylib, scooter, specialarchiver,
3326             wget, yandexbot, yandeximages, java, headlesschrome, amazonbot,
3327             unknown
3328              
3329             Returns "unknown" when the user agent is believed to be a robot but
3330             is not identified as one of the above specific robots.
3331              
3332             Returns C<undef> if the user agent is not a robot or cannot be
3333             identified.
3334              
3335             Note that if a robot crafts a user agent designed to impersonate a
3336             particular browser, we generally set properties appropriate to both
3337             the actual robot, and the browser it is impersonating. For example,
3338             googlebot-mobile pretends to be mobile safari so that it will get
3339             mobile versions of pages. In this case, browser() will return
3340             'safari', the properties will generally be set as if for Mobile
3341             Safari, the 'robot' property will be set, and robot() will return
3342             'googlemobile'.
3343              
3344             =head3 lib()
3345              
3346             Returns true if the user agent appears to be an HTTP library or tool
3347             (e.g. LWP, curl, wget, java). Generally libraries are also classified
3348             as robots, although it is impossible to tell whether they are being
3349             operated by an automated system or a human.
3350              
3351             =head3 robot_string()
3352              
3353             Returns a human formatted version of the robot name. These names are
3354             subject to change and are meant for display purposes. This may include
3355             additional information (e.g. robots which return "unknown" from
3356             robot() generally can be identified in a human-readable fashion by
3357             reading robot_string() ).
3358              
3359             =head3 robot_id()
3360              
3361             This method is currently in beta.
3362              
3363             Returns an id consisting of lower case letters, numbers and dashes. This id
3364             will remain constant, so you can use it for matching against a particular
3365             robot. The ids were introduced in version 3.14. There may still be a few
3366             corrections to ids in subsequent releases. Once this method becomes stable the
3367             ids will also be frozen.
3368              
3369             =head3 all_robot_ids()
3370              
3371             This method returns an C<ArrayRef> of all possible C<robot_id> values.
3372              
3373             =head2 robot_version(), robot_major(), robot_minor(), robot_beta()
3374              
3375             Returns version information for the robot, if any could be
3376             detected. The format is the same as for the browser_version()
3377             functions.
3378              
3379             Note that if a robot crafts a user agent designed to impersonate a
3380             particular browser, we generally return results appropriate to both
3381             the actual robot, and the browser it is impersonating. For example,
3382             googlebot-mobile pretends to be mobile safari so that it will get
3383             mobile versions of pages. In this case, robot_version() will return
3384             the version of googlebot-mobile, and browser_version() will return the
3385             version of Safari that googlebot-mobile is impersonating.
3386              
3387             =head1 Browser Properties
3388              
3389             Operating systems, devices, browser names, rendering engines, and
3390             true-or-false methods (e.g. "mobile" and "lib") are all browser
3391             properties. For example, calling browser_properties() for Mobile
3392             Safari running on an Android will return this list:
3393              
3394             ('android', 'device', 'mobile', 'mobile_safari', 'safari', 'webkit')
3395              
3396             =head2 browser_properties()
3397              
3398             Returns all properties for this user agent, as a list. Note that
3399             because a large number of cases must be considered, this will take
3400             significantly more time than simply querying the particular methods
3401             you care about.
3402              
3403             A mostly complete list of properties follows (i.e. each of these
3404             methods is both a method you can call, and also a property that may
3405             be in the list returned by browser_properties() ). In addition to this
3406             list, robot(), lib(), device(), mobile(), and tablet() are all
3407             browser properties.
3408              
3409             =head2 OS related properties
3410              
3411             The following methods are available, each returning a true or false value.
3412             Some methods also test for the operating system version. The indentations
3413             below show the hierarchy of tests (for example, win2k is considered a type of
3414             winnt, which is a type of win32)
3415              
3416             =head3 windows()
3417              
3418             win16 win3x win31
3419             win32
3420             winme win95 win98
3421             winnt
3422             win2k winxp win2k3 winvista win7
3423             win8
3424             win8_0 win8_1
3425             win10
3426             win10_0
3427             wince
3428             winphone
3429             winphone7 winphone7_5 winphone8 winphone10
3430              
3431             =head3 dotnet()
3432              
3433             =head3 x11()
3434              
3435             =head3 webview()
3436              
3437             =head3 chromeos()
3438              
3439             =head3 firefoxos()
3440              
3441             =head3 mac()
3442              
3443             mac68k macppc macosx ios
3444              
3445             =head3 os2()
3446              
3447             =head3 bb10()
3448              
3449             =head3 rimtabletos()
3450              
3451             =head3 unix()
3452              
3453             sun sun4 sun5 suni86 irix irix5 irix6 hpux hpux9 hpux10
3454             aix aix1 aix2 aix3 aix4 linux sco unixware mpras reliant
3455             dec sinix freebsd bsd
3456              
3457             =head3 vms()
3458              
3459             =head3 amiga()
3460              
3461             =head3 ps3gameos()
3462              
3463             =head3 pspgameos()
3464              
3465             It may not be possible to detect Win98 in Netscape 4.x and earlier. On Opera
3466             3.0, the userAgent string includes "Windows 95/NT4" on all Win32, so you can't
3467             distinguish between Win95 and WinNT.
3468              
3469             =head2 Browser related properties
3470              
3471             The following methods are available, each returning a true or false value.
3472             Some methods also test for the browser version, saving you from checking the
3473             version separately.
3474              
3475             =head3 adm
3476              
3477             =head3 aol aol3 aol4 aol5 aol6
3478              
3479             =head3 applecoremedia
3480              
3481             =head3 avantgo
3482              
3483             =head3 browsex
3484              
3485             =head3 chrome
3486              
3487             =head3 dalvik
3488              
3489             =head3 emacs
3490              
3491             =head3 epiphany
3492              
3493             =head3 firefox
3494              
3495             =head3 galeon
3496              
3497             =head3 icab
3498              
3499             =head3 ie ie3 ie4 ie4up ie5 ie5up ie55 ie55up ie6 ie7 ie8 ie9 ie10 ie11
3500              
3501             =head3 ie_compat_mode
3502              
3503             The ie_compat_mode is used to determine if the IE user agent is for
3504             the compatibility mode view, in which case the real version of IE is
3505             higher than that detected. The true version of IE can be inferred from
3506             the version of Trident in the engine_version method.
3507              
3508             =head3 konqueror
3509              
3510             =head3 lotusnotes
3511              
3512             =head3 lynx links elinks
3513              
3514             =head3 mobile_safari
3515              
3516             =head3 mosaic
3517              
3518             =head3 mozilla
3519              
3520             =head3 neoplanet neoplanet2
3521              
3522             =head3 netfront
3523              
3524             =head3 netscape nav2 nav3 nav4 nav4up nav45 nav45up navgold nav6 nav6up
3525              
3526             =head3 obigo
3527              
3528             =head3 opera opera3 opera4 opera5 opera6 opera7
3529              
3530             =head3 polaris
3531              
3532             =head3 pubsub
3533              
3534             =head3 realplayer
3535              
3536             The realplayer method above tests for the presence of either the RealPlayer
3537             plug-in "(r1 " or the browser "RealPlayer".
3538              
3539             =head3 realplayer_browser
3540              
3541             The realplayer_browser method tests for the presence of the RealPlayer
3542             browser (but returns 0 for the plugin).
3543              
3544             =head3 safari
3545              
3546             =head3 samsung
3547              
3548             =head3 seamonkey
3549              
3550             =head3 silk
3551              
3552             =head3 staroffice
3553              
3554             =head3 ucbrowser
3555              
3556             =head3 webtv
3557              
3558             Netscape 6, even though it's called six, in the User-Agent string has version
3559             number 5. The nav6 and nav6up methods correctly handle this quirk. The Firefox
3560             test correctly detects the older-named versions of the browser (Phoenix,
3561             Firebird).
3562              
3563             =head2 Device related properties
3564              
3565             The following methods are available, each returning a true or false value.
3566              
3567             =head3 android
3568              
3569             =head3 audrey
3570              
3571             =head3 avantgo
3572              
3573             =head3 blackberry
3574              
3575             =head3 dsi
3576              
3577             =head3 iopener
3578              
3579             =head3 iphone
3580              
3581             =head3 ipod
3582              
3583             =head3 ipad
3584              
3585             =head3 kindle
3586              
3587             =head3 kindlefire
3588              
3589             =head3 n3ds
3590              
3591             =head3 palm
3592              
3593             =head3 webos
3594              
3595             =head3 wap
3596              
3597             Note that 'wap' indicates that the device is capable of WAP, not
3598             necessarily that the device is limited to WAP only. Most modern WAP
3599             devices are also capable of rendering standard HTML.
3600              
3601             =head3 psp
3602              
3603             =head3 ps3
3604              
3605             =head2 Robot properties
3606              
3607             These methods are now deprecated and will be removed in a future release.
3608             Please use the C<robot()> and C<robot_id()> methods to identify the bots. Use
3609             C<robot_id()> if you need to match on a string, since the value that is
3610             returned by C<robot> could possibly change in a future release.
3611              
3612             The following additional methods are available, each returning a true or false
3613             value. This is by no means a complete list of robots that exist on the Web.
3614              
3615             =head3 ahrefs
3616              
3617             =head3 altavista
3618              
3619             =head3 apache
3620              
3621             =head3 askjeeves
3622              
3623             =head3 baidu
3624              
3625             =head3 bingbot
3626              
3627             =head3 curl
3628              
3629             =head3 facebook
3630              
3631             =head3 getright
3632              
3633             =head3 golib
3634              
3635             =head3 google
3636              
3637             =head3 googleadsbot
3638              
3639             =head3 googleadsense
3640              
3641             =head3 googlemobile
3642              
3643             =head3 indy
3644              
3645             =head3 infoseek
3646              
3647             =head3 ipsagent
3648              
3649             =head3 java
3650              
3651             =head3 linkexchange
3652              
3653             =head3 lwp
3654              
3655             =head3 lycos
3656              
3657             =head3 malware
3658              
3659             =head3 mj12bot
3660              
3661             =head3 msn
3662              
3663             =head3 msoffice
3664              
3665             =head3 puf
3666              
3667             =head3 pythonurllib
3668              
3669             =head3 rubylib
3670              
3671             =head3 slurp
3672              
3673             =head3 wget
3674              
3675             =head3 yahoo
3676              
3677             =head3 yandex
3678              
3679             =head3 yandeximages
3680              
3681             =head3 headlesschrome
3682              
3683             =head2 Engine properties
3684              
3685             The following properties indicate if a particular rendering engine is
3686             being used.
3687              
3688             =head3 webkit
3689              
3690             =head3 blink
3691              
3692             =head3 gecko
3693              
3694             =head3 trident
3695              
3696             =head3 presto
3697              
3698             =head3 khtml
3699              
3700             =head1 Other methods
3701              
3702             =head2 user_agent()
3703              
3704             Returns the value of the user agent string.
3705              
3706             Calling this method with a parameter to set the user agent has now
3707             been removed; please use HTTP::BrowserDetect->new() to pass the user
3708             agent string.
3709              
3710             =head2 u2f()
3711              
3712             Returns true if this browser and version are known to support Universal Second
3713             Factor (U2F). This method will need future updates as more browsers fully
3714             support this standard.
3715              
3716             =head2 country()
3717              
3718             Returns the country string as it may be found in the user agent string. This
3719             will be in the form of an upper case 2 character code. ie: US, DE, etc
3720              
3721             =head2 language()
3722              
3723             Returns the language string as it is found in the user agent string. This will
3724             be in the form of an upper case 2 character code. ie: EN, DE, etc
3725              
3726             =head2 engine()
3727              
3728             Returns the rendering engine, one of the following:
3729              
3730             gecko, webkit, blink, khtml, trident, ie, presto, netfront
3731              
3732             Note that Chrome versions 38 and above return "blink", while earlier
3733             Chrome versions and other WebKit-based browsers return "webkit". This is
3734             a change from previous versions of this library which returned "webkit"
3735             for all WebKit-based browsers including Chrome/Blink.
3736              
3737             Returns C<undef> if none of the above rendering engines can be
3738             detected.
3739              
3740             =head2 engine_string()
3741              
3742             Returns a human formatted version of the rendering engine.
3743              
3744             Note that Chrome versions 38 and above return "Blink", while earlier
3745             Chrome versions and other WebKit-based browsers return "WebKit". This is
3746             a change from previous versions of this library which returned "WebKit"
3747             for all WebKit-based browsers including Chrome/Blink.
3748              
3749             Returns C<undef> if none of the known rendering engines can be
3750             detected.
3751              
3752             =head2 engine_version(), engine_major(), engine_minor(), engine_beta()
3753              
3754             Returns version information for the rendering engine, if any could be
3755             detected. The format is the same as for the browser_version()
3756             functions.
3757              
3758             =head1 Deprecated methods
3759              
3760             =head2 device_name()
3761              
3762             Deprecated alternate name for device_string()
3763              
3764             =head2 version()
3765              
3766             This is probably not what you want. Please use either browser_version() or
3767             engine_version() instead.
3768              
3769             Returns the version (major and minor) as a string.
3770              
3771             This function returns wrong values for some Safari versions, for
3772             compatibility with earlier code. browser_version() returns correct
3773             version numbers for Safari.
3774              
3775             =head2 major()
3776              
3777             This is probably not what you want. Please use either browser_major()
3778             or engine_major() instead.
3779              
3780             Returns the integer portion of the browser version as a string.
3781              
3782             This function returns wrong values for some Safari versions, for
3783             compatibility with earlier code. browser_version() returns correct
3784             version numbers for Safari.
3785              
3786             =head2 minor()
3787              
3788             This is probably not what you want. Please use either browser_minor()
3789             or engine_minor() instead.
3790              
3791             Returns the decimal portion of the browser version as a string.
3792              
3793             This function returns wrong values for some Safari versions, for
3794             compatibility with earlier code. browser_version() returns correct
3795             version numbers for Safari.
3796              
3797             =head2 beta()
3798              
3799             This is probably not what you want. Please use browser_beta() instead.
3800              
3801             Returns the beta version, consisting of any characters after the major
3802             and minor version number, as a string.
3803              
3804             This function returns wrong values for some Safari versions, for
3805             compatibility with earlier code. browser_version() returns correct
3806             version numbers for Safari.
3807              
3808             =head2 public_version(), public_major(), public_minor(), public_beta()
3809              
3810             Deprecated. Please use browser_version() and related functions
3811             instead.
3812              
3813             =head2 gecko_version()
3814              
3815             If a Gecko rendering engine is used (as in Mozilla or Firefox), returns the
3816             engine version. If no Gecko browser is being used, or the version
3817             number can't be detected, returns undef.
3818              
3819             This is an old function, preserved for compatibility; please use
3820             engine_version() in new code.
3821              
3822             =head1 CREDITS
3823              
3824             Lee Semel, lee@semel.net (Original Author)
3825              
3826             Peter Walsham (co-maintainer)
3827              
3828             Olaf Alders, C<olaf at wundercounter.com> (co-maintainer)
3829              
3830             =head1 ACKNOWLEDGEMENTS
3831              
3832             Thanks to the following for their contributions:
3833              
3834             cho45
3835              
3836             Leonardo Herrera
3837              
3838             Denis F. Latypoff
3839              
3840             merlynkline
3841              
3842             Simon Waters
3843              
3844             Toni Cebrin
3845              
3846             Florian Merges
3847              
3848             david.hilton.p
3849              
3850             Steve Purkis
3851              
3852             Andrew McGregor
3853              
3854             Robin Smidsrod
3855              
3856             Richard Noble
3857              
3858             Josh Ritter
3859              
3860             Mike Clarke
3861              
3862             Marc Sebastian Pelzer
3863              
3864             Alexey Surikov
3865              
3866             Maros Kollar
3867              
3868             Jay Rifkin
3869              
3870             Luke Saunders
3871              
3872             Jacob Rask
3873              
3874             Heiko Weber
3875              
3876             Jon Jensen
3877              
3878             Jesse Thompson
3879              
3880             Graham Barr
3881              
3882             Enrico Sorcinelli
3883              
3884             Olivier Bilodeau
3885              
3886             Yoshiki Kurihara
3887              
3888             Paul Findlay
3889              
3890             Uwe Voelker
3891              
3892             Douglas Christopher Wilson
3893              
3894             John Oatis
3895              
3896             Atsushi Kato
3897              
3898             Ronald J. Kimball
3899              
3900             Bill Rhodes
3901              
3902             Thom Blake
3903              
3904             Aran Deltac
3905              
3906             yeahoffline
3907              
3908             David Ihnen
3909              
3910             Hao Wu
3911              
3912             Perlover
3913              
3914             Daniel Stadie
3915              
3916             ben hengst
3917              
3918             Andrew Moise
3919              
3920             Atsushi Kato
3921              
3922             Marco Fontani
3923              
3924             Nicolas Doye
3925              
3926             =head1 TO DO
3927              
3928             POD coverage is not 100%.
3929              
3930             =head1 SEE ALSO
3931              
3932             "Browser ID (User-Agent) Strings", L<http://www.zytrax.com/tech/web/browser_ids.htm>
3933              
3934             L<HTML::ParseBrowser>.
3935              
3936             =head1 SUPPORT
3937              
3938             You can find documentation for this module with the perldoc command.
3939              
3940             perldoc HTTP::BrowserDetect
3941              
3942             You can also look for information at:
3943              
3944             =over 4
3945              
3946             =item * GitHub Source Repository
3947              
3948             L<https://github.com/oalders/http-browserdetect>
3949              
3950             =item * Reporting Issues
3951              
3952             L<https://github.com/oalders/http-browserdetect/issues>
3953              
3954             =item * Search CPAN
3955              
3956             L<https://metacpan.org/module/HTTP::BrowserDetect>
3957              
3958             =back
3959              
3960             =head1 CONTRIBUTING
3961              
3962             Patches are certainly welcome, with many thanks for the excellent contributions
3963             which have already been received. The preferred method of patching would be to
3964             fork the GitHub repo and then send a pull request.
3965              
3966             Please include a test case as this will speed up the time to release your
3967             changes. Just edit t/useragents.json so that the test coverage includes any
3968             changes you have made. Please open a GitHub issue if you have any questions.
3969              
3970             =head1 AUTHORS
3971              
3972             =over 4
3973              
3974             =item *
3975              
3976             Lee Semel <lee@semel.net>
3977              
3978             =item *
3979              
3980             Peter Walsham
3981              
3982             =item *
3983              
3984             Olaf Alders <olaf@wundercounter.com> (current maintainer)
3985              
3986             =back
3987              
3988             =head1 COPYRIGHT AND LICENSE
3989              
3990             This software is copyright (c) 1999 by Lee Semel.
3991              
3992             This is free software; you can redistribute it and/or modify it under
3993             the same terms as the Perl 5 programming language system itself.
3994              
3995             =cut