Skip to content

Commit 468fc9a

Browse files
v1.1.0 (#4)
* refactor(matchers): revamp matchers to use parser - uses single copy of user agent, reducing mem foot print - user agent is abstracted away with parser * refactor(devices): revamp devices to use parser - uses single copy of user agent, reducing mem foot print - user agent is abstracted away with parser * refactor(platforms): revamp platforms to use parser - uses single copy of user agent, reducing mem foot print - user agent is abstracted away with parser * feat(matcher): add firefox browser * feat(matcher): add brave browser * feat(matcher): add vivaldi browser * fix(matcher): add compile time check for UAParser * docs: update readme and logo
1 parent 24d1df6 commit 468fc9a

File tree

152 files changed

+1678
-1402
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

152 files changed

+1678
-1402
lines changed

README.md

Lines changed: 3 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# browser [![MIT](https://img.shields.io/github/license/dineshgowda24/browser)](https://github.com/dineshgowda24/browser/blob/main/LICENSE) [![Go Reference](https://pkg.go.dev/badge/github.com/dineshgowda24/browser.svg)](https://pkg.go.dev/github.com/dineshgowda24/browser) [![Go report card](https://goreportcard.com/badge/github.com/dineshgowda24/browser)](https://goreportcard.com/report/github.com/dineshgowda24/browser) [![Build Status](https://dl.circleci.com/status-badge/img/circleci/MQTLZJuBejHgr2yqrojz3u/5NTLeuQeViQw2JaPQf7gKa/tree/main.svg?style=shield&circle-token=ab7a417fe410b8387c767f83568f7d2f2788ac4f)](https://dl.circleci.com/status-badge/redirect/circleci/MQTLZJuBejHgr2yqrojz3u/5NTLeuQeViQw2JaPQf7gKa/tree/main) [![Coverage](https://codecov.io/gh/dineshgowda24/browser/graph/badge.svg?token=XUA2VJW5FU)](https://codecov.io/gh/dineshgowda24/browser) [![X](https://img.shields.io/twitter/follow/_dineshgowda)](https://twitter.com/_dineshgowda)
22

33
<p align="center">
4-
<img src="logo.png" width="125">
4+
<img src="logo.png">
55
</p>
66

77
## Why?
@@ -21,73 +21,9 @@ I wanted a relatively extensible package that I could use in all the above use c
2121

2222
The ruby gem [fnando/browser](https://github.com/fnando/browser) inspires this package. I have used the gem in some of my previous projects and liked it. All the credit goes to the author of the ruby gem, who has done a great job.
2323

24-
## Design
24+
## Documentation
2525

26-
I have kept the design as similar as possible to the gem but made changes where I felt necessary per the Go.
27-
28-
The following are the sub-packages:
29-
30-
- **matchers**: This package defines all the browser matchers.
31-
- **devices**: This package defines all the device matchers.
32-
- **platforms**: This package defines all the platform matchers.
33-
- **bots**: This package defines all the bots matchers.
34-
35-
A `Matcher` interface defines a matching behaviour for a user agent string.
36-
37-
```go
38-
type Matchers interface {
39-
Match() bool // Match returns true if the user agent string matches the matcher.
40-
Name() string // Name returns the name of the matcher.
41-
}
42-
```
43-
44-
### Matchers
45-
46-
`BrowserMatcher` interface matches the user agent string with the browser. Implement the `BrowserMatcher` interface to add a new browser.
47-
48-
```go
49-
type BrowserMatcher interface {
50-
Matcher
51-
Version() string // Version returns the full version of the browser.
52-
}
53-
```
54-
55-
### Devices
56-
57-
`DeviceMatcher` interface matches the user agent string with the device. Implement the `DeviceMatcher` interface to add a new device.
58-
59-
```go
60-
type DeviceMatcher interface {
61-
Matcher
62-
}
63-
```
64-
65-
### Platforms
66-
67-
`PlatformMatcher` interface matches the user agent string with the platform. Implement the `PlatformMatcher` interface to add a new device.
68-
69-
```go
70-
type PlatformMatcher interface {
71-
Matcher
72-
Version() string // Version returns the version of the platform.
73-
}
74-
```
75-
76-
### Bots
77-
78-
`BotMatcher` interface matches the user agent string with the bot. Implement the `BotMatcher` interface to add a new bot.
79-
80-
```go
81-
type BotMatcher interface {
82-
Matcher
83-
}
84-
```
85-
86-
### Browser Struct
87-
88-
`Browser` struct abstracts a lot of functionality. It uses the `BrowserMatcher`, `DeviceMatcher`, `PlatformMatcher` and `BotMatcher` interfaces to match the user agent string with the browser, device, platform and bot respectively. All the matchers are executed in the order they are defined in the `Browser` struct. The first matcher that returns `true` will be used.
89-
90-
A ton of helper functions are defined in the `Browser` struct to make it easy to use.
26+
For detailed documentation visit [browser.dineshgowda.com](https://browser.dineshgowda.com). It has adoption guides, usage, contributing guidelines and also the list of all the matchers and browsers supported.
9127

9228
## Usage
9329

assets/test/matchers.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ blackberry:
1010
linux: Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.1.0.2342 Mobile Safari/537.10+(Linux LLC 1.2)
1111
mac: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36@i-0bb1056c6cb023f60
1212
windows: Mozilla/5.0 (Blackberry; U; BlackBerry 9900; en) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.1.0.346 Mobile Safari/534.11+ (Windows NT 6.2; WOW64; Trident/7.0; rv:11.0) like Gecko
13+
brave:
14+
linux: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) brave/0.7.7 Chrome/47.0.2526.73 Electron/0.36.2 Safari/537.36
15+
mac: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) brave/0.7.7 Chrome/47.0.2526.73 Electron/0.36.3 Safari/537.36
16+
windows: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) brave/0.7.7 Chrome/47.0.2526.73 Electron/0.36.2 Safari/537.36
1317
chrome:
1418
android: Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/_BuildID_) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36
1519
ios: Mozilla/5.0 (iPhone; CPU iPhone OS 8_2 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) CriOS/44.0.2403.67 Mobile/12D508 Safari/600.1.4
@@ -31,6 +35,12 @@ electron:
3135
linux: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Atom/1.1.0 Chrome/43.0.2357.65 Electron/0.30.7 Safari/537.36
3236
mac: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Franz/0.9.8 Chrome/47.0.2526.110 Electron/0.36.9 Safari/537.36
3337
windows: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Electron/0.36.4 Safari/537.36
38+
firefox:
39+
android: Mozilla/5.0 (Android 8.0.0; Mobile; rv:59.0.2) Gecko/59.0.2 Firefox/59.0.2
40+
ios: Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/7.0.4 Mobile/16A404 Safari/605.1.15
41+
linux: Mozilla/5.0 (Linux; U; Linux 2.6; en-US; rv:1.9.9.2) Gecko/20100722 Firefox/3.6.8
42+
mac: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14_1; rv:64.0) Gecko/20100101 Firefox/64.0
43+
windows: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.8) Gecko/20100722 AskTbBLT/3.8.0.12304 Firefox/3.6.8 GTB7.1
3444
gsa:
3545
android: Mozilla/5.0 (LINUX; ANDROID 4.4.2; LG-X135 Build/KOT49H.V10c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 MOBILE Safari/537.36 GSA/5.10.32.19.arm
3646
ios: Mozilla/5.0 (iPhone; CPU iPhone OS 8_4_1 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) GSA/7.0.55539 Mobile/12H321 Safari/600.1.4
@@ -143,6 +153,10 @@ uc-browser:
143153
unknown:
144154
ios: Mozilla/30.0 (AppleCoreMedia/1.0.0.11B554a (iPad; U; CPU OS 7_0_4 like Mac OS X; en_us)
145155
mac: QuickTime\x5Cxaa.7.0.4 (qtver=7.0.4;cpu=PPC;os=Mac 10.3.9)
156+
vivaldi:
157+
linux: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.86 Safari/537.36 Vivaldi/1.0.264.3
158+
mac: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.43 Safari/537.36 Vivaldi/1.0.252.3
159+
windows: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.75 Safari/537.36 Vivaldi/1.0.219.50
146160
vivo-browser:
147161
android: Mozilla/5.0 (Linux; Android 5.1.1; vivo V3Max A Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/38.0.0.0 Mobile Safari/537.36 VivoBrowser/5.0.10
148162
ios: Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X; zh-CN) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/4.0 Chrome/38.0.0.0 Mobile Safari/537.36 VivoBrowser/5.0.10

browser.go

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
const (
10-
userAgentSizeLimit = 2048 // 2KB
10+
userAgentSizeLimit = 2048 // 2KiB
1111
)
1212

1313
// Matcher is an interface for user agent matchers.
@@ -66,38 +66,42 @@ func NewBrowser(userAgent string) (*Browser, error) {
6666
// register registers the browser matcher detected from the user agent string.
6767
func (b *Browser) register() {
6868
// add all your browser matchers here, order matters
69+
parser := matchers.NewUAParser(b.userAgent)
6970
matchers := []BrowserMatcher{
70-
matchers.NewAlipay(b.userAgent),
71-
matchers.NewNokia(b.userAgent),
72-
matchers.NewUCBrowser(b.userAgent),
73-
matchers.NewBlackBerry(b.userAgent),
74-
matchers.NewOpera(b.userAgent),
75-
matchers.NewOtter(b.userAgent),
76-
matchers.NewInstagram(b.userAgent),
77-
matchers.NewSnapchat(b.userAgent),
78-
matchers.NewWeibo(b.userAgent),
79-
matchers.NewMicroMessenger(b.userAgent),
80-
matchers.NewQQ(b.userAgent),
81-
matchers.NewElectron(b.userAgent),
82-
matchers.NewDuckDuckGo(b.userAgent),
83-
matchers.NewGoogleSearchApp(b.userAgent),
84-
matchers.NewHuaweiBrowser(b.userAgent),
85-
matchers.NewKonqueror(b.userAgent),
86-
matchers.NewMaxthon(b.userAgent),
87-
matchers.NewMiuiBrowser(b.userAgent),
88-
matchers.NewPaleMoon(b.userAgent),
89-
matchers.NewPuffin(b.userAgent),
90-
matchers.NewEdge(b.userAgent),
91-
matchers.NewInternetExplorer(b.userAgent),
92-
matchers.NewSamsungBrowser(b.userAgent),
93-
matchers.NewSogouBrowser(b.userAgent),
94-
matchers.NewVivoBrowser(b.userAgent),
95-
matchers.NewSputnik(b.userAgent),
96-
matchers.NewYaaniBrowser(b.userAgent),
97-
matchers.NewYandex(b.userAgent),
98-
matchers.NewChrome(b.userAgent), // chrome should be before safari
99-
matchers.NewSafari(b.userAgent), // chrome and safari must be at the end
100-
matchers.NewUnknown(b.userAgent),
71+
matchers.NewAlipay(parser),
72+
matchers.NewNokia(parser),
73+
matchers.NewUCBrowser(parser),
74+
matchers.NewBlackBerry(parser),
75+
matchers.NewBrave(parser),
76+
matchers.NewOpera(parser),
77+
matchers.NewOtter(parser),
78+
matchers.NewInstagram(parser),
79+
matchers.NewSnapchat(parser),
80+
matchers.NewWeibo(parser),
81+
matchers.NewMicroMessenger(parser),
82+
matchers.NewQQ(parser),
83+
matchers.NewElectron(parser),
84+
matchers.NewDuckDuckGo(parser),
85+
matchers.NewGoogleSearchApp(parser),
86+
matchers.NewHuaweiBrowser(parser),
87+
matchers.NewKonqueror(parser),
88+
matchers.NewMaxthon(parser),
89+
matchers.NewMiuiBrowser(parser),
90+
matchers.NewPaleMoon(parser),
91+
matchers.NewPuffin(parser),
92+
matchers.NewEdge(parser),
93+
matchers.NewInternetExplorer(parser),
94+
matchers.NewSamsungBrowser(parser),
95+
matchers.NewSogouBrowser(parser),
96+
matchers.NewVivaldi(parser),
97+
matchers.NewVivoBrowser(parser),
98+
matchers.NewSputnik(parser),
99+
matchers.NewYaaniBrowser(parser),
100+
matchers.NewYandex(parser),
101+
matchers.NewFirefox(parser),
102+
matchers.NewChrome(parser), // chrome should be before safari
103+
matchers.NewSafari(parser), // chrome and safari must be at the end
104+
matchers.NewUnknown(parser),
101105
}
102106

103107
for _, matcher := range matchers {
@@ -204,6 +208,17 @@ func (b *Browser) IsBlackBerry() bool {
204208
return false
205209
}
206210

211+
// IsBrave returns true if the browser is Brave.
212+
//
213+
// https://brave.com/
214+
func (b *Browser) IsBrave() bool {
215+
if _, ok := b.getMatcher().(*matchers.Brave); ok {
216+
return true
217+
}
218+
219+
return false
220+
}
221+
207222
// IsOpera returns true if the browser is Opera.
208223
//
209224
// https://www.opera.com/
@@ -296,6 +311,17 @@ func (b *Browser) IsElectron() bool {
296311
return false
297312
}
298313

314+
// IsFirefox returns true if the browser is Firefox.
315+
//
316+
// https://www.mozilla.org/firefox/
317+
func (b *Browser) IsFirefox() bool {
318+
if _, ok := b.getMatcher().(*matchers.Firefox); ok {
319+
return true
320+
}
321+
322+
return false
323+
}
324+
299325
// IsDuckDuckGo returns true if the browser is DuckDuckGo.
300326
//
301327
// https://duckduckgo.com/
@@ -420,6 +446,17 @@ func (b *Browser) IsSougouBrowser() bool {
420446
return false
421447
}
422448

449+
// IsVivaldi returns true if the browser is Vivaldi.
450+
//
451+
// https://vivaldi.com/
452+
func (b *Browser) IsVivaldi() bool {
453+
if _, ok := b.getMatcher().(*matchers.Vivaldi); ok {
454+
return true
455+
}
456+
457+
return false
458+
}
459+
423460
// IsVivoBrowser returns true if the browser is VivoBrowser.
424461
//
425462
// https://www.vivo.com/

browser_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,3 +966,63 @@ func TestBrowserIsEdge(t *testing.T) {
966966
})
967967
})
968968
}
969+
970+
func TestBrowserIsFirefox(t *testing.T) {
971+
Convey("Subject: #IsFirefox", t, func() {
972+
Convey("When the browser is Firefox", func() {
973+
Convey("It should return true", func() {
974+
ua := testUserAgents["firefox"]
975+
b, _ := NewBrowser(ua.Mac)
976+
So(b.IsFirefox(), ShouldBeTrue)
977+
})
978+
})
979+
980+
Convey("When the browser is not Firefox", func() {
981+
Convey("It should return false", func() {
982+
ua := testUserAgents["chrome"]
983+
b, _ := NewBrowser(ua.Windows)
984+
So(b.IsFirefox(), ShouldBeFalse)
985+
})
986+
})
987+
})
988+
}
989+
990+
func TestBrowserIsBrave(t *testing.T) {
991+
Convey("Subject: #IsBrave", t, func() {
992+
Convey("When the browser is Brave", func() {
993+
Convey("It should return true", func() {
994+
ua := testUserAgents["brave"]
995+
b, _ := NewBrowser(ua.Windows)
996+
So(b.IsBrave(), ShouldBeTrue)
997+
})
998+
})
999+
1000+
Convey("When the browser is not Brave", func() {
1001+
Convey("It should return false", func() {
1002+
ua := testUserAgents["chrome"]
1003+
b, _ := NewBrowser(ua.Windows)
1004+
So(b.IsBrave(), ShouldBeFalse)
1005+
})
1006+
})
1007+
})
1008+
}
1009+
1010+
func TestBrowserIsVivaldi(t *testing.T) {
1011+
Convey("Subject: #IsVivaldi", t, func() {
1012+
Convey("When the browser is Vivaldi", func() {
1013+
Convey("It should return true", func() {
1014+
ua := testUserAgents["vivaldi"]
1015+
b, _ := NewBrowser(ua.Windows)
1016+
So(b.IsVivaldi(), ShouldBeTrue)
1017+
})
1018+
})
1019+
1020+
Convey("When the browser is not Vivaldi", func() {
1021+
Convey("It should return false", func() {
1022+
ua := testUserAgents["chrome"]
1023+
b, _ := NewBrowser(ua.Windows)
1024+
So(b.IsVivaldi(), ShouldBeFalse)
1025+
})
1026+
})
1027+
})
1028+
}

device.go

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -42,30 +42,31 @@ func NewDevice(userAgent string) (*Device, error) {
4242

4343
// register registers the device matcher detected from the user agent string
4444
func (d *Device) register() {
45+
parser := devices.NewUAParser(d.userAgent)
4546
// define all your device matchers here
4647
matchers := []DeviceMatcher{
47-
devices.NewBlackberryPlaybook(d.userAgent),
48-
devices.NewIphone(d.userAgent),
49-
devices.NewIpad(d.userAgent),
50-
devices.NewIpodTouch(d.userAgent),
51-
devices.NewKindleFire(d.userAgent),
52-
devices.NewKindle(d.userAgent),
53-
devices.NewPlayStation3(d.userAgent),
54-
devices.NewPlayStation4(d.userAgent),
55-
devices.NewPlayStation5(d.userAgent),
56-
devices.NewPSVita(d.userAgent),
57-
devices.NewPSP(d.userAgent),
58-
devices.NewWiiU(d.userAgent),
59-
devices.NewWii(d.userAgent),
60-
devices.NewXboxOne(d.userAgent), // this must be before xbox 360
61-
devices.NewXbox360(d.userAgent),
62-
devices.NewSwitch(d.userAgent),
63-
devices.NewSurface(d.userAgent),
64-
devices.NewKindle(d.userAgent),
65-
devices.NewSamsung(d.userAgent),
66-
devices.NewTV(d.userAgent), // this must be before android
67-
devices.NewAndroid(d.userAgent),
68-
devices.NewUnknown(d.userAgent),
48+
devices.NewBlackberryPlaybook(parser),
49+
devices.NewIphone(parser),
50+
devices.NewIpad(parser),
51+
devices.NewIpodTouch(parser),
52+
devices.NewKindleFire(parser),
53+
devices.NewKindle(parser),
54+
devices.NewPlayStation3(parser),
55+
devices.NewPlayStation4(parser),
56+
devices.NewPlayStation5(parser),
57+
devices.NewPSVita(parser),
58+
devices.NewPSP(parser),
59+
devices.NewWiiU(parser),
60+
devices.NewWii(parser),
61+
devices.NewXboxOne(parser), // this must be before xbox 360
62+
devices.NewXbox360(parser),
63+
devices.NewSwitch(parser),
64+
devices.NewSurface(parser),
65+
devices.NewKindle(parser),
66+
devices.NewSamsung(parser),
67+
devices.NewTV(parser), // this must be before android
68+
devices.NewAndroid(parser),
69+
devices.NewUnknown(parser),
6970
}
7071

7172
for _, matcher := range matchers {

0 commit comments

Comments
 (0)