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.
- data/lib/vapir-firefox/browser.rb +226 -115
- data/lib/vapir-firefox/clear_tracks.rb +6 -6
- data/lib/vapir-firefox/config.rb +5 -0
- data/lib/vapir-firefox/container.rb +74 -65
- data/lib/vapir-firefox/element.rb +13 -13
- data/lib/vapir-firefox/firefox_socket/base.rb +790 -0
- data/lib/vapir-firefox/firefox_socket/jssh.rb +49 -0
- data/lib/vapir-firefox/firefox_socket/mozrepl.rb +58 -0
- data/lib/vapir-firefox/{prototype.functional.js → firefox_socket/prototype.functional.js} +0 -0
- data/lib/vapir-firefox/javascript_object.rb +736 -0
- data/lib/vapir-firefox/modal_dialog.rb +4 -4
- data/lib/vapir-firefox/page_container.rb +4 -4
- data/lib/vapir-firefox/version.rb +1 -1
- metadata +16 -13
- data/lib/vapir-firefox/jssh_socket.rb +0 -1418
@@ -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
|
101
|
-
def self.
|
102
|
-
|
103
|
-
@@
|
104
|
-
|
105
|
-
|
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
|
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.
|
110
|
-
if options[:reset] || !(class_variable_defined?('@@
|
111
|
-
|
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
|
-
@@
|
116
|
-
rescue
|
117
|
-
Kernel.warn "WARNING:
|
118
|
-
|
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
|
-
@@
|
200
|
+
@@firefox_socket
|
122
201
|
end
|
123
|
-
# unsets a the current
|
124
|
-
def self.
|
125
|
-
@@
|
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
|
129
|
-
options ? self.class.
|
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
|
-
|
162
|
-
#
|
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
|
-
|
166
|
-
rescue
|
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
|
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
|
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
|
-
|
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
|
262
|
+
rescue FirefoxSocketUnableToStart
|
186
263
|
false
|
187
264
|
end
|
188
265
|
end
|
189
266
|
end
|
190
|
-
@
|
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
|
-
|
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
|
-
#
|
221
|
-
|
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
|
-
|
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 =
|
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 =
|
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
|
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=@
|
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
|
-
@
|
330
|
-
@requests_in_progress=@
|
331
|
-
@
|
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=@
|
334
|
-
@updated_at_offset=Time.now.to_f-
|
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
|
-
@
|
341
|
-
listener_object=@
|
342
|
-
listener_object[:QueryInterface]=
|
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]=
|
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
|
-
{ #{@
|
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
|
-
|
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
|
-
|
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(
|
420
|
-
|
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
|
-
|
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 &&
|
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
|
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
|
-
|
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=
|
460
|
-
quitSeverity = options[:force] ?
|
461
|
-
|
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(
|
464
|
-
|
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 =
|
485
|
-
rescue
|
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
|
-
|
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
|
-
|
499
|
-
|
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(
|
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
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
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
|
-
|
595
|
-
|
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
|
-
|
598
|
-
self
|
701
|
+
private :firefox_socket_class_options
|
599
702
|
end
|
600
|
-
|
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=
|
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
|
-
|
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=
|
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=
|
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
|
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
|
-
|
669
|
-
|
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?(
|
696
|
-
end || raise(RuntimeError, "expected currentDocumentChannel to exist and be a nsIHttpChannel but it wasn't; was #{channel.is_a?(
|
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 =>
|
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<=@
|
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
|
-
|
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;
|