Affects: cuprite 0.18 with ferrum 0.18.0
Works in: cuprite 0.17 with ferrum 0.17.2
Summary
Any page that spawns a dedicated web worker now reports extra window handles. window_opened_by raises Capybara::WindowError because it sees the worker target appear alongside the real tab:
block passed to #window_opened_by opened 2 windows instead of 1
Ferrum 0.18.0 added worker support (#613) and broadened the tracked target types:
# ferrum-0.18.0/lib/ferrum/contexts.rb:14
ALLOWED_TARGET_TYPES = %w[page iframe worker shared_worker service_worker].freeze
Those all land in the same Context#targets map. Cuprite's window_handles returns that map's keys unfiltered, so workers (and, by the same path, OOPIF iframes and service workers) are now treated as browser windows:
# cuprite-0.18/lib/capybara/cuprite/browser.rb:111
def window_handles
targets.keys
end
Reproduction
# frozen_string_literal: true
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "capybara"
gem "cuprite", ENV.fetch("CUPRITE_VERSION", "0.18")
gem "ferrum", ENV.fetch("FERRUM_VERSION", "0.18.0")
gem "puma"
end
require "capybara"
require "capybara/cuprite"
def page_with_worker(body)
<<~HTML
<!doctype html>
<html><body>
#{body}
<script>
const blob = new Blob(["self.onmessage = () => {}"], { type: "text/javascript" })
new Worker(URL.createObjectURL(blob))
</script>
</body></html>
HTML
end
APP = lambda do |env|
body = if env["PATH_INFO"] == "/other"
page_with_worker("<h1>Other page</h1>")
else
page_with_worker('<a href="/other" target="_blank">Open tab</a>')
end
[200, { "content-type" => "text/html" }, [body]]
end
Capybara.register_driver(:cuprite) { |app| Capybara::Cuprite::Driver.new(app, headless: true) }
Capybara.server = :puma, { Silent: true }
session = Capybara::Session.new(:cuprite, APP)
session.visit "/"
puts "cuprite #{Gem.loaded_specs['cuprite'].version} ferrum #{Gem.loaded_specs['ferrum'].version}"
puts "window_handles before click: #{session.driver.browser.window_handles.size}"
begin
new_window = session.window_opened_by { session.click_link "Open tab" }
session.within_window(new_window) { puts "RESULT: ok" }
rescue Capybara::WindowError => e
puts "RESULT: #{e.class}: #{e.message}"
end
puts "window_handles after click: #{session.driver.browser.window_handles.size}"
session.driver.browser.targets.each_value { |t| puts " type=#{t.type.inspect} url=#{t.url.inspect}" }
session.driver.quit
cuprite 0.18 / ferrum 0.18.0 — fails
window_handles before click: 2
RESULT: Capybara::WindowError: block passed to #window_opened_by opened 2 windows instead of 1
window_handles after click: 4
type="page" url="http://127.0.0.1:51927/"
type="worker" url="blob:http://127.0.0.1:51927/1df799cf-..."
type="page" url="http://127.0.0.1:51927/other"
type="worker" url="blob:http://127.0.0.1:51927/b530c457-..."
cuprite 0.17 / ferrum 0.17.2 — passes
CUPRITE_VERSION=0.17 FERRUM_VERSION=0.17.2 ruby repro.rb
window_handles before click: 1
RESULT: ok
window_handles after click: 2
type="page" url="http://127.0.0.1:51942/"
type="page" url="http://127.0.0.1:51942/other"
Expected
window_handles returns only page targets, so window_opened_by sees exactly the one tab the block opened, as it did in 0.17.
Affects: cuprite 0.18 with ferrum 0.18.0
Works in: cuprite 0.17 with ferrum 0.17.2
Summary
Any page that spawns a dedicated web worker now reports extra window handles.
window_opened_byraisesCapybara::WindowErrorbecause it sees the worker target appear alongside the real tab:Ferrum 0.18.0 added worker support (#613) and broadened the tracked target types:
Those all land in the same
Context#targetsmap. Cuprite'swindow_handlesreturns that map's keys unfiltered, so workers (and, by the same path, OOPIF iframes and service workers) are now treated as browser windows:Reproduction
cuprite 0.18 / ferrum 0.18.0 — fails
cuprite 0.17 / ferrum 0.17.2 — passes
Expected
window_handlesreturns only page targets, sowindow_opened_bysees exactly the one tab the block opened, as it did in 0.17.