browser2 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.hound.yml +90 -0
- data/.rubocop.yml +90 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +44 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +125 -0
- data/Rakefile +33 -0
- data/browser2.gemspec +42 -0
- data/config.ru +2 -0
- data/data/bots.json +176 -0
- data/data/languages.json +115 -0
- data/gemfiles/rails3.gemfile +4 -0
- data/lib/browser/action_controller.rb +20 -0
- data/lib/browser/data/bots.rb +5 -0
- data/lib/browser/data/languages.rb +5 -0
- data/lib/browser/data/search_engines.rb +14 -0
- data/lib/browser/meta/base.rb +20 -0
- data/lib/browser/meta/generic_browser.rb +15 -0
- data/lib/browser/meta/id.rb +9 -0
- data/lib/browser/meta/ie.rb +18 -0
- data/lib/browser/meta/ios.rb +9 -0
- data/lib/browser/meta/mobile.rb +9 -0
- data/lib/browser/meta/modern.rb +9 -0
- data/lib/browser/meta/platform.rb +9 -0
- data/lib/browser/meta/safari.rb +9 -0
- data/lib/browser/meta/webkit.rb +9 -0
- data/lib/browser/methods/blackberry.rb +51 -0
- data/lib/browser/methods/bots.rb +37 -0
- data/lib/browser/methods/consoles.rb +43 -0
- data/lib/browser/methods/devices.rb +41 -0
- data/lib/browser/methods/ie.rb +104 -0
- data/lib/browser/methods/language.rb +16 -0
- data/lib/browser/methods/mobile.rb +30 -0
- data/lib/browser/methods/platform.rb +142 -0
- data/lib/browser/methods/tv.rb +8 -0
- data/lib/browser/middleware/context/additions.rb +16 -0
- data/lib/browser/middleware/context/url_methods.rb +13 -0
- data/lib/browser/middleware/context.rb +20 -0
- data/lib/browser/middleware.rb +66 -0
- data/lib/browser/rails.rb +14 -0
- data/lib/browser/version.rb +8 -0
- data/lib/browser.rb +255 -0
- data/test/benchmark_test.rb +83 -0
- data/test/browser_test.rb +248 -0
- data/test/middleware_test.rb +50 -0
- data/test/sample_app.rb +31 -0
- data/test/test_helper.rb +20 -0
- data/test/ua.yml +122 -0
- data/test/unit/adobe_air_test.rb +13 -0
- data/test/unit/android_test.rb +93 -0
- data/test/unit/blackberry_test.rb +104 -0
- data/test/unit/bots_test.rb +116 -0
- data/test/unit/chrome_test.rb +56 -0
- data/test/unit/console_test.rb +67 -0
- data/test/unit/firefox_test.rb +37 -0
- data/test/unit/ie_test.rb +377 -0
- data/test/unit/ios_test.rb +139 -0
- data/test/unit/kindle_test.rb +37 -0
- data/test/unit/opera_test.rb +42 -0
- data/test/unit/windows_phone_test.rb +50 -0
- data/test/unit/windows_test.rb +60 -0
- metadata +240 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
class Browser
|
|
2
|
+
module Platform
|
|
3
|
+
# Detect if browser is Android.
|
|
4
|
+
def android?(version = nil)
|
|
5
|
+
!!(ua =~ /Android/ && !opera?) && detect_version?(android_version, version)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Detect Android version.
|
|
9
|
+
def android_version
|
|
10
|
+
ua[/Android ([\d.]+)/, 1]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Return the iOS version.
|
|
14
|
+
def ios_version
|
|
15
|
+
ua[/OS (\d)/, 1]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Detect if browser is ios?.
|
|
19
|
+
def ios?(version = nil)
|
|
20
|
+
(ipod? || ipad? || iphone?) && detect_version?(ios_version, version)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Detect if is iOS4.
|
|
24
|
+
def ios4?
|
|
25
|
+
deprecate "Browser##{__method__} is deprecated; use Browser#ios?(version) instead"
|
|
26
|
+
ios?(4)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Detect if is iOS5.
|
|
30
|
+
def ios5?
|
|
31
|
+
deprecate "Browser##{__method__} is deprecated; use Browser#ios?(version) instead"
|
|
32
|
+
ios?(5)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Detect if is iOS6.
|
|
36
|
+
def ios6?
|
|
37
|
+
deprecate "Browser##{__method__} is deprecated; use Browser#ios?(version) instead"
|
|
38
|
+
ios?(6)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Detect if is iOS7.
|
|
42
|
+
def ios7?
|
|
43
|
+
deprecate "Browser##{__method__} is deprecated; use Browser#ios?(version) instead"
|
|
44
|
+
ios?(7)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Detect if is iOS8.
|
|
48
|
+
def ios8?
|
|
49
|
+
deprecate "Browser##{__method__} is deprecated; use Browser#ios?(version) instead"
|
|
50
|
+
ios?(8)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Detect if is iOS9.
|
|
54
|
+
def ios9?
|
|
55
|
+
deprecate "Browser##{__method__} is deprecated; use Browser#ios?(version) instead"
|
|
56
|
+
ios?(9)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Detect if current platform is Macintosh.
|
|
60
|
+
def mac?
|
|
61
|
+
!!(ua =~ /Mac OS X/ && !ios?)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Detect if current platform is Windows.
|
|
65
|
+
def windows?
|
|
66
|
+
!!(ua =~ /Windows/)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
## More info here => http://msdn.microsoft.com/fr-FR/library/ms537503.aspx#PltToken
|
|
70
|
+
def windows_xp?
|
|
71
|
+
windows? && !!(ua =~ /Windows NT 5.1/)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def windows_vista?
|
|
75
|
+
windows? && !!(ua =~ /Windows NT 6.0/)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def windows7?
|
|
79
|
+
windows? && !!(ua =~ /Windows NT 6.1/)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def windows8?
|
|
83
|
+
windows? && !!(ua =~ /Windows NT 6.[2-3]/)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def windows8_1?
|
|
87
|
+
windows? && !!(ua =~ /Windows NT 6\.3/)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def windows10?
|
|
91
|
+
windows? && !!(ua =~ /Windows NT 10/)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def windows_rt?
|
|
95
|
+
windows8? && !!(ua =~ /ARM/)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Detect if current platform is Windows Mobile.
|
|
99
|
+
def windows_mobile?
|
|
100
|
+
!!(ua =~ /Windows CE/)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Detect if current platform is Windows Phone.
|
|
104
|
+
def windows_phone?
|
|
105
|
+
!!(ua =~ /Windows Phone/)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Detect if current platform is Windows in 64-bit architecture.
|
|
109
|
+
def windows_x64?
|
|
110
|
+
windows? && !!(ua =~ /(Win64|x64)/) && !!(ua =~ /x64/)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def windows_wow64?
|
|
114
|
+
windows? && !!(ua =~ /WOW64/i)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def windows_x64_inclusive?
|
|
118
|
+
windows_x64? || windows_wow64?
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Detect if current platform is Linux flavor.
|
|
122
|
+
def linux?
|
|
123
|
+
in_ua? 'Linux'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Detect if current platform is ChromeOS
|
|
127
|
+
def chrome_os?
|
|
128
|
+
in_ua? 'CrOS'
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Return the platform.
|
|
132
|
+
def platform
|
|
133
|
+
case
|
|
134
|
+
when linux? then :linux
|
|
135
|
+
when mac? then :mac
|
|
136
|
+
when windows? then :windows
|
|
137
|
+
else
|
|
138
|
+
:other
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "browser/middleware/context/url_methods"
|
|
2
|
+
|
|
3
|
+
class Browser
|
|
4
|
+
class Middleware
|
|
5
|
+
class Context
|
|
6
|
+
module Additions
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
include Rails.application.routes.url_helpers
|
|
11
|
+
include UrlMethods
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class Browser
|
|
2
|
+
class Middleware
|
|
3
|
+
class Context
|
|
4
|
+
attr_reader :browser, :request
|
|
5
|
+
|
|
6
|
+
def initialize(request)
|
|
7
|
+
@request = request
|
|
8
|
+
|
|
9
|
+
@browser = Browser.new(
|
|
10
|
+
ua: request.user_agent,
|
|
11
|
+
accept_language: request.env["HTTP_ACCEPT_LANGUAGE"]
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def redirect_to(path)
|
|
16
|
+
throw :redirected, path.to_s
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
|
|
3
|
+
class Browser
|
|
4
|
+
class Middleware
|
|
5
|
+
# Detect the most common assets.
|
|
6
|
+
ASSETS_REGEX = /\.(css|png|jpe?g|gif|js|svg|ico|flv|mov|m4v|ogg|swf)\z/i
|
|
7
|
+
|
|
8
|
+
# Detect the ACCEPT header. IE8 send */*.
|
|
9
|
+
ACCEPT_REGEX = %r[(text/html|\*/\*)]
|
|
10
|
+
|
|
11
|
+
def initialize(app, &block)
|
|
12
|
+
fail ArgumentError, "Browser::Middleware requires a block" unless block
|
|
13
|
+
|
|
14
|
+
@app = app
|
|
15
|
+
@block = block
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(env)
|
|
19
|
+
request = Rack::Request.new(env)
|
|
20
|
+
|
|
21
|
+
# Only apply verification on HTML requests.
|
|
22
|
+
# This ensures that images, CSS and JavaScript
|
|
23
|
+
# will be rendered.
|
|
24
|
+
return run_app(env) unless process?(request)
|
|
25
|
+
|
|
26
|
+
path = catch(:redirected) do
|
|
27
|
+
Context.new(request).instance_eval(&@block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# No path, no match.
|
|
31
|
+
return run_app(env) unless path
|
|
32
|
+
|
|
33
|
+
resolve_redirection(env, request.path, path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def resolve_redirection(env, current_path, path)
|
|
37
|
+
uri = URI.parse(path)
|
|
38
|
+
|
|
39
|
+
if uri.path == current_path
|
|
40
|
+
run_app(env)
|
|
41
|
+
else
|
|
42
|
+
redirect(path)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def redirect(path)
|
|
47
|
+
[302, {"Content-Type" => "text/html", "Location" => path}, []]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def run_app(env)
|
|
51
|
+
@app.call(env)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def process?(request)
|
|
55
|
+
html?(request) && !assets?(request)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def html?(request)
|
|
59
|
+
request.env["HTTP_ACCEPT"].to_s.match(ACCEPT_REGEX)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def assets?(request)
|
|
63
|
+
request.path.match(ASSETS_REGEX)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "rails/railtie"
|
|
2
|
+
require "browser/action_controller"
|
|
3
|
+
require "browser/middleware/context/additions"
|
|
4
|
+
|
|
5
|
+
class Browser
|
|
6
|
+
class Railtie < Rails::Railtie
|
|
7
|
+
config.browser = ActiveSupport::OrderedOptions.new
|
|
8
|
+
|
|
9
|
+
initializer "browser" do
|
|
10
|
+
::ActionController::Base.send :include, Browser::ActionController
|
|
11
|
+
Browser::Middleware::Context.send :include, Browser::Middleware::Context::Additions
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/browser.rb
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "multi_json"
|
|
4
|
+
require "pathname"
|
|
5
|
+
require "set"
|
|
6
|
+
require "yaml"
|
|
7
|
+
|
|
8
|
+
require "browser/data/bots"
|
|
9
|
+
require "browser/data/languages"
|
|
10
|
+
require "browser/data/search_engines"
|
|
11
|
+
|
|
12
|
+
require "browser/middleware"
|
|
13
|
+
require "browser/middleware/context"
|
|
14
|
+
require "browser/rails" if defined?(::Rails)
|
|
15
|
+
|
|
16
|
+
require "browser/methods/ie"
|
|
17
|
+
require "browser/methods/blackberry"
|
|
18
|
+
require "browser/methods/platform"
|
|
19
|
+
require "browser/methods/mobile"
|
|
20
|
+
require "browser/methods/devices"
|
|
21
|
+
require "browser/methods/consoles"
|
|
22
|
+
require "browser/methods/language"
|
|
23
|
+
require "browser/methods/bots"
|
|
24
|
+
require "browser/methods/tv"
|
|
25
|
+
|
|
26
|
+
require "browser/meta/base"
|
|
27
|
+
require "browser/meta/generic_browser"
|
|
28
|
+
require "browser/meta/id"
|
|
29
|
+
require "browser/meta/ie"
|
|
30
|
+
require "browser/meta/ios"
|
|
31
|
+
require "browser/meta/mobile"
|
|
32
|
+
require "browser/meta/modern"
|
|
33
|
+
require "browser/meta/platform"
|
|
34
|
+
require "browser/meta/safari"
|
|
35
|
+
require "browser/meta/webkit"
|
|
36
|
+
|
|
37
|
+
class Browser
|
|
38
|
+
include IE
|
|
39
|
+
include BlackBerry
|
|
40
|
+
include Platform
|
|
41
|
+
include Mobile
|
|
42
|
+
include Devices
|
|
43
|
+
include Consoles
|
|
44
|
+
include Language
|
|
45
|
+
include Bots
|
|
46
|
+
include Tv
|
|
47
|
+
|
|
48
|
+
# Get the browser's UA string; this is immutable after initialization.
|
|
49
|
+
attr_reader :user_agent
|
|
50
|
+
alias_method :ua, :user_agent
|
|
51
|
+
|
|
52
|
+
NAMES = {
|
|
53
|
+
edge: "Microsoft Edge", # Must come before everything
|
|
54
|
+
ie: "Internet Explorer", # Must come before android
|
|
55
|
+
chrome: "Chrome", # Must come before android
|
|
56
|
+
firefox: "Firefox", # Must come before android
|
|
57
|
+
android: "Android",
|
|
58
|
+
blackberry_running_safari: "Safari",
|
|
59
|
+
blackberry: "BlackBerry",
|
|
60
|
+
core_media: "Apple CoreMedia",
|
|
61
|
+
ipad: "iPad", # Must come before safari
|
|
62
|
+
iphone: "iPhone", # Must come before safari
|
|
63
|
+
ipod: "iPod Touch", # Must come before safari
|
|
64
|
+
nintendo: "Nintendo",
|
|
65
|
+
opera: "Opera",
|
|
66
|
+
phantom_js: "PhantomJS",
|
|
67
|
+
psp: "PlayStation Portable",
|
|
68
|
+
playstation: "PlayStation",
|
|
69
|
+
quicktime: "QuickTime",
|
|
70
|
+
safari: "Safari",
|
|
71
|
+
xbox: "Xbox",
|
|
72
|
+
|
|
73
|
+
# This must be last item, since Ruby 1.9+ has ordered keys.
|
|
74
|
+
other: "Other",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
VERSIONS = {
|
|
78
|
+
edge: %r[Edge/([\d.]+)],
|
|
79
|
+
chrome: %r[(?:Chrome|CriOS)/([\d.]+)],
|
|
80
|
+
default: %r[(?:Version|MSIE|Firefox|QuickTime|BlackBerry[^/]+|CoreMedia v|PhantomJS|AdobeAIR)[/ ]?([a-z0-9.]+)]i,
|
|
81
|
+
opera: %r[(?:Opera/.*? Version/([\d.]+)|Chrome/.*?OPR/([\d.]+))],
|
|
82
|
+
ie: %r[(?:MSIE |Trident/.*?; rv:)([\d.]+)]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Define the rules which define a modern browser.
|
|
86
|
+
# A rule must be a proc/lambda or any object that implements the method
|
|
87
|
+
# === and accepts the browser object.
|
|
88
|
+
#
|
|
89
|
+
# To redefine all rules, clear the existing rules before adding your own.
|
|
90
|
+
#
|
|
91
|
+
# # Only Chrome Canary is considered modern.
|
|
92
|
+
# Browser.modern_rules.clear
|
|
93
|
+
# Browser.modern_rules << -> b { b.chrome? && b.version >= '37' }
|
|
94
|
+
#
|
|
95
|
+
def self.modern_rules
|
|
96
|
+
@modern_rules ||= []
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
self.modern_rules.tap do |rules|
|
|
100
|
+
rules << -> b { b.webkit? }
|
|
101
|
+
rules << -> b { b.firefox? && b.version.to_i >= 17 }
|
|
102
|
+
rules << -> b { b.ie? && b.version.to_i >= 9 && !b.compatibility_view? }
|
|
103
|
+
rules << -> b { b.edge? && !b.compatibility_view? }
|
|
104
|
+
rules << -> b { b.opera? && b.version.to_i >= 12 }
|
|
105
|
+
rules << -> b { b.firefox? && b.tablet? && b.android? && b.version.to_i >= 14 }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Create a new browser instance and set
|
|
109
|
+
# the UA and Accept-Language headers.
|
|
110
|
+
#
|
|
111
|
+
# browser = Browser.new({
|
|
112
|
+
# :ua => "Safari",
|
|
113
|
+
# :accept_language => "pt-br"
|
|
114
|
+
# })
|
|
115
|
+
#
|
|
116
|
+
def initialize(options = {})
|
|
117
|
+
user_agent = options[:user_agent] || options[:ua]
|
|
118
|
+
unless user_agent
|
|
119
|
+
raise ArgumentError, 'Must receive a :user_agent or :ua option'
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
@user_agent = user_agent.to_s
|
|
123
|
+
self.accept_language = options[:accept_language].to_s
|
|
124
|
+
|
|
125
|
+
yield self if block_given?
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Get readable browser name.
|
|
129
|
+
def name
|
|
130
|
+
NAMES[id]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Get the browser identifier.
|
|
134
|
+
def id
|
|
135
|
+
@id ||=
|
|
136
|
+
NAMES.keys
|
|
137
|
+
.find {|id| respond_to?("#{id}?", true) ? send("#{id}?") : id }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Return major version.
|
|
141
|
+
def version
|
|
142
|
+
if ie?
|
|
143
|
+
ie_version
|
|
144
|
+
else
|
|
145
|
+
full_version.to_s.split(".").first
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Return the full version.
|
|
150
|
+
def full_version
|
|
151
|
+
@full_version ||=
|
|
152
|
+
if ie?
|
|
153
|
+
ie_full_version
|
|
154
|
+
else
|
|
155
|
+
_, *v = *ua.match(VERSIONS.fetch(id, VERSIONS[:default]))
|
|
156
|
+
v.compact.first || "0.0"
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Return true if browser is modern (Webkit, Firefox 17+, IE9+, Opera 12+).
|
|
161
|
+
def modern?
|
|
162
|
+
self.class.modern_rules.any? { |rule| rule.call self }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Detect if browser is WebKit-based.
|
|
166
|
+
def webkit?
|
|
167
|
+
ua =~ /AppleWebKit/i && !edge?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Detect if browser is QuickTime
|
|
171
|
+
def quicktime?
|
|
172
|
+
!!(ua =~ /QuickTime/i)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Detect if browser is Apple CoreMedia.
|
|
176
|
+
def core_media?
|
|
177
|
+
!!(ua =~ /CoreMedia/)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Detect if browser is PhantomJS
|
|
181
|
+
def phantom_js?
|
|
182
|
+
!!(ua =~ /PhantomJS/)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Detect if browser is Safari.
|
|
186
|
+
def safari?
|
|
187
|
+
(in_ua?('Safari') || safari_webapp_mode?) && ua !~ /Android|Chrome|CriOS|PhantomJS/
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def safari_webapp_mode?
|
|
191
|
+
(ipad? || iphone?) && in_ua?('AppleWebKit')
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Detect if browser is Firefox.
|
|
195
|
+
def firefox?
|
|
196
|
+
in_ua? 'Firefox'
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Detect if browser is Chrome.
|
|
200
|
+
def chrome?
|
|
201
|
+
(in_ua?('Chrome') || in_ua?('CriOS')) && !opera? && !edge?
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Detect if browser is Opera.
|
|
205
|
+
def opera?
|
|
206
|
+
in_ua?('Opera') || in_ua?('OPR')
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Detect if browser is Silk.
|
|
210
|
+
def silk?
|
|
211
|
+
in_ua? 'Silk'
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Detect if browser is Yandex.
|
|
215
|
+
def yandex?
|
|
216
|
+
in_ua? 'YaBrowser'
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def known?
|
|
220
|
+
id != :other
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# Return a meta info about this browser.
|
|
224
|
+
def meta
|
|
225
|
+
@meta ||=
|
|
226
|
+
Meta.constants.each_with_object(Set.new) do |meta_name, meta|
|
|
227
|
+
meta_class = Meta.const_get(meta_name)
|
|
228
|
+
meta.merge(meta_class.new(self).to_a)
|
|
229
|
+
end.to_a
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
alias_method :to_a, :meta
|
|
233
|
+
|
|
234
|
+
# Return meta representation as string.
|
|
235
|
+
def to_s
|
|
236
|
+
meta.to_a.join(" ")
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
private
|
|
240
|
+
|
|
241
|
+
def detect_version?(actual_version, expected_version)
|
|
242
|
+
return true unless expected_version
|
|
243
|
+
actual_version.to_s.start_with?(expected_version.to_s)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Private shorthand for checking if a given string is in the user agent.
|
|
247
|
+
def in_ua? string
|
|
248
|
+
ua.include? string
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def deprecate(message)
|
|
252
|
+
offender = caller[1].to_s[/^(.*?\.rb:\d+).*?$/, 1]
|
|
253
|
+
$stderr << "\n#{message} (called from #{offender})\n"
|
|
254
|
+
end
|
|
255
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
require 'benchmark'
|
|
4
|
+
require 'minitest/benchmark'
|
|
5
|
+
|
|
6
|
+
class BenchmarkTest < Minitest::Benchmark
|
|
7
|
+
def self.bench_range
|
|
8
|
+
[100, 10_000]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def bench_chrome
|
|
12
|
+
assert_browser_performance($ua['CHROME']) do |browser|
|
|
13
|
+
assert_equal "Chrome", browser.name
|
|
14
|
+
assert browser.chrome?
|
|
15
|
+
refute browser.safari?
|
|
16
|
+
assert browser.webkit?
|
|
17
|
+
assert browser.modern?
|
|
18
|
+
assert_equal "5.0.375.99", browser.full_version
|
|
19
|
+
assert_equal "5", browser.version
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def bench_firefox
|
|
24
|
+
assert_browser_performance($ua["FIREFOX_MODERN"]) do |browser|
|
|
25
|
+
assert_equal :firefox, browser.id
|
|
26
|
+
assert_equal "Firefox", browser.name
|
|
27
|
+
assert browser.firefox?
|
|
28
|
+
assert browser.modern?
|
|
29
|
+
assert_equal "17.0", browser.full_version
|
|
30
|
+
assert_equal "17", browser.version
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def bench_bots
|
|
35
|
+
assert_browser_performance($ua['GOOGLE_BOT']) do |browser|
|
|
36
|
+
assert browser.bot?
|
|
37
|
+
assert_equal browser.bot_name, 'Googlebot'
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
WARMUP = 1_000
|
|
44
|
+
ITERATIONS = 10_000
|
|
45
|
+
GC_ITERATIONS = 1_00
|
|
46
|
+
|
|
47
|
+
def assert_browser_performance ua, &test_block
|
|
48
|
+
block = proc do
|
|
49
|
+
browser = Browser.new ua: ua
|
|
50
|
+
|
|
51
|
+
test_block.call browser
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
WARMUP.times &block
|
|
55
|
+
|
|
56
|
+
time = Benchmark.realtime do
|
|
57
|
+
ITERATIONS.times &block
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
objects = benchmark_memory &block
|
|
61
|
+
|
|
62
|
+
print "#{name}\t%10.5f seconds\t%d objects\n" % [time, objects]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def validation_noop
|
|
66
|
+
proc { }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def benchmark_memory &block
|
|
70
|
+
GC.start
|
|
71
|
+
|
|
72
|
+
before_allocated = GC.stat[:total_allocated_objects]
|
|
73
|
+
GC.disable
|
|
74
|
+
|
|
75
|
+
GC_ITERATIONS.times &block
|
|
76
|
+
|
|
77
|
+
after_allocated = GC.stat[:total_allocated_objects]
|
|
78
|
+
GC.enable
|
|
79
|
+
|
|
80
|
+
return after_allocated - before_allocated
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|