vapir-firefox 1.7.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- default_extra_for_contained.merge(:jssh_socket => jssh_socket)
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
- fire_event_func=jssh_socket.object("(function(element_object, event){return function(){element_object.dispatchEvent(event)};})").pass(element_object, event)
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
- click_func=jssh_socket.object("(function(element_object, mouse_down_event, mouse_up_event, click_event)
190
- { return function()
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
- })").pass(element_object, mouse_down_event, mouse_up_event, click_event)
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.object("(function(parent, document_object)
238
- { while(true)
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
- })").call(@element_object, container.document_object) # use the container's document so that frames look at their parent document, not their own document
270
+ }"
271
+ end
248
272
  end
249
273
 
250
274
  end # Element