Module: Palapala::ChromeProcess

Defined in:
lib/palapala/chrome_process.rb

Overview

Manage the Chrome child process

Class Method Summary collapse

Class Method Details

.chrome_pathObject

Get the path to the Chrome executable, if it’s not set, then guess based on the OS



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/palapala/chrome_process.rb', line 43

def self.chrome_path
  return Palapala.headless_chrome_path if Palapala.headless_chrome_path

  case RbConfig::CONFIG["host_os"]
  when /darwin/
    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
  when /linux/
    "/usr/bin/google-chrome" # or "/usr/bin/chromium-browser"
  when /win|mingw|cygwin/
    "#{ENV.fetch("ProgramFiles(x86)", nil)}\\Google\\Chrome\\Application\\chrome.exe"
  else
    raise "Unsupported OS"
  end
end

.chrome_process_healthy?Boolean

Check if the Chrome process is healthy

Returns:

  • (Boolean)


17
18
19
20
21
22
23
24
25
26
# File 'lib/palapala/chrome_process.rb', line 17

def self.chrome_process_healthy?
  return false if @chrome_process_id.nil?

  begin
    Process.kill(0, @chrome_process_id) # Check if the process is alive
    true
  rescue Errno::ESRCH, Errno::EPERM
    false
  end
end

.chrome_running?Boolean

Check if a Chrome is running

Returns:

  • (Boolean)


29
30
31
32
# File 'lib/palapala/chrome_process.rb', line 29

def self.chrome_running?
  port_in_use? || # Check if the port is in use and Chrome is running externally
  chrome_process_healthy? # Check if the process is still alive
end

.kill_chromeObject

Kill the Chrome child process



35
36
37
38
39
40
# File 'lib/palapala/chrome_process.rb', line 35

def self.kill_chrome
  return if @chrome_process_id.nil?

  Process.kill("KILL", @chrome_process_id) # Kill the process
  Process.wait(@chrome_process_id) # Wait for the process to finish
end

.npx_installed?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/palapala/chrome_process.rb', line 58

def self.npx_installed?
  system("which npx > /dev/null 2>&1")
end

.port_in_use?(port = 9222, host = "127.0.0.1") ⇒ Boolean

Check if the port is in use

Returns:

  • (Boolean)


8
9
10
11
12
13
14
# File 'lib/palapala/chrome_process.rb', line 8

def self.port_in_use?(port = 9222, host = "127.0.0.1")
  server = TCPServer.new(host, port)
  server.close
  false
rescue Errno::EADDRINUSE
  true
end

.spawn_chromeObject

Spawn a Chrome child process



97
98
99
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
# File 'lib/palapala/chrome_process.rb', line 97

def self.spawn_chrome
  return if chrome_running?

  if self.npx_installed?
    @chrome_process_id = spawn_chrome_headless_server
  else
    params = [ "--headless", "--disable-gpu", "--remote-debugging-port=9222" ]
    params.merge!(Palapala.chrome_params) if Palapala.chrome_params
    # Spawn an existing chrome with the path and parameters
    @chrome_process_id = Process.spawn(chrome_path, *params)
  end

  # Wait until the port is in use
  sleep 0.1 until port_in_use?
  # Detach the process so it runs in the background
  Process.detach(@chrome_process_id)

  at_exit do
    if @chrome_process_id
      begin
        Process.kill("TERM", @chrome_process_id)
        Process.wait(@chrome_process_id)
        puts "Child process #{@chrome_process_id} terminated."
      rescue Errno::ESRCH
        puts "Child process #{@chrome_process_id} is already terminated."
      rescue Errno::ECHILD
        puts "No child process #{@chrome_process_id} found."
      end
    end
  end

  # Handle when the process is killed
  trap("SIGCHLD") do
    while (@chrome_process_id = Process.wait(-1, Process::WNOHANG))
      break if @chrome_process_id.nil?

      puts "Process #{@chrome_process_id} was killed."
      # Handle the error or restart the process if necessary
      @chrome_process_id = nil
    end
  rescue Errno::ECHILD
    @chrome_process_id = nil
  end
end

.spawn_chrome_headless_serverObject



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/palapala/chrome_process.rb', line 62

def self.spawn_chrome_headless_server
  # Run the command and capture the output
  puts "Installing latest stable chrome-headless-shell..."
  output, status = Open3.capture2("npx --yes @puppeteer/browsers install chrome-headless-shell@#{Palapala.chrome_headless_shell_version}")

  if status.success?
    # Extract the path from the output
    result = output.lines.find { |line| line.include?("chrome-headless-shell@") }
    if result.nil?
      raise "Failed to install chrome-headless-shell"
    end
    _, chrome_path = result.split(" ", 2).map(&:strip)

    # Directory you want the relative path from (current working directory)
    base_dir = Dir.pwd

    # Convert absolute path to relative path
    relative_path = Pathname.new(chrome_path).relative_path_from(Pathname.new(base_dir)).to_s

    puts "Launching chrome-headless-shell at #{relative_path}" if Palapala.debug
    # Display the version
    system("#{chrome_path} --version") if Palapala.debug
    # Launch chrome-headless-shell with the --remote-debugging-port parameter
    if Palapala.debug
      puts "spawning with output"
      spawn(chrome_path, "--remote-debugging-port=9222", "--disable-gpu")
    else
      spawn(chrome_path, "--remote-debugging-port=9222", "--disable-gpu", out: "/dev/null", err: "/dev/null")
    end
  else
    raise "Failed to install chrome-headless-shell"
  end
end