vapir-firefox 1.8.1 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -85,7 +85,6 @@ require 'vapir-common/waiter'
85
85
  require 'vapir-common/browser'
86
86
  require 'vapir-firefox/window'
87
87
  require 'vapir-firefox/modal_dialog'
88
- require 'vapir-firefox/jssh_socket'
89
88
  require 'vapir-firefox/container'
90
89
  require 'vapir-firefox/page_container'
91
90
 
@@ -97,36 +96,115 @@ module Vapir
97
96
  include Firefox::Window
98
97
  include Firefox::ModalDialogContainer
99
98
 
100
- # initializes a JsshSocket and stores in a class variable.
101
- def self.initialize_jssh_socket # :nodoc:
102
- uninitialize_jssh_socket
103
- @@jssh_socket=JsshSocket.new
104
- @@firewatir_jssh_objects=@@jssh_socket.object("Vapir").assign({})
105
- @@jssh_socket
99
+ # initializes a FirefoxSocket and stores in a class variable.
100
+ def self.initialize_firefox_socket(socket_class, socket_options) # :nodoc:
101
+ uninitialize_firefox_socket
102
+ @@firefox_socket=socket_class.new(socket_options)
103
+ vapir_javascript_functions = {
104
+ :element_real_visibility => %Q(
105
+ function(element_to_check, document_object)
106
+ { var real_visibility = null;
107
+ while(element_to_check && real_visibility==null)
108
+ { var style = element_to_check.nodeType==1 ? document_object.defaultView.getComputedStyle(element_to_check, null) : null;
109
+ if(style)
110
+ { // only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements),
111
+ // or 'visible'. ignore 'inherit'; keep looking upward.
112
+ // this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up.
113
+ // this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
114
+ if(style.visibility)
115
+ { var visibility=style.visibility.toLowerCase();
116
+ if($A(['hidden', 'collapse', 'visible']).include(visibility))
117
+ { real_visibility = visibility;
118
+ }
119
+ }
120
+ }
121
+ element_to_check=element_to_check.parentNode;
122
+ }
123
+ return real_visibility;
124
+ }
125
+ ),
126
+ :element_displayed => %Q(
127
+ function(node, document_object)
128
+ { var style = node.nodeType==1 ? document_object.defaultView.getComputedStyle(node, null) : null;
129
+ var display = style && style.display;
130
+ var displayed = display ? display.toLowerCase() != 'none' : true;
131
+ return displayed;
132
+ }
133
+ ),
134
+ :visible_text_nodes => %Q(
135
+ function(element_object, document_object)
136
+ { var recurse_text_nodes = Vapir.Ycomb(function(recurse)
137
+ { return function(node, parent_visibility)
138
+ { if(node.nodeType==1 || node.nodeType==9)
139
+ { var style = node.nodeType==1 ? document_object.defaultView.getComputedStyle(node, null) : null;
140
+ var our_visibility = style && style.visibility;
141
+ if(!(our_visibility && $A(['hidden', 'collapse', 'visible']).include(our_visibility.toLowerCase())))
142
+ { our_visibility = parent_visibility;
143
+ }
144
+ if(!Vapir.element_displayed(node, document_object))
145
+ { return [];
146
+ }
147
+ else
148
+ { return $A(node.childNodes).inject([], function(result, child_node)
149
+ { return result.concat(recurse(child_node, our_visibility));
150
+ });
151
+ }
152
+ }
153
+ else if(node.nodeType==3)
154
+ { if(parent_visibility && $A(['hidden', 'collapse']).include(parent_visibility.toLowerCase()))
155
+ { return [];
156
+ }
157
+ else
158
+ { return [node.data];
159
+ }
160
+ }
161
+ else
162
+ { return [];
163
+ }
164
+ };
165
+ });
166
+ var element_to_check = element_object;
167
+ while(element_to_check)
168
+ { // check for display property. this is not inherited, so ascend the heirarchy looking.
169
+ //
170
+ // any parent with display of 'none' overrides a more-immediate visibility='visible', so if any parent has
171
+ // display=none, this is not visible and has no visible child text nodes.
172
+ if(!Vapir.element_displayed(element_to_check, document_object))
173
+ { return [];
174
+ }
175
+ element_to_check=element_to_check.parentNode;
176
+ }
177
+ return recurse_text_nodes(element_object, Vapir.element_real_visibility(document_object, element_object));
178
+ }
179
+ )
180
+ }
181
+ vapir_javascript_functions.each do |name, function_string|
182
+ @@firefox_socket.root.Vapir[name]=@@firefox_socket.object(function_string)
183
+ end
184
+ @@firefox_socket
106
185
  end
107
- # returns a connected JsshSocket. pass :reset_if_dead => true if you suspect an existing
186
+ # returns a connected FirefoxSocket. pass :reset_if_dead => true if you suspect an existing
108
187
  # socket may be dead, and you want a new one. a warning will be printed if this occurs.
109
- def self.jssh_socket(options={}) # :nodoc:
110
- if options[:reset] || !(class_variable_defined?('@@jssh_socket') && @@jssh_socket)
111
- initialize_jssh_socket
188
+ def self.firefox_socket(options={}) # :nodoc:
189
+ if options[:reset] || !(class_variable_defined?('@@firefox_socket') && @@firefox_socket)
190
+ initialize_firefox_socket(options[:socket_class], options[:socket_options])
112
191
  end
113
192
  if options[:reset_if_dead]
114
193
  begin
115
- @@jssh_socket.assert_socket
116
- rescue JsshConnectionError
117
- Kernel.warn "WARNING: JsshSocket RESET: resetting jssh socket. Any active javascript references will not exist on the new socket!"
118
- initialize_jssh_socket
194
+ @@firefox_socket.assert_socket
195
+ rescue FirefoxSocketConnectionError
196
+ Kernel.warn "WARNING: Firefox socket RESET: resetting connection to firefox. Any active javascript references will not exist on the new socket!"
197
+ initialize_firefox_socket(options[:socket_class], options[:socket_options])
119
198
  end
120
199
  end
121
- @@jssh_socket
200
+ @@firefox_socket
122
201
  end
123
- # unsets a the current jssh socket
124
- def self.uninitialize_jssh_socket # :nodoc:
125
- @@jssh_socket=nil
126
- @@firewatir_jssh_objects=nil
202
+ # unsets a the current firefox socket
203
+ def self.uninitialize_firefox_socket # :nodoc:
204
+ @@firefox_socket=nil
127
205
  end
128
- def jssh_socket(options=nil)
129
- options ? self.class.jssh_socket(options) : @@jssh_socket
206
+ def firefox_socket(options=nil)
207
+ options ? self.class.firefox_socket(options) : @@firefox_socket
130
208
  end
131
209
 
132
210
  # Description:
@@ -158,15 +236,14 @@ module Vapir
158
236
  @binary_path=options[:binary_path]
159
237
  end
160
238
 
161
- # check for jssh not running, firefox may be open but not with -jssh
162
- # if its not open at all, regardless of the :suppress_launch_process option start it
163
- # error if running without jssh, we don't want to kill their current window (mac only)
239
+ require 'vapir-firefox/firefox_socket/base'
240
+ # check for extension not listening; firefox may be open but not listening (extension not set up)
164
241
  begin
165
- jssh_socket(:reset_if_dead => true).assert_socket
166
- rescue JsshError
242
+ firefox_socket(:reset_if_dead => true, :socket_class => firefox_socket_class, :socket_options => firefox_socket_class_options).assert_socket
243
+ rescue FirefoxSocketConnectionError
167
244
  # here we're going to assume that since it's not connecting, we need to launch firefox.
168
245
  if options[:attach]
169
- raise Vapir::Exception::NoBrowserException, "cannot attach using #{options[:attach].inspect} - could not connect to Firefox with JSSH"
246
+ raise Vapir::Exception::NoBrowserException, "cannot attach using #{options[:attach].inspect} - could not connect to Firefox"
170
247
  else
171
248
  launch_browser(options)
172
249
  # if we just launched a the browser process, attach to the window
@@ -178,16 +255,16 @@ module Vapir
178
255
  options[:attach]=[:title, //]
179
256
  end
180
257
  end
181
- ::Waiter.try_for(options[:timeout], :exception => Vapir::Exception::NoBrowserException.new("Could not connect to the JSSH socket on the browser after #{options[:timeout]} seconds. Either Firefox did not start or JSSH is not installed and listening.")) do
258
+ ::Waiter.try_for(options[:timeout], :exception => Vapir::Exception::NoBrowserException.new("Could not connect to the #{config.firefox_extension} socket on the browser after #{options[:timeout]} seconds. Either Firefox did not start or #{config.firefox_extension} is not installed and listening.")) do
182
259
  begin
183
- jssh_socket(:reset_if_dead => true).assert_socket
260
+ firefox_socket(:reset_if_dead => true, :socket_class => firefox_socket_class, :socket_options => firefox_socket_class_options).assert_socket
184
261
  true
185
- rescue JsshUnableToStart
262
+ rescue FirefoxSocketUnableToStart
186
263
  false
187
264
  end
188
265
  end
189
266
  end
190
- @browser_jssh_objects = jssh_socket.object('{}').store_rand_object_key(@@firewatir_jssh_objects) # this is an object that holds stuff for this browser
267
+ @browser_objects = firefox_socket.object('{}').store_rand_temp
191
268
 
192
269
  @pid = begin
193
270
  self.pid
@@ -196,7 +273,16 @@ module Vapir
196
273
  end
197
274
 
198
275
  if options[:attach]
199
- attach(*options[:attach])
276
+ how, what = *options[:attach]
277
+ @browser_window_object = case how
278
+ when :browser_window_object
279
+ what
280
+ else
281
+ ::Waiter.try_for(options[:timeout], :exception => Vapir::Exception::NoMatchingWindowFoundException.new("Unable to locate a window with #{how} of #{what}")) do
282
+ find_window(how, what)
283
+ end
284
+ end
285
+ set_browser_document
200
286
  else
201
287
  open_window
202
288
  end
@@ -217,13 +303,14 @@ module Vapir
217
303
  end
218
304
 
219
305
  def exists?
220
- # jssh_socket may be nil if the window has closed
221
- jssh_socket && browser_window_object && self.class.browser_window_objects.include?(browser_window_object)
306
+ # firefox_socket may be nil if the window has closed
307
+ # if the socket disconnects, we'll assume that means the browser quit and therefore the window doesn't exist
308
+ firefox_socket && firefox_socket.handling_connection_error(:handle => proc{ return false }) do
309
+ browser_window_object && self.class.browser_window_objects.include?(browser_window_object)
310
+ end
222
311
  end
223
312
 
224
313
  # Launches firebox browser
225
- # options as .new
226
-
227
314
  def launch_browser(options = {})
228
315
  ff_options = []
229
316
  if(options[:profile])
@@ -232,7 +319,8 @@ module Vapir
232
319
 
233
320
  bin = path_to_bin()
234
321
  @self_launched_browser = true
235
- @t = Thread.new { system(bin, '-jssh', *ff_options) } # TODO: launch process in such a way that @pid can be noted
322
+ ff_options += firefox_socket_class.command_line_flags(firefox_socket_class_options).map(&:to_s)
323
+ @t = Thread.new { system(bin, *ff_options) } # TODO: launch process in such a way that @pid can be noted
236
324
  end
237
325
  private :launch_browser
238
326
 
@@ -263,13 +351,13 @@ module Vapir
263
351
  end
264
352
  CGI.escape(key.to_s)+'='+CGI.escape(val)
265
353
  end.join("&")
266
- stringStream = jssh_socket.Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(jssh_socket.Components.interfaces.nsIStringInputStream)
354
+ stringStream = firefox_socket.Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(firefox_socket.Components.interfaces.nsIStringInputStream)
267
355
  if stringStream.to_hash.key?('data')
268
356
  stringStream.data=dataString
269
357
  else
270
358
  stringStream.setData(dataString, dataString.unpack("U*").length)
271
359
  end
272
- postData = jssh_socket.Components.classes["@mozilla.org/network/mime-input-stream;1"].createInstance(jssh_socket.Components.interfaces.nsIMIMEInputStream)
360
+ postData = firefox_socket.Components.classes["@mozilla.org/network/mime-input-stream;1"].createInstance(firefox_socket.Components.interfaces.nsIMIMEInputStream)
273
361
  postData.addHeader("Content-Type", "application/x-www-form-urlencoded")
274
362
  postData.addContentLength = true
275
363
  postData.setData(stringStream)
@@ -309,12 +397,12 @@ module Vapir
309
397
  @error_checkers = []
310
398
  end
311
399
 
312
- # Sets the document, window and browser variables to point to correct object in JSSh.
400
+ # Sets the document, window and browser variables to point to correct objects.
313
401
  def set_browser_document
314
402
  unless browser_window_object
315
403
  raise "Window must be set (using open_window or attach) before the browser document can be set!"
316
404
  end
317
- @browser_object=@browser_jssh_objects[:browser]= ::Waiter.try_for(2, :exception => Vapir::Exception::NoMatchingWindowFoundException.new("The browser could not be found on the specified Firefox window!")) do
405
+ @browser_object=@browser_objects[:browser]= ::Waiter.try_for(2, :exception => Vapir::Exception::NoMatchingWindowFoundException.new("The browser could not be found on the specified Firefox window!")) do
318
406
  if browser_window_object.respond_to?(:getBrowser)
319
407
  browser_window_object.getBrowser
320
408
  end
@@ -326,27 +414,27 @@ module Vapir
326
414
  @content_window_object=browser_object.contentWindow
327
415
  # 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
328
416
  @body_object=document_object.body
329
- @browser_jssh_objects[:requests_in_progress]=[]
330
- @requests_in_progress=@browser_jssh_objects[:requests_in_progress].to_array
331
- @browser_jssh_objects[:unmatched_stopped_requests_count]=0
417
+ @browser_objects[:requests_in_progress]=[]
418
+ @requests_in_progress=@browser_objects[:requests_in_progress].to_array
419
+ @browser_objects[:unmatched_stopped_requests_count]=0
332
420
 
333
- @updated_at_epoch_ms=@browser_jssh_objects.attr(:updated_at_epoch_ms).assign_expr('new Date().getTime()')
334
- @updated_at_offset=Time.now.to_f-jssh_socket.value_json('new Date().getTime()')/1000.0
421
+ @updated_at_epoch_ms=@browser_objects.attr(:updated_at_epoch_ms).assign_expr('new Date().getTime()')
422
+ @updated_at_offset=Time.now.to_f-firefox_socket.value_json('new Date().getTime()')/1000.0
335
423
 
336
424
  # Add eventlistener for browser window so that we can reset the document back whenever there is redirect
337
425
  # or browser loads on its own after some time. Useful when you are searching for flight results etc and
338
426
  # page goes to search page after that it goes automatically to results page.
339
427
  # Details : http://zenit.senecac.on.ca/wiki/index.php/Mozilla.dev.tech.xul#What_is_an_example_of_addProgressListener.3F
340
- @browser_jssh_objects[:listener_object]={}
341
- listener_object=@browser_jssh_objects[:listener_object]
342
- listener_object[:QueryInterface]=jssh_socket.function(:aIID) do %Q(
428
+ @browser_objects[:listener_object]={}
429
+ listener_object=@browser_objects[:listener_object]
430
+ listener_object[:QueryInterface]=firefox_socket.function(:aIID) do %Q(
343
431
  if(aIID.equals(Components.interfaces.nsIWebProgressListener) || aIID.equals(Components.interfaces.nsISupportsWeakReference) || aIID.equals(Components.interfaces.nsISupports))
344
432
  { return this;
345
433
  }
346
434
  throw Components.results.NS_NOINTERFACE;
347
435
  )
348
436
  end
349
- listener_object[:onStateChange]= jssh_socket.function(:aWebProgress, :aRequest, :aStateFlag, :aStatus) do %Q(
437
+ listener_object[:onStateChange]= firefox_socket.function(:aWebProgress, :aRequest, :aStateFlags, :aStatus) do %Q(
350
438
  var requests_in_progress=#{@requests_in_progress.ref};
351
439
  if(aStateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP)
352
440
  { #{@updated_at_epoch_ms.ref}=new Date().getTime();
@@ -365,7 +453,7 @@ module Vapir
365
453
  }
366
454
  }
367
455
  if(!matched)
368
- { #{@browser_jssh_objects.attr(:unmatched_stopped_requests_count).ref}++; //.push({webProgress: aWebProgress, request: aRequest, stateFlags: aStateFlags, status: aStatus});
456
+ { #{@browser_objects.attr(:unmatched_stopped_requests_count).ref}++; //.push({webProgress: aWebProgress, request: aRequest, stateFlags: aStateFlags, status: aStatus});
369
457
  // count any stop requests that we fail to match so that we can compare that count to the number of unmatched start requests.
370
458
  }
371
459
  }
@@ -409,27 +497,28 @@ module Vapir
409
497
  assert_exists
410
498
  # we expect the browser may exit if there are no windows which aren't ourself. except on mac.
411
499
  expect_exit = !self.class.window_objects.any?{|other_window| other_window != self.browser_window_object } && current_os != :macosx
412
- begin
500
+
501
+ connection_error_handler = proc do
502
+ Vapir::Firefox.uninitialize_firefox_socket
503
+ wait_for_process_exit(@pid)
504
+ end
505
+ firefox_socket.handling_connection_error(:handle => connection_error_handler) do
413
506
  browser_window_object.close
414
- # TODO/fix timeout; this shouldn't be a hard-coded magic number.
415
- ::Waiter.try_for(32, :exception => Exception::WindowFailedToCloseException.new("The browser window did not close")) do
507
+ ::Waiter.try_for(config.close_timeout, :exception => Exception::WindowFailedToCloseException.new("The browser window did not close")) do
416
508
  !exists?
417
509
  end
418
510
  if expect_exit
419
- ::Waiter.try_for(8, :exception => nil) do
420
- jssh_socket.assert_socket # this should error, going up past the waiter to the rescue block above
511
+ ::Waiter.try_for(config.quit_timeout, :exception => nil) do
512
+ firefox_socket.assert_socket # this should error, going up past the waiter to #handling_connection_error
421
513
  false
422
514
  end
423
515
  else
424
- jssh_socket.assert_socket
516
+ firefox_socket.assert_socket
425
517
  end
426
- rescue JsshConnectionError # the socket may disconnect when we close the browser, causing the JsshSocket to complain
427
- Vapir::Firefox.uninitialize_jssh_socket
428
- wait_for_process_exit(@pid)
429
518
  end
430
-
519
+
431
520
  @browser_window_object=@browser_object=@document_object=@content_window_object=@body_object=nil
432
- if @self_launched_browser && jssh_socket && !self.class.window_objects.any?{ true }
521
+ if @self_launched_browser && firefox_socket && !self.class.window_objects.any?{ true }
433
522
  quit_browser(:force => false)
434
523
  end
435
524
  end
@@ -444,9 +533,9 @@ module Vapir
444
533
  #
445
534
  # quit_browser(:force => true) will force the browser to quit.
446
535
  #
447
- # if there is no existing connection to JSSH, this will attempt to create one. If that fails, JsshUnableToStart will be raised.
536
+ # if there is no existing connection to firefox, this will attempt to create one. If that fails, FirefoxSocketUnableToStart will be raised.
448
537
  def quit_browser(options={})
449
- jssh_socket(:reset_if_dead => true).assert_socket
538
+ firefox_socket(:reset_if_dead => true, :socket_class => firefox_socket_class, :socket_options => firefox_socket_class_options).assert_socket
450
539
  options=handle_options(options, :force => false)
451
540
 
452
541
  pid = @pid || begin
@@ -456,16 +545,14 @@ module Vapir
456
545
  end
457
546
 
458
547
  # from https://developer.mozilla.org/en/How_to_Quit_a_XUL_Application
459
- appStartup= jssh_socket.Components.classes['@mozilla.org/toolkit/app-startup;1'].getService(jssh_socket.Components.interfaces.nsIAppStartup)
460
- quitSeverity = options[:force] ? jssh_socket.Components.interfaces.nsIAppStartup.eForceQuit : jssh_socket.Components.interfaces.nsIAppStartup.eAttemptQuit
461
- begin
548
+ appStartup= firefox_socket.Components.classes['@mozilla.org/toolkit/app-startup;1'].getService(firefox_socket.Components.interfaces.nsIAppStartup)
549
+ quitSeverity = options[:force] ? firefox_socket.Components.interfaces.nsIAppStartup.eForceQuit : firefox_socket.Components.interfaces.nsIAppStartup.eAttemptQuit
550
+ firefox_socket.handling_connection_error(:handle => proc{ Vapir::Firefox.uninitialize_firefox_socket }) do
462
551
  appStartup.quit(quitSeverity)
463
- ::Waiter.try_for(8, :exception => Exception::WindowFailedToCloseException.new("The browser did not quit")) do
464
- jssh_socket.assert_socket # this should error, going up past the waiter to the rescue block above
552
+ ::Waiter.try_for(config.quit_timeout, :exception => Exception::WindowFailedToCloseException.new("The browser did not quit")) do
553
+ firefox_socket.assert_socket # this should error, going up past the waiter to #handling_connection_error
465
554
  false
466
555
  end
467
- rescue JsshConnectionError
468
- Vapir::Firefox.uninitialize_jssh_socket
469
556
  end
470
557
 
471
558
  wait_for_process_exit(pid)
@@ -481,8 +568,8 @@ module Vapir
481
568
  def pid
482
569
  begin
483
570
  begin
484
- ctypes = jssh_socket.Components.utils.import("resource://gre/modules/ctypes.jsm").ctypes
485
- rescue JsshJavascriptError
571
+ ctypes = firefox_socket.Components.utils.import("resource://gre/modules/ctypes.jsm").ctypes
572
+ rescue FirefoxSocketJavascriptError
486
573
  raise NotImplementedError, "Firefox 3.6 or greater is required for this method.\n\nOriginal error from firefox: #{$!.class}: #{$!.message}", $!.backtrace
487
574
  end
488
575
  lib, pidfunction, abi = *case current_os
@@ -491,12 +578,25 @@ module Vapir
491
578
  when :linux
492
579
  ["libc.so.6", 'getpid', ctypes.default_abi]
493
580
  when :windows
494
- ['kernel32', 'GetCurrentProcessId', ctypes.stdcall_abi]
581
+ # winapi is correct - we do not want stdcall's mangling. ff4+ does the stdcall
582
+ # mangling correctly with ctypes.stdcall_abi, and since we don't want mangling,
583
+ # that fails here. ff < 4 does not do stdcall mangling (and has no winapi abi
584
+ # to skip mangling) so ends up working correctly.
585
+ # so, we want winapi if it's defined to skip mangling, but if
586
+ # it's not defined, use stdcall because it won't mangle anyway.
587
+ #
588
+ # see https://bugzilla.mozilla.org/show_bug.cgi?id=585175
589
+ ['kernel32', 'GetCurrentProcessId', ctypes['winapi_abi'] || ctypes['stdcall_abi']]
495
590
  else
496
591
  raise NotImplementedError, "don't know how to get pid for #{current_os}"
497
592
  end
498
- getpid = ctypes.open(lib).declare(pidfunction, abi, ctypes.int32_t)
499
- getpid.call()
593
+ lib = ctypes.open(lib)
594
+ begin
595
+ getpid = lib.declare(pidfunction, abi, ctypes['int32_t'])
596
+ return getpid.call()
597
+ ensure
598
+ lib.close
599
+ end
500
600
  end
501
601
  end
502
602
 
@@ -540,7 +640,7 @@ module Vapir
540
640
  # the pfirefox process has exited.
541
641
  def wait_for_process_exit(pid)
542
642
  if pid
543
- ::Waiter.try_for(16, :exception => Exception::WindowFailedToCloseException.new("The browser did not quit")) do
643
+ ::Waiter.try_for(config.quit_timeout, :exception => Exception::WindowFailedToCloseException.new("The browser did not quit")) do
544
644
  !process_running?(pid)
545
645
  end
546
646
  else
@@ -571,45 +671,53 @@ module Vapir
571
671
  end
572
672
  end
573
673
  end
574
- end
575
- include FirefoxClassAndInstanceMethods
576
- extend FirefoxClassAndInstanceMethods
577
-
578
-
579
674
 
580
- # Used for attaching pop up window to an existing Firefox window, either by url or title.
581
- # ff.attach(:url, 'http://www.google.com')
582
- # ff.attach(:title, 'Google')
583
- #
584
- # Output:
585
- # Instance of newly attached window.
586
- def attach(how, what)
587
- @browser_window_object = case how
588
- when :browser_window_object
589
- what
590
- else
591
- find_window(how, what)
675
+ private
676
+ # returns the class inheriting from FirefoxSocket which will be used to connect to a firefox extension
677
+ def firefox_socket_class
678
+ if config.firefox_extension.is_a?(String) && config.firefox_extension.downcase=='mozrepl'
679
+ require 'vapir-firefox/firefox_socket/mozrepl'
680
+ MozreplSocket
681
+ elsif config.firefox_extension.is_a?(String) && config.firefox_extension.downcase=='jssh'
682
+ require 'vapir-firefox/firefox_socket/jssh'
683
+ JsshSocket
684
+ else
685
+ raise Vapir::Configuration::InvalidValueError, "Unrecognized firefox extension #{config.firefox_extension.inspect}. Vapir supports the JSSH and Mozrepl extensions."
686
+ end
592
687
  end
593
-
594
- unless @browser_window_object
595
- raise Exception::NoMatchingWindowFoundException.new("Unable to locate window, using #{how} and #{what}")
688
+ # returns a hash of options in the current configuration, prefixed with the firefox extension
689
+ # to be used, to pass along to to the firefox socket class as needed
690
+ def firefox_socket_class_options
691
+ # pass any config options that look like firefox_jssh_something or firefox_mozrepl_something
692
+ prefix = "firefox_#{config.firefox_extension}_"
693
+ config.defined_hash.inject({}) do |opts, (key, val)|
694
+ if key[0...prefix.length] == prefix
695
+ opts.merge(key[prefix.length..-1] => val)
696
+ else
697
+ opts
698
+ end
699
+ end
596
700
  end
597
- set_browser_document
598
- self
701
+ private :firefox_socket_class_options
599
702
  end
600
- private :attach
703
+ include FirefoxClassAndInstanceMethods
704
+ extend FirefoxClassAndInstanceMethods
601
705
 
602
706
  # loads up a new window in an existing process
603
- # Vapir::Browser.attach() with no arguments passed the attach method will create a new window
604
- # this will only be called one time per instance we're only ever going to run in 1 window
605
707
  def open_window
606
708
  begin
607
709
  @browser_window_name="firewatir_window_%.16x"%rand(2**64)
608
710
  end while self.class.browser_window_objects.any?{|browser_window_object| browser_window_object.name == @browser_window_name }
609
- watcher=jssh_socket.Components.classes["@mozilla.org/embedcomp/window-watcher;1"].getService(jssh_socket.Components.interfaces.nsIWindowWatcher)
711
+ watcher=firefox_socket.Components.classes["@mozilla.org/embedcomp/window-watcher;1"].getService(firefox_socket.Components.interfaces.nsIWindowWatcher)
610
712
  # nsIWindowWatcher is used to launch new top-level windows. see https://developer.mozilla.org/en/Working_with_windows_in_chrome_code
611
713
 
612
- @browser_window_object=@browser_jssh_objects[:browser_window]=watcher.openWindow(nil, 'chrome://browser/content/browser.xul', @browser_window_name, 'resizable', nil)
714
+ # for openWindow's fourth argument, turn on all the features listed https://developer.mozilla.org/en/DOM/window.open
715
+ # this doesn't enable anything that's not shown by default (for example, having personalbar won't turn on the bookmorks bar
716
+ # if it's set to not be shown), but lacking anything here does disable things which are on by default (if the bookmarks bar is
717
+ # set to show by default and personalbar is not set here, it will not show)
718
+ #
719
+ # see https://developer.mozilla.org/en/DOM/window.open
720
+ @browser_window_object=@browser_objects[:browser_window]=watcher.openWindow(nil, 'chrome://browser/content/browser.xul', @browser_window_name, 'chrome,resizable,toolbar,menubar,personalbar,location,status,scrollbars,titlebar', nil)
613
721
  return @browser_window_object
614
722
  end
615
723
  private :open_window
@@ -625,7 +733,7 @@ module Vapir
625
733
  Enumerator.new(self, :each_browser)
626
734
  end
627
735
  def each_browser_window_object
628
- mediator=jssh_socket.Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(jssh_socket.Components.interfaces.nsIWindowMediator)
736
+ mediator=firefox_socket.Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(firefox_socket.Components.interfaces.nsIWindowMediator)
629
737
  enumerator=mediator.getEnumerator("navigator:browser")
630
738
  while enumerator.hasMoreElements
631
739
  win=enumerator.getNext
@@ -637,7 +745,7 @@ module Vapir
637
745
  Enumerator.new(self, :each_browser_window_object)
638
746
  end
639
747
  def each_window_object
640
- mediator=jssh_socket.Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(jssh_socket.Components.interfaces.nsIWindowMediator)
748
+ mediator=firefox_socket.Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(firefox_socket.Components.interfaces.nsIWindowMediator)
641
749
  enumerator=mediator.getEnumerator(nil)
642
750
  while enumerator.hasMoreElements
643
751
  win=enumerator.getNext
@@ -650,7 +758,7 @@ module Vapir
650
758
  end
651
759
  end
652
760
 
653
- # return the window jssh object for the browser window with the given title or url.
761
+ # return the window javascript object for the browser window with the given title or url.
654
762
  # how - :url, :title, or :name
655
763
  # what - string or regexp
656
764
  #
@@ -665,8 +773,11 @@ module Vapir
665
773
  raise ArgumentError, "how should be one of: #{hows.keys.inspect} (was #{orig_how.inspect})" unless how
666
774
  found_win=nil
667
775
  self.class.each_browser_window_object do |win|
668
- found_win=win if Vapir::fuzzy_match(hows[how].call(win.getBrowser.contentDocument),what)
669
- # we don't break here if found_win is set because we want the last match if there are multiple.
776
+ contentDocument = win.getBrowser.contentDocument rescue nil # this can result in a Javascript TypeError: this.docShell is null if the window isn't ready yet
777
+ if contentDocument && Vapir::fuzzy_match(hows[how].call(contentDocument),what)
778
+ found_win=win
779
+ # we don't break here if found_win is set because we want the last match if there are multiple.
780
+ end
670
781
  end
671
782
  return found_win
672
783
  end
@@ -692,8 +803,8 @@ module Vapir
692
803
  channel = nil
693
804
  ::Waiter.try_for(8, :exception => nil) do
694
805
  channel=browser.browser_object.docShell.currentDocumentChannel
695
- channel.is_a?(JsshObject) && channel.instanceof(browser.jssh_socket.Components.interfaces.nsIHttpChannel) && channel.respond_to?(:responseStatus)
696
- end || raise(RuntimeError, "expected currentDocumentChannel to exist and be a nsIHttpChannel but it wasn't; was #{channel.is_a?(JsshObject) ? channel.toString : channel.inspect}")
806
+ channel.is_a?(JavascriptObject) && channel.instanceof(browser.firefox_socket.Components.interfaces.nsIHttpChannel) && channel.respond_to?(:responseStatus)
807
+ end || raise(RuntimeError, "expected currentDocumentChannel to exist and be a nsIHttpChannel but it wasn't; was #{channel.is_a?(JavascriptObject) ? channel.toString : channel.inspect}")
697
808
  status = channel.responseStatus
698
809
  end
699
810
 
@@ -713,7 +824,7 @@ module Vapir
713
824
  unless options.is_a?(Hash)
714
825
  raise ArgumentError, "given options should be a Hash, not #{options.inspect} (#{options.class})\nold conflicting arguments of no_sleep or last_url are gone"
715
826
  end
716
- options={:sleep => false, :last_url => nil, :timeout => 120}.merge(options)
827
+ options={:sleep => false, :last_url => nil, :timeout => config.wait_timeout}.merge(options)
717
828
  started=Time.now
718
829
  ::Waiter.try_for(options[:timeout] - (Time.now - started), :exception => "Waiting for the document to finish loading timed out") do
719
830
  browser_object.webProgress.isLoadingDocument==false
@@ -747,7 +858,7 @@ module Vapir
747
858
  end
748
859
  end
749
860
  ::Waiter.try_for(options[:timeout] - (Time.now - started), :exception => "Waiting for requests in progress to complete timed out.") do
750
- @requests_in_progress.length<=@browser_jssh_objects[:unmatched_stopped_requests_count]
861
+ @requests_in_progress.length<=@browser_objects[:unmatched_stopped_requests_count]
751
862
  end
752
863
  run_error_checks
753
864
  return self
@@ -769,7 +880,7 @@ module Vapir
769
880
 
770
881
  if options[:dc] == :page
771
882
  options[:format] ||= 'png'
772
- jssh_socket.call_function(:window => content_window_object, :options => options, :filename => File.expand_path(filename)) do
883
+ firefox_socket.call_function(:window => content_window_object, :options => options, :filename => File.expand_path(filename)) do
773
884
  %q(
774
885
  // this is adapted from Selenium's method Selenium.prototype.doCaptureEntirePageScreenshot
775
886
  var document = window.document;