ferrum 0.8 → 0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -1
- data/lib/ferrum.rb +13 -0
- data/lib/ferrum/browser.rb +2 -1
- data/lib/ferrum/browser/command.rb +25 -24
- data/lib/ferrum/browser/options/base.rb +46 -0
- data/lib/ferrum/browser/options/chrome.rb +73 -0
- data/lib/ferrum/browser/options/firefox.rb +34 -0
- data/lib/ferrum/browser/process.rb +18 -8
- data/lib/ferrum/browser/subscriber.rb +1 -1
- data/lib/ferrum/browser/web_socket.rb +17 -1
- data/lib/ferrum/browser/xvfb.rb +37 -0
- data/lib/ferrum/frame/runtime.rb +15 -1
- data/lib/ferrum/network/intercepted_request.rb +1 -1
- data/lib/ferrum/node.rb +38 -17
- data/lib/ferrum/page.rb +7 -1
- data/lib/ferrum/version.rb +1 -1
- metadata +8 -6
- data/lib/ferrum/browser/chrome.rb +0 -76
- data/lib/ferrum/browser/firefox.rb +0 -34
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8b4d6dc7aa1827fbf559e6025b82d29d15ed0e36a89793049266d8049fadabb9
         | 
| 4 | 
            +
              data.tar.gz: 6fab0202e85a17971d613db12e37a7ef85325eaf23f718b6801812df565ac64c
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: fb109c1b65e73e8d0088734fa004fae3d15650121d01b44dc9086cdb193bb3c7f56c3ad911a2f6de5cd28522231b4e695b67eb515d90afe93b862eb64cfe7050
         | 
| 7 | 
            +
              data.tar.gz: a4d5e4c192cbd634c640d86027f3a8faeaf42efcd8fbaa9b0e8c6e81c04aa9921b7f2353eb739c35850363f1ed58f84d5895714f52ecd333d9ebdb50c1de4031
         | 
    
        data/README.md
    CHANGED
    
    | @@ -90,7 +90,7 @@ Interact with a page: | |
| 90 90 | 
             
            ```ruby
         | 
| 91 91 | 
             
            browser = Ferrum::Browser.new
         | 
| 92 92 | 
             
            browser.goto("https://google.com")
         | 
| 93 | 
            -
            input = browser.at_xpath("// | 
| 93 | 
            +
            input = browser.at_xpath("//input[@name='q']")
         | 
| 94 94 | 
             
            input.focus.type("Ruby headless driver for Chrome", :Enter)
         | 
| 95 95 | 
             
            browser.at_css("a > h3").text # => "rubycdp/ferrum: Ruby Chrome/Chromium driver - GitHub"
         | 
| 96 96 | 
             
            browser.quit
         | 
| @@ -147,6 +147,7 @@ Ferrum::Browser.new(options) | |
| 147 147 |  | 
| 148 148 | 
             
            * options `Hash`
         | 
| 149 149 | 
             
              * `:headless` (Boolean) - Set browser as headless or not, `true` by default.
         | 
| 150 | 
            +
              * `:xvfb` (Boolean) - Run browser in a virtual framebuffer, `false` by default.
         | 
| 150 151 | 
             
              * `:window_size` (Array) - The dimensions of the browser window in which to
         | 
| 151 152 | 
             
                  test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768]
         | 
| 152 153 | 
             
              * `:extensions` (Array[String | Hash]) - An array of paths to files or JS
         | 
| @@ -166,6 +167,10 @@ Ferrum::Browser.new(options) | |
| 166 167 | 
             
              * `:browser_options` (Hash) - Additional command line options,
         | 
| 167 168 | 
             
                  [see them all](https://peter.sh/experiments/chromium-command-line-switches/)
         | 
| 168 169 | 
             
                  e.g. `{ "ignore-certificate-errors" => nil }`
         | 
| 170 | 
            +
              * `:ignore_default_browser_options` (Boolean) - Ferrum has a number of default
         | 
| 171 | 
            +
                  options it passes to the browser, if you set this to `true` then only
         | 
| 172 | 
            +
                  options you put in `:browser_options` will be passed to the browser,
         | 
| 173 | 
            +
                  except required ones of course.
         | 
| 169 174 | 
             
              * `:port` (Integer) - Remote debugging port for headless Chrome
         | 
| 170 175 | 
             
              * `:host` (String) - Remote debugging address for headless Chrome
         | 
| 171 176 | 
             
              * `:url` (String) - URL for a running instance of Chrome. If this is set, a
         | 
    
        data/lib/ferrum.rb
    CHANGED
    
    | @@ -48,6 +48,19 @@ module Ferrum | |
| 48 48 | 
             
                end
         | 
| 49 49 | 
             
              end
         | 
| 50 50 |  | 
| 51 | 
            +
              class NodeIsMovingError < Error
         | 
| 52 | 
            +
                def initialize(node, prev, current)
         | 
| 53 | 
            +
                  @node, @prev, @current = node, prev, current
         | 
| 54 | 
            +
                  super(message)
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def message
         | 
| 58 | 
            +
                  "#{@node.inspect} that you're trying to click is moving, hence " \
         | 
| 59 | 
            +
                  "we cannot. Previosuly it was at #{@prev.inspect} but now at " \
         | 
| 60 | 
            +
                  "#{@current.inspect}."
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 51 64 | 
             
              class BrowserError < Error
         | 
| 52 65 | 
             
                attr_reader :response
         | 
| 53 66 |  | 
    
        data/lib/ferrum/browser.rb
    CHANGED
    
    | @@ -4,6 +4,7 @@ require "base64" | |
| 4 4 | 
             
            require "forwardable"
         | 
| 5 5 | 
             
            require "ferrum/page"
         | 
| 6 6 | 
             
            require "ferrum/contexts"
         | 
| 7 | 
            +
            require "ferrum/browser/xvfb"
         | 
| 7 8 | 
             
            require "ferrum/browser/process"
         | 
| 8 9 | 
             
            require "ferrum/browser/client"
         | 
| 9 10 |  | 
| @@ -16,7 +17,7 @@ module Ferrum | |
| 16 17 | 
             
                extend Forwardable
         | 
| 17 18 | 
             
                delegate %i[default_context] => :contexts
         | 
| 18 19 | 
             
                delegate %i[targets create_target create_page page pages windows] => :default_context
         | 
| 19 | 
            -
                delegate %i[goto back forward refresh reload stop
         | 
| 20 | 
            +
                delegate %i[goto back forward refresh reload stop wait_for_reload
         | 
| 20 21 | 
             
                            at_css at_xpath css xpath current_url current_title url title
         | 
| 21 22 | 
             
                            body doctype set_content
         | 
| 22 23 | 
             
                            headers cookies network
         | 
| @@ -3,8 +3,6 @@ | |
| 3 3 | 
             
            module Ferrum
         | 
| 4 4 | 
             
              class Browser
         | 
| 5 5 | 
             
                class Command
         | 
| 6 | 
            -
                  BROWSER_HOST = "127.0.0.1"
         | 
| 7 | 
            -
                  BROWSER_PORT = "0"
         | 
| 8 6 | 
             
                  NOT_FOUND = "Could not find an executable for the browser. Try to make " \
         | 
| 9 7 | 
             
                              "it available on the PATH or set environment varible for " \
         | 
| 10 8 | 
             
                              "example BROWSER_PATH=\"/usr/bin/chrome\"".freeze
         | 
| @@ -12,44 +10,47 @@ module Ferrum | |
| 12 10 | 
             
                  # Currently only these browsers support CDP:
         | 
| 13 11 | 
             
                  # https://github.com/cyrus-and/chrome-remote-interface#implementations
         | 
| 14 12 | 
             
                  def self.build(options, user_data_dir)
         | 
| 15 | 
            -
                    case options[:browser_name]
         | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 13 | 
            +
                    defaults = case options[:browser_name]
         | 
| 14 | 
            +
                               when :firefox
         | 
| 15 | 
            +
                                 Options::Firefox.options
         | 
| 16 | 
            +
                               when :chrome, :opera, :edge, nil
         | 
| 17 | 
            +
                                 Options::Chrome.options
         | 
| 18 | 
            +
                               else
         | 
| 19 | 
            +
                                 raise NotImplementedError, "not supported browser"
         | 
| 20 | 
            +
                               end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    new(defaults, options, user_data_dir)
         | 
| 23 23 | 
             
                  end
         | 
| 24 24 |  | 
| 25 | 
            -
                  attr_reader : | 
| 25 | 
            +
                  attr_reader :defaults, :path, :options
         | 
| 26 26 |  | 
| 27 | 
            -
                  def initialize(options, user_data_dir)
         | 
| 27 | 
            +
                  def initialize(defaults, options, user_data_dir)
         | 
| 28 28 | 
             
                    @flags = {}
         | 
| 29 | 
            +
                    @defaults = defaults
         | 
| 29 30 | 
             
                    @options, @user_data_dir = options, user_data_dir
         | 
| 30 | 
            -
                    @path = options[:browser_path] || ENV["BROWSER_PATH"] || detect_path
         | 
| 31 | 
            +
                    @path = options[:browser_path] || ENV["BROWSER_PATH"] || defaults.detect_path
         | 
| 31 32 | 
             
                    raise Cliver::Dependency::NotFound.new(NOT_FOUND) unless @path
         | 
| 33 | 
            +
                    merge_options
         | 
| 34 | 
            +
                  end
         | 
| 32 35 |  | 
| 33 | 
            -
             | 
| 36 | 
            +
                  def xvfb?
         | 
| 37 | 
            +
                    !!options[:xvfb]
         | 
| 34 38 | 
             
                  end
         | 
| 35 39 |  | 
| 36 40 | 
             
                  def to_a
         | 
| 37 | 
            -
                    [path] + flags.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
         | 
| 41 | 
            +
                    [path] + @flags.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
         | 
| 38 42 | 
             
                  end
         | 
| 39 43 |  | 
| 40 44 | 
             
                  private
         | 
| 41 45 |  | 
| 42 | 
            -
                  def  | 
| 43 | 
            -
                     | 
| 44 | 
            -
             | 
| 45 | 
            -
                     | 
| 46 | 
            -
                       | 
| 47 | 
            -
                        .find { |b| p = Cliver.detect(b) and break(p) }
         | 
| 46 | 
            +
                  def merge_options
         | 
| 47 | 
            +
                    @flags = defaults.merge_required(@flags, options, @user_data_dir)
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    unless options[:ignore_default_browser_options]
         | 
| 50 | 
            +
                      @flags = defaults.merge_default(@flags, options)
         | 
| 48 51 | 
             
                    end
         | 
| 49 | 
            -
                  end
         | 
| 50 52 |  | 
| 51 | 
            -
             | 
| 52 | 
            -
                    raise NotImplementedError
         | 
| 53 | 
            +
                    @flags.merge!(options.fetch(:browser_options, {}))
         | 
| 53 54 | 
             
                  end
         | 
| 54 55 | 
             
                end
         | 
| 55 56 | 
             
              end
         | 
| @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "singleton"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Ferrum
         | 
| 6 | 
            +
              class Browser
         | 
| 7 | 
            +
                module Options
         | 
| 8 | 
            +
                  class Base
         | 
| 9 | 
            +
                    BROWSER_HOST = "127.0.0.1"
         | 
| 10 | 
            +
                    BROWSER_PORT = "0"
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    include Singleton
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    def self.options
         | 
| 15 | 
            +
                      instance
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def to_h
         | 
| 19 | 
            +
                      self.class::DEFAULT_OPTIONS
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def except(*keys)
         | 
| 23 | 
            +
                      to_h.reject { |n, _| keys.include?(n) }
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    def detect_path
         | 
| 27 | 
            +
                      if Ferrum.mac?
         | 
| 28 | 
            +
                        self.class::MAC_BIN_PATH.find { |n| File.exist?(n) }
         | 
| 29 | 
            +
                      else
         | 
| 30 | 
            +
                        self.class::LINUX_BIN_PATH.find do |name|
         | 
| 31 | 
            +
                          path = Cliver.detect(name) and break(path)
         | 
| 32 | 
            +
                        end
         | 
| 33 | 
            +
                      end
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def merge_required(flags, options, user_data_dir)
         | 
| 37 | 
            +
                      raise NotImplementedError
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def merge_default(flags, options)
         | 
| 41 | 
            +
                      raise NotImplementedError
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
            end
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Ferrum
         | 
| 4 | 
            +
              class Browser
         | 
| 5 | 
            +
                module Options
         | 
| 6 | 
            +
                  class Chrome < Base
         | 
| 7 | 
            +
                    DEFAULT_OPTIONS = {
         | 
| 8 | 
            +
                      "headless" => nil,
         | 
| 9 | 
            +
                      "disable-gpu" => nil,
         | 
| 10 | 
            +
                      "hide-scrollbars" => nil,
         | 
| 11 | 
            +
                      "mute-audio" => nil,
         | 
| 12 | 
            +
                      "enable-automation" => nil,
         | 
| 13 | 
            +
                      "disable-web-security" => nil,
         | 
| 14 | 
            +
                      "disable-session-crashed-bubble" => nil,
         | 
| 15 | 
            +
                      "disable-breakpad" => nil,
         | 
| 16 | 
            +
                      "disable-sync" => nil,
         | 
| 17 | 
            +
                      "no-first-run" => nil,
         | 
| 18 | 
            +
                      "use-mock-keychain" => nil,
         | 
| 19 | 
            +
                      "keep-alive-for-test" => nil,
         | 
| 20 | 
            +
                      "disable-popup-blocking" => nil,
         | 
| 21 | 
            +
                      "disable-extensions" => nil,
         | 
| 22 | 
            +
                      "disable-hang-monitor" => nil,
         | 
| 23 | 
            +
                      "disable-features" => "site-per-process,TranslateUI",
         | 
| 24 | 
            +
                      "disable-translate" => nil,
         | 
| 25 | 
            +
                      "disable-background-networking" => nil,
         | 
| 26 | 
            +
                      "enable-features" => "NetworkService,NetworkServiceInProcess",
         | 
| 27 | 
            +
                      "disable-background-timer-throttling" => nil,
         | 
| 28 | 
            +
                      "disable-backgrounding-occluded-windows" => nil,
         | 
| 29 | 
            +
                      "disable-client-side-phishing-detection" => nil,
         | 
| 30 | 
            +
                      "disable-default-apps" => nil,
         | 
| 31 | 
            +
                      "disable-dev-shm-usage" => nil,
         | 
| 32 | 
            +
                      "disable-ipc-flooding-protection" => nil,
         | 
| 33 | 
            +
                      "disable-prompt-on-repost" => nil,
         | 
| 34 | 
            +
                      "disable-renderer-backgrounding" => nil,
         | 
| 35 | 
            +
                      "force-color-profile" => "srgb",
         | 
| 36 | 
            +
                      "metrics-recording-only" => nil,
         | 
| 37 | 
            +
                      "safebrowsing-disable-auto-update" => nil,
         | 
| 38 | 
            +
                      "password-store" => "basic",
         | 
| 39 | 
            +
                      # Note: --no-sandbox is not needed if you properly setup a user in the container.
         | 
| 40 | 
            +
                      # https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
         | 
| 41 | 
            +
                      # "no-sandbox" => nil,
         | 
| 42 | 
            +
                    }.freeze
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    MAC_BIN_PATH = [
         | 
| 45 | 
            +
                      "/Applications/Chromium.app/Contents/MacOS/Chromium",
         | 
| 46 | 
            +
                      "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
         | 
| 47 | 
            +
                    ].freeze
         | 
| 48 | 
            +
                    LINUX_BIN_PATH = %w[chromium google-chrome-unstable google-chrome-beta
         | 
| 49 | 
            +
                                        google-chrome chrome chromium-browser
         | 
| 50 | 
            +
                                        google-chrome-stable].freeze
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def merge_required(flags, options, user_data_dir)
         | 
| 53 | 
            +
                      port = options.fetch(:port, BROWSER_PORT)
         | 
| 54 | 
            +
                      host = options.fetch(:host, BROWSER_HOST)
         | 
| 55 | 
            +
                      flags.merge("remote-debugging-port" => port,
         | 
| 56 | 
            +
                                  "remote-debugging-address" => host,
         | 
| 57 | 
            +
                                  # Doesn't work on MacOS, so we need to set it by CDP
         | 
| 58 | 
            +
                                  "window-size" => options[:window_size].join(","),
         | 
| 59 | 
            +
                                  "user-data-dir" => user_data_dir)
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    def merge_default(flags, options)
         | 
| 63 | 
            +
                      unless options.fetch(:headless, true)
         | 
| 64 | 
            +
                        defaults = except("headless", "disable-gpu")
         | 
| 65 | 
            +
                      end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                      defaults ||= DEFAULT_OPTIONS
         | 
| 68 | 
            +
                      defaults.merge(flags)
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Ferrum
         | 
| 4 | 
            +
              class Browser
         | 
| 5 | 
            +
                module Options
         | 
| 6 | 
            +
                  class Firefox < Base
         | 
| 7 | 
            +
                    DEFAULT_OPTIONS = {
         | 
| 8 | 
            +
                      "headless" => nil,
         | 
| 9 | 
            +
                    }.freeze
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    MAC_BIN_PATH = [
         | 
| 12 | 
            +
                      "/Applications/Firefox.app/Contents/MacOS/firefox-bin"
         | 
| 13 | 
            +
                    ].freeze
         | 
| 14 | 
            +
                    LINUX_BIN_PATH = %w[firefox].freeze
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    def merge_required(flags, options, user_data_dir)
         | 
| 17 | 
            +
                      port = options.fetch(:port, BROWSER_PORT)
         | 
| 18 | 
            +
                      host = options.fetch(:host, BROWSER_HOST)
         | 
| 19 | 
            +
                      flags.merge("remote-debugger" => "#{host}:#{port}",
         | 
| 20 | 
            +
                                  "profile" => user_data_dir)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    def merge_default(flags, options)
         | 
| 24 | 
            +
                      unless options.fetch(:headless, true)
         | 
| 25 | 
            +
                        defaults = except("headless")
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      defaults ||= DEFAULT_OPTIONS
         | 
| 29 | 
            +
                      defaults.merge(flags)
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| @@ -6,9 +6,10 @@ require "json" | |
| 6 6 | 
             
            require "addressable"
         | 
| 7 7 | 
             
            require "tmpdir"
         | 
| 8 8 | 
             
            require "forwardable"
         | 
| 9 | 
            +
            require "ferrum/browser/options/base"
         | 
| 10 | 
            +
            require "ferrum/browser/options/chrome"
         | 
| 11 | 
            +
            require "ferrum/browser/options/firefox"
         | 
| 9 12 | 
             
            require "ferrum/browser/command"
         | 
| 10 | 
            -
            require "ferrum/browser/chrome"
         | 
| 11 | 
            -
            require "ferrum/browser/firefox"
         | 
| 12 13 |  | 
| 13 14 | 
             
            module Ferrum
         | 
| 14 15 | 
             
              class Browser
         | 
| @@ -19,7 +20,7 @@ module Ferrum | |
| 19 20 |  | 
| 20 21 | 
             
                  attr_reader :host, :port, :ws_url, :pid, :command,
         | 
| 21 22 | 
             
                              :default_user_agent, :browser_version, :protocol_version,
         | 
| 22 | 
            -
                              :v8_version, :webkit_version
         | 
| 23 | 
            +
                              :v8_version, :webkit_version, :xvfb
         | 
| 23 24 |  | 
| 24 25 |  | 
| 25 26 | 
             
                  extend Forwardable
         | 
| @@ -81,7 +82,12 @@ module Ferrum | |
| 81 82 | 
             
                      process_options[:pgroup] = true unless Ferrum.windows?
         | 
| 82 83 | 
             
                      process_options[:out] = process_options[:err] = write_io
         | 
| 83 84 |  | 
| 84 | 
            -
                      @ | 
| 85 | 
            +
                      if @command.xvfb?
         | 
| 86 | 
            +
                        @xvfb = Xvfb.start(@command.options)
         | 
| 87 | 
            +
                        ObjectSpace.define_finalizer(self, self.class.process_killer(@xvfb.pid))
         | 
| 88 | 
            +
                      end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                      @pid = ::Process.spawn(Hash(@xvfb&.to_env), *@command.to_a, process_options)
         | 
| 85 91 | 
             
                      ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
         | 
| 86 92 |  | 
| 87 93 | 
             
                      parse_ws_url(read_io, @process_timeout)
         | 
| @@ -92,7 +98,12 @@ module Ferrum | |
| 92 98 | 
             
                  end
         | 
| 93 99 |  | 
| 94 100 | 
             
                  def stop
         | 
| 95 | 
            -
                     | 
| 101 | 
            +
                    if @pid
         | 
| 102 | 
            +
                      kill(@pid)
         | 
| 103 | 
            +
                      kill(@xvfb.pid) if @xvfb&.pid
         | 
| 104 | 
            +
                      @pid = nil
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 96 107 | 
             
                    remove_user_data_dir if @user_data_dir
         | 
| 97 108 | 
             
                    ObjectSpace.undefine_finalizer(self)
         | 
| 98 109 | 
             
                  end
         | 
| @@ -104,9 +115,8 @@ module Ferrum | |
| 104 115 |  | 
| 105 116 | 
             
                  private
         | 
| 106 117 |  | 
| 107 | 
            -
                  def kill
         | 
| 108 | 
            -
                    self.class.process_killer( | 
| 109 | 
            -
                    @pid = nil
         | 
| 118 | 
            +
                  def kill(pid)
         | 
| 119 | 
            +
                    self.class.process_killer(pid).call
         | 
| 110 120 | 
             
                  end
         | 
| 111 121 |  | 
| 112 122 | 
             
                  def remove_user_data_dir
         | 
| @@ -8,6 +8,7 @@ module Ferrum | |
| 8 8 | 
             
              class Browser
         | 
| 9 9 | 
             
                class WebSocket
         | 
| 10 10 | 
             
                  WEBSOCKET_BUG_SLEEP = 0.01
         | 
| 11 | 
            +
                  SKIP_LOGGING_SCREENSHOTS = !ENV["FERRUM_LOGGING_SCREENSHOTS"]
         | 
| 11 12 |  | 
| 12 13 | 
             
                  attr_reader :url, :messages
         | 
| 13 14 |  | 
| @@ -20,6 +21,10 @@ module Ferrum | |
| 20 21 | 
             
                    @driver   = ::WebSocket::Driver.client(self, max_length: max_receive_size)
         | 
| 21 22 | 
             
                    @messages = Queue.new
         | 
| 22 23 |  | 
| 24 | 
            +
                    if SKIP_LOGGING_SCREENSHOTS
         | 
| 25 | 
            +
                      @screenshot_commands = Concurrent::Hash.new
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 23 28 | 
             
                    @driver.on(:open,    &method(:on_open))
         | 
| 24 29 | 
             
                    @driver.on(:message, &method(:on_message))
         | 
| 25 30 | 
             
                    @driver.on(:close,   &method(:on_close))
         | 
| @@ -50,7 +55,14 @@ module Ferrum | |
| 50 55 | 
             
                  def on_message(event)
         | 
| 51 56 | 
             
                    data = JSON.parse(event.data)
         | 
| 52 57 | 
             
                    @messages.push(data)
         | 
| 53 | 
            -
             | 
| 58 | 
            +
             | 
| 59 | 
            +
                    output = event.data
         | 
| 60 | 
            +
                    if SKIP_LOGGING_SCREENSHOTS && @screenshot_commands[data["id"]]
         | 
| 61 | 
            +
                      @screenshot_commands.delete(data["id"])
         | 
| 62 | 
            +
                      output.sub!(/{"data":"(.*)"}/, %("Set FERRUM_LOGGING_SCREENSHOTS=true to see screenshots in Base64"))
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    @logger&.puts("    ◀ #{Ferrum.elapsed_time} #{output}\n")
         | 
| 54 66 | 
             
                  end
         | 
| 55 67 |  | 
| 56 68 | 
             
                  def on_close(_event)
         | 
| @@ -59,6 +71,10 @@ module Ferrum | |
| 59 71 | 
             
                  end
         | 
| 60 72 |  | 
| 61 73 | 
             
                  def send_message(data)
         | 
| 74 | 
            +
                    if SKIP_LOGGING_SCREENSHOTS
         | 
| 75 | 
            +
                      @screenshot_commands[data[:id]] = true
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 62 78 | 
             
                    json = data.to_json
         | 
| 63 79 | 
             
                    @driver.text(json)
         | 
| 64 80 | 
             
                    @logger&.puts("\n\n▶ #{Ferrum.elapsed_time} #{json}")
         | 
| @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Ferrum
         | 
| 4 | 
            +
              class Browser
         | 
| 5 | 
            +
                class Xvfb
         | 
| 6 | 
            +
                  NOT_FOUND = "Could not find an executable for the Xvfb. Try to install " \
         | 
| 7 | 
            +
                              "it with your package manager".freeze
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  def self.start(*args)
         | 
| 10 | 
            +
                    new(*args).tap(&:start)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def self.xvfb_path
         | 
| 14 | 
            +
                    Cliver.detect("Xvfb")
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  attr_reader :screen_size, :display_id, :pid
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def initialize(options)
         | 
| 20 | 
            +
                    @path = self.class.xvfb_path
         | 
| 21 | 
            +
                    raise Cliver::Dependency::NotFound.new(NOT_FOUND) unless @path
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    @screen_size = options.fetch(:window_size, [1024, 768]).join("x") + "x24"
         | 
| 24 | 
            +
                    @display_id = (Time.now.to_f * 1000).to_i % 100_000_000
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def start
         | 
| 28 | 
            +
                    @pid = ::Process.spawn("#{@path} :#{display_id} -screen 0 #{screen_size}")
         | 
| 29 | 
            +
                    ::Process.detach(@pid)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def to_env
         | 
| 33 | 
            +
                    { "DISPLAY" => ":#{display_id}" }
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
            end
         | 
    
        data/lib/ferrum/frame/runtime.rb
    CHANGED
    
    | @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "singleton"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Ferrum
         | 
| 4 6 | 
             
              class Frame
         | 
| 5 7 | 
             
                module Runtime
         | 
| @@ -215,7 +217,7 @@ module Ferrum | |
| 215 217 |  | 
| 216 218 | 
             
                  def reduce_props(object_id, to)
         | 
| 217 219 | 
             
                    if cyclic?(object_id).dig("result", "value")
         | 
| 218 | 
            -
                      return  | 
| 220 | 
            +
                      return to.is_a?(Array) ? [cyclic_object] : cyclic_object
         | 
| 219 221 | 
             
                    else
         | 
| 220 222 | 
             
                      props = @page.command("Runtime.getProperties", ownProperties: true, objectId: object_id)
         | 
| 221 223 | 
             
                      props["result"].reduce(to) do |memo, prop|
         | 
| @@ -259,6 +261,18 @@ module Ferrum | |
| 259 261 | 
             
                      JS
         | 
| 260 262 | 
             
                    )
         | 
| 261 263 | 
             
                  end
         | 
| 264 | 
            +
             | 
| 265 | 
            +
                  def cyclic_object
         | 
| 266 | 
            +
                    CyclicObject.instance
         | 
| 267 | 
            +
                  end
         | 
| 268 | 
            +
                end
         | 
| 269 | 
            +
              end
         | 
| 270 | 
            +
             | 
| 271 | 
            +
              class CyclicObject
         | 
| 272 | 
            +
                include Singleton
         | 
| 273 | 
            +
             | 
| 274 | 
            +
                def inspect
         | 
| 275 | 
            +
                  %(#<#{self.class} JavaScript object that cannot be represented in Ruby>)
         | 
| 262 276 | 
             
                end
         | 
| 263 277 | 
             
              end
         | 
| 264 278 | 
             
            end
         | 
| @@ -39,7 +39,7 @@ module Ferrum | |
| 39 39 | 
             
                      requestId: request_id,
         | 
| 40 40 | 
             
                      responseHeaders: header_array(headers),
         | 
| 41 41 | 
             
                    })
         | 
| 42 | 
            -
                    options = options.merge(body: Base64. | 
| 42 | 
            +
                    options = options.merge(body: Base64.strict_encode64(options.fetch(:body, ""))) if has_body
         | 
| 43 43 |  | 
| 44 44 | 
             
                    @status = :responded
         | 
| 45 45 | 
             
                    @page.command("Fetch.fulfillRequest", **options)
         | 
    
        data/lib/ferrum/node.rb
    CHANGED
    
    | @@ -2,6 +2,9 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Ferrum
         | 
| 4 4 | 
             
              class Node
         | 
| 5 | 
            +
                MOVING_WAIT = ENV.fetch("FERRUM_NODE_MOVING_WAIT", 0.01).to_f
         | 
| 6 | 
            +
                MOVING_ATTEMPTS = ENV.fetch("FERRUM_NODE_MOVING_ATTEMPTS", 50).to_i
         | 
| 7 | 
            +
             | 
| 5 8 | 
             
                attr_reader :page, :target_id, :node_id, :description, :tag_name
         | 
| 6 9 |  | 
| 7 10 | 
             
                def initialize(frame, target_id, node_id, description)
         | 
| @@ -121,16 +124,42 @@ module Ferrum | |
| 121 124 | 
             
                end
         | 
| 122 125 |  | 
| 123 126 | 
             
                def find_position(x: nil, y: nil, position: :top)
         | 
| 124 | 
            -
                   | 
| 125 | 
            -
             | 
| 127 | 
            +
                  prev = get_content_quads
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  # FIXME: Case when a few quads returned
         | 
| 130 | 
            +
                  points = Ferrum.with_attempts(errors: NodeIsMovingError, max: MOVING_ATTEMPTS, wait: 0) do
         | 
| 131 | 
            +
                    sleep(MOVING_WAIT)
         | 
| 132 | 
            +
                    current = get_content_quads
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                    if current != prev
         | 
| 135 | 
            +
                      error = NodeIsMovingError.new(self, prev, current)
         | 
| 136 | 
            +
                      prev = current
         | 
| 137 | 
            +
                      raise(error)
         | 
| 138 | 
            +
                    end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                    current
         | 
| 141 | 
            +
                  end.map { |q| to_points(q) }.first
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                  get_position(points, x, y, position)
         | 
| 144 | 
            +
                end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                private
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                def get_content_quads
         | 
| 149 | 
            +
                  quads = page.command("DOM.getContentQuads", nodeId: node_id)["quads"]
         | 
| 150 | 
            +
                  raise "Node is either not visible or not an HTMLElement" if quads.size == 0
         | 
| 151 | 
            +
                  quads
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def get_position(points, offset_x, offset_y, position)
         | 
| 126 155 | 
             
                  x = y = nil
         | 
| 127 156 |  | 
| 128 157 | 
             
                  if offset_x && offset_y && position == :top
         | 
| 129 | 
            -
                    point =  | 
| 158 | 
            +
                    point = points.first
         | 
| 130 159 | 
             
                    x = point[:x] + offset_x.to_i
         | 
| 131 160 | 
             
                    y = point[:y] + offset_y.to_i
         | 
| 132 161 | 
             
                  else
         | 
| 133 | 
            -
                    x, y =  | 
| 162 | 
            +
                    x, y = points.inject([0, 0]) do |memo, point|
         | 
| 134 163 | 
             
                      [memo[0] + point[:x],
         | 
| 135 164 | 
             
                       memo[1] + point[:y]]
         | 
| 136 165 | 
             
                    end
         | 
| @@ -147,19 +176,11 @@ module Ferrum | |
| 147 176 | 
             
                  [x, y]
         | 
| 148 177 | 
             
                end
         | 
| 149 178 |  | 
| 150 | 
            -
                 | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 156 | 
            -
                  # FIXME: Case when a few quads returned
         | 
| 157 | 
            -
                  result["quads"].map do |quad|
         | 
| 158 | 
            -
                    [{x: quad[0], y: quad[1]},
         | 
| 159 | 
            -
                     {x: quad[2], y: quad[3]},
         | 
| 160 | 
            -
                     {x: quad[4], y: quad[5]},
         | 
| 161 | 
            -
                     {x: quad[6], y: quad[7]}]
         | 
| 162 | 
            -
                  end.first
         | 
| 179 | 
            +
                def to_points(quad)
         | 
| 180 | 
            +
                  [{x: quad[0], y: quad[1]},
         | 
| 181 | 
            +
                   {x: quad[2], y: quad[3]},
         | 
| 182 | 
            +
                   {x: quad[4], y: quad[5]},
         | 
| 183 | 
            +
                   {x: quad[6], y: quad[7]}]
         | 
| 163 184 | 
             
                end
         | 
| 164 185 | 
             
              end
         | 
| 165 186 | 
             
            end
         | 
    
        data/lib/ferrum/page.rb
    CHANGED
    
    | @@ -44,7 +44,7 @@ module Ferrum | |
| 44 44 |  | 
| 45 45 | 
             
                def initialize(target_id, browser)
         | 
| 46 46 | 
             
                  @frames = {}
         | 
| 47 | 
            -
                  @main_frame = Frame.new(nil, self) | 
| 47 | 
            +
                  @main_frame = Frame.new(nil, self)
         | 
| 48 48 | 
             
                  @target_id, @browser = target_id, browser
         | 
| 49 49 | 
             
                  @event = Event.new.tap(&:set)
         | 
| 50 50 |  | 
| @@ -125,6 +125,12 @@ module Ferrum | |
| 125 125 | 
             
                  history_navigate(delta: 1)
         | 
| 126 126 | 
             
                end
         | 
| 127 127 |  | 
| 128 | 
            +
                def wait_for_reload(sec = 1)
         | 
| 129 | 
            +
                  @event.reset if @event.set?
         | 
| 130 | 
            +
                  @event.wait(sec)
         | 
| 131 | 
            +
                  @event.set
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 128 134 | 
             
                def bypass_csp(value = true)
         | 
| 129 135 | 
             
                  enabled = !!value
         | 
| 130 136 | 
             
                  command("Page.setBypassCSP", enabled: enabled)
         | 
    
        data/lib/ferrum/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ferrum
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: '0. | 
| 4 | 
            +
              version: '0.9'
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Dmitry Vorotilin
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2020- | 
| 11 | 
            +
            date: 2020-07-24 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: websocket-driver
         | 
| @@ -64,14 +64,14 @@ dependencies: | |
| 64 64 | 
             
                requirements:
         | 
| 65 65 | 
             
                - - "~>"
         | 
| 66 66 | 
             
                  - !ruby/object:Gem::Version
         | 
| 67 | 
            -
                    version: '2. | 
| 67 | 
            +
                    version: '2.5'
         | 
| 68 68 | 
             
              type: :runtime
         | 
| 69 69 | 
             
              prerelease: false
         | 
| 70 70 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 71 71 | 
             
                requirements:
         | 
| 72 72 | 
             
                - - "~>"
         | 
| 73 73 | 
             
                  - !ruby/object:Gem::Version
         | 
| 74 | 
            -
                    version: '2. | 
| 74 | 
            +
                    version: '2.5'
         | 
| 75 75 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 76 76 | 
             
              name: rake
         | 
| 77 77 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -181,13 +181,15 @@ files: | |
| 181 181 | 
             
            - README.md
         | 
| 182 182 | 
             
            - lib/ferrum.rb
         | 
| 183 183 | 
             
            - lib/ferrum/browser.rb
         | 
| 184 | 
            -
            - lib/ferrum/browser/chrome.rb
         | 
| 185 184 | 
             
            - lib/ferrum/browser/client.rb
         | 
| 186 185 | 
             
            - lib/ferrum/browser/command.rb
         | 
| 187 | 
            -
            - lib/ferrum/browser/ | 
| 186 | 
            +
            - lib/ferrum/browser/options/base.rb
         | 
| 187 | 
            +
            - lib/ferrum/browser/options/chrome.rb
         | 
| 188 | 
            +
            - lib/ferrum/browser/options/firefox.rb
         | 
| 188 189 | 
             
            - lib/ferrum/browser/process.rb
         | 
| 189 190 | 
             
            - lib/ferrum/browser/subscriber.rb
         | 
| 190 191 | 
             
            - lib/ferrum/browser/web_socket.rb
         | 
| 192 | 
            +
            - lib/ferrum/browser/xvfb.rb
         | 
| 191 193 | 
             
            - lib/ferrum/context.rb
         | 
| 192 194 | 
             
            - lib/ferrum/contexts.rb
         | 
| 193 195 | 
             
            - lib/ferrum/cookies.rb
         | 
| @@ -1,76 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Ferrum
         | 
| 4 | 
            -
              class Browser
         | 
| 5 | 
            -
                class Chrome < Command
         | 
| 6 | 
            -
                  DEFAULT_OPTIONS = {
         | 
| 7 | 
            -
                    "headless" => nil,
         | 
| 8 | 
            -
                    "disable-gpu" => nil,
         | 
| 9 | 
            -
                    "hide-scrollbars" => nil,
         | 
| 10 | 
            -
                    "mute-audio" => nil,
         | 
| 11 | 
            -
                    "enable-automation" => nil,
         | 
| 12 | 
            -
                    "disable-web-security" => nil,
         | 
| 13 | 
            -
                    "disable-session-crashed-bubble" => nil,
         | 
| 14 | 
            -
                    "disable-breakpad" => nil,
         | 
| 15 | 
            -
                    "disable-sync" => nil,
         | 
| 16 | 
            -
                    "no-first-run" => nil,
         | 
| 17 | 
            -
                    "use-mock-keychain" => nil,
         | 
| 18 | 
            -
                    "keep-alive-for-test" => nil,
         | 
| 19 | 
            -
                    "disable-popup-blocking" => nil,
         | 
| 20 | 
            -
                    "disable-extensions" => nil,
         | 
| 21 | 
            -
                    "disable-hang-monitor" => nil,
         | 
| 22 | 
            -
                    "disable-features" => "site-per-process,TranslateUI",
         | 
| 23 | 
            -
                    "disable-translate" => nil,
         | 
| 24 | 
            -
                    "disable-background-networking" => nil,
         | 
| 25 | 
            -
                    "enable-features" => "NetworkService,NetworkServiceInProcess",
         | 
| 26 | 
            -
                    "disable-background-timer-throttling" => nil,
         | 
| 27 | 
            -
                    "disable-backgrounding-occluded-windows" => nil,
         | 
| 28 | 
            -
                    "disable-client-side-phishing-detection" => nil,
         | 
| 29 | 
            -
                    "disable-default-apps" => nil,
         | 
| 30 | 
            -
                    "disable-dev-shm-usage" => nil,
         | 
| 31 | 
            -
                    "disable-ipc-flooding-protection" => nil,
         | 
| 32 | 
            -
                    "disable-prompt-on-repost" => nil,
         | 
| 33 | 
            -
                    "disable-renderer-backgrounding" => nil,
         | 
| 34 | 
            -
                    "force-color-profile" => "srgb",
         | 
| 35 | 
            -
                    "metrics-recording-only" => nil,
         | 
| 36 | 
            -
                    "safebrowsing-disable-auto-update" => nil,
         | 
| 37 | 
            -
                    "password-store" => "basic",
         | 
| 38 | 
            -
                    # Note: --no-sandbox is not needed if you properly setup a user in the container.
         | 
| 39 | 
            -
                    # https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
         | 
| 40 | 
            -
                    # "no-sandbox" => nil,
         | 
| 41 | 
            -
                  }.freeze
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                  MAC_BIN_PATH = [
         | 
| 44 | 
            -
                    "/Applications/Chromium.app/Contents/MacOS/Chromium",
         | 
| 45 | 
            -
                    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
         | 
| 46 | 
            -
                  ].freeze
         | 
| 47 | 
            -
                  LINUX_BIN_PATH = %w[chromium google-chrome-unstable google-chrome-beta
         | 
| 48 | 
            -
                                      google-chrome chrome chromium-browser
         | 
| 49 | 
            -
                                      google-chrome-stable].freeze
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                  private
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                  def combine_flags
         | 
| 54 | 
            -
                    # Doesn't work on MacOS, so we need to set it by CDP as well
         | 
| 55 | 
            -
                    @flags.merge!("window-size" => options[:window_size].join(","))
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                    port = options.fetch(:port, BROWSER_PORT)
         | 
| 58 | 
            -
                    @flags.merge!("remote-debugging-port" => port)
         | 
| 59 | 
            -
             | 
| 60 | 
            -
                    host = options.fetch(:host, BROWSER_HOST)
         | 
| 61 | 
            -
                    @flags.merge!("remote-debugging-address" => host)
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                    @flags.merge!("user-data-dir" => @user_data_dir)
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                    @flags = DEFAULT_OPTIONS.merge(@flags)
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                    unless options.fetch(:headless, true)
         | 
| 68 | 
            -
                      @flags.delete("headless")
         | 
| 69 | 
            -
                      @flags.delete("disable-gpu")
         | 
| 70 | 
            -
                    end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                    @flags.merge!(options.fetch(:browser_options, {}))
         | 
| 73 | 
            -
                  end
         | 
| 74 | 
            -
                end
         | 
| 75 | 
            -
              end
         | 
| 76 | 
            -
            end
         | 
| @@ -1,34 +0,0 @@ | |
| 1 | 
            -
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            module Ferrum
         | 
| 4 | 
            -
              class Browser
         | 
| 5 | 
            -
                class Firefox < Command
         | 
| 6 | 
            -
                  DEFAULT_OPTIONS = {
         | 
| 7 | 
            -
                    "headless" => nil,
         | 
| 8 | 
            -
                  }.freeze
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  MAC_BIN_PATH = [
         | 
| 11 | 
            -
                    "/Applications/Firefox.app/Contents/MacOS/firefox-bin"
         | 
| 12 | 
            -
                  ].freeze
         | 
| 13 | 
            -
                  LINUX_BIN_PATH = %w[firefox].freeze
         | 
| 14 | 
            -
             | 
| 15 | 
            -
                  private
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                  def combine_flags
         | 
| 18 | 
            -
                    port = options.fetch(:port, BROWSER_PORT)
         | 
| 19 | 
            -
                    host = options.fetch(:host, BROWSER_HOST)
         | 
| 20 | 
            -
                    @flags.merge!("remote-debugger" => "#{host}:#{port}")
         | 
| 21 | 
            -
             | 
| 22 | 
            -
                    @flags.merge!("profile" => @user_data_dir)
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    @flags = DEFAULT_OPTIONS.merge(@flags)
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                    unless options.fetch(:headless, true)
         | 
| 27 | 
            -
                      @flags.delete("headless")
         | 
| 28 | 
            -
                    end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
                    @flags.merge!(options.fetch(:browser_options, {}))
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
                end
         | 
| 33 | 
            -
              end
         | 
| 34 | 
            -
            end
         |