Class: Capybara::Apparition::Browser::Launcher

Inherits:
Object
  • Object
show all
Defined in:
lib/capybara/apparition/driver/launcher.rb

Constant Summary collapse

KILL_TIMEOUT =
2

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(headless:, **options) ⇒ Launcher

Returns a new instance of Launcher.



34
35
36
37
38
39
40
41
42
# File 'lib/capybara/apparition/driver/launcher.rb', line 34

def initialize(headless:, **options)
  @path = ENV['BROWSER_PATH']
  @options = DEFAULT_OPTIONS.merge(options[:browser_options] || {})
  if headless
    @options.merge!(HEADLESS_OPTIONS)
    @options['disable-gpu'] = nil if Capybara::Apparition.windows?
  end
  @options['user-data-dir'] = Dir.mktmpdir
end

Class Method Details

.process_killer(pid) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/capybara/apparition/driver/launcher.rb', line 12

def self.process_killer(pid)
  proc do
    begin
      if Capybara::Apparition.windows?
        ::Process.kill('KILL', pid)
      else
        ::Process.kill('TERM', pid)
        timer = Capybara::Helpers.timer(expire_in: KILL_TIMEOUT)
        while ::Process.wait(pid, ::Process::WNOHANG).nil?
          sleep 0.05
          next unless timer.expired?

          ::Process.kill('KILL', pid)
          ::Process.wait(pid)
          break
        end
      end
    rescue Errno::ESRCH, Errno::ECHILD # rubocop:disable Lint/HandleExceptions
    end
  end
end

.start(*args) ⇒ Object



8
9
10
# File 'lib/capybara/apparition/driver/launcher.rb', line 8

def self.start(*args)
  new(*args).tap(&:start)
end

Instance Method Details

#hostObject



77
78
79
# File 'lib/capybara/apparition/driver/launcher.rb', line 77

def host
  @host ||= ws_url.host
end

#portObject



81
82
83
# File 'lib/capybara/apparition/driver/launcher.rb', line 81

def port
  @port ||= ws_url.port
end

#restartObject



72
73
74
75
# File 'lib/capybara/apparition/driver/launcher.rb', line 72

def restart
  stop
  start
end

#startObject



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/capybara/apparition/driver/launcher.rb', line 44

def start
  @output = Queue.new
  @read_io, @write_io = IO.pipe

  @out_thread = Thread.new do
    while !@read_io.eof? && (data = @read_io.readpartial(512))
      @output << data
    end
  end

  process_options = { in: File::NULL }
  process_options[:pgroup] = true unless Capybara::Apparition.windows?
  process_options[:out] = process_options[:err] = @write_io if Capybara::Apparition.mri?

  redirect_stdout do
    cmd = [path] + @options.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
    @pid = ::Process.spawn(*cmd, process_options)
    ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
  end
end

#stopObject



65
66
67
68
69
70
# File 'lib/capybara/apparition/driver/launcher.rb', line 65

def stop
  return unless @pid

  kill
  ObjectSpace.undefine_finalizer(self)
end

#ws_urlObject



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/capybara/apparition/driver/launcher.rb', line 85

def ws_url
  @ws_url ||= begin
    regexp = %r{DevTools listening on (ws://.*)}
    url = nil
    loop do
      break if (url = @output.pop.scan(regexp)[0])
    end
    @out_thread.kill
    close_io
    Addressable::URI.parse(url[0])
  end
end