vapir-firefox 1.7.2 → 1.8.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/{firefox.rb → browser.rb} +358 -145
- data/lib/vapir-firefox/clear_tracks.rb +50 -0
- data/lib/vapir-firefox/config.rb +17 -0
- data/lib/vapir-firefox/container.rb +67 -1
- data/lib/vapir-firefox/element.rb +62 -38
- data/lib/vapir-firefox/jssh_socket.rb +527 -206
- data/lib/vapir-firefox/page_container.rb +44 -19
- data/lib/vapir-firefox/version.rb +1 -1
- data/lib/vapir-firefox/window.rb +2 -2
- data/lib/vapir-firefox.rb +2 -5
- metadata +11 -32
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'vapir-firefox/browser.rb'
|
2
|
+
|
3
|
+
module Vapir
|
4
|
+
class Firefox
|
5
|
+
module ClearTracksMethods
|
6
|
+
#--
|
7
|
+
#
|
8
|
+
# currently defined sanitizer items are:
|
9
|
+
# ["cache", "cookies", "offlineApps", "history", "formdata", "downloads", "passwords", "sessions", "siteSettings"]
|
10
|
+
def sanitizer # :nodoc:
|
11
|
+
@@sanitizer ||= begin
|
12
|
+
sanitizer_class = jssh_socket.object('Sanitizer')
|
13
|
+
if sanitizer_class.type=='undefined'
|
14
|
+
loader = jssh_socket.Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(jssh_socket.Components.interfaces.mozIJSSubScriptLoader)
|
15
|
+
loader.loadSubScript("chrome://browser/content/sanitize.js")
|
16
|
+
sanitizer_class = jssh_socket.object('Sanitizer')
|
17
|
+
end
|
18
|
+
sanitizer_class.new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
#
|
22
|
+
def clear_history
|
23
|
+
sanitizer.items.history.clear()
|
24
|
+
end
|
25
|
+
def clear_cookies
|
26
|
+
sanitizer.items.cookies.clear()
|
27
|
+
#cookie_manager = jssh_socket.Components.classes["@mozilla.org/cookiemanager;1"].getService(jssh_socket.Components.interfaces.nsICookieManager)
|
28
|
+
#cookie_manager.removeAll()
|
29
|
+
end
|
30
|
+
def clear_cache
|
31
|
+
sanitizer.items.cache.clear
|
32
|
+
end
|
33
|
+
alias clear_temporary_files clear_cache
|
34
|
+
def clear_all_tracks
|
35
|
+
sanitizer.items.to_hash.inject({}) do |hash, (key, item)|
|
36
|
+
# don't try to clear siteSettings; sometimes siteSettings.clear() raises
|
37
|
+
# an error which jssh doesn't handle properly - it somehow bypasses the
|
38
|
+
# try/catch block and shows up on the socket outside of the returned value.
|
39
|
+
# jssh bug?
|
40
|
+
if key!='siteSettings' && hash[key].canClear
|
41
|
+
hash[key]=(item.clear() rescue $!)
|
42
|
+
end
|
43
|
+
hash
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
include ClearTracksMethods
|
48
|
+
extend ClearTracksMethods
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'vapir-common/config'
|
2
|
+
require 'vapir-firefox' # need the class to be set first
|
3
|
+
|
4
|
+
module Vapir
|
5
|
+
# if vapir-firefox is required before any other browser-specific library, then set the default browser to firefox
|
6
|
+
@base_configuration.default_browser = :firefox unless @base_configuration.locally_defined_key?(:default_browser)
|
7
|
+
|
8
|
+
# add firefox-specific stuff to base, and then bring them in from env and yaml
|
9
|
+
@base_configuration.create(:firefox_profile)
|
10
|
+
@base_configuration.create(:firefox_binary_path)
|
11
|
+
@base_configuration.create_update(:firefox_quit_sleep_time, 4, :validator => :numeric)
|
12
|
+
@configurations.update_from_source
|
13
|
+
class Firefox
|
14
|
+
@configuration_parent = Vapir.config
|
15
|
+
extend Configurable
|
16
|
+
end
|
17
|
+
end
|
@@ -44,7 +44,7 @@ module Vapir
|
|
44
44
|
include Vapir::Container
|
45
45
|
|
46
46
|
def extra_for_contained
|
47
|
-
|
47
|
+
base_extra_for_contained.merge(:jssh_socket => jssh_socket)
|
48
48
|
end
|
49
49
|
|
50
50
|
public
|
@@ -79,5 +79,71 @@ module Vapir
|
|
79
79
|
base_element_class.factory(element_object, extra_for_contained)
|
80
80
|
end
|
81
81
|
end
|
82
|
+
|
83
|
+
# returns a JsshObject representing an array (in javascript) of the visible text nodes of this container. same as
|
84
|
+
# the Vapir::Common #visible_text_nodes implementation, but much much faster.
|
85
|
+
def visible_text_nodes
|
86
|
+
text_nodes = jssh_socket.call_function(:element_object => containing_object, :document_object => document_object) do %Q(
|
87
|
+
var Ycomb = function(gen){ return function(f){ return f(f); }(function(f){ return gen(function(){ return f(f).apply(null, arguments); }); }); }; // TODO: move this somewhere better - jssh_socket?
|
88
|
+
var recurse_text_nodes = Ycomb(function(recurse)
|
89
|
+
{ return function(node, parent_visibility)
|
90
|
+
{ if(node.nodeType==1 || node.nodeType==9)
|
91
|
+
{ var style = node.nodeType==1 ? document_object.defaultView.getComputedStyle(node, null) : null;
|
92
|
+
var our_visibility = style && style.visibility;
|
93
|
+
if(!(our_visibility && $A(['hidden', 'collapse', 'visible']).include(our_visibility.toLowerCase())))
|
94
|
+
{ our_visibility = parent_visibility;
|
95
|
+
}
|
96
|
+
var display = style && style.display;
|
97
|
+
if(display && display.toLowerCase()=='none')
|
98
|
+
{ return [];
|
99
|
+
}
|
100
|
+
else
|
101
|
+
{ return $A(node.childNodes).inject([], function(result, child_node)
|
102
|
+
{ return result.concat(recurse(child_node, our_visibility));
|
103
|
+
});
|
104
|
+
}
|
105
|
+
}
|
106
|
+
else if(node.nodeType==3)
|
107
|
+
{ if(parent_visibility && $A(['hidden', 'collapse']).include(parent_visibility.toLowerCase()))
|
108
|
+
{ return [];
|
109
|
+
}
|
110
|
+
else
|
111
|
+
{ return [node.data];
|
112
|
+
}
|
113
|
+
}
|
114
|
+
else
|
115
|
+
{ return [];
|
116
|
+
}
|
117
|
+
};
|
118
|
+
});
|
119
|
+
var element_to_check = element_object;
|
120
|
+
var real_visibility = null;
|
121
|
+
while(element_to_check)
|
122
|
+
{ var style = element_to_check.nodeType==1 ? document_object.defaultView.getComputedStyle(element_object, null) : null;
|
123
|
+
if(style)
|
124
|
+
{ // only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements),
|
125
|
+
// or 'visible'. ignore 'inherit'; keep looking upward.
|
126
|
+
// this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up.
|
127
|
+
// this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
|
128
|
+
if(real_visibility==null && (visibility=style.visibility))
|
129
|
+
{ var visibility=visibility.toLowerCase();
|
130
|
+
if($A(['hidden', 'collapse', 'visible']).include(visibility))
|
131
|
+
{ real_visibility=visibility;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
// check for display property. this is not inherited, and a parent with display of 'none' overrides an immediate visibility='visible'
|
135
|
+
var display=style.display;
|
136
|
+
if(display && (display=display.toLowerCase())=='none')
|
137
|
+
{ // if display is none, then this element is not visible, and thus has no visible text nodes underneath.
|
138
|
+
return [];
|
139
|
+
}
|
140
|
+
}
|
141
|
+
element_to_check=element_to_check.parentNode;
|
142
|
+
}
|
143
|
+
return recurse_text_nodes(element_object, real_visibility);
|
144
|
+
)
|
145
|
+
end.to_array
|
146
|
+
end
|
147
|
+
|
82
148
|
end
|
83
149
|
end # module
|
@@ -23,15 +23,10 @@ module Vapir
|
|
23
23
|
attr_reader :jssh_socket
|
24
24
|
|
25
25
|
def outer_html
|
26
|
-
orig_siblings_length = (parentNode = element_object.parentNode) && parentNode.childNodes.length
|
27
|
-
|
28
26
|
temp_parent_element=document_object.createElement('div')
|
29
27
|
temp_parent_element.appendChild(element_object.cloneNode(true))
|
30
28
|
self_outer_html=temp_parent_element.innerHTML
|
31
29
|
|
32
|
-
new_siblings_length = (parentNode = element_object.parentNode) && parentNode.childNodes.length
|
33
|
-
#debug code:
|
34
|
-
raise "the parent somehow changed - had #{orig_siblings_length} children; now has #{new_siblings_length}" unless orig_siblings_length==new_siblings_length
|
35
30
|
return self_outer_html
|
36
31
|
end
|
37
32
|
alias outerHTML outer_html
|
@@ -70,8 +65,7 @@ raise "the parent somehow changed - had #{orig_siblings_length} children; now ha
|
|
70
65
|
event=create_event_object(event_type, options)
|
71
66
|
if !options[:wait]
|
72
67
|
raise "need a content window on which to setTimeout if we are not waiting" unless content_window_object
|
73
|
-
|
74
|
-
content_window_object.setTimeout(fire_event_func, 0)
|
68
|
+
content_window_object.setTimeout(jssh_socket.call_function(:element_object => element_object, :event => event){ "return function(){ element_object.dispatchEvent(event) };" }, 0)
|
75
69
|
nil
|
76
70
|
else
|
77
71
|
result=element_object.dispatchEvent(event)
|
@@ -116,12 +110,12 @@ raise "the parent somehow changed - had #{orig_siblings_length} children; now ha
|
|
116
110
|
[:bubbles, true],
|
117
111
|
[:cancelable, true],
|
118
112
|
[:windowObject, content_window_object],
|
119
|
-
[:ctrlKey, false],
|
120
|
-
[:altKey, false],
|
121
|
-
[:shiftKey, false],
|
122
|
-
[:metaKey, false],
|
123
|
-
[:keyCode, 0],
|
124
|
-
[:charCode, 0],
|
113
|
+
[:ctrlKey, options[:ctrlKey] || false],
|
114
|
+
[:altKey, options[:altKey] || false],
|
115
|
+
[:shiftKey, options[:shiftKey] || false],
|
116
|
+
[:metaKey, options[:metaKey] || false],
|
117
|
+
[:keyCode, options[:keyCode] || 0],
|
118
|
+
[:charCode, options[:charCode] || 0],
|
125
119
|
]
|
126
120
|
when 'click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup'
|
127
121
|
dom_event_type = 'MouseEvents'
|
@@ -137,10 +131,10 @@ raise "the parent somehow changed - had #{orig_siblings_length} children; now ha
|
|
137
131
|
[:screenY, options[:screenY] || 0],
|
138
132
|
[:clientX, options[:clientX] || client_center[0]], # by default, assume the mouse is at the center of the element
|
139
133
|
[:clientY, options[:clientY] || client_center[1]],
|
140
|
-
[:ctrlKey, false],
|
141
|
-
[:altKey, false],
|
142
|
-
[:shiftKey, false],
|
143
|
-
[:metaKey, false],
|
134
|
+
[:ctrlKey, options[:ctrlKey] || false],
|
135
|
+
[:altKey, options[:altKey] || false],
|
136
|
+
[:shiftKey, options[:shiftKey] || false],
|
137
|
+
[:metaKey, options[:metaKey] || false],
|
144
138
|
[:button, MouseButtonCodes[options[:button] || :left]],
|
145
139
|
[:relatedTarget, nil],
|
146
140
|
]
|
@@ -186,14 +180,13 @@ raise "the parent somehow changed - had #{orig_siblings_length} children; now ha
|
|
186
180
|
mouse_down_event=create_event_object('mousedown', options)
|
187
181
|
mouse_up_event=create_event_object('mouseup', options)
|
188
182
|
click_event=create_event_object('click', options)
|
189
|
-
|
190
|
-
|
183
|
+
content_window_object.setTimeout(jssh_socket.call_function(:element_object => element_object, :mouse_down_event => mouse_down_event, :mouse_up_event => mouse_up_event, :click_event => click_event) do
|
184
|
+
" return function()
|
191
185
|
{ element_object.dispatchEvent(mouse_down_event);
|
192
186
|
element_object.dispatchEvent(mouse_up_event);
|
193
187
|
element_object.dispatchEvent(click_event);
|
194
|
-
};
|
195
|
-
|
196
|
-
content_window_object.setTimeout(click_func, 0)
|
188
|
+
};"
|
189
|
+
end, 0)
|
197
190
|
end
|
198
191
|
end
|
199
192
|
result
|
@@ -205,11 +198,51 @@ raise "the parent somehow changed - had #{orig_siblings_length} children; now ha
|
|
205
198
|
def click_no_wait(options={})
|
206
199
|
click(options.merge(:wait => false))
|
207
200
|
end
|
208
|
-
|
201
|
+
|
209
202
|
# Waits for the browser to finish loading, if it is loading. See Firefox#wait.
|
210
203
|
def wait(options={})
|
211
204
|
@container.wait(options)
|
212
205
|
end
|
206
|
+
|
207
|
+
# Checks this element and its parents for display: none or visibility: hidden, these are
|
208
|
+
# the most common methods to hide an html element. Returns false if this seems to be hidden
|
209
|
+
# or a parent is hidden.
|
210
|
+
def visible?
|
211
|
+
assert_exists do
|
212
|
+
jssh_socket.call_function(:element_to_check => element_object, :document_object => document_object) do %Q(
|
213
|
+
var really_visible=null;
|
214
|
+
while(element_to_check) //&& !(element_to_check instanceof Components.interfaces.nsIDOMDocument)
|
215
|
+
{ var style = element_to_check.nodeType==1 ? document_object.defaultView.getComputedStyle(element_to_check, null) : null;
|
216
|
+
if(style)
|
217
|
+
{ // only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements),
|
218
|
+
// or 'visible'. ignore 'inherit'; keep looking upward.
|
219
|
+
// this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up.
|
220
|
+
// this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
|
221
|
+
var visibility=style && style.visibility;
|
222
|
+
if(really_visible==null && visibility)
|
223
|
+
{ visibility=visibility.toLowerCase();
|
224
|
+
if(visibility=='hidden' || visibility=='collapse')
|
225
|
+
{ really_visible=false;
|
226
|
+
return false; // don't need to continue knowing it's not visible.
|
227
|
+
}
|
228
|
+
else if(visibility=='visible')
|
229
|
+
{ really_visible=true; // we don't return true yet because a parent with display of 'none' can override
|
230
|
+
}
|
231
|
+
}
|
232
|
+
// check for display property. this is not inherited, and a parent with display of 'none' overrides an immediate visibility='visible'
|
233
|
+
var display=style && style.display;
|
234
|
+
if(display && display.toLowerCase()=='none')
|
235
|
+
{ return false;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
element_to_check=element_to_check.parentNode;
|
239
|
+
}
|
240
|
+
return true;
|
241
|
+
)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
213
246
|
|
214
247
|
def self.element_object_style(element_object, document_object)
|
215
248
|
if element_object.nodeType==1 #element_object.instanceof(element_object.jssh_socket.Components.interfaces.nsIDOMDocument)
|
@@ -224,27 +257,18 @@ raise "the parent somehow changed - had #{orig_siblings_length} children; now ha
|
|
224
257
|
|
225
258
|
private
|
226
259
|
def element_object_exists?
|
227
|
-
# parent=@element_object
|
228
|
-
# while true
|
229
|
-
# return false unless parent # if we encounter a parent such that parentNode is nil, we aren't on the document.
|
230
|
-
# return true if parent==document_object # if we encounter the document as a parent, we are on the document.
|
231
|
-
# new_parent=parent.parentNode
|
232
|
-
# raise(RuntimeError, "Circular reference in parents!") if new_parent==parent
|
233
|
-
# parent=new_parent
|
234
|
-
# end
|
235
|
-
# above is horrendously slow; optimized below.
|
236
260
|
return false unless @element_object
|
237
|
-
return jssh_socket.
|
238
|
-
|
261
|
+
return jssh_socket.call_function(:parent => @element_object, :document_object => container.document_object) do # use the container's document so that frames look at their parent document, not their own document
|
262
|
+
" while(true)
|
239
263
|
{ if(!parent)
|
240
|
-
{ return false;
|
264
|
+
{ return false; // if we encounter a parent such that parentNode is nil, we aren't on the document.
|
241
265
|
}
|
242
|
-
if(parent==document_object)
|
266
|
+
if(parent==document_object) // if we encounter the document as a parent, we are on the document.
|
243
267
|
{ return true;
|
244
268
|
}
|
245
269
|
parent=parent.parentNode;
|
246
|
-
}
|
247
|
-
|
270
|
+
}"
|
271
|
+
end
|
248
272
|
end
|
249
273
|
|
250
274
|
end # Element
|