Class: Vapir::Firefox

Inherits:
Browser
  • Object
show all
Extended by:
Configurable, ClearTracksMethods, FirefoxClassAndInstanceMethods
Includes:
ClearTracksMethods, FirefoxClassAndInstanceMethods, ModalDialogContainer, PageContainer, Window
Defined in:
lib/vapir-firefox/config.rb,
lib/vapir-firefox/browser.rb,
lib/vapir-firefox/version.rb,
lib/vapir-firefox/clear_tracks.rb

Defined Under Namespace

Modules: ClearTracksMethods, Container, FirefoxClassAndInstanceMethods, ModalDialogContainer, PageContainer, RadioCheckboxCommon, Window Classes: Area, Button, CheckBox, Dd, Div, Dl, Dt, Element, Em, FileField, Form, Frame, H1, H2, H3, H4, H5, H6, Hidden, Image, InputElement, Label, Li, Link, Map, ModalDialog, ModalDialogDocument, Ol, Option, P, Pre, Radio, SelectList, Span, Strong, TBody, Table, TableCell, TableRow, TextField, Ul

Constant Summary collapse

VERSION =
'1.10.1'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from FirefoxClassAndInstanceMethods

current_os, firefox_socket_host, pid, process_running?, quit_browser, wait_for_process_exit

Methods included from ClearTracksMethods

clear_all_tracks, clear_cache, clear_cookies, clear_history, sanitizer

Methods included from ModalDialogContainer

#modal_dialog, #modal_dialog!

Methods included from Window

#bring_to_front, #hwnd, #win_window

Methods included from PageContainer

#execute_script, #outer_html

Methods included from Container

#element_by_xpath, #element_object_by_xpath, #element_objects_by_xpath, #elements_by_xpath, #extra_for_contained, #innermost_by_node, #innermost_matching_visible_text, #visible_text_nodes

Constructor Details

#initialize(options = {}) ⇒ Firefox

Description:

Starts the firefox browser. 
On windows this starts the first version listed in the registry.

Input:

options  - Hash of any of the following options:
  :timeout  - Time to wait for Firefox to start. By default it waits for 2 seconds.
              This is done because if Firefox is not started and we try to connect
              to jssh on port 9997 an exception is thrown.
  :profile  - The Firefox profile to use. If none is specified, Firefox will use
              the last used profile.


221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/vapir-firefox/browser.rb', line 221

def initialize(options = {})
  if(options.kind_of?(Integer))
    options = {:timeout => options}
    if config.warn_deprecated
      Kernel.warn_with_caller "DEPRECATION WARNING: #{self.class.name}.new takes an options hash - passing a number is deprecated. Please use #{self.class.name}.new(:timeout => #{options[:timeout]})"
    end
  end
  options = options_from_config(options, {:timeout => :attach_timeout, :binary_path => :firefox_binary_path, :profile => :firefox_profile, :wait => :wait}, [:attach, :goto, :wait_time])
  if options[:wait_time]
    if config.warn_deprecated
      Kernel.warn_with_caller "DEPRECATION WARNING: the :wait_time option for #{self.class.name}.new has been renamed to :timeout for consistency. Please use #{self.class.name}.new(:timeout => #{options[:wait_time]})"
    end
    options[:timeout] = options[:wait_time]
  end
  if options[:binary_path]
    @binary_path=options[:binary_path]
  end
  
  require 'vapir-firefox/firefox_socket/base'
  # check for extension not listening; firefox may be open but not listening (extension not set up) 
  begin
    firefox_socket(:reset_if_dead => true, :socket_class => firefox_socket_class, :socket_options => firefox_socket_class_options).assert_socket
  rescue FirefoxSocketConnectionError
    # here we're going to assume that since it's not connecting, we need to launch firefox. 
    if options[:attach]
      raise Vapir::Exception::NoBrowserException, "cannot attach using #{options[:attach].inspect} - could not connect to Firefox"
    else
      launch_browser(options)
      # if we just launched a the browser process, attach to the window
      # that opened when we did that. 
      # but if options[:attach] is explicitly given as false (not nil), 
      # take that to mean we don't want to attach to the window launched 
      # when the process starts. 
      unless options[:attach]==false
        options[:attach]=[:title, //]
      end
    end
    ::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
      begin
        firefox_socket(:reset_if_dead => true, :socket_class => firefox_socket_class, :socket_options => firefox_socket_class_options).assert_socket
        true
      rescue FirefoxSocketUnableToStart
        false
      end
    end
  end
  @browser_objects = firefox_socket.object('{}').store_rand_temp
  
  @pid = begin
    self.pid
  rescue CannotHandlePid
    nil
  end
  
  if options[:attach]
    how, what = *options[:attach]
    @browser_window_object = case how
    when :browser_window_object
      what
    else
      ::Waiter.try_for(options[:timeout], :exception => Vapir::Exception::NoMatchingWindowFoundException.new("Unable to locate a window with #{how} of #{what}")) do
        find_window(how, what)
      end
    end
  else
    open_window
  end
  set_browser_document
  set_defaults
  if options[:goto]
    goto(options[:goto])
  end
  wait if options[:wait]
end

Instance Attribute Details

#browser_objectObject (readonly)

Returns the value of attribute browser_object.



480
481
482
# File 'lib/vapir-firefox/browser.rb', line 480

def browser_object
  @browser_object
end

#browser_window_objectObject (readonly)

Returns the value of attribute browser_window_object.



478
479
480
# File 'lib/vapir-firefox/browser.rb', line 478

def browser_window_object
  @browser_window_object
end

#content_window_objectObject (readonly)

Returns the value of attribute content_window_object.



479
480
481
# File 'lib/vapir-firefox/browser.rb', line 479

def content_window_object
  @content_window_object
end

#document_objectObject (readonly)

Returns the value of attribute document_object.



481
482
483
# File 'lib/vapir-firefox/browser.rb', line 481

def document_object
  @document_object
end

Class Method Details

.browser_window_objectsObject



755
756
757
# File 'lib/vapir-firefox/browser.rb', line 755

def browser_window_objects
  Enumerator.new(self, :each_browser_window_object)
end

.browsersObject



743
744
745
# File 'lib/vapir-firefox/browser.rb', line 743

def browsers
  Enumerator.new(self, :each_browser)
end

.each_browserObject Also known as: each



737
738
739
740
741
# File 'lib/vapir-firefox/browser.rb', line 737

def each_browser
  each_browser_window_object do |win|
    yield self.attach(:browser_window_object, win)
  end
end

.each_browser_window_objectObject



746
747
748
749
750
751
752
753
754
# File 'lib/vapir-firefox/browser.rb', line 746

def each_browser_window_object
  mediator=firefox_socket.Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(firefox_socket.Components.interfaces.nsIWindowMediator)
  enumerator=mediator.getEnumerator("navigator:browser")
  while enumerator.hasMoreElements
    win=enumerator.getNext
    yield win
  end
  nil
end

.each_window_objectObject



758
759
760
761
762
763
764
765
766
# File 'lib/vapir-firefox/browser.rb', line 758

def each_window_object
  mediator=firefox_socket.Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(firefox_socket.Components.interfaces.nsIWindowMediator)
  enumerator=mediator.getEnumerator(nil)
  while enumerator.hasMoreElements
    win=enumerator.getNext
    yield win
  end
  nil
end

.firefox_socket(options = {}) ⇒ Object

returns a connected FirefoxSocket. pass :reset_if_dead => true if you suspect an existing socket may be dead, and you want a new one. a warning will be printed if this occurs.



188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/vapir-firefox/browser.rb', line 188

def self.firefox_socket(options={}) # :nodoc:
  if options[:reset] || !(class_variable_defined?('@@firefox_socket') && @@firefox_socket)
    initialize_firefox_socket(options[:socket_class] || firefox_socket_class, options[:socket_options] || firefox_socket_class_options)
  end
  if options[:reset_if_dead]
    begin
      @@firefox_socket.assert_socket
    rescue FirefoxSocketConnectionError
      Kernel.warn "WARNING: Firefox socket RESET: resetting connection to firefox. Any active javascript references will not exist on the new socket!"
      initialize_firefox_socket(options[:socket_class], options[:socket_options])
    end
  end
  @@firefox_socket
end

.initialize_firefox_socket(socket_class, socket_options) ⇒ Object

initializes a FirefoxSocket and stores in a class variable.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/vapir-firefox/browser.rb', line 100

def self.initialize_firefox_socket(socket_class, socket_options) # :nodoc:
  uninitialize_firefox_socket
  @@firefox_socket=socket_class.new(socket_options)
  vapir_javascript_functions = {
    :element_real_visibility => %Q(
      function(element_to_check, document_object)
      { var real_visibility = null;
        while(element_to_check && real_visibility==null)
        { var style = element_to_check.nodeType==1 ? document_object.defaultView.getComputedStyle(element_to_check, null) : null;
          if(style)
          { // only pay attention to the innermost definition that really defines visibility - one of 'hidden', 'collapse' (only for table elements), 
            // or 'visible'. ignore 'inherit'; keep looking upward. 
            // this makes it so that if we encounter an explicit 'visible', we don't pay attention to any 'hidden' further up. 
            // this style is inherited - may be pointless for firefox, but IE uses the 'inherited' value. not sure if/when ff does.
            if(style.visibility)
            { var visibility=style.visibility.toLowerCase();
              if($A(['hidden', 'collapse', 'visible']).include(visibility))
              { real_visibility = visibility;
              }
            }
          }
          element_to_check=element_to_check.parentNode;
        }
        return real_visibility;
      }
    ),
    :element_displayed => %Q(
      function(node, document_object)
      { var style = node.nodeType==1 ? document_object.defaultView.getComputedStyle(node, null) : null;
        var display = style && style.display;
        var displayed = display ? display.toLowerCase() != 'none' : true;
        return displayed;
      }
    ),
    :visible_text_nodes => %Q(
      function(element_object, document_object)
      { var recurse_text_nodes = Vapir.Ycomb(function(recurse)
        { return function(node, parent_visibility)
          { if(node.nodeType==1 || node.nodeType==9)
            { var style = node.nodeType==1 ? document_object.defaultView.getComputedStyle(node, null) : null;
              var our_visibility = style && style.visibility;
              if(!(our_visibility && $A(['hidden', 'collapse', 'visible']).include(our_visibility.toLowerCase())))
              { our_visibility = parent_visibility;
              }
              if(!Vapir.element_displayed(node, document_object))
              { return [];
              }
              else
              { return $A(node.childNodes).inject([], function(result, child_node)
                { return result.concat(recurse(child_node, our_visibility));
                });
              }
            }
            else if(node.nodeType==3)
            { if(parent_visibility && $A(['hidden', 'collapse']).include(parent_visibility.toLowerCase()))
              { return [];
              }
              else
              { return [node.data];
              }
            }
            else
            { return [];
            }
          };
        });
        var element_to_check = element_object;
        while(element_to_check)
        { // check for display property. this is not inherited, so ascend the heirarchy looking. 
          //
          // any parent with display of 'none' overrides a more-immediate visibility='visible', so if any parent has 
          // display=none, this is not visible and has no visible child text nodes.
          if(!Vapir.element_displayed(element_to_check, document_object))
          { return [];
          }
          element_to_check=element_to_check.parentNode;
        }
        return recurse_text_nodes(element_object, Vapir.element_real_visibility(document_object, element_object));
      }
    )
  }
  vapir_javascript_functions.each do |name, function_string|
    @@firefox_socket.root.Vapir[name]=@@firefox_socket.object(function_string)
  end
  @@firefox_socket
end

.uninitialize_firefox_socketObject

unsets a the current firefox socket



203
204
205
# File 'lib/vapir-firefox/browser.rb', line 203

def self.uninitialize_firefox_socket # :nodoc:
  @@firefox_socket=nil
end

.window_objectsObject



767
768
769
# File 'lib/vapir-firefox/browser.rb', line 767

def window_objects
  Enumerator.new(self, :each_window_object)
end

Instance Method Details

#add_checker(checker) ⇒ Object

Add an error checker that gets called on every page load.

  • checker - a Proc object



985
986
987
# File 'lib/vapir-firefox/browser.rb', line 985

def add_checker(checker)
  @error_checkers << checker
end

#backObject

Loads the previous page (if there is any) in the browser. Waits for the page to get loaded.



369
370
371
372
373
374
375
376
# File 'lib/vapir-firefox/browser.rb', line 369

def back
  if browser_object.canGoBack
    browser_object.goBack
  else
    raise Vapir::Exception::NavigationException, "Cannot go back!"
  end
  wait
end

#body_objectObject



482
483
484
# File 'lib/vapir-firefox/browser.rb', line 482

def body_object
  document_object.body
end

#browserObject



300
301
302
# File 'lib/vapir-firefox/browser.rb', line 300

def browser
  self
end

#closeObject

Closes the browser window.

This will also quit the browser (see #quit_browser) only if this instance of Vapir::Firefox launched the browser when it was created, AND there are no other windows remaining open. On Windows, closing the last browser window quits the browser anyway; on other operating systems it does not.



496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
# File 'lib/vapir-firefox/browser.rb', line 496

def close
  assert_exists
  # we expect the browser may exit if there are no windows which aren't ourself. except on mac. 
  expect_exit = !self.class.window_objects.any?{|other_window| other_window != self.browser_window_object } && current_os != :macosx

  connection_error_handler = proc do
    Vapir::Firefox.uninitialize_firefox_socket
    wait_for_process_exit(@pid)
  end
  firefox_socket.handling_connection_error(:handle => connection_error_handler) do
    browser_window_object.close
    ::Waiter.try_for(config.close_timeout, :exception => Exception::WindowFailedToCloseException.new("The browser window did not close")) do
      !exists?
    end
    if expect_exit
      ::Waiter.try_for(config.quit_timeout, :exception => nil) do
        firefox_socket.assert_socket # this should error, going up past the waiter to #handling_connection_error
        false
      end
    else
      firefox_socket.assert_socket
    end
  end

  @browser_window_object=@browser_object=@document_object=@content_window_object=nil
  if @self_launched_browser && firefox_socket && !self.class.window_objects.any?{ true }
    quit_browser(:force => false)
  end
end

#close_allObject

Closes all firefox windows by quitting the browser



527
528
529
# File 'lib/vapir-firefox/browser.rb', line 527

def close_all
  quit_browser(:force => false)
end

#current_document_channel_objectObject

a nsIHttpChannel for the currently loaded document - the browser’s docShell.currentDocumentChannel

see developer.mozilla.org/en/XPCOM_Interface_Reference/nsIHttpChannel



837
838
839
840
841
842
843
844
# File 'lib/vapir-firefox/browser.rb', line 837

def current_document_channel_object
  channel = nil
  ::Waiter.try_for(8, :exception => nil) do
    channel=browser.browser_object.docShell.currentDocumentChannel
    channel.is_a?(JavascriptObject) && channel.instanceof(browser.firefox_socket.Components.interfaces.nsIHttpChannel) && channel.respond_to?(:responseStatus)
  end || raise(RuntimeError, "expected currentDocumentChannel to exist and be a nsIHttpChannel but it wasn't; was #{channel.is_a?(JavascriptObject) ? channel.toString : channel.inspect}")
  channel
end

#disable_checker(checker) ⇒ Object

Disable an error checker

  • checker - a Proc object that is to be disabled



991
992
993
# File 'lib/vapir-firefox/browser.rb', line 991

def disable_checker(checker)
  @error_checkers.delete(checker)
end

#exists?Boolean

Returns:

  • (Boolean)


304
305
306
307
308
309
310
# File 'lib/vapir-firefox/browser.rb', line 304

def exists?
  # firefox_socket may be nil if the window has closed 
  # if the socket disconnects, we'll assume that means the browser quit and therefore the window doesn't exist 
  firefox_socket && firefox_socket.handling_connection_error(:handle => proc{ return false }) do
    browser_window_object && self.class.browser_window_objects.include?(browser_window_object)
  end
end

#firefox_socket(options = nil) ⇒ Object



206
207
208
# File 'lib/vapir-firefox/browser.rb', line 206

def firefox_socket(options=nil)
  options ? self.class.firefox_socket(options) : @@firefox_socket
end

#forwardObject

Loads the next page (if there is any) in the browser. Waits for the page to get loaded.



379
380
381
382
383
384
385
386
# File 'lib/vapir-firefox/browser.rb', line 379

def forward
  if browser_object.canGoForward
    browser_object.goForward
  else
    raise Vapir::Exception::NavigationException, "Cannot go forward!"
  end
  wait
end

#goto(url) ⇒ Object

Loads the given url in the browser. Waits for the page to get loaded.



327
328
329
330
331
# File 'lib/vapir-firefox/browser.rb', line 327

def goto(url)
  assert_exists
  browser_object.loadURI url
  wait
end

#maximizeObject

Maximize the current browser window.



847
848
849
# File 'lib/vapir-firefox/browser.rb', line 847

def maximize()
  browser_window_object.maximize
end

#minimizeObject

Minimize the current browser window.



852
853
854
# File 'lib/vapir-firefox/browser.rb', line 852

def minimize()
  browser_window_object.minimize
end

#mozilla_window_class_namesObject



296
297
298
# File 'lib/vapir-firefox/browser.rb', line 296

def mozilla_window_class_names
  ['MozillaUIWindowClass', 'MozillaWindowClass']
end

#post_to(url, post_data_hash = {}) ⇒ Object

Performs a HTTP POST action to an arbitrary URL with the given data. The data are represented to this method as a Hash, which is converted to the standard form of &-separated key=value strings POST data use.

The data hash should be keyed with strings or symbols (which are converted to strings before being sent along), and its values should all be strings.

If no post_data_hash is given, the body of the POST is empty.

Raises:

  • (ArgumentError)


341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/vapir-firefox/browser.rb', line 341

def post_to(url, post_data_hash={})
  require 'cgi'
  raise ArgumentError, "post_data_hash must be a Hash!" unless post_data_hash.is_a?(Hash)
  dataString = post_data_hash.map do |(key, val)|
    unless key.is_a?(String) || key.is_a?(Symbol)
      raise ArgumentError, "post_data_hash keys must be strings or symbols; got key #{key.inspect} in hash #{post_data_hash.inspect}"
    end
    unless val.is_a?(String)
      raise ArgumentError, "post_data_hash values must all be string;s got value #{val.inspect} in hash #{post_data_hash.inspect}"
    end
    CGI.escape(key.to_s)+'='+CGI.escape(val)
  end.join("&")
  stringStream = firefox_socket.Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance(firefox_socket.Components.interfaces.nsIStringInputStream)
  if stringStream.to_hash.key?('data')
    stringStream.data=dataString
  else
    stringStream.setData(dataString, dataString.unpack("U*").length)
  end
  postData = firefox_socket.Components.classes["@mozilla.org/network/mime-input-stream;1"].createInstance(firefox_socket.Components.interfaces.nsIMIMEInputStream)
  postData.addHeader("Content-Type", "application/x-www-form-urlencoded")
  postData.addContentLength = true
  postData.setData(stringStream)

  browser_object.loadURIWithFlags(url, 0, nil, nil, postData)
  wait
end

#refreshObject

Reloads the current page in the browser. Waits for the page to get loaded.



389
390
391
392
# File 'lib/vapir-firefox/browser.rb', line 389

def refresh
  browser_object.reload
  wait
end

#request_headersObject

a hash of the request headers the browser sent for the currently loaded document



826
827
828
829
830
831
832
833
# File 'lib/vapir-firefox/browser.rb', line 826

def request_headers
  firefox_socket.call_function(:channel => current_document_channel_object) do %Q{
    var headers={};
    channel.visitRequestHeaders({visitHeader: function(header, value){ headers[header]=value; }});
    return headers;
  }
  end.val.freeze
end

#response_headersObject

a hash of the response headers returned for the currently loaded document



817
818
819
820
821
822
823
824
# File 'lib/vapir-firefox/browser.rb', line 817

def response_headers
  firefox_socket.call_function(:channel => current_document_channel_object) do %Q{
    var headers={};
    channel.visitResponseHeaders({visitHeader: function(header, value){ headers[header]=value; }});
    return headers;
  }
  end.val.freeze
end

#response_status_codeObject

the HTTP response status code for the currently loaded document



813
814
815
# File 'lib/vapir-firefox/browser.rb', line 813

def response_status_code
  current_document_channel_object.responseStatus
end

#run_error_checksObject

Run the predefined error checks. This is automatically called on every page load.



996
997
998
# File 'lib/vapir-firefox/browser.rb', line 996

def run_error_checks
  @error_checkers.each { |e| e.call(self) }
end

#screen_capture(filename, options = {}) ⇒ Object

saves a screenshot of this browser window to the given filename.

the last argument is an optional options hash, taking options:

  • :dc => context to capture (stands for device context). default is :page. may be one of:

    • :page takes a screenshot of the full page, and none of the browser chrome. this is supported cross-platform.

    • :client takes a screenshot of the client area, which excludes the menu bar and other window trimmings. only supported on windows.

    • :window takes a screenshot of the full browser window. only supported on windows.

    • :desktop takes a screenshot of the full desktop. only supported on windows.

  • :format => a valid format. if :dc is :window, the default is ‘png’ (‘jpeg’ is also supported); if :dc is anything else, ‘bmp’ is both the default and the only supported format.



914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
# File 'lib/vapir-firefox/browser.rb', line 914

def screen_capture(filename, options = {})
  if filename =~ /\.(\w+)\z/
    extension = $1
    file_format = %w(jpeg png).inject({'jpg' => 'jpeg'}){|h,f| h.merge(f => f) }[extension]
  end
  options = handle_options(options, :format => file_format, :dc => :page)
  
  if options[:dc] == :page
    options[:format] ||= 'png'
    firefox_socket.call_function(:window => content_window_object, :options => options, :filename => File.expand_path(filename)) do
    %q(
      // this is adapted from Selenium's method Selenium.prototype.doCaptureEntirePageScreenshot
      var document = window.document;
      var document_element = document.documentElement;
      var width = document_element.scrollWidth;
      var height = document_element.scrollHeight;
      var styleWidth = width.toString() + 'px';
      var styleHeight = height.toString() + 'px';

      var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'html:canvas'), grabCanvas=canvas;
      grabCanvas.style.display = 'none';
      grabCanvas.width = width;
      grabCanvas.style.width = styleWidth;
      grabCanvas.style.maxWidth = styleWidth;
      grabCanvas.height = height;
      grabCanvas.style.height = styleHeight;
      grabCanvas.style.maxHeight = styleHeight;
      
      document_element.appendChild(canvas);
      try
      {
        var context = canvas.getContext('2d');
        context.clearRect(0, 0, width, height);
        context.save();
        
        var prefs=Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch);
        var background_color = prefs.getCharPref('browser.display.background_color');
        
        context.drawWindow(window, 0, 0, width, height, background_color);
        context.restore();
        var dataUrl = canvas.toDataURL("image/" + options['format']);
        
        var nsIoService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
        var channel = nsIoService.newChannelFromURI(nsIoService.newURI(dataUrl, null, null));
        var binaryInputStream = Components.classes["@mozilla.org/binaryinputstream;1"].createInstance(Components.interfaces.nsIBinaryInputStream);
        binaryInputStream.setInputStream(channel.open());
        var numBytes = binaryInputStream.available();
        var bytes = binaryInputStream.readBytes(numBytes);

        var nsFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
        nsFile.initWithPath(filename);
        var writeFlag = 0x02; // write only
        var createFlag = 0x08; // create
        var truncateFlag = 0x20; // truncate
        var fileOutputStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
        fileOutputStream.init(nsFile, writeFlag | createFlag | truncateFlag, 0664, null);
        fileOutputStream.write(bytes, numBytes);
        fileOutputStream.close();
      }
      finally
      { document_element.removeChild(canvas);
      }
    )
    end
  else
    screen_capture_win_window(filename, options)
  end
end

#startClicker(*args) ⇒ Object

Raises:

  • (NotImplementedError)


1001
1002
1003
# File 'lib/vapir-firefox/browser.rb', line 1001

def startClicker(*args)
  raise NotImplementedError, "startClicker is gone. Use Firefox#modal_dialog.click_button (generally preceded by a Element#click_no_wait)"
end

#statusObject

Returns the Status of the page currently loaded in the browser from statusbar.

Output:

Status of the page.


802
803
804
805
# File 'lib/vapir-firefox/browser.rb', line 802

def status
  #content_window_object.status
  browser_window_object.XULBrowserWindow.statusText
end

#textObject

Returns the text of the page currently loaded in the browser.



808
809
810
# File 'lib/vapir-firefox/browser.rb', line 808

def text
  body_object.textContent
end

#updated_atObject



486
487
488
# File 'lib/vapir-firefox/browser.rb', line 486

def updated_at
  Time.at(@updated_at_epoch_ms.val/1000.0)+@updated_at_offset
end

#wait(options = {}) ⇒ Object

Waits for the page to get loaded.



857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
# File 'lib/vapir-firefox/browser.rb', line 857

def wait(options={})
  return unless config.wait
  return unless exists?
  unless options.is_a?(Hash)
    raise ArgumentError, "given options should be a Hash, not #{options.inspect} (#{options.class})\nold conflicting arguments of no_sleep or last_url are gone"
  end
  options={:sleep => false, :last_url => nil, :timeout => config.wait_timeout}.merge(options)
  started=Time.now
  ::Waiter.try_for(options[:timeout] - (Time.now - started), :exception => "Waiting for the document to finish loading timed out") do
    browser_object.webProgress.isLoadingDocument==false
  end

  # If the redirect is to a download attachment that does not reload this page, this
  # method will loop forever. Therefore, we need to ensure that if this method is called
  # twice with the same URL, we simply accept that we're done.
  url= document_object['URL']

  if(url && url != options[:last_url])
    # check for meta redirects, except for redirects back to the same page (infinite
    # loop redirects). 
    metas=document_object.getElementsByTagName 'meta'
    wait_time=metas.to_array.map do |meta|
      return_time=true
      return_time &&= meta.httpEquiv =~ /\Arefresh\z/i 
      return_time &&= begin
        content_split=meta.content.split(';')
        content_split[1] && content_split[1] !~ /\A\s*url=#{Regexp.escape(url)}\s*\z/ # if there is no url, or if the url is the current url, it's just a reload, not a redirect; don't wait. 
      end
      return_time ? content_split[0].to_i : nil
    end.compact.max
    
    if wait_time
      if wait_time > (options[:timeout] - (Time.now - started)) # don't wait longer than what's left in the timeout would for any other timeout. 
        raise "waiting for a meta refresh would take #{wait_time} seconds but remaining time before timeout is #{options[:timeout] - (Time.now - started)} seconds - giving up"
      end
      sleep(wait_time)
      wait(:last_url => url, :timeout => options[:timeout] - (Time.now - started))
    end
  end
  ::Waiter.try_for(options[:timeout] - (Time.now - started), :exception => "Waiting for requests in progress to complete timed out.") do
    @requests_in_progress.length<=@browser_objects[:unmatched_stopped_requests_count]
  end
  run_error_checks
  return self
end