bbc-selenium-webdriver 1.17.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.
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