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