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.
- 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;
|