vapir-firefox 1.9.0 → 1.10.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.
@@ -187,7 +187,7 @@ module Vapir
187
187
  # socket may be dead, and you want a new one. a warning will be printed if this occurs.
188
188
  def self.firefox_socket(options={}) # :nodoc:
189
189
  if options[:reset] || !(class_variable_defined?('@@firefox_socket') && @@firefox_socket)
190
- initialize_firefox_socket(options[:socket_class], options[:socket_options])
190
+ initialize_firefox_socket(options[:socket_class] || firefox_socket_class, options[:socket_options] || firefox_socket_class_options)
191
191
  end
192
192
  if options[:reset_if_dead]
193
193
  begin
@@ -268,7 +268,7 @@ module Vapir
268
268
 
269
269
  @pid = begin
270
270
  self.pid
271
- rescue NotImplementedError
271
+ rescue CannotHandlePid
272
272
  nil
273
273
  end
274
274
 
@@ -282,7 +282,6 @@ module Vapir
282
282
  find_window(how, what)
283
283
  end
284
284
  end
285
- set_browser_document
286
285
  else
287
286
  open_window
288
287
  end
@@ -294,8 +293,8 @@ module Vapir
294
293
  wait if options[:wait]
295
294
  end
296
295
 
297
- def mozilla_window_class_name
298
- 'MozillaUIWindowClass'
296
+ def mozilla_window_class_names
297
+ ['MozillaUIWindowClass', 'MozillaWindowClass']
299
298
  end
300
299
 
301
300
  def browser
@@ -413,7 +412,6 @@ module Vapir
413
412
  @document_object=browser_object.contentDocument
414
413
  @content_window_object=browser_object.contentWindow
415
414
  # note that browser_window_object.content is the same thing, but simpler to refer to stuff on browser_object since that is updated by the nsIWebProgressListener below
416
- @body_object=document_object.body
417
415
  @browser_objects[:requests_in_progress]=[]
418
416
  @requests_in_progress=@browser_objects[:requests_in_progress].to_array
419
417
  @browser_objects[:unmatched_stopped_requests_count]=0
@@ -481,7 +479,9 @@ module Vapir
481
479
  attr_reader :content_window_object
482
480
  attr_reader :browser_object
483
481
  attr_reader :document_object
484
- attr_reader :body_object
482
+ def body_object
483
+ document_object.body
484
+ end
485
485
 
486
486
  def updated_at
487
487
  Time.at(@updated_at_epoch_ms.val/1000.0)+@updated_at_offset
@@ -517,7 +517,7 @@ module Vapir
517
517
  end
518
518
  end
519
519
 
520
- @browser_window_object=@browser_object=@document_object=@content_window_object=@body_object=nil
520
+ @browser_window_object=@browser_object=@document_object=@content_window_object=nil
521
521
  if @self_launched_browser && firefox_socket && !self.class.window_objects.any?{ true }
522
522
  quit_browser(:force => false)
523
523
  end
@@ -540,7 +540,7 @@ module Vapir
540
540
 
541
541
  pid = @pid || begin
542
542
  self.pid
543
- rescue NotImplementedError
543
+ rescue CannotHandlePid
544
544
  nil
545
545
  end
546
546
 
@@ -557,20 +557,24 @@ module Vapir
557
557
 
558
558
  wait_for_process_exit(pid)
559
559
 
560
- @browser_window_object=@browser_object=@document_object=@content_window_object=@body_object=nil
560
+ @browser_window_object=@browser_object=@document_object=@content_window_object=nil
561
561
  nil
562
562
  end
563
563
 
564
+ # class representing an inability to either retrieve or check the status of firefox's pid
565
+ class CannotHandlePid < StandardError; end
566
+
564
567
  # returns the pid of the currently-attached Firefox process.
565
568
  #
566
569
  # This only works on Firefox >= 3.6, on platforms supported (see #current_os), and raises
567
- # NotImplementedError if it can't get the pid.
570
+ # CannotHandlePid if it can't get the pid.
568
571
  def pid
569
572
  begin
573
+ raise CannotHandlePid unless firefox_socket.host == 'localhost'
570
574
  begin
571
575
  ctypes = firefox_socket.Components.utils.import("resource://gre/modules/ctypes.jsm").ctypes
572
576
  rescue FirefoxSocketJavascriptError
573
- raise NotImplementedError, "Firefox 3.6 or greater is required for this method.\n\nOriginal error from firefox: #{$!.class}: #{$!.message}", $!.backtrace
577
+ raise CannotHandlePid, "Firefox 3.6 or greater is required for this method.\n\nOriginal error from firefox: #{$!.class}: #{$!.message}", $!.backtrace
574
578
  end
575
579
  lib, pidfunction, abi = *case current_os
576
580
  when :macosx
@@ -588,7 +592,7 @@ module Vapir
588
592
  # see https://bugzilla.mozilla.org/show_bug.cgi?id=585175
589
593
  ['kernel32', 'GetCurrentProcessId', ctypes['winapi_abi'] || ctypes['stdcall_abi']]
590
594
  else
591
- raise NotImplementedError, "don't know how to get pid for #{current_os}"
595
+ raise CannotHandlePid, "don't know how to get pid for #{current_os}"
592
596
  end
593
597
  lib = ctypes.open(lib)
594
598
  begin
@@ -600,9 +604,16 @@ module Vapir
600
604
  end
601
605
  end
602
606
 
607
+ # returns the host which will be configured for a socket. same as firefox_socket.host,
608
+ # if that exist, but this is correct even if firefox_socket isn't initialized.
609
+ def firefox_socket_host
610
+ firefox_socket_class_options['host'] || firefox_socket_class.config.host
611
+ end
612
+
603
613
  # attempts to determine whether the given process is still running. will raise
604
- # NotImplementedError if it can't determine this.
614
+ # CannotHandlePid if it can't determine this.
605
615
  def process_running?(pid)
616
+ raise CannotHandlePid unless firefox_socket_host == 'localhost'
606
617
  case current_os
607
618
  when :windows
608
619
  kernel32 = Vapir::Firefox.instance_eval do # define this on the class for reuse
@@ -630,7 +641,7 @@ module Vapir
630
641
  `ps -p #{pid}`
631
642
  $? == 0
632
643
  else
633
- raise NotImplementedError
644
+ raise CannotHandlePid
634
645
  end
635
646
  end
636
647
 
@@ -800,12 +811,36 @@ module Vapir
800
811
 
801
812
  # the HTTP response status code for the currently loaded document
802
813
  def response_status_code
814
+ current_document_channel_object.responseStatus
815
+ end
816
+ # a hash of the response headers returned for the currently loaded document
817
+ def response_headers
818
+ firefox_socket.call_function(:channel => current_document_channel_object) do %Q{
819
+ var headers={};
820
+ channel.visitResponseHeaders({visitHeader: function(header, value){ headers[header]=value; }});
821
+ return headers;
822
+ }
823
+ end.val.freeze
824
+ end
825
+ # a hash of the request headers the browser sent for the currently loaded document
826
+ def request_headers
827
+ firefox_socket.call_function(:channel => current_document_channel_object) do %Q{
828
+ var headers={};
829
+ channel.visitRequestHeaders({visitHeader: function(header, value){ headers[header]=value; }});
830
+ return headers;
831
+ }
832
+ end.val.freeze
833
+ end
834
+ # a nsIHttpChannel for the currently loaded document - the browser's docShell.currentDocumentChannel
835
+ #
836
+ # see https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIHttpChannel
837
+ def current_document_channel_object
803
838
  channel = nil
804
839
  ::Waiter.try_for(8, :exception => nil) do
805
840
  channel=browser.browser_object.docShell.currentDocumentChannel
806
841
  channel.is_a?(JavascriptObject) && channel.instanceof(browser.firefox_socket.Components.interfaces.nsIHttpChannel) && channel.respond_to?(:responseStatus)
807
842
  end || raise(RuntimeError, "expected currentDocumentChannel to exist and be a nsIHttpChannel but it wasn't; was #{channel.is_a?(JavascriptObject) ? channel.toString : channel.inspect}")
808
- status = channel.responseStatus
843
+ channel
809
844
  end
810
845
 
811
846
  # Maximize the current browser window.
@@ -820,6 +855,7 @@ module Vapir
820
855
 
821
856
  # Waits for the page to get loaded.
822
857
  def wait(options={})
858
+ return unless config.wait
823
859
  return unless exists?
824
860
  unless options.is_a?(Hash)
825
861
  raise ArgumentError, "given options should be a Hash, not #{options.inspect} (#{options.class})\nold conflicting arguments of no_sleep or last_url are gone"
@@ -833,9 +869,9 @@ module Vapir
833
869
  # If the redirect is to a download attachment that does not reload this page, this
834
870
  # method will loop forever. Therefore, we need to ensure that if this method is called
835
871
  # twice with the same URL, we simply accept that we're done.
836
- url= document_object.URL
872
+ url= document_object['URL']
837
873
 
838
- if(url != options[:last_url])
874
+ if(url && url != options[:last_url])
839
875
  # check for meta redirects, except for redirects back to the same page (infinite
840
876
  # loop redirects).
841
877
  metas=document_object.getElementsByTagName 'meta'
@@ -876,7 +912,11 @@ module Vapir
876
912
  # - :format => a valid format. if :dc is :window, the default is 'png' ('jpeg' is also supported); if :dc is anything else, 'bmp' is both the
877
913
  # default and the only supported format.
878
914
  def screen_capture(filename, options = {})
879
- options = handle_options(options, :format => nil, :dc => :page)
915
+ if filename =~ /\.(\w+)\z/
916
+ extension = $1
917
+ file_format = %w(jpeg png).inject({'jpg' => 'jpeg'}){|h,f| h.merge(f => f) }[extension]
918
+ end
919
+ options = handle_options(options, :format => file_format, :dc => :page)
880
920
 
881
921
  if options[:dc] == :page
882
922
  options[:format] ||= 'png'
@@ -929,9 +969,8 @@ module Vapir
929
969
  fileOutputStream.init(nsFile, writeFlag | createFlag | truncateFlag, 0664, null);
930
970
  fileOutputStream.write(bytes, numBytes);
931
971
  fileOutputStream.close();
932
- document_element.removeChild(canvas);
933
972
  }
934
- catch(e)
973
+ finally
935
974
  { document_element.removeChild(canvas);
936
975
  }
937
976
  )
@@ -984,10 +1023,19 @@ module Vapir
984
1023
  raise NotImplementedError, "(need to know how to access windows registry on JRuby)" if RUBY_PLATFORM =~ /java/
985
1024
  require 'win32/registry'
986
1025
  lm = ::Win32::Registry::HKEY_LOCAL_MACHINE
987
- lm.open('SOFTWARE\Mozilla\Mozilla Firefox') do |reg|
988
- reg1 = lm.open("SOFTWARE\\Mozilla\\Mozilla Firefox\\#{reg.keys[0]}\\Main")
989
- if entry = reg1.find { |key, type, data| key =~ /pathtoexe/i }
990
- return entry.last
1026
+ lm.open('SOFTWARE\Mozilla\Mozilla Firefox') do |firefox_reg|
1027
+ firefox_reg.keys.map do |firefox_version_key|
1028
+ firefox_reg.open(firefox_version_key) do |firefox_version_reg|
1029
+ if firefox_version_reg.keys.include?('Main')
1030
+ firefox_version_reg.open('Main') do |firefox_version_main_reg|
1031
+ firefox_version_main_reg.map do |key, type, data|
1032
+ if key=='PathToExe'
1033
+ return data
1034
+ end
1035
+ end
1036
+ end
1037
+ end
1038
+ end
991
1039
  end
992
1040
  end
993
1041
  end
@@ -60,7 +60,7 @@ module Vapir
60
60
  # TODO: Provide ability to specify event parameters like keycode for key events, and click screen
61
61
  # coordinates for mouse events.
62
62
  def fire_event(event_type, options={})
63
- options={:wait => true, :highlight => true}.merge(options)
63
+ options={:wait => config.wait, :highlight => true}.merge(options)
64
64
  with_highlight(options) do
65
65
  event=create_event_object(event_type, options)
66
66
  if !options[:wait]
@@ -159,10 +159,10 @@ module Vapir
159
159
  # Options:
160
160
  # - :wait => true or false. If true, waits for the javascript call to return, and calls the #wait method.
161
161
  # If false, does not wait for the javascript to return and does not call #wait.
162
- # Default is true.
162
+ # Default is the current config.wait value (which is by default true).
163
163
  # - :highlight => true or false. Highlights the element while clicking if true. Default is true.
164
164
  def click(options={})
165
- options={:wait => true, :highlight => true}.merge(options)
165
+ options={:wait => config.wait, :highlight => true}.merge(options)
166
166
  result=nil
167
167
  with_highlight(options) do
168
168
  assert_enabled if respond_to?(:assert_enabled)
@@ -196,12 +196,12 @@ module Vapir
196
196
  # Takes options:
197
197
  # - :highlight => true or false. Highlights the element while clicking if true. Default is true.
198
198
  def click_no_wait(options={})
199
- click(options.merge(:wait => false))
199
+ with_config(:wait => false) { click(options) }
200
200
  end
201
201
 
202
202
  # Waits for the browser to finish loading, if it is loading. See Firefox#wait.
203
203
  def wait(options={})
204
- @container.wait(options)
204
+ @container.wait(options) if config.wait
205
205
  end
206
206
 
207
207
  # Checks this element and its parents for display: none or visibility: hidden, these are
@@ -240,7 +240,7 @@ class FirefoxSocket
240
240
  # immediately.
241
241
  def read_value(options={})
242
242
  options=options_from_config(options, {:timeout => :default_timeout, :read_size => :read_size}, [:length_before_value])
243
- received_data = []
243
+ # received_data = []
244
244
  value_string = ""
245
245
  size_to_read=options[:read_size]
246
246
  timeout=options[:timeout]
@@ -249,7 +249,13 @@ class FirefoxSocket
249
249
  # logger.add(-1) { "RECV_SOCKET is starting. timeout=#{timeout}" }
250
250
  while size_to_read > 0 && ensuring_extra_handled { @socket.ready_to_recv?(timeout) }
251
251
  data = ensuring_extra_handled { @socket.recv(size_to_read) }
252
- received_data << data
252
+
253
+ # if the peer has performed an orderly shutdown, recv returns a 0-length string.
254
+ if data.size==0
255
+ raise FirefoxSocketConnectionError, "The connection to Firefox has been closed."
256
+ end
257
+
258
+ # received_data << data
253
259
  value_string << data
254
260
  if @prompt && @expecting_prompt && utf8_length_safe(value_string) > @prompt.length
255
261
  if value_string =~ /\A#{Regexp.escape(@prompt)}/
@@ -280,11 +286,6 @@ class FirefoxSocket
280
286
  timeout=config.short_timeout
281
287
  end
282
288
  end
283
-
284
- # Kernel.select seems to indicate that a dead socket is ready to read, and returns endless blank strings to recv. rather irritating.
285
- if received_data.length >= 3 && received_data[-3..-1].all?{|rd| rd==''}
286
- raise FirefoxSocketConnectionError, "Socket seems to no longer be connected"
287
- end
288
289
  # logger.add(-1) { "RECV_SOCKET is continuing. timeout=#{timeout}; data=#{data.inspect}" }
289
290
  end
290
291
  # logger.debug { "RECV_SOCKET is done. received_data=#{received_data.inspect}; value_string=#{value_string.inspect}" }
@@ -143,7 +143,7 @@ class JavascriptObject
143
143
  def val_or_object(options={})
144
144
  options={:error_on_undefined=>true, :define_methods => self.class.always_define_methods}.merge(options)
145
145
  if function_result # calling functions multiple times is bad, so store in temp before figuring out what to do with it
146
- store_rand_object_key(firefox_socket.temp_object).val_or_object(options.merge(:error_on_undefined => false))
146
+ store_rand_temp.val_or_object(options.merge(:error_on_undefined => false))
147
147
  else
148
148
  # if we don't know our type, stick everything into one call to avoid multiple socket calls
149
149
  types_to_convert = ['boolean', 'number', 'string', 'null']
@@ -192,7 +192,7 @@ class JavascriptObject
192
192
  end
193
193
  end
194
194
  # does the work of #method_missing to determine whether to call a function what to return based
195
- # on the defined behavior of the given suffix. see #method_missing for more. information.
195
+ # on the defined behavior of the given suffix. see #method_missing for more information.
196
196
  def assign_or_call_or_val_or_object_by_suffix(suffix, *args)
197
197
  if suffix=='='
198
198
  assign(*args)
@@ -472,7 +472,9 @@ class JavascriptObject
472
472
  # in irb, mostly.
473
473
  def define_methods! # :nodoc:
474
474
  metaclass=(class << self; self; end)
475
- keys=firefox_socket.object("function(obj) { var keys=[]; for(var key in obj) { keys.push(key); } return keys; }").pass(self).val
475
+ # the following needs the try/catch because sometimes it raises NS_ERROR_NOT_AVAILABLE: Component is not available
476
+ # bug: https://bugzilla.mozilla.org/show_bug.cgi?id=683978
477
+ keys=firefox_socket.object("function(obj) { var keys=[]; try { for(var key in obj) { keys.push(key); } } catch(e) {} return keys; }").pass(self).val
476
478
 
477
479
  keys.grep(/\A[a-z_][a-z0-9_]*\z/i).reject{|k| self.class.method_defined?(k)}.each do |key|
478
480
  metaclass.send(:define_method, key) do |*args|
@@ -545,6 +547,9 @@ class JavascriptObject
545
547
  def to_function
546
548
  JavascriptFunction.new(self.ref, self.firefox_socket, :debug_name => debug_name)
547
549
  end
550
+ def to_simple_enumerator
551
+ JavascriptSimpleEnumerator.new(self.ref, self.firefox_socket, :debug_name => debug_name)
552
+ end
548
553
 
549
554
  # returns a ruby Hash. each key/value pair of this object
550
555
  # is represented in the returned hash.
@@ -649,6 +654,7 @@ class JavascriptDOMNode < JavascriptObject
649
654
  # call #dump(:recurse => n) to recurse down only n levels. default is to recurse all the way down the dom tree.
650
655
  def dump(options={})
651
656
  options={:recurse => nil, :level => 0}.merge(options)
657
+ options[:document] ||= self.ownerDocument
652
658
  next_options=options.merge(:recurse => options[:recurse] && (options[:recurse]-1), :level => options[:level]+1)
653
659
  result=(" "*options[:level]*2)+self.inspect+"\n"
654
660
  if options[:recurse]==0
@@ -657,6 +663,13 @@ class JavascriptDOMNode < JavascriptObject
657
663
  self.childNodes.to_array.each do |child|
658
664
  result+=child.to_dom.dump(next_options)
659
665
  end
666
+ anons = self.nodeType == 1 && options[:document].getAnonymousNodes(self)
667
+ if anons
668
+ result+=(" "*next_options[:level]*2)+"ANONYMOUS:\n"
669
+ anons.to_array.each do |child|
670
+ result+=child.to_dom.dump(next_options)
671
+ end
672
+ end
660
673
  end
661
674
  result
662
675
  end
@@ -734,3 +747,13 @@ class JavascriptFunction < JavascriptObject
734
747
  end
735
748
  end
736
749
  end
750
+
751
+ class JavascriptSimpleEnumerator < JavascriptObject
752
+ def each
753
+ stored = store_rand_temp
754
+ while stored.hasMoreElements
755
+ yield stored.getNext
756
+ end
757
+ end
758
+ include Enumerable
759
+ end
@@ -51,7 +51,7 @@ module Vapir
51
51
  # raise if no anonymous nodes are found (this is where the buttons are)
52
52
  anonymous_dialog_nodes=@modal_window.document.getAnonymousNodes(@modal_window.document.documentElement) || raise("Could not find anonymous nodes on which to look for buttons")
53
53
  xul_buttons=[]
54
- anonymous_dialog_nodes.to_array.each do |node|
54
+ anonymous_dialog_nodes.to_array.select{|node| node.nodeType == 1 }.each do |node|
55
55
  xul_buttons+=node.getElementsByTagName('xul:button').to_array.select do |button|
56
56
  Vapir::fuzzy_match(button.label, button_text)
57
57
  end
@@ -74,8 +74,8 @@ module Vapir
74
74
  assert_exists
75
75
  modal_window
76
76
  end
77
- def mozilla_window_class_name
78
- 'MozillaDialogClass'
77
+ def mozilla_window_class_names
78
+ ['MozillaDialogClass']
79
79
  end
80
80
  end
81
81
 
@@ -134,7 +134,8 @@ module Vapir
134
134
  end
135
135
 
136
136
  def wait(options=nil)
137
- ::Waiter.try_for(Firefox::ARBITRARY_TIMEOUT) do
137
+ return unless config.wait
138
+ ::Waiter.try_for(config.wait_timeout) do
138
139
  !browser_object.webProgress.isLoadingDocument
139
140
  end
140
141
  end
@@ -11,10 +11,12 @@ module Vapir
11
11
  end
12
12
 
13
13
  # evaluates a given javascript string in the context of the browser's content window. anything
14
- # that is a top-level variable on the window will be seen as a top-level variable in the
15
- # evaluated script.
14
+ # that is a top-level variable on the window (such as document or alert) will be seen as a
15
+ # top-level variable in the evaluated script.
16
16
  #
17
- # returns the last evaluated expression.
17
+ # returns the last evaluated expression. WARNING! DO NOT RUN #execute_script AGAINST UNTRUSTED
18
+ # PAGES! the resulting value has no security wrappers and could execute malicious code in a
19
+ # privileged context.
18
20
  #
19
21
  # raises an error if the given javascript errors.
20
22
  #
@@ -43,8 +45,17 @@ module Vapir
43
45
  # >> browser.execute_script('element.PercentLoaded()', :element => browser.element(:tag_name => 'embed').element_object)
44
46
  # => 100
45
47
  def execute_script(javascript, other_variables={})
46
- sandbox=firefox_socket.Components.utils.Sandbox(content_window_object)
47
- sandbox.window=content_window_object.window
48
+ # TODO: add tests for cross-context expando properties
49
+ xpcNativeWrapper = firefox_socket.root['XPCNativeWrapper']
50
+ unwrapped_window = if xpcNativeWrapper.respond_to?('unwrap')
51
+ xpcNativeWrapper.unwrap(content_window_object)
52
+ elsif content_window_object.respond_to?('wrappedJSObject')
53
+ content_window_object.wrappedJSObject
54
+ else
55
+ content_window_object.window
56
+ end
57
+ sandbox=firefox_socket.Components.utils.Sandbox(unwrapped_window, :wantXrays => false)
58
+ sandbox.window = unwrapped_window
48
59
  other_variables.each do |name, var|
49
60
  sandbox[name]=var
50
61
  end
@@ -1,5 +1,5 @@
1
1
  module Vapir
2
2
  class Firefox
3
- VERSION = '1.9.0'
3
+ VERSION = '1.10.0'
4
4
  end
5
5
  end
@@ -18,7 +18,7 @@ module Vapir
18
18
  begin
19
19
  candidates=::Waiter.try_for(2, :condition => proc{|ret| ret.size > 0}, :exception => nil) do
20
20
  WinWindow::All.select do |win|
21
- [mozilla_window_class_name].include?(win.class_name) && win.text==browser_window_object.document.title
21
+ mozilla_window_class_names.include?(win.class_name) && win.text==browser_window_object.document.title
22
22
  end
23
23
  end
24
24
  unless candidates.size==1
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vapir-firefox
3
3
  version: !ruby/object:Gem::Version
4
- hash: 51
5
4
  prerelease: false
6
5
  segments:
7
6
  - 1
8
- - 9
7
+ - 10
9
8
  - 0
10
- version: 1.9.0
9
+ version: 1.10.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - Ethan
@@ -15,34 +14,30 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2011-08-04 00:00:00 -04:00
17
+ date: 2011-09-28 00:00:00 -04:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: vapir-common
23
22
  prerelease: false
24
23
  requirement: &id001 !ruby/object:Gem::Requirement
25
- none: false
26
24
  requirements:
27
25
  - - "="
28
26
  - !ruby/object:Gem::Version
29
- hash: 51
30
27
  segments:
31
28
  - 1
32
- - 9
29
+ - 10
33
30
  - 0
34
- version: 1.9.0
31
+ version: 1.10.0
35
32
  type: :runtime
36
33
  version_requirements: *id001
37
34
  - !ruby/object:Gem::Dependency
38
35
  name: json
39
36
  prerelease: false
40
37
  requirement: &id002 !ruby/object:Gem::Requirement
41
- none: false
42
38
  requirements:
43
39
  - - ">="
44
40
  - !ruby/object:Gem::Version
45
- hash: 3
46
41
  segments:
47
42
  - 0
48
43
  version: "0"
@@ -109,27 +104,23 @@ rdoc_options:
109
104
  require_paths:
110
105
  - lib
111
106
  required_ruby_version: !ruby/object:Gem::Requirement
112
- none: false
113
107
  requirements:
114
108
  - - ">="
115
109
  - !ruby/object:Gem::Version
116
- hash: 3
117
110
  segments:
118
111
  - 0
119
112
  version: "0"
120
113
  required_rubygems_version: !ruby/object:Gem::Requirement
121
- none: false
122
114
  requirements:
123
115
  - - ">="
124
116
  - !ruby/object:Gem::Version
125
- hash: 3
126
117
  segments:
127
118
  - 0
128
119
  version: "0"
129
120
  requirements:
130
121
  - Firefox browser with MozRepl or JSSH extension installed
131
122
  rubyforge_project:
132
- rubygems_version: 1.3.7
123
+ rubygems_version: 1.3.6
133
124
  signing_key:
134
125
  specification_version: 3
135
126
  summary: Library for automating the Firefox browser in Ruby