Module: Vapir::Firefox::FirefoxClassAndInstanceMethods

Included in:
Vapir::Firefox, Vapir::Firefox
Defined in:
lib/vapir-firefox/browser.rb

Defined Under Namespace

Classes: CannotHandlePid

Instance Method Summary collapse

Instance Method Details

#current_osObject

returns a symbol representing the platform we’re currently running on - currently implemented platforms are :windows, :macosx, and :linux. raises NotImplementedError if the current platform isn’t one of those.



665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
# File 'lib/vapir-firefox/browser.rb', line 665

def current_os
  @current_os ||= begin
    platform= if RUBY_PLATFORM =~ /java/
      require 'java'
      java.lang.System.getProperty("os.name")
    else
      RUBY_PLATFORM
    end
    case platform
    when /mswin|windows|mingw32/i
      :windows
    when /darwin|mac os/i
      :macosx
    when /linux/i
      :linux
    else
      raise NotImplementedError, "Unidentified platform #{platform}"
    end
  end
end

#firefox_socket_hostObject

returns the host which will be configured for a socket. same as firefox_socket.host, if that exist, but this is correct even if firefox_socket isn’t initialized.



609
610
611
# File 'lib/vapir-firefox/browser.rb', line 609

def firefox_socket_host
  firefox_socket_class_options['host'] || firefox_socket_class.config.host
end

#pidObject

returns the pid of the currently-attached Firefox process.

This only works on Firefox >= 3.6, on platforms supported (see #current_os), and raises CannotHandlePid if it can’t get the pid.



571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/vapir-firefox/browser.rb', line 571

def pid
  begin
    raise CannotHandlePid unless firefox_socket.host == 'localhost'
    begin
      ctypes = firefox_socket.Components.utils.import("resource://gre/modules/ctypes.jsm").ctypes
    rescue FirefoxSocketJavascriptError
      raise CannotHandlePid, "Firefox 3.6 or greater is required for this method.\n\nOriginal error from firefox: #{$!.class}: #{$!.message}", $!.backtrace
    end
    lib, pidfunction, abi = *case current_os
    when :macosx
      ["libc.dylib", 'getpid', ctypes.default_abi]
    when :linux
      ["libc.so.6", 'getpid', ctypes.default_abi]
    when :windows
      # winapi is correct - we do not want stdcall's mangling. ff4+ does the stdcall
      # mangling correctly with ctypes.stdcall_abi, and since we don't want mangling, 
      # that fails here. ff < 4 does not do stdcall mangling (and has no winapi abi 
      # to skip mangling) so ends up working correctly. 
      # so, we want winapi if it's defined to skip mangling, but if 
      # it's not defined, use stdcall because it won't mangle anyway. 
      #
      # see https://bugzilla.mozilla.org/show_bug.cgi?id=585175
      ['kernel32', 'GetCurrentProcessId', ctypes['winapi_abi'] || ctypes['stdcall_abi']]
    else
      raise CannotHandlePid, "don't know how to get pid for #{current_os}"
    end
     lib = ctypes.open(lib)
    begin
      getpid = lib.declare(pidfunction, abi, ctypes['int32_t'])
      return getpid.call()
    ensure
      lib.close
    end
  end
end

#process_running?(pid) ⇒ Boolean

attempts to determine whether the given process is still running. will raise CannotHandlePid if it can’t determine this.

Returns:

  • (Boolean)

Raises:



615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
# File 'lib/vapir-firefox/browser.rb', line 615

def process_running?(pid)
  raise CannotHandlePid unless firefox_socket_host == 'localhost'
  case current_os
  when :windows
    kernel32 = Vapir::Firefox.instance_eval do # define this on the class for reuse 
      @kernel32 ||= begin
        require 'ffi'
        kernel32 = Module.new
        kernel32.send :extend, FFI::Library
        kernel32.ffi_lib 'kernel32'
        kernel32.ffi_convention :stdcall
        kernel32.attach_function :OpenProcess, [:ulong, :char, :ulong], :pointer
        kernel32.attach_function :GetExitCodeProcess, [:pointer, :pointer], :char
        kernel32.const_set('PROCESS_QUERY_INFORMATION', 0x0400)
        kernel32.const_set('STILL_ACTIVE', 259)
        kernel32
      end
    end
    process_handle = kernel32.OpenProcess(kernel32::PROCESS_QUERY_INFORMATION, 0, pid)
    exit_code=FFI::MemoryPointer.new(:ulong)
    result = kernel32.GetExitCodeProcess(process_handle, exit_code)
    if result == 0
      raise "GetExitCodeProcess failed"
    end
    return exit_code.get_ulong(0)==kernel32::STILL_ACTIVE
  when :linux, :macosx
    `ps -p #{pid}`
    $? == 0
  else
    raise CannotHandlePid
  end
end

#quit_browser(options = {}) ⇒ Object

quits the browser.

quit_browser(:force => true) will force the browser to quit.

if there is no existing connection to firefox, this will attempt to create one. If that fails, FirefoxSocketUnableToStart will be raised.



537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
# File 'lib/vapir-firefox/browser.rb', line 537

def quit_browser(options={})
  firefox_socket(:reset_if_dead => true, :socket_class => firefox_socket_class, :socket_options => firefox_socket_class_options).assert_socket
  options=handle_options(options, :force => false)
  
  pid = @pid || begin
    self.pid
  rescue CannotHandlePid
    nil
  end
  
  # from https://developer.mozilla.org/en/How_to_Quit_a_XUL_Application
  appStartup= firefox_socket.Components.classes['@mozilla.org/toolkit/app-startup;1'].getService(firefox_socket.Components.interfaces.nsIAppStartup)
  quitSeverity = options[:force] ? firefox_socket.Components.interfaces.nsIAppStartup.eForceQuit : firefox_socket.Components.interfaces.nsIAppStartup.eAttemptQuit
  firefox_socket.handling_connection_error(:handle => proc{ Vapir::Firefox.uninitialize_firefox_socket }) do
    appStartup.quit(quitSeverity)
    ::Waiter.try_for(config.quit_timeout, :exception => Exception::WindowFailedToCloseException.new("The browser did not quit")) do
      firefox_socket.assert_socket # this should error, going up past the waiter to #handling_connection_error
      false
    end
  end
  
  wait_for_process_exit(pid)
  
  @browser_window_object=@browser_object=@document_object=@content_window_object=nil
  nil
end

#wait_for_process_exit(pid) ⇒ Object

waits until the Firefox process with the given pid has exited.

if no pid is given, waits the configured amount of time until it is safe to assume the pfirefox process has exited.



652
653
654
655
656
657
658
659
660
# File 'lib/vapir-firefox/browser.rb', line 652

def wait_for_process_exit(pid)
  if pid
    ::Waiter.try_for(config.quit_timeout, :exception => Exception::WindowFailedToCloseException.new("The browser did not quit")) do
      !process_running?(pid)
    end
  else
    sleep config.firefox_quit_sleep_time
  end
end