browser 2.5.3 → 5.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +1 -1
- data/.github/workflows/tests.yml +57 -0
- data/.gitignore +1 -0
- data/.prettierignore +1 -0
- data/.rubocop.yml +22 -107
- data/CHANGELOG.md +170 -6
- data/FUNDING.yml +3 -0
- data/Gemfile +2 -0
- data/README.md +139 -89
- data/Rakefile +11 -2
- data/bot_exceptions.yml +2 -0
- data/bots.yml +302 -265
- data/browser.gemspec +12 -7
- data/gemfiles/rails6_0.gemfile +6 -0
- data/gemfiles/rails6_1.gemfile +6 -0
- data/lib/browser/accept_language.rb +6 -9
- data/lib/browser/action_controller.rb +1 -3
- data/lib/browser/aliases.rb +5 -5
- data/lib/browser/alipay.rb +2 -2
- data/lib/browser/base.rb +101 -19
- data/lib/browser/blackberry.rb +3 -3
- data/lib/browser/bot/empty_user_agent_matcher.rb +11 -0
- data/lib/browser/bot/keyword_matcher.rb +11 -0
- data/lib/browser/bot/known_bots_matcher.rb +11 -0
- data/lib/browser/bot.rb +39 -27
- data/lib/browser/browser.rb +65 -54
- data/lib/browser/chrome.rb +18 -5
- data/lib/browser/detect_version.rb +5 -5
- data/lib/browser/device/android.rb +20 -0
- data/lib/browser/device/blackberry_playbook.rb +1 -1
- data/lib/browser/device/ipad.rb +1 -1
- data/lib/browser/device/iphone.rb +1 -1
- data/lib/browser/device/ipod_touch.rb +1 -1
- data/lib/browser/device/kindle.rb +1 -1
- data/lib/browser/device/kindle_fire.rb +1 -1
- data/lib/browser/device/playstation3.rb +1 -1
- data/lib/browser/device/playstation4.rb +1 -1
- data/lib/browser/device/psp.rb +1 -1
- data/lib/browser/device/psvita.rb +1 -1
- data/lib/browser/device/samsung.rb +33 -0
- data/lib/browser/device/surface.rb +2 -4
- data/lib/browser/device/switch.rb +19 -0
- data/lib/browser/device/tv.rb +1 -1
- data/lib/browser/device/unknown.rb +1 -1
- data/lib/browser/device/wii.rb +1 -1
- data/lib/browser/device/wiiu.rb +1 -1
- data/lib/browser/device/xbox_360.rb +1 -1
- data/lib/browser/device/xbox_one.rb +1 -1
- data/lib/browser/device.rb +47 -30
- data/lib/browser/duck_duck_go.rb +22 -0
- data/lib/browser/edge.rb +6 -2
- data/lib/browser/electron.rb +2 -2
- data/lib/browser/facebook.rb +4 -2
- data/lib/browser/firefox.rb +2 -2
- data/lib/browser/google_search_app.rb +21 -0
- data/lib/browser/huawei_browser.rb +21 -0
- data/lib/browser/instagram.rb +21 -0
- data/lib/browser/internet_explorer.rb +9 -10
- data/lib/browser/maxthon.rb +21 -0
- data/lib/browser/meta/base.rb +0 -1
- data/lib/browser/meta/generic_browser.rb +1 -3
- data/lib/browser/meta.rb +12 -13
- data/lib/browser/micro_messenger.rb +2 -2
- data/lib/browser/middleware/context/additions.rb +1 -1
- data/lib/browser/middleware.rb +4 -3
- data/lib/browser/miui_browser.rb +21 -0
- data/lib/browser/nokia.rb +2 -2
- data/lib/browser/opera.rb +2 -2
- data/lib/browser/otter.rb +2 -2
- data/lib/browser/phantom_js.rb +2 -2
- data/lib/browser/platform/adobe_air.rb +2 -2
- data/lib/browser/platform/android.rb +1 -1
- data/lib/browser/platform/base.rb +3 -2
- data/lib/browser/platform/blackberry.rb +2 -2
- data/lib/browser/platform/chrome_os.rb +1 -1
- data/lib/browser/platform/firefox_os.rb +1 -1
- data/lib/browser/platform/ios.rb +17 -4
- data/lib/browser/platform/kai_os.rb +23 -0
- data/lib/browser/platform/linux.rb +1 -1
- data/lib/browser/platform/mac.rb +5 -3
- data/lib/browser/platform/{other.rb → unknown.rb} +3 -3
- data/lib/browser/platform/windows.rb +2 -2
- data/lib/browser/platform/windows_mobile.rb +1 -1
- data/lib/browser/platform/windows_phone.rb +1 -1
- data/lib/browser/platform.rb +37 -28
- data/lib/browser/qq.rb +5 -5
- data/lib/browser/rails.rb +7 -5
- data/lib/browser/safari.rb +19 -4
- data/lib/browser/samsung_browser.rb +21 -0
- data/lib/browser/snapchat.rb +21 -0
- data/lib/browser/sougou_browser.rb +24 -0
- data/lib/browser/sputnik.rb +21 -0
- data/lib/browser/uc_browser.rb +2 -2
- data/lib/browser/{generic.rb → unknown.rb} +6 -8
- data/lib/browser/version.rb +1 -1
- data/lib/browser/weibo.rb +2 -2
- data/lib/browser/yandex.rb +21 -0
- data/lib/browser.rb +2 -2
- data/samsung.yml +138 -0
- data/search_engines.yml +2 -2
- data/test/browser_test.rb +42 -10
- data/test/rails_test.rb +10 -0
- data/test/sample_app.rb +14 -0
- data/test/test_helper.rb +9 -1
- data/test/ua.yml +147 -109
- data/test/ua_bots.yml +95 -60
- data/test/ua_search_engines.yml +7 -6
- data/test/unit/accept_language_test.rb +16 -0
- data/test/unit/adobe_air_test.rb +1 -1
- data/test/unit/alipay_test.rb +6 -0
- data/test/unit/blackberry_test.rb +0 -6
- data/test/unit/bots_test.rb +37 -36
- data/test/unit/chrome_test.rb +8 -19
- data/test/unit/console_test.rb +2 -2
- data/test/unit/device_test.rb +60 -3
- data/test/unit/duck_duck_go_test.rb +37 -0
- data/test/unit/edge_test.rb +49 -5
- data/test/unit/facebook_test.rb +20 -0
- data/test/unit/firefox_test.rb +0 -3
- data/test/unit/google_search_app_test.rb +54 -0
- data/test/unit/huawei_browser_test.rb +25 -0
- data/test/unit/instagram_test.rb +30 -0
- data/test/unit/internet_explorer_test.rb +0 -12
- data/test/unit/ios_test.rb +7 -5
- data/test/unit/kai_os_test.rb +31 -0
- data/test/unit/kindle_test.rb +0 -2
- data/test/unit/maxthon_test.rb +25 -0
- data/test/unit/meta_test.rb +10 -2
- data/test/unit/micro_messenger_test.rb +21 -6
- data/test/unit/miui_browser_test.rb +25 -0
- data/test/unit/opera_test.rb +1 -2
- data/test/unit/platform_test.rb +34 -8
- data/test/unit/qq_test.rb +12 -0
- data/test/unit/safari_test.rb +12 -7
- data/test/unit/samsung_browser_test.rb +23 -0
- data/test/unit/snapchat_test.rb +40 -0
- data/test/unit/sougou_browser_test.rb +41 -0
- data/test/unit/sputnik_test.rb +22 -0
- data/test/unit/yandex_test.rb +37 -0
- metadata +83 -27
- data/.bundle/config +0 -2
- data/.travis.yml +0 -16
- data/bin/rake +0 -17
- data/gemfiles/rails4.gemfile +0 -4
- data/gemfiles/rails5.gemfile +0 -4
- data/lib/browser/meta/modern.rb +0 -11
@@ -14,8 +14,7 @@ module Browser
|
|
14
14
|
.map {|string| string.squeeze(" ").strip }
|
15
15
|
.map {|part| new(part) }
|
16
16
|
.reject {|al| al.quality.zero? }
|
17
|
-
.sort_by
|
18
|
-
.reverse
|
17
|
+
.sort_by.with_index {|al, idx| [-al.quality, idx] }
|
19
18
|
end
|
20
19
|
|
21
20
|
attr_reader :part
|
@@ -35,14 +34,14 @@ module Browser
|
|
35
34
|
def code
|
36
35
|
@code ||= begin
|
37
36
|
code = part[/\A([^-;]+)/, 1]
|
38
|
-
code
|
37
|
+
code&.downcase
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
42
41
|
def region
|
43
42
|
@region ||= begin
|
44
43
|
region = part[/\A(?:.*?)-([^;-]+)/, 1]
|
45
|
-
region
|
44
|
+
region&.upcase
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
@@ -54,12 +53,10 @@ module Browser
|
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
57
|
-
private
|
58
|
-
|
59
|
-
def quality_value
|
56
|
+
private def quality_value
|
60
57
|
qvalue = part[/;q=([\d.]+)/, 1]
|
61
|
-
qvalue =
|
62
|
-
qvalue = qvalue.
|
58
|
+
qvalue = /\A0\.0?\z/.match?(qvalue) ? "0.0" : qvalue
|
59
|
+
qvalue = qvalue.squeeze(".") if qvalue
|
63
60
|
qvalue
|
64
61
|
end
|
65
62
|
end
|
data/lib/browser/aliases.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative "../browser"
|
4
4
|
require "forwardable"
|
5
5
|
|
6
6
|
module Browser
|
@@ -15,10 +15,10 @@ module Browser
|
|
15
15
|
|
16
16
|
DEVICE_ALIASES = %w[
|
17
17
|
blackberry_playbook? console? ipad? iphone? ipod_touch? kindle?
|
18
|
-
kindle_fire? mobile? nintendo?
|
19
|
-
|
20
|
-
|
21
|
-
xbox_one?
|
18
|
+
kindle_fire? mobile? nintendo? nintendo_switch? nintendo_wii?
|
19
|
+
nintendo_wiiu? playbook? playstation3? playstation4? playstation?
|
20
|
+
playstation_vita? ps3? ps4? psp? psp_vita? silk? surface? tablet? tv?
|
21
|
+
vita? wii? wiiu? xbox? xbox_360? xbox_one?
|
22
22
|
].freeze
|
23
23
|
|
24
24
|
def self.included(target)
|
data/lib/browser/alipay.rb
CHANGED
data/lib/browser/base.rb
CHANGED
@@ -6,12 +6,11 @@ module Browser
|
|
6
6
|
|
7
7
|
attr_reader :ua
|
8
8
|
|
9
|
-
# Return an array with all preferred languages that this browser accepts.
|
10
|
-
attr_reader :accept_language
|
11
|
-
|
12
9
|
def initialize(ua, accept_language: nil)
|
10
|
+
validate_size(:user_agent, ua.to_s)
|
11
|
+
|
13
12
|
@ua = ua
|
14
|
-
@
|
13
|
+
@accept_language_raw = accept_language.to_s
|
15
14
|
end
|
16
15
|
|
17
16
|
# Return a meta info about this browser.
|
@@ -19,6 +18,14 @@ module Browser
|
|
19
18
|
Meta.get(self)
|
20
19
|
end
|
21
20
|
|
21
|
+
# Return an array with all preferred languages that this browser accepts.
|
22
|
+
def accept_language
|
23
|
+
@accept_language ||= begin
|
24
|
+
validate_size(:accept_language, @accept_language_raw)
|
25
|
+
AcceptLanguage.parse(@accept_language_raw)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
22
29
|
alias_method :to_a, :meta
|
23
30
|
|
24
31
|
# Return meta representation as string.
|
@@ -50,11 +57,6 @@ module Browser
|
|
50
57
|
@device ||= Device.new(ua)
|
51
58
|
end
|
52
59
|
|
53
|
-
# Return true if browser is modern (Webkit, Firefox 17+, IE9+, Opera 12+).
|
54
|
-
def modern?
|
55
|
-
Browser.modern_rules.any? {|rule| rule === self } # rubocop:disable Metrics/LineLength, Style/CaseEquality
|
56
|
-
end
|
57
|
-
|
58
60
|
# Detect if browser is Microsoft Internet Explorer.
|
59
61
|
def ie?(expected_version = nil)
|
60
62
|
InternetExplorer.new(ua).match? &&
|
@@ -78,6 +80,18 @@ module Browser
|
|
78
80
|
"0"
|
79
81
|
end
|
80
82
|
|
83
|
+
# Detect if browser is Instagram.
|
84
|
+
def instagram?(expected_version = nil)
|
85
|
+
Instagram.new(ua).match? &&
|
86
|
+
detect_version?(full_version, expected_version)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Detect if browser is Snapchat.
|
90
|
+
def snapchat?(expected_version = nil)
|
91
|
+
Snapchat.new(ua).match? &&
|
92
|
+
detect_version?(full_version, expected_version)
|
93
|
+
end
|
94
|
+
|
81
95
|
# Detect if browser if Facebook.
|
82
96
|
def facebook?(expected_version = nil)
|
83
97
|
Facebook.new(ua).match? &&
|
@@ -92,19 +106,20 @@ module Browser
|
|
92
106
|
|
93
107
|
# Detect if browser is WebKit-based.
|
94
108
|
def webkit?(expected_version = nil)
|
95
|
-
ua
|
96
|
-
!edge? &&
|
109
|
+
ua.match?(/AppleWebKit/i) &&
|
110
|
+
(!edge? || Edge.new(ua).chrome_based?) &&
|
97
111
|
detect_version?(webkit_full_version, expected_version)
|
98
112
|
end
|
99
113
|
|
100
114
|
# Detect if browser is QuickTime
|
101
115
|
def quicktime?(expected_version = nil)
|
102
|
-
ua
|
116
|
+
ua.match?(/QuickTime/i) && detect_version?(full_version, expected_version)
|
103
117
|
end
|
104
118
|
|
105
119
|
# Detect if browser is Apple CoreMedia.
|
106
120
|
def core_media?(expected_version = nil)
|
107
|
-
ua
|
121
|
+
ua.include?("CoreMedia") && detect_version?(full_version,
|
122
|
+
expected_version)
|
108
123
|
end
|
109
124
|
|
110
125
|
# Detect if browser is PhantomJS
|
@@ -115,11 +130,11 @@ module Browser
|
|
115
130
|
|
116
131
|
# Detect if browser is Safari.
|
117
132
|
def safari?(expected_version = nil)
|
118
|
-
Safari.new(ua).match? && detect_version?(
|
133
|
+
Safari.new(ua).match? && detect_version?(full_version, expected_version)
|
119
134
|
end
|
120
135
|
|
121
136
|
def safari_webapp_mode?
|
122
|
-
(device.ipad? || device.iphone?) && ua
|
137
|
+
(device.ipad? || device.iphone?) && ua.include?("AppleWebKit")
|
123
138
|
end
|
124
139
|
|
125
140
|
# Detect if browser is Firefox.
|
@@ -137,10 +152,16 @@ module Browser
|
|
137
152
|
Opera.new(ua).match? && detect_version?(full_version, expected_version)
|
138
153
|
end
|
139
154
|
|
155
|
+
# Detect if browser is Sputnik.
|
156
|
+
def sputnik?(expected_version = nil)
|
157
|
+
Sputnik.new(ua).match? && detect_version?(full_version, expected_version)
|
158
|
+
end
|
159
|
+
|
140
160
|
# Detect if browser is Yandex.
|
141
161
|
def yandex?(expected_version = nil)
|
142
|
-
ua
|
162
|
+
Yandex.new(ua).match? && detect_version?(full_version, expected_version)
|
143
163
|
end
|
164
|
+
alias_method :yandex_browser?, :yandex?
|
144
165
|
|
145
166
|
# Detect if browser is UCBrowser.
|
146
167
|
def uc_browser?(expected_version = nil)
|
@@ -171,15 +192,65 @@ module Browser
|
|
171
192
|
|
172
193
|
# Detect if browser is Opera Mini.
|
173
194
|
def opera_mini?(expected_version = nil)
|
174
|
-
ua
|
195
|
+
ua.include?("Opera Mini") && detect_version?(full_version,
|
196
|
+
expected_version)
|
197
|
+
end
|
198
|
+
|
199
|
+
# Detect if browser is DuckDuckGo.
|
200
|
+
def duck_duck_go?(expected_version = nil)
|
201
|
+
ua.include?("DuckDuckGo") && detect_version?(full_version,
|
202
|
+
expected_version)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Detect if browser is Samsung.
|
206
|
+
def samsung_browser?(expected_version = nil)
|
207
|
+
ua.include?("SamsungBrowser") && detect_version?(full_version,
|
208
|
+
expected_version)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Detect if browser is Huawei.
|
212
|
+
def huawei_browser?(expected_version = nil)
|
213
|
+
HuaweiBrowser.new(ua).match? &&
|
214
|
+
detect_version?(full_version, expected_version)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Detect if browser is Xiaomi Miui.
|
218
|
+
def miui_browser?(expected_version = nil)
|
219
|
+
MiuiBrowser.new(ua).match? &&
|
220
|
+
detect_version?(full_version, expected_version)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Detect if browser is Maxthon.
|
224
|
+
def maxthon?(expected_version = nil)
|
225
|
+
Maxthon.new(ua).match? && detect_version?(full_version, expected_version)
|
226
|
+
end
|
227
|
+
|
228
|
+
# Detect if browser is QQ.
|
229
|
+
def qq?(expected_version = nil)
|
230
|
+
QQ.new(ua).match? && detect_version?(full_version, expected_version)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Detect if browser is Sougou.
|
234
|
+
def sougou_browser?(expected_version = nil)
|
235
|
+
SougouBrowser.new(ua).match? &&
|
236
|
+
detect_version?(full_version, expected_version)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Detect if browser is Google Search App
|
240
|
+
def google_search_app?(expected_version = nil)
|
241
|
+
ua.include?("GSA") && detect_version?(full_version, expected_version)
|
175
242
|
end
|
176
243
|
|
177
244
|
def webkit_full_version
|
178
|
-
ua[%r
|
245
|
+
ua[%r{AppleWebKit/([\d.]+)}, 1] || "0.0"
|
179
246
|
end
|
180
247
|
|
181
248
|
def known?
|
182
|
-
|
249
|
+
!unknown?
|
250
|
+
end
|
251
|
+
|
252
|
+
def unknown?
|
253
|
+
id == :unknown_browser
|
183
254
|
end
|
184
255
|
|
185
256
|
# Detect if browser is a proxy browser.
|
@@ -191,5 +262,16 @@ module Browser
|
|
191
262
|
def electron?(expected_version = nil)
|
192
263
|
Electron.new(ua).match? && detect_version?(full_version, expected_version)
|
193
264
|
end
|
265
|
+
|
266
|
+
private def validate_size(subject, input)
|
267
|
+
actual_bytesize = input.bytesize
|
268
|
+
size_limit = Browser.public_send("#{subject}_size_limit")
|
269
|
+
|
270
|
+
return if actual_bytesize < size_limit
|
271
|
+
|
272
|
+
raise Error,
|
273
|
+
"#{subject} cannot be larger than #{size_limit} bytes; " \
|
274
|
+
"actual size is #{actual_bytesize} bytes"
|
275
|
+
end
|
194
276
|
end
|
195
277
|
end
|
data/lib/browser/blackberry.rb
CHANGED
@@ -11,13 +11,13 @@ module Browser
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def full_version
|
14
|
-
ua[%r
|
15
|
-
ua[%r
|
14
|
+
ua[%r{BlackBerry[\da-z]+/([\d.]+)}, 1] ||
|
15
|
+
ua[%r{Version/([\d.]+)}, 1] ||
|
16
16
|
"0.0"
|
17
17
|
end
|
18
18
|
|
19
19
|
def match?
|
20
|
-
ua
|
20
|
+
ua.match?(/BlackBerry|BB10/)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/lib/browser/bot.rb
CHANGED
@@ -2,64 +2,76 @@
|
|
2
2
|
|
3
3
|
module Browser
|
4
4
|
class Bot
|
5
|
-
|
6
|
-
|
5
|
+
GENERIC_NAME = "Generic Bot"
|
6
|
+
|
7
|
+
def self.matchers
|
8
|
+
@matchers ||= default_matchers
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.default_matchers
|
12
|
+
[
|
13
|
+
EmptyUserAgentMatcher,
|
14
|
+
KnownBotsMatcher,
|
15
|
+
KeywordMatcher
|
16
|
+
]
|
7
17
|
end
|
8
18
|
|
9
|
-
def self.
|
10
|
-
|
19
|
+
def self.load_yaml(path)
|
20
|
+
YAML.load_file(Browser.root.join(path))
|
11
21
|
end
|
12
22
|
|
13
23
|
def self.bots
|
14
|
-
@bots ||=
|
24
|
+
@bots ||= load_yaml("bots.yml")
|
15
25
|
end
|
16
26
|
|
17
27
|
def self.bot_exceptions
|
18
|
-
@bot_exceptions ||=
|
19
|
-
.load_file(Browser.root.join("bot_exceptions.yml"))
|
28
|
+
@bot_exceptions ||= load_yaml("bot_exceptions.yml")
|
20
29
|
end
|
21
30
|
|
22
31
|
def self.search_engines
|
23
|
-
@search_engines ||=
|
24
|
-
|
32
|
+
@search_engines ||= load_yaml("search_engines.yml")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.why?(ua)
|
36
|
+
ua = ua.downcase.strip
|
37
|
+
browser = Browser.new(ua)
|
38
|
+
matchers.find {|matcher| matcher.call(ua, browser) }
|
25
39
|
end
|
26
40
|
|
27
|
-
attr_reader :ua
|
41
|
+
attr_reader :ua, :browser
|
28
42
|
|
29
43
|
def initialize(ua)
|
30
|
-
@ua = ua
|
44
|
+
@ua = ua.downcase.strip
|
45
|
+
@browser = Browser.new(@ua)
|
31
46
|
end
|
32
47
|
|
33
48
|
def bot?
|
34
|
-
|
49
|
+
!bot_exception? && detect_bot?
|
50
|
+
end
|
51
|
+
|
52
|
+
def why?
|
53
|
+
self.class.matchers.find {|matcher| matcher.call(ua, self) }
|
35
54
|
end
|
36
55
|
|
37
56
|
def search_engine?
|
38
|
-
self.class.search_engines.any? {|key, _|
|
57
|
+
self.class.search_engines.any? {|key, _| ua.include?(key) }
|
39
58
|
end
|
40
59
|
|
41
60
|
def name
|
42
61
|
return unless bot?
|
43
|
-
return "Generic Bot" if bot_with_empty_ua?
|
44
|
-
self.class.bots.find {|key, _| downcased_ua.include?(key) }.last
|
45
|
-
end
|
46
62
|
|
47
|
-
|
48
|
-
|
49
|
-
def bot_with_empty_ua?
|
50
|
-
self.class.detect_empty_ua? && ua.strip == ""
|
63
|
+
self.class.bots.find {|key, _| ua.include?(key) }&.last || GENERIC_NAME
|
51
64
|
end
|
52
65
|
|
53
|
-
def bot_exception?
|
54
|
-
self.class.bot_exceptions.any? {|key|
|
66
|
+
private def bot_exception?
|
67
|
+
self.class.bot_exceptions.any? {|key| ua.include?(key) }
|
55
68
|
end
|
56
69
|
|
57
|
-
def detect_bot?
|
58
|
-
self.class.
|
70
|
+
private def detect_bot?
|
71
|
+
self.class.matchers.any? {|matcher| matcher.call(ua, browser) }
|
59
72
|
end
|
60
73
|
|
61
|
-
|
62
|
-
|
63
|
-
end
|
74
|
+
private :ua
|
75
|
+
private :browser
|
64
76
|
end
|
65
77
|
end
|
data/lib/browser/browser.rb
CHANGED
@@ -4,43 +4,66 @@ require "set"
|
|
4
4
|
require "yaml"
|
5
5
|
require "pathname"
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
7
|
+
require_relative "version"
|
8
|
+
require_relative "detect_version"
|
9
|
+
require_relative "accept_language"
|
10
|
+
require_relative "base"
|
11
|
+
require_relative "safari"
|
12
|
+
require_relative "chrome"
|
13
|
+
require_relative "internet_explorer"
|
14
|
+
require_relative "firefox"
|
15
|
+
require_relative "edge"
|
16
|
+
require_relative "opera"
|
17
|
+
require_relative "blackberry"
|
18
|
+
require_relative "unknown"
|
19
|
+
require_relative "phantom_js"
|
20
|
+
require_relative "uc_browser"
|
21
|
+
require_relative "nokia"
|
22
|
+
require_relative "micro_messenger"
|
23
|
+
require_relative "weibo"
|
24
|
+
require_relative "qq"
|
25
|
+
require_relative "alipay"
|
26
|
+
require_relative "electron"
|
27
|
+
require_relative "facebook"
|
28
|
+
require_relative "otter"
|
29
|
+
require_relative "instagram"
|
30
|
+
require_relative "yandex"
|
31
|
+
require_relative "sputnik"
|
32
|
+
require_relative "snapchat"
|
33
|
+
require_relative "duck_duck_go"
|
34
|
+
require_relative "samsung_browser"
|
35
|
+
require_relative "huawei_browser"
|
36
|
+
require_relative "miui_browser"
|
37
|
+
require_relative "maxthon"
|
38
|
+
require_relative "sougou_browser"
|
39
|
+
require_relative "google_search_app"
|
29
40
|
|
30
|
-
|
31
|
-
|
41
|
+
require_relative "bot"
|
42
|
+
require_relative "bot/empty_user_agent_matcher"
|
43
|
+
require_relative "bot/keyword_matcher"
|
44
|
+
require_relative "bot/known_bots_matcher"
|
32
45
|
|
33
|
-
|
34
|
-
|
35
|
-
|
46
|
+
require_relative "middleware"
|
47
|
+
require_relative "platform"
|
48
|
+
require_relative "device"
|
49
|
+
require_relative "meta"
|
36
50
|
|
37
51
|
module Browser
|
38
|
-
EMPTY_STRING = ""
|
52
|
+
EMPTY_STRING = ""
|
53
|
+
|
54
|
+
Error = Class.new(StandardError)
|
39
55
|
|
40
56
|
def self.root
|
41
|
-
@root ||= Pathname.new(File.expand_path("
|
57
|
+
@root ||= Pathname.new(File.expand_path("../..", __dir__))
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
attr_accessor :user_agent_size_limit, :accept_language_size_limit
|
42
62
|
end
|
43
63
|
|
64
|
+
self.user_agent_size_limit = 2048
|
65
|
+
self.accept_language_size_limit = 2048
|
66
|
+
|
44
67
|
# Hold the list of browser matchers.
|
45
68
|
# Order is important.
|
46
69
|
def self.matchers
|
@@ -55,40 +78,28 @@ module Browser
|
|
55
78
|
Firefox,
|
56
79
|
Otter,
|
57
80
|
Facebook, # must be placed before Chrome and Safari
|
81
|
+
Instagram, # must be placed before Chrome and Safari
|
82
|
+
Snapchat, # must be placed before Chrome and Safari
|
58
83
|
Weibo, # must be placed before Chrome and Safari
|
84
|
+
MicroMessenger, # must be placed before QQ
|
59
85
|
QQ, # must be placed before Chrome and Safari
|
60
86
|
Alipay, # must be placed before Chrome and Safari
|
61
87
|
Electron, # must be placed before Chrome and Safari
|
88
|
+
Yandex, # must be placed before Chrome and Safari
|
89
|
+
Sputnik, # must be placed before Chrome and Safari
|
90
|
+
DuckDuckGo, # must be placed before Chrome and Safari
|
91
|
+
SamsungBrowser, # must be placed before Chrome and Safari
|
92
|
+
HuaweiBrowser, # must be placed before Chrome and Safari
|
93
|
+
MiuiBrowser, # must be placed before Chrome and Safari
|
94
|
+
Maxthon, # must be placed before Chrome and Safari
|
95
|
+
SougouBrowser, # must be placed before Chrome and Safari
|
96
|
+
GoogleSearchApp, # must be placed before Chrome and Safari
|
62
97
|
Chrome,
|
63
98
|
Safari,
|
64
|
-
|
65
|
-
Generic
|
99
|
+
Unknown
|
66
100
|
]
|
67
101
|
end
|
68
102
|
|
69
|
-
# Define the rules which define a modern browser.
|
70
|
-
# A rule must be a proc/lambda or any object that implements the method
|
71
|
-
# === and accepts the browser object.
|
72
|
-
#
|
73
|
-
# To redefine all rules, clear the existing rules before adding your own.
|
74
|
-
#
|
75
|
-
# # Only Chrome Canary is considered modern.
|
76
|
-
# Browser.modern_rules.clear
|
77
|
-
# Browser.modern_rules << -> b { b.chrome? && b.version >= "37" }
|
78
|
-
#
|
79
|
-
def self.modern_rules
|
80
|
-
@modern_rules ||= []
|
81
|
-
end
|
82
|
-
|
83
|
-
modern_rules.tap do |rules|
|
84
|
-
rules << ->(b) { b.webkit? }
|
85
|
-
rules << ->(b) { b.firefox? && b.version.to_i >= 17 }
|
86
|
-
rules << ->(b) { b.ie? && b.version.to_i >= 9 && !b.compatibility_view? }
|
87
|
-
rules << ->(b) { b.edge? && !b.compatibility_view? }
|
88
|
-
rules << ->(b) { b.opera? && b.version.to_i >= 12 }
|
89
|
-
rules << ->(b) { b.firefox? && b.device.tablet? && b.platform.android? && b.version.to_i >= 14 } # rubocop:disable Metrics/LineLength
|
90
|
-
end
|
91
|
-
|
92
103
|
def self.new(user_agent, **kwargs)
|
93
104
|
matchers
|
94
105
|
.map {|klass| klass.new(user_agent || EMPTY_STRING, **kwargs) }
|
data/lib/browser/chrome.rb
CHANGED
@@ -12,15 +12,28 @@ module Browser
|
|
12
12
|
|
13
13
|
def full_version
|
14
14
|
# Each regex on its own line to enforce precedence.
|
15
|
-
ua[%r
|
16
|
-
ua[%r
|
17
|
-
ua[%r
|
18
|
-
ua[%r
|
15
|
+
ua[%r{Chrome/([\d.]+)}, 1] ||
|
16
|
+
ua[%r{CriOS/([\d.]+)}, 1] ||
|
17
|
+
ua[%r{Safari/([\d.]+)}, 1] ||
|
18
|
+
ua[%r{AppleWebKit/([\d.]+)}, 1] ||
|
19
19
|
"0.0"
|
20
20
|
end
|
21
21
|
|
22
22
|
def match?
|
23
|
-
ua
|
23
|
+
ua.match?(/Chrome|CriOS/) &&
|
24
|
+
!ua.match?(/PhantomJS|FxiOS|ArchiveBot/) &&
|
25
|
+
!opera? &&
|
26
|
+
!edge? &&
|
27
|
+
!duck_duck_go? &&
|
28
|
+
!yandex? &&
|
29
|
+
!sputnik? &&
|
30
|
+
!samsung_browser? &&
|
31
|
+
!huawei_browser? &&
|
32
|
+
!miui_browser? &&
|
33
|
+
!maxthon? &&
|
34
|
+
!qq? &&
|
35
|
+
!sougou_browser? &&
|
36
|
+
!google_search_app?
|
24
37
|
end
|
25
38
|
end
|
26
39
|
end
|
@@ -2,9 +2,7 @@
|
|
2
2
|
|
3
3
|
module Browser
|
4
4
|
module DetectVersion
|
5
|
-
private
|
6
|
-
|
7
|
-
def detect_version?(actual_version, expected_version)
|
5
|
+
private def detect_version?(actual_version, expected_version)
|
8
6
|
return true unless expected_version
|
9
7
|
return false if expected_version && !actual_version
|
10
8
|
|
@@ -13,10 +11,12 @@ module Browser
|
|
13
11
|
|
14
12
|
Gem::Requirement.create(expected_version)
|
15
13
|
.satisfied_by?(Gem::Version.create(actual_version))
|
14
|
+
rescue ArgumentError
|
15
|
+
false
|
16
16
|
end
|
17
17
|
|
18
|
-
def parse_version(version)
|
19
|
-
version.
|
18
|
+
private def parse_version(version)
|
19
|
+
version.is_a?(Numeric) ? version.to_s : version
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|