Class: Capybara::Cuprite::Browser::Process

Inherits:
Object
  • Object
show all
Defined in:
lib/capybara/cuprite/browser/process.rb

Constant Summary collapse

KILL_TIMEOUT =
2
PROCESS_TIMEOUT =
1
BROWSER_PATH =
ENV["BROWSER_PATH"]
BROWSER_HOST =
"127.0.0.1"
BROWSER_PORT =
"0"
DEFAULT_OPTIONS =
{
  "headless" => nil,
  "disable-gpu" => nil,
  "hide-scrollbars" => nil,
  "mute-audio" => nil,
  "enable-automation" => nil,
  "disable-web-security" => nil,
  "disable-session-crashed-bubble" => nil,
  "disable-breakpad" => nil,
  "disable-sync" => nil,
  "no-first-run" => nil,
  "use-mock-keychain" => nil,
  "keep-alive-for-test" => nil,
  "disable-popup-blocking" => nil,
  "disable-extensions" => nil,
  "disable-hang-monitor" => nil,
  "disable-features" => "site-per-process,TranslateUI",
  "disable-translate" => nil,
  "disable-background-networking" => nil,
  "enable-features" => "NetworkService,NetworkServiceInProcess",
  "disable-background-timer-throttling" => nil,
  "disable-backgrounding-occluded-windows" => nil,
  "disable-client-side-phishing-detection" => nil,
  "disable-default-apps" => nil,
  "disable-dev-shm-usage" => nil,
  "disable-ipc-flooding-protection" => nil,
  "disable-prompt-on-repost" => nil,
  "disable-renderer-backgrounding" => nil,
  "force-color-profile" => "srgb",
  "metrics-recording-only" => nil,
  "safebrowsing-disable-auto-update" => nil,
  "password-store" => "basic",
  # Note: --no-sandbox is not needed if you properly setup a user in the container.
  # https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
  # "no-sandbox" => nil,
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Process

Returns a new instance of Process.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/capybara/cuprite/browser/process.rb', line 77

def initialize(options)
  @options = {}

  detect_browser_path(options)

  # Doesn't work on MacOS, so we need to set it by CDP as well
  @options.merge!("window-size" => options[:window_size].join(","))

  port = options.fetch(:port, BROWSER_PORT)
  @options.merge!("remote-debugging-port" => port)

  host = options.fetch(:host, BROWSER_HOST)
  @options.merge!("remote-debugging-address" => host)

  @options.merge!("user-data-dir" => Dir.mktmpdir)

  @options = DEFAULT_OPTIONS.merge(@options)

  unless options.fetch(:headless, true)
    @options.delete("headless")
    @options.delete("disable-gpu")
  end

  @process_timeout = options.fetch(:process_timeout, PROCESS_TIMEOUT)

  @options.merge!(options.fetch(:browser_options, {}))

  @logger = options.fetch(:logger, nil)
end

Instance Attribute Details

#cmdObject (readonly)

Returns the value of attribute cmd.



50
51
52
# File 'lib/capybara/cuprite/browser/process.rb', line 50

def cmd
  @cmd
end

#hostObject (readonly)

Returns the value of attribute host.



50
51
52
# File 'lib/capybara/cuprite/browser/process.rb', line 50

def host
  @host
end

#optionsObject (readonly)

Returns the value of attribute options.



50
51
52
# File 'lib/capybara/cuprite/browser/process.rb', line 50

def options
  @options
end

#pathObject (readonly)

Returns the value of attribute path.



50
51
52
# File 'lib/capybara/cuprite/browser/process.rb', line 50

def path
  @path
end

#pidObject (readonly)

Returns the value of attribute pid.



50
51
52
# File 'lib/capybara/cuprite/browser/process.rb', line 50

def pid
  @pid
end

#portObject (readonly)

Returns the value of attribute port.



50
51
52
# File 'lib/capybara/cuprite/browser/process.rb', line 50

def port
  @port
end

#ws_urlObject (readonly)

Returns the value of attribute ws_url.



50
51
52
# File 'lib/capybara/cuprite/browser/process.rb', line 50

def ws_url
  @ws_url
end

Class Method Details

.process_killer(pid) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/capybara/cuprite/browser/process.rb', line 56

def self.process_killer(pid)
  proc do
    begin
      if Capybara::Cuprite.windows?
        ::Process.kill("KILL", pid)
      else
        ::Process.kill("USR1", pid)
        start = Capybara::Helpers.monotonic_time
        while ::Process.wait(pid, ::Process::WNOHANG).nil?
          sleep 0.05
          next unless (Capybara::Helpers.monotonic_time - start) > KILL_TIMEOUT
          ::Process.kill("KILL", pid)
          ::Process.wait(pid)
          break
        end
      end
    rescue Errno::ESRCH, Errno::ECHILD
    end
  end
end

.start(*args) ⇒ Object



52
53
54
# File 'lib/capybara/cuprite/browser/process.rb', line 52

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

Instance Method Details

#restartObject



132
133
134
135
# File 'lib/capybara/cuprite/browser/process.rb', line 132

def restart
  stop
  start
end

#startObject



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/capybara/cuprite/browser/process.rb', line 107

def start
  read_io, write_io = IO.pipe
  process_options = { in: File::NULL }
  process_options[:pgroup] = true unless Capybara::Cuprite.windows?
  if Capybara::Cuprite.mri?
    process_options[:out] = process_options[:err] = write_io
  end

  redirect_stdout(write_io) 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

  parse_ws_url(read_io, @process_timeout)
ensure
  close_io(read_io, write_io)
end

#stopObject



126
127
128
129
130
# File 'lib/capybara/cuprite/browser/process.rb', line 126

def stop
  return unless @pid
  kill
  ObjectSpace.undefine_finalizer(self)
end