From 07b93549b5fca973236f0418d04fcac1367b88b2 Mon Sep 17 00:00:00 2001 From: Dmitry Vorotilin Date: Thu, 14 Sep 2023 13:16:21 +0300 Subject: [PATCH] Support for headless=new (#379) * feat: Support for headless=new * chore: comments * fix: linux position spec * fix: README.md --- README.md | 3 +- lib/ferrum/browser.rb | 4 + lib/ferrum/browser/client.rb | 3 +- lib/ferrum/browser/command.rb | 4 + lib/ferrum/browser/options/chrome.rb | 11 +- lib/ferrum/page.rb | 3 +- spec/browser_spec.rb | 23 +++- spec/frame_spec.rb | 198 +++++++++++++-------------- spec/network_spec.rb | 32 ++++- spec/page_spec.rb | 6 +- spec/support/global_helpers.rb | 5 +- 11 files changed, 176 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index 83f4c2b3..0f645862 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,8 @@ Ferrum::Browser.new(options) ``` * options `Hash` - * `:headless` (Boolean) - Set browser as headless or not, `true` by default. + * `:headless` (String | Boolean) - Set browser as headless or not, `true` by default. You can set `"new"` to support + [new headless mode](https://developer.chrome.com/articles/new-headless/). * `:xvfb` (Boolean) - Run browser in a virtual framebuffer, `false` by default. * `:window_size` (Array) - The dimensions of the browser window in which to test, expressed as a 2-element array, e.g. [1024, 768]. Default: [1024, 768] diff --git a/lib/ferrum/browser.rb b/lib/ferrum/browser.rb index cbad3b3e..8a4f3621 100644 --- a/lib/ferrum/browser.rb +++ b/lib/ferrum/browser.rb @@ -264,6 +264,10 @@ def version VersionInfo.new(command("Browser.getVersion")) end + def headless_new? + process&.command&.headless_new? + end + private def start diff --git a/lib/ferrum/browser/client.rb b/lib/ferrum/browser/client.rb index b5284e00..e1136504 100644 --- a/lib/ferrum/browser/client.rb +++ b/lib/ferrum/browser/client.rb @@ -84,7 +84,8 @@ def raise_browser_error(error) case error["message"] # Node has disappeared while we were trying to get it when "No node with given id found", - "Could not find node with given id" + "Could not find node with given id", + "Inspected target navigated or closed" raise NodeNotFoundError, error # Context is lost, page is reloading when "Cannot find context with specified id" diff --git a/lib/ferrum/browser/command.rb b/lib/ferrum/browser/command.rb index a04c8d68..e86649d3 100644 --- a/lib/ferrum/browser/command.rb +++ b/lib/ferrum/browser/command.rb @@ -39,6 +39,10 @@ def xvfb? !!options.xvfb end + def headless_new? + @flags["headless"] == "new" + end + def to_a [path] + @flags.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" } end diff --git a/lib/ferrum/browser/options/chrome.rb b/lib/ferrum/browser/options/chrome.rb index 1cce92df..3461b715 100644 --- a/lib/ferrum/browser/options/chrome.rb +++ b/lib/ferrum/browser/options/chrome.rb @@ -19,8 +19,9 @@ class Chrome < Base "keep-alive-for-test" => nil, "disable-popup-blocking" => nil, "disable-extensions" => nil, + "disable-component-extensions-with-background-pages" => nil, "disable-hang-monitor" => nil, - "disable-features" => "site-per-process,TranslateUI", + "disable-features" => "site-per-process,IsolateOrigins,TranslateUI", "disable-translate" => nil, "disable-background-networking" => nil, "enable-features" => "NetworkService,NetworkServiceInProcess", @@ -32,6 +33,7 @@ class Chrome < Base "disable-ipc-flooding-protection" => nil, "disable-prompt-on-repost" => nil, "disable-renderer-backgrounding" => nil, + "disable-site-isolation-trials" => nil, "force-color-profile" => "srgb", "metrics-recording-only" => nil, "safebrowsing-disable-auto-update" => nil, @@ -74,7 +76,12 @@ def merge_required(flags, options, user_data_dir) end def merge_default(flags, options) - defaults = except("headless", "disable-gpu") unless options.headless + defaults = case options.headless + when false + except("headless", "disable-gpu") + when "new" + except("headless").merge("headless" => "new") + end defaults ||= DEFAULT_OPTIONS defaults.merge(flags) diff --git a/lib/ferrum/page.rb b/lib/ferrum/page.rb index e10d18e4..de82e690 100644 --- a/lib/ferrum/page.rb +++ b/lib/ferrum/page.rb @@ -390,7 +390,8 @@ def prepare_page resize(width: width, height: height) response = command("Page.getNavigationHistory") - return unless response.dig("entries", 0, "transitionType") != "typed" + transition_type = response.dig("entries", 0, "transitionType") + return if transition_type == "auto_toplevel" # If we create page by clicking links, submitting forms and so on it # opens a new window for which `frameStoppedLoading` event never diff --git a/spec/browser_spec.rb b/spec/browser_spec.rb index 4a2370ec..ff6c6e46 100644 --- a/spec/browser_spec.rb +++ b/spec/browser_spec.rb @@ -181,7 +181,15 @@ proxy: { host: proxy.host, port: proxy.port, user: "u1", password: "p1" } ) - browser.go_to("https://example.com") + if browser.headless_new? + expect { browser.go_to("https://example.com") }.to raise_error( + Ferrum::StatusError, + "Request to https://example.com failed (net::ERR_HTTP_RESPONSE_CODE_FAILURE)" + ) + else + browser.go_to("https://example.com") + end + expect(browser.network.status).to eq(407) ensure browser&.quit @@ -234,6 +242,9 @@ let(:save_path) { "/tmp/ferrum" } it "saves an attachment" do + # Also https://github.com/puppeteer/puppeteer/issues/10161 + skip "https://bugs.chromium.org/p/chromium/issues/detail?id=1444729" if browser.headless_new? + browser.go_to("/#{filename}") expect(File.exist?("#{save_path}/#{filename}")).to be true @@ -531,7 +542,15 @@ it "fails with wrong password" do page = browser.create_page(proxy: { host: proxy.host, port: proxy.port, user: options[:user], password: "$$" }) - page.go_to("https://example.com") + + if browser.headless_new? + expect { page.go_to("https://example.com") }.to raise_error( + Ferrum::StatusError, + "Request to https://example.com failed (net::ERR_HTTP_RESPONSE_CODE_FAILURE)" + ) + else + page.go_to("https://example.com") + end expect(page.network.status).to eq(407) end diff --git a/spec/frame_spec.rb b/spec/frame_spec.rb index 247c92f3..b29bed66 100644 --- a/spec/frame_spec.rb +++ b/spec/frame_spec.rb @@ -3,33 +3,33 @@ describe Ferrum::Frame do describe "#at_xpath" do it "works correctly when JSON is overwritten" do - browser.go_to("/ferrum/index") - browser.execute("JSON = {};") - expect { browser.at_xpath("//a[text() = 'JS redirect']") }.not_to raise_error + page.go_to("/ferrum/index") + page.execute("JSON = {};") + expect { page.at_xpath("//a[text() = 'JS redirect']") }.not_to raise_error end end it "supports selection by index" do - browser.go_to("/ferrum/frames") - frame = browser.at_xpath("//iframe").frame + page.go_to("/ferrum/frames") + frame = page.at_xpath("//iframe").frame expect(frame.url).to end_with("/ferrum/slow") end it "supports selection by element" do - browser.go_to("/ferrum/frames") - frame = browser.at_css("iframe[name]").frame + page.go_to("/ferrum/frames") + frame = page.at_css("iframe[name]").frame expect(frame.url).to end_with("/ferrum/slow") end it "supports selection by element without name or id" do - browser.go_to("/ferrum/frames") - frame = browser.at_css("iframe:not([name]):not([id])").frame + page.go_to("/ferrum/frames") + frame = page.at_css("iframe:not([name]):not([id])").frame expect(frame.url).to end_with("/ferrum/headers") end it "supports selection by element with id but no name" do - browser.go_to("/ferrum/frames") - frame = browser.at_css("iframe[id]:not([name])").frame + page.go_to("/ferrum/frames") + frame = page.at_css("iframe[id]:not([name])").frame expect(frame.url).to end_with("/ferrum/get_cookie") end @@ -44,42 +44,42 @@ end it "waits for the frame to load" do - browser.go_to - browser.execute <<-JS + page.go_to + page.execute <<-JS document.body.innerHTML += "