vapir-firefox 1.8.1 → 1.9.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.
@@ -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;