bbc-selenium-webdriver 1.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (168) hide show
  1. data/CHANGES +473 -0
  2. data/README +31 -0
  3. data/lib/selenium-client.rb +2 -0
  4. data/lib/selenium-webdriver.rb +1 -0
  5. data/lib/selenium/client.rb +37 -0
  6. data/lib/selenium/client/base.rb +118 -0
  7. data/lib/selenium/client/driver.rb +10 -0
  8. data/lib/selenium/client/errors.rb +9 -0
  9. data/lib/selenium/client/extensions.rb +118 -0
  10. data/lib/selenium/client/idiomatic.rb +488 -0
  11. data/lib/selenium/client/javascript_expression_builder.rb +116 -0
  12. data/lib/selenium/client/javascript_frameworks/jquery.rb +13 -0
  13. data/lib/selenium/client/javascript_frameworks/prototype.rb +13 -0
  14. data/lib/selenium/client/legacy_driver.rb +1720 -0
  15. data/lib/selenium/client/protocol.rb +104 -0
  16. data/lib/selenium/client/selenium_helper.rb +34 -0
  17. data/lib/selenium/rake/server_task.rb +157 -0
  18. data/lib/selenium/server.rb +223 -0
  19. data/lib/selenium/webdriver.rb +64 -0
  20. data/lib/selenium/webdriver/android.rb +9 -0
  21. data/lib/selenium/webdriver/android/bridge.rb +51 -0
  22. data/lib/selenium/webdriver/chrome.rb +26 -0
  23. data/lib/selenium/webdriver/chrome/bridge.rb +106 -0
  24. data/lib/selenium/webdriver/chrome/profile.rb +83 -0
  25. data/lib/selenium/webdriver/chrome/service.rb +74 -0
  26. data/lib/selenium/webdriver/common.rb +30 -0
  27. data/lib/selenium/webdriver/common/action_builder.rb +352 -0
  28. data/lib/selenium/webdriver/common/alert.rb +26 -0
  29. data/lib/selenium/webdriver/common/bridge_helper.rb +67 -0
  30. data/lib/selenium/webdriver/common/core_ext/base64.rb +9 -0
  31. data/lib/selenium/webdriver/common/core_ext/dir.rb +42 -0
  32. data/lib/selenium/webdriver/common/core_ext/string.rb +5 -0
  33. data/lib/selenium/webdriver/common/driver.rb +301 -0
  34. data/lib/selenium/webdriver/common/driver_extensions/has_input_devices.rb +39 -0
  35. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +44 -0
  36. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +43 -0
  37. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +46 -0
  38. data/lib/selenium/webdriver/common/element.rb +262 -0
  39. data/lib/selenium/webdriver/common/error.rb +212 -0
  40. data/lib/selenium/webdriver/common/file_reaper.rb +47 -0
  41. data/lib/selenium/webdriver/common/keyboard.rb +52 -0
  42. data/lib/selenium/webdriver/common/keys.rb +109 -0
  43. data/lib/selenium/webdriver/common/mouse.rb +65 -0
  44. data/lib/selenium/webdriver/common/navigation.rb +43 -0
  45. data/lib/selenium/webdriver/common/options.rb +128 -0
  46. data/lib/selenium/webdriver/common/platform.rb +166 -0
  47. data/lib/selenium/webdriver/common/port_prober.rb +25 -0
  48. data/lib/selenium/webdriver/common/profile_helper.rb +67 -0
  49. data/lib/selenium/webdriver/common/proxy.rb +120 -0
  50. data/lib/selenium/webdriver/common/search_context.rb +92 -0
  51. data/lib/selenium/webdriver/common/socket_poller.rb +100 -0
  52. data/lib/selenium/webdriver/common/target_locator.rb +81 -0
  53. data/lib/selenium/webdriver/common/timeouts.rb +29 -0
  54. data/lib/selenium/webdriver/common/wait.rb +60 -0
  55. data/lib/selenium/webdriver/common/window.rb +96 -0
  56. data/lib/selenium/webdriver/common/zipper.rb +82 -0
  57. data/lib/selenium/webdriver/firefox.rb +37 -0
  58. data/lib/selenium/webdriver/firefox/binary.rb +161 -0
  59. data/lib/selenium/webdriver/firefox/bridge.rb +58 -0
  60. data/lib/selenium/webdriver/firefox/extension.rb +52 -0
  61. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  62. data/lib/selenium/webdriver/firefox/launcher.rb +104 -0
  63. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  64. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  65. data/lib/selenium/webdriver/firefox/profile.rb +315 -0
  66. data/lib/selenium/webdriver/firefox/profiles_ini.rb +62 -0
  67. data/lib/selenium/webdriver/firefox/socket_lock.rb +61 -0
  68. data/lib/selenium/webdriver/firefox/util.rb +29 -0
  69. data/lib/selenium/webdriver/ie.rb +19 -0
  70. data/lib/selenium/webdriver/ie/bridge.rb +60 -0
  71. data/lib/selenium/webdriver/ie/native/win32/IEDriver.dll +0 -0
  72. data/lib/selenium/webdriver/ie/native/x64/IEDriver.dll +0 -0
  73. data/lib/selenium/webdriver/ie/server.rb +64 -0
  74. data/lib/selenium/webdriver/iphone.rb +9 -0
  75. data/lib/selenium/webdriver/iphone/bridge.rb +50 -0
  76. data/lib/selenium/webdriver/opera.rb +24 -0
  77. data/lib/selenium/webdriver/opera/bridge.rb +113 -0
  78. data/lib/selenium/webdriver/opera/service.rb +49 -0
  79. data/lib/selenium/webdriver/remote.rb +21 -0
  80. data/lib/selenium/webdriver/remote/bridge.rb +473 -0
  81. data/lib/selenium/webdriver/remote/capabilities.rb +241 -0
  82. data/lib/selenium/webdriver/remote/commands.rb +181 -0
  83. data/lib/selenium/webdriver/remote/http/common.rb +74 -0
  84. data/lib/selenium/webdriver/remote/http/curb.rb +79 -0
  85. data/lib/selenium/webdriver/remote/http/default.rb +102 -0
  86. data/lib/selenium/webdriver/remote/http/persistent.rb +38 -0
  87. data/lib/selenium/webdriver/remote/response.rb +99 -0
  88. data/lib/selenium/webdriver/remote/server_error.rb +17 -0
  89. data/lib/selenium/webdriver/support.rb +4 -0
  90. data/lib/selenium/webdriver/support/abstract_event_listener.rb +28 -0
  91. data/lib/selenium/webdriver/support/block_event_listener.rb +17 -0
  92. data/lib/selenium/webdriver/support/event_firing_bridge.rb +112 -0
  93. data/lib/selenium/webdriver/support/select.rb +293 -0
  94. data/spec/integration/selenium/client/api/backward_compatible_api_spec.rb +22 -0
  95. data/spec/integration/selenium/client/api/browser_xpath_library_spec.rb +17 -0
  96. data/spec/integration/selenium/client/api/click_spec.rb +39 -0
  97. data/spec/integration/selenium/client/api/cookie_spec.rb +39 -0
  98. data/spec/integration/selenium/client/api/element_spec.rb +14 -0
  99. data/spec/integration/selenium/client/api/highlight_located_element_spec.rb +19 -0
  100. data/spec/integration/selenium/client/api/retrieve_last_remote_control_logs_spec.rb +33 -0
  101. data/spec/integration/selenium/client/api/screenshot_spec.rb +29 -0
  102. data/spec/integration/selenium/client/api/select_window_spec.rb +46 -0
  103. data/spec/integration/selenium/client/api/start_stop_spec.rb +9 -0
  104. data/spec/integration/selenium/client/api/wait_for_ajax_spec.rb +27 -0
  105. data/spec/integration/selenium/client/api/wait_for_element_spec.rb +56 -0
  106. data/spec/integration/selenium/client/api/wait_for_field_value_spec.rb +52 -0
  107. data/spec/integration/selenium/client/api/wait_for_text_spec.rb +98 -0
  108. data/spec/integration/selenium/client/sample-app/public/jquery-1.3.2.js +4376 -0
  109. data/spec/integration/selenium/client/sample-app/public/jquery.html +55 -0
  110. data/spec/integration/selenium/client/sample-app/public/prototype-1.6.0.3.js +4320 -0
  111. data/spec/integration/selenium/client/sample-app/public/prototype.html +59 -0
  112. data/spec/integration/selenium/client/sample-app/sample_app.rb +32 -0
  113. data/spec/integration/selenium/client/spec_helper.rb +125 -0
  114. data/spec/integration/selenium/webdriver/chrome/driver_spec.rb +28 -0
  115. data/spec/integration/selenium/webdriver/chrome/profile_spec.rb +42 -0
  116. data/spec/integration/selenium/webdriver/driver_spec.rb +253 -0
  117. data/spec/integration/selenium/webdriver/element_spec.rb +185 -0
  118. data/spec/integration/selenium/webdriver/error_spec.rb +30 -0
  119. data/spec/integration/selenium/webdriver/firefox/driver_spec.rb +21 -0
  120. data/spec/integration/selenium/webdriver/firefox/profile_spec.rb +141 -0
  121. data/spec/integration/selenium/webdriver/keyboard_spec.rb +57 -0
  122. data/spec/integration/selenium/webdriver/mouse_spec.rb +55 -0
  123. data/spec/integration/selenium/webdriver/navigation_spec.rb +44 -0
  124. data/spec/integration/selenium/webdriver/opera/driver_spec.rb +47 -0
  125. data/spec/integration/selenium/webdriver/options_spec.rb +49 -0
  126. data/spec/integration/selenium/webdriver/remote/element_spec.rb +24 -0
  127. data/spec/integration/selenium/webdriver/spec_helper.rb +44 -0
  128. data/spec/integration/selenium/webdriver/spec_support.rb +13 -0
  129. data/spec/integration/selenium/webdriver/spec_support/guards.rb +86 -0
  130. data/spec/integration/selenium/webdriver/spec_support/helpers.rb +46 -0
  131. data/spec/integration/selenium/webdriver/spec_support/jruby_test_environment.rb +29 -0
  132. data/spec/integration/selenium/webdriver/spec_support/rack_server.rb +123 -0
  133. data/spec/integration/selenium/webdriver/spec_support/test_environment.rb +199 -0
  134. data/spec/integration/selenium/webdriver/target_locator_spec.rb +170 -0
  135. data/spec/integration/selenium/webdriver/timeout_spec.rb +56 -0
  136. data/spec/integration/selenium/webdriver/window_spec.rb +56 -0
  137. data/spec/integration/selenium/webdriver/zipper_spec.rb +66 -0
  138. data/spec/unit/selenium/client/base_spec.rb +239 -0
  139. data/spec/unit/selenium/client/extensions_spec.rb +174 -0
  140. data/spec/unit/selenium/client/idiomatic_spec.rb +500 -0
  141. data/spec/unit/selenium/client/javascript_expression_builder_spec.rb +79 -0
  142. data/spec/unit/selenium/client/javascript_frameworks/jquery_spec.rb +10 -0
  143. data/spec/unit/selenium/client/javascript_frameworks/prototype_spec.rb +10 -0
  144. data/spec/unit/selenium/client/protocol_spec.rb +124 -0
  145. data/spec/unit/selenium/client/selenium_helper_spec.rb +56 -0
  146. data/spec/unit/selenium/client/spec_helper.rb +24 -0
  147. data/spec/unit/selenium/rake/task_spec.rb +79 -0
  148. data/spec/unit/selenium/server_spec.rb +131 -0
  149. data/spec/unit/selenium/webdriver/action_builder_spec.rb +90 -0
  150. data/spec/unit/selenium/webdriver/android/bridge_spec.rb +31 -0
  151. data/spec/unit/selenium/webdriver/chrome/bridge_spec.rb +94 -0
  152. data/spec/unit/selenium/webdriver/chrome/profile_spec.rb +57 -0
  153. data/spec/unit/selenium/webdriver/chrome/service_spec.rb +44 -0
  154. data/spec/unit/selenium/webdriver/error_spec.rb +41 -0
  155. data/spec/unit/selenium/webdriver/firefox/bridge_spec.rb +17 -0
  156. data/spec/unit/selenium/webdriver/iphone/bridge_spec.rb +30 -0
  157. data/spec/unit/selenium/webdriver/proxy_spec.rb +87 -0
  158. data/spec/unit/selenium/webdriver/remote/bridge_spec.rb +16 -0
  159. data/spec/unit/selenium/webdriver/remote/capabilities_spec.rb +111 -0
  160. data/spec/unit/selenium/webdriver/remote/http/common_spec.rb +24 -0
  161. data/spec/unit/selenium/webdriver/remote/http/default_spec.rb +46 -0
  162. data/spec/unit/selenium/webdriver/search_context_spec.rb +61 -0
  163. data/spec/unit/selenium/webdriver/socket_poller_spec.rb +68 -0
  164. data/spec/unit/selenium/webdriver/spec_helper.rb +6 -0
  165. data/spec/unit/selenium/webdriver/support/event_firing_spec.rb +111 -0
  166. data/spec/unit/selenium/webdriver/support/select_spec.rb +290 -0
  167. data/spec/unit/selenium/webdriver/wait_spec.rb +40 -0
  168. metadata +215 -0
@@ -0,0 +1,37 @@
1
+ require 'timeout'
2
+ require 'socket'
3
+ require 'rexml/document'
4
+
5
+ require 'selenium/webdriver/firefox/util'
6
+ require 'selenium/webdriver/firefox/extension'
7
+ require 'selenium/webdriver/firefox/socket_lock'
8
+ require 'selenium/webdriver/firefox/binary'
9
+ require 'selenium/webdriver/firefox/profiles_ini'
10
+ require 'selenium/webdriver/firefox/profile'
11
+ require 'selenium/webdriver/firefox/launcher'
12
+ require 'selenium/webdriver/firefox/bridge'
13
+
14
+ module Selenium
15
+ module WebDriver
16
+ module Firefox
17
+
18
+ DEFAULT_PORT = 7055
19
+ DEFAULT_ENABLE_NATIVE_EVENTS = Platform.os == :windows
20
+ DEFAULT_SECURE_SSL = false
21
+ DEFAULT_ASSUME_UNTRUSTED_ISSUER = true
22
+ DEFAULT_LOAD_NO_FOCUS_LIB = false
23
+
24
+ def self.path=(path)
25
+ Binary.path = path
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ # SocketError was added in Ruby 1.8.7.
34
+ # If it's not defined, we add it here so it can be used in rescues.
35
+ unless defined? SocketError
36
+ class SocketError < IOError; end
37
+ end
@@ -0,0 +1,161 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Firefox
4
+
5
+ # @api private
6
+ class Binary
7
+
8
+ NO_FOCUS_LIBRARY_NAME = "x_ignore_nofocus.so"
9
+ NO_FOCUS_LIBRARIES = [
10
+ ["#{WebDriver.root}/selenium/webdriver/firefox/native/linux/amd64/#{NO_FOCUS_LIBRARY_NAME}", "amd64/#{NO_FOCUS_LIBRARY_NAME}"],
11
+ ["#{WebDriver.root}/selenium/webdriver/firefox/native/linux/x86/#{NO_FOCUS_LIBRARY_NAME}", "x86/#{NO_FOCUS_LIBRARY_NAME}"],
12
+ ]
13
+
14
+ WAIT_TIMEOUT = 90
15
+ QUIT_TIMEOUT = 5
16
+
17
+ def start_with(profile, profile_path, *args)
18
+ profile_path = profile_path.gsub("/", "\\") if Platform.windows?
19
+
20
+ ENV['XRE_CONSOLE_LOG'] = profile.log_file if profile.log_file
21
+ ENV['XRE_PROFILE_PATH'] = profile_path
22
+ ENV['MOZ_NO_REMOTE'] = '1' # able to launch multiple instances
23
+ ENV['MOZ_CRASHREPORTER_DISABLE'] = '1' # disable breakpad
24
+ ENV['NO_EM_RESTART'] = '1' # prevent the binary from detaching from the console
25
+
26
+ if Platform.linux? && (profile.native_events? || profile.load_no_focus_lib?)
27
+ modify_link_library_path profile_path
28
+ end
29
+
30
+ execute(*args)
31
+ cope_with_mac_strangeness(args) if Platform.mac?
32
+ end
33
+
34
+ def quit
35
+ return unless @process
36
+ @process.poll_for_exit QUIT_TIMEOUT
37
+ rescue ChildProcess::TimeoutError
38
+ # ok, force quit
39
+ @process.stop QUIT_TIMEOUT
40
+ end
41
+
42
+ def wait
43
+ @process.poll_for_exit(WAIT_TIMEOUT) if @process
44
+ end
45
+
46
+ private
47
+
48
+ def execute(*extra_args)
49
+ args = [self.class.path, "-no-remote"] + extra_args
50
+ @process = ChildProcess.build(*args)
51
+ @process.io.inherit! if $DEBUG
52
+ @process.start
53
+ end
54
+
55
+ def cope_with_mac_strangeness(args)
56
+ sleep 0.3
57
+
58
+ if @process.crashed?
59
+ # ok, trying a restart
60
+ sleep 7
61
+ execute(*args)
62
+ end
63
+
64
+ # ensure we're ok
65
+ sleep 0.3
66
+ if @process.crashed?
67
+ raise Error::WebDriverError, "unable to start Firefox cleanly, args: #{args.inspect}"
68
+ end
69
+ end
70
+
71
+ def modify_link_library_path(profile_path)
72
+ paths = []
73
+
74
+ NO_FOCUS_LIBRARIES.each do |from, to|
75
+ dest = File.join(profile_path, to)
76
+ FileUtils.mkdir_p File.dirname(dest)
77
+ FileUtils.cp from, dest
78
+
79
+ paths << File.expand_path(File.dirname(dest))
80
+ end
81
+
82
+ paths += ENV['LD_LIBRARY_PATH'].to_s.split(File::PATH_SEPARATOR)
83
+
84
+ ENV['LD_LIBRARY_PATH'] = paths.uniq.join(File::PATH_SEPARATOR)
85
+ ENV['LD_PRELOAD'] = NO_FOCUS_LIBRARY_NAME
86
+ end
87
+
88
+ class << self
89
+
90
+ #
91
+ # @api private
92
+ #
93
+ # @see Firefox.path=
94
+ #
95
+
96
+ def path=(path)
97
+ Platform.assert_executable(path)
98
+ @path = path
99
+ end
100
+
101
+ def path
102
+ @path ||= case Platform.os
103
+ when :macosx
104
+ macosx_path
105
+ when :windows
106
+ windows_path
107
+ when :linux, :unix
108
+ Platform.find_binary("firefox3", "firefox2", "firefox") || "/usr/bin/firefox"
109
+ else
110
+ raise Error::WebDriverError, "unknown platform: #{Platform.os}"
111
+ end
112
+
113
+ @path = Platform.cygwin_path(@path) if Platform.cygwin?
114
+
115
+ unless File.file?(@path.to_s)
116
+ raise Error::WebDriverError, "Could not find Firefox binary (os=#{Platform.os}). Make sure Firefox is installed or set the path manually with #{self}.path="
117
+ end
118
+
119
+ @path
120
+ end
121
+
122
+ private
123
+
124
+ def windows_path
125
+ windows_registry_path || likely_windows_path || Platform.find_binary("firefox")
126
+ end
127
+
128
+ def macosx_path
129
+ path = "/Applications/Firefox.app/Contents/MacOS/firefox-bin"
130
+ path = Platform.find_binary("firefox-bin") unless File.exist?(path)
131
+
132
+ path
133
+ end
134
+
135
+ def windows_registry_path
136
+ require 'win32/registry'
137
+
138
+ lm = Win32::Registry::HKEY_LOCAL_MACHINE
139
+ lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox") do |reg|
140
+ main = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main")
141
+ if entry = main.find { |key, type, data| key =~ /pathtoexe/i }
142
+ return entry.last
143
+ end
144
+ end
145
+ rescue LoadError
146
+ # older JRuby or IronRuby does not have win32/registry
147
+ rescue Win32::Registry::Error
148
+ end
149
+
150
+ def likely_windows_path
151
+ [
152
+ "#{ ENV['PROGRAMFILES'] || "\\Program Files" }\\Mozilla Firefox\\firefox.exe",
153
+ "#{ ENV['ProgramFiles(x86)'] || "\\Program Files (x86)" }\\Mozilla Firefox\\firefox.exe"
154
+ ].find { |path| File.executable?(path) }
155
+ end
156
+ end # class << self
157
+
158
+ end # Binary
159
+ end # Firefox
160
+ end # WebDriver
161
+ end # Selenium
@@ -0,0 +1,58 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Firefox
4
+
5
+ # @api private
6
+ class Bridge < Remote::Bridge
7
+
8
+ def initialize(opts = {})
9
+ port = opts.delete(:port) || DEFAULT_PORT
10
+ profile = opts.delete(:profile)
11
+ http_client = opts.delete(:http_client)
12
+
13
+ @launcher = create_launcher(port, profile)
14
+
15
+ unless opts.empty?
16
+ raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
17
+ end
18
+
19
+ @launcher.launch
20
+
21
+ remote_opts = {
22
+ :url => @launcher.url,
23
+ :desired_capabilities => Remote::Capabilities.firefox(:native_events => DEFAULT_ENABLE_NATIVE_EVENTS)
24
+ }
25
+
26
+ remote_opts.merge!(:http_client => http_client) if http_client
27
+
28
+ super(remote_opts)
29
+ end
30
+
31
+ def browser
32
+ :firefox
33
+ end
34
+
35
+ def driver_extensions
36
+ [
37
+ DriverExtensions::TakesScreenshot,
38
+ DriverExtensions::HasInputDevices
39
+ ]
40
+ end
41
+
42
+ def quit
43
+ super
44
+ @launcher.quit
45
+
46
+ nil
47
+ end
48
+
49
+ private
50
+
51
+ def create_launcher(port, profile)
52
+ Launcher.new Binary.new, port, profile
53
+ end
54
+
55
+ end # Bridge
56
+ end # Firefox
57
+ end # WebDriver
58
+ end # Selenium
@@ -0,0 +1,52 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Firefox
4
+
5
+ # @api private
6
+ class Extension
7
+ def initialize(path)
8
+ unless File.exist?(path)
9
+ raise Error::WebDriverError, "could not find extension at #{path.inspect}"
10
+ end
11
+
12
+ @path = path
13
+ @should_reap_root = false
14
+ end
15
+
16
+ def write_to(extensions_dir)
17
+ root_dir = create_root
18
+ ext_path = File.join extensions_dir, read_id_from_install_rdf(root_dir)
19
+
20
+ FileUtils.rm_rf ext_path
21
+ FileUtils.mkdir_p File.dirname(ext_path), :mode => 0700
22
+ FileUtils.cp_r root_dir, ext_path
23
+
24
+ FileReaper.reap(root_dir) if @should_reap_root
25
+ end
26
+
27
+ private
28
+
29
+ def create_root
30
+ if File.directory? @path
31
+ @path
32
+ else
33
+ unless Zipper::EXTENSIONS.include? File.extname(@path)
34
+ raise Error::WebDriverError, "expected #{Zipper::EXTENSIONS.join(" or ")}, got #{@path.inspect}"
35
+ end
36
+
37
+ @should_reap_root = true
38
+ Zipper.unzip(@path)
39
+ end
40
+ end
41
+
42
+ def read_id_from_install_rdf(directory)
43
+ rdf_path = File.join(directory, "install.rdf")
44
+ doc = REXML::Document.new(File.read(rdf_path))
45
+
46
+ REXML::XPath.first(doc, "//em:id").text
47
+ end
48
+
49
+ end # Extension
50
+ end # Firefox
51
+ end # WebDriver
52
+ end # Selenium
@@ -0,0 +1,104 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Firefox
4
+
5
+ # @api private
6
+ class Launcher
7
+
8
+ SOCKET_LOCK_TIMEOUT = 45
9
+ STABLE_CONNECTION_TIMEOUT = 60
10
+
11
+ def initialize(binary, port, profile = nil)
12
+ @binary = binary
13
+ @port = Integer(port)
14
+
15
+ raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
16
+
17
+ if profile.kind_of? Profile
18
+ @profile = profile
19
+ else
20
+ @profile_name = profile
21
+ @profile = nil
22
+ end
23
+
24
+ @host = "127.0.0.1"
25
+ end
26
+
27
+ def url
28
+ "http://#{@host}:#{@port}/hub"
29
+ end
30
+
31
+ def launch
32
+ socket_lock.locked do
33
+ find_free_port
34
+ create_profile
35
+ start_silent_and_wait
36
+ start
37
+ connect_until_stable
38
+ end
39
+
40
+ self
41
+ end
42
+
43
+ def quit
44
+ @binary.quit
45
+ FileReaper.reap(@profile_dir) if @profile_dir
46
+ end
47
+
48
+ def find_free_port
49
+ @port = PortProber.above @port
50
+ end
51
+
52
+ def create_profile
53
+ fetch_profile unless @profile
54
+
55
+ @profile.add_webdriver_extension
56
+ @profile.port = @port
57
+ @profile_dir = @profile.layout_on_disk
58
+ end
59
+
60
+ def start
61
+ assert_profile
62
+ @binary.start_with @profile, @profile_dir, "-foreground"
63
+ end
64
+
65
+ def start_silent_and_wait
66
+ assert_profile
67
+
68
+ @binary.start_with @profile, @profile_dir, "-silent"
69
+ @binary.wait
70
+ end
71
+
72
+ def connect_until_stable
73
+ poller = SocketPoller.new(@host, @port, STABLE_CONNECTION_TIMEOUT)
74
+
75
+ unless poller.connected?
76
+ @binary.quit
77
+ raise Error::WebDriverError, "unable to obtain stable firefox connection in #{STABLE_CONNECTION_TIMEOUT} seconds (#{@host}:#{@port})"
78
+ end
79
+ end
80
+
81
+ def fetch_profile
82
+ if @profile_name
83
+ @profile = Profile.from_name @profile_name
84
+
85
+ unless @profile
86
+ raise Error::WebDriverError, "unable to find profile named: #{@profile_name.inspect}"
87
+ end
88
+ else
89
+ @profile = Profile.new
90
+ end
91
+ end
92
+
93
+ def assert_profile
94
+ raise Error::WebDriverError, "must create_profile first" unless @profile && @profile_dir
95
+ end
96
+
97
+ def socket_lock
98
+ @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
99
+ end
100
+
101
+ end # Launcher
102
+ end # Firefox
103
+ end # WebDriver
104
+ end # Selenium
@@ -0,0 +1,315 @@
1
+ module Selenium
2
+ module WebDriver
3
+ module Firefox
4
+ class Profile
5
+ include ProfileHelper
6
+
7
+ WEBDRIVER_EXTENSION_PATH = File.expand_path("#{WebDriver.root}/selenium/webdriver/firefox/extension/webdriver.xpi")
8
+ WEBDRIVER_PREFS = {
9
+ :native_events => 'webdriver_enable_native_events',
10
+ :untrusted_certs => 'webdriver_accept_untrusted_certs',
11
+ :untrusted_issuer => 'webdriver_assume_untrusted_issuer',
12
+ :port => 'webdriver_firefox_port',
13
+ :log_file => 'webdriver.log.file'
14
+ }
15
+
16
+ attr_reader :name, :log_file
17
+ attr_writer :secure_ssl, :native_events, :load_no_focus_lib
18
+
19
+ class << self
20
+ def ini
21
+ @ini ||= ProfilesIni.new
22
+ end
23
+
24
+ def from_name(name)
25
+ ini[name]
26
+ end
27
+ end
28
+
29
+ #
30
+ # Create a new Profile instance
31
+ #
32
+ # @example User configured profile
33
+ #
34
+ # profile = Selenium::WebDriver::Firefox::Profile.new
35
+ # profile['network.proxy.http'] = 'localhost'
36
+ # profile['network.proxy.http_port'] = 9090
37
+ #
38
+ # driver = Selenium::WebDriver.for :firefox, :profile => profile
39
+ #
40
+
41
+ def initialize(model = nil)
42
+ @model = verify_model(model)
43
+
44
+ model_prefs = read_model_prefs
45
+
46
+ if model_prefs.empty?
47
+ @native_events = DEFAULT_ENABLE_NATIVE_EVENTS
48
+ @secure_ssl = DEFAULT_SECURE_SSL
49
+ @untrusted_issuer = DEFAULT_ASSUME_UNTRUSTED_ISSUER
50
+ @load_no_focus_lib = DEFAULT_LOAD_NO_FOCUS_LIB
51
+
52
+ @additional_prefs = {}
53
+ else
54
+ # TODO: clean this up
55
+ @native_events = model_prefs.delete(WEBDRIVER_PREFS[:native_events]) == "true"
56
+ @secure_ssl = model_prefs.delete(WEBDRIVER_PREFS[:untrusted_certs]) != "true"
57
+ @untrusted_issuer = model_prefs.delete(WEBDRIVER_PREFS[:untrusted_issuer]) == "true"
58
+ @load_no_focus_lib = model_prefs.delete(WEBDRIVER_PREFS[:load_no_focus_lib]) == "true" # not stored in profile atm, so will always be false.
59
+ @additional_prefs = model_prefs
60
+ end
61
+
62
+ @extensions = {}
63
+ end
64
+
65
+ def layout_on_disk
66
+ profile_dir = @model ? create_tmp_copy(@model) : Dir.mktmpdir("webdriver-profile")
67
+ FileReaper << profile_dir
68
+
69
+ install_extensions(profile_dir)
70
+ delete_lock_files(profile_dir)
71
+ delete_extensions_cache(profile_dir)
72
+ update_user_prefs_in(profile_dir)
73
+
74
+ profile_dir
75
+ end
76
+
77
+
78
+ #
79
+ # Set a preference for this particular profile.
80
+ #
81
+ # @see http://kb.mozillazine.org/About:config_entries
82
+ # @see http://preferential.mozdev.org/preferences.html
83
+ #
84
+
85
+ def []=(key, value)
86
+ case value
87
+ when String
88
+ if Util.stringified?(value)
89
+ raise ArgumentError, "preference values must be plain strings: #{key.inspect} => #{value.inspect}"
90
+ end
91
+
92
+ value = MultiJson.encode(value)
93
+ when TrueClass, FalseClass, Integer, Float
94
+ value = value.to_s
95
+ else
96
+ raise TypeError, "invalid preference: #{value.inspect}:#{value.class}"
97
+ end
98
+
99
+ @additional_prefs[key.to_s] = value
100
+ end
101
+
102
+ def port=(port)
103
+ self[WEBDRIVER_PREFS[:port]] = port
104
+ end
105
+
106
+ def log_file=(file)
107
+ @log_file = file
108
+ self[WEBDRIVER_PREFS[:log_file]] = file
109
+ end
110
+
111
+ def add_webdriver_extension
112
+ unless @extensions.has_key?(:webdriver)
113
+ add_extension(WEBDRIVER_EXTENSION_PATH, :webdriver)
114
+ end
115
+ end
116
+
117
+ #
118
+ # Add the extension (directory, .zip or .xpi) at the given path to the profile.
119
+ #
120
+
121
+ def add_extension(path, name = extension_name_for(path))
122
+ @extensions[name] = Extension.new(path)
123
+ end
124
+
125
+ def native_events?
126
+ @native_events == true
127
+ end
128
+
129
+ def load_no_focus_lib?
130
+ @load_no_focus_lib == true
131
+ end
132
+
133
+ def secure_ssl?
134
+ @secure_ssl == true
135
+ end
136
+
137
+ def assume_untrusted_certificate_issuer?
138
+ @untrusted_issuer == true
139
+ end
140
+
141
+ def assume_untrusted_certificate_issuer=(bool)
142
+ @untrusted_issuer = bool
143
+ end
144
+
145
+ def proxy=(proxy)
146
+ unless proxy.kind_of? Proxy
147
+ raise TypeError, "expected #{Proxy.name}, got #{proxy.inspect}:#{proxy.class}"
148
+ end
149
+
150
+ case proxy.type
151
+ when :manual
152
+ self['network.proxy.type'] = 1
153
+
154
+ set_manual_proxy_preference "ftp", proxy.ftp
155
+ set_manual_proxy_preference "http", proxy.http
156
+ set_manual_proxy_preference "ssl", proxy.ssl
157
+
158
+ if proxy.no_proxy
159
+ self["network.proxy.no_proxies_on"] = proxy.no_proxy
160
+ else
161
+ self["network.proxy.no_proxies_on"] = ""
162
+ end
163
+ when :pac
164
+ self['network.proxy.type'] = 2
165
+ self['network.proxy.autoconfig_url'] = proxy.pac
166
+ when :auto_detect
167
+ self['network.proxy.type'] = 4
168
+ else
169
+ raise ArgumentError, "unsupported proxy type #{proxy.type}"
170
+ end
171
+
172
+ proxy
173
+ end
174
+
175
+ private
176
+
177
+ def set_manual_proxy_preference(key, value)
178
+ return unless value
179
+
180
+ host, port = value.to_s.split(":", 2)
181
+
182
+ self["network.proxy.#{key}"] = host
183
+ self["network.proxy.#{key}_port"] = Integer(port) if port
184
+ end
185
+
186
+ def install_extensions(directory)
187
+ destination = File.join(directory, "extensions")
188
+
189
+ @extensions.each do |name, extension|
190
+ p :extension => name if $DEBUG
191
+ extension.write_to(destination)
192
+ end
193
+ end
194
+
195
+ def read_model_prefs
196
+ return {} unless @model
197
+
198
+ read_user_prefs(File.join(@model, 'user.js'))
199
+ end
200
+
201
+ def delete_extensions_cache(directory)
202
+ FileUtils.rm_f File.join(directory, "extensions.cache")
203
+ end
204
+
205
+ def delete_lock_files(directory)
206
+ %w[.parentlock parent.lock].each do |name|
207
+ FileUtils.rm_f File.join(directory, name)
208
+ end
209
+ end
210
+
211
+ def extension_name_for(path)
212
+ File.basename(path, File.extname(path))
213
+ end
214
+
215
+ def update_user_prefs_in(directory)
216
+ path = File.join(directory, 'user.js')
217
+ prefs = read_user_prefs(path)
218
+
219
+ prefs.merge! DEFAULT_PREFERENCES
220
+ prefs.merge! @additional_prefs
221
+ prefs.merge! FROZEN_PREFERENCES
222
+
223
+ prefs[WEBDRIVER_PREFS[:untrusted_certs]] = !secure_ssl?
224
+ prefs[WEBDRIVER_PREFS[:native_events]] = native_events?
225
+ prefs[WEBDRIVER_PREFS[:untrusted_issuer]] = assume_untrusted_certificate_issuer?
226
+
227
+ # If the user sets the home page, we should also start up there
228
+ prefs["startup.homepage_welcome_url"] = prefs["browser.startup.homepage"]
229
+
230
+ write_prefs prefs, path
231
+ end
232
+
233
+ def read_user_prefs(path)
234
+ return {} unless File.exist?(path)
235
+
236
+ prefs = {}
237
+
238
+ File.read(path).split("\n").each do |line|
239
+ if line =~ /user_pref\("([^"]+)"\s*,\s*(.+?)\);/
240
+ prefs[$1.strip] = $2.strip
241
+ end
242
+ end
243
+
244
+ prefs
245
+ end
246
+
247
+ def write_prefs(prefs, path)
248
+ File.open(path, "w") { |file|
249
+ prefs.each do |key, value|
250
+ p key => value if $DEBUG
251
+ file.puts %{user_pref("#{key}", #{value});}
252
+ end
253
+ }
254
+ end
255
+
256
+ DEFAULT_PREFERENCES = {
257
+ "browser.startup.page" => '0',
258
+ "browser.startup.homepage" => '"about:blank"',
259
+ "dom.max_script_run_time" => '30',
260
+ }.freeze
261
+
262
+
263
+ # Profile preferences that are essential to the Firefox driver operating
264
+ # correctly. Users are not permitted to override these values.
265
+
266
+ FROZEN_PREFERENCES = {
267
+ "app.update.auto" => 'false',
268
+ "app.update.enabled" => 'false',
269
+ "browser.download.manager.showWhenStarting" => 'false',
270
+ "browser.EULA.override" => 'true',
271
+ "browser.EULA.3.accepted" => 'true',
272
+ "browser.link.open_external" => '2',
273
+ "browser.link.open_newwindow" => '2',
274
+ "browser.safebrowsing.enabled" => 'false',
275
+ "browser.safebrowsing.malware.enabled" => 'false',
276
+ "browser.search.update" => 'false',
277
+ "browser.sessionstore.resume_from_crash" => 'false',
278
+ "browser.shell.checkDefaultBrowser" => 'false',
279
+ "browser.tabs.warnOnClose" => 'false',
280
+ "browser.tabs.warnOnOpen" => 'false',
281
+ "devtools.errorconsole.enabled" => 'true',
282
+ "dom.disable_open_during_load" => 'false',
283
+ "extensions.autoDisableScopes" => '10',
284
+ "extensions.logging.enabled" => 'true',
285
+ "extensions.update.enabled" => 'false',
286
+ "extensions.update.notifyUser" => 'false',
287
+ "network.manage-offline-status" => 'false',
288
+ "network.http.phishy-userpass-length" => '255',
289
+ "network.http.max-connections-per-server" => '10',
290
+ "offline-apps.allow_by_default" => 'true',
291
+ "prompts.tab_modal.enabled" => "false",
292
+ "security.warn_entering_secure" => 'false',
293
+ "security.warn_submit_insecure" => 'false',
294
+ "security.warn_entering_secure.show_once" => 'false',
295
+ "security.warn_entering_weak" => 'false',
296
+ "security.warn_entering_weak.show_once" => 'false',
297
+ "security.warn_leaving_secure" => 'false',
298
+ "security.warn_leaving_secure.show_once" => 'false',
299
+ "security.warn_submit_insecure" => 'false',
300
+ "security.warn_viewing_mixed" => 'false',
301
+ "security.warn_viewing_mixed.show_once" => 'false',
302
+ "signon.rememberSignons" => 'false',
303
+ "toolkit.networkmanager.disable" => 'true',
304
+ "toolkit.telemetry.prompted" => '2',
305
+ "toolkit.telemetry.enabled" => 'false',
306
+ "toolkit.telemetry.rejected" => 'true',
307
+ "javascript.options.showInConsole" => 'true',
308
+ "browser.dom.window.dump.enabled" => 'true',
309
+ "dom.report_all_js_exceptions" => "true"
310
+ }.freeze
311
+
312
+ end # Profile
313
+ end # Firefox
314
+ end # WebDriver
315
+ end # Selenium