diff --git a/CHANGELOG.md b/CHANGELOG.md index 01ce6578..e78ea435 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ `429 Too Many Requests` for a full remote browser service, and exposes it as `#status` ### Fixed +- `workers` skips detached workers, `target.worker` with no session raises `NoSuchTargetError` instead of a CDP error - `attach_target` reuses a target's session and returns the target, instead of opening one more that reruns extensions - Service workers registered by a page stayed attached and alive for the browser's lifetime, now Chrome can stop them - An out-of-process iframe kept its parent page's session when `Target.targetCreated` came before diff --git a/lib/ferrum/context.rb b/lib/ferrum/context.rb index 94b0cbd0..c9fe0e6b 100644 --- a/lib/ferrum/context.rb +++ b/lib/ferrum/context.rb @@ -56,11 +56,12 @@ def pages @targets.values.select(&:page?).map(&:page) end - # Dedicated and shared workers spawned by any page in this context. + # Dedicated and shared workers spawned by any page in this context, + # leaving out the ones we have no session with, e.g. detached ones. # # @return [Array] def workers - @targets.values.select { |t| t.worker? || t.shared_worker? }.map(&:worker) + @targets.values.select { |t| (t.worker? || t.shared_worker?) && t.session_id }.map(&:worker) end # Service worker targets registered in this context. Unlike {#workers}, diff --git a/lib/ferrum/target.rb b/lib/ferrum/target.rb index 5fb97fc1..882998b9 100644 --- a/lib/ferrum/target.rb +++ b/lib/ferrum/target.rb @@ -55,6 +55,9 @@ def page # Connects to and returns this target's {Worker}. # # @return [Worker] + # + # @raise [NoSuchTargetError] if there's no session with the target, e.g. a service worker + # not attached to with {Context#attach_target}. def worker @worker ||= build_worker end @@ -82,6 +85,10 @@ def build_page(**options) # # @return [Worker] def build_worker + if options.flatten && session_id.nil? + raise NoSuchTargetError, "No session with #{type} #{id}, attach to it with Context#attach_target first" + end + Worker.new(client, target_id: id, url: url) end diff --git a/spec/worker_spec.rb b/spec/worker_spec.rb index beb08493..83a4998f 100644 --- a/spec/worker_spec.rb +++ b/spec/worker_spec.rb @@ -50,6 +50,23 @@ def wait_for_target(&block) wait_for { worker.console.map(&:text) }.to eq(["from worker"]) expect(worker.console.first).to be_warning end + + it "is left out of Context#workers once detached" do + page.go_to + + page.execute <<~JS + new Worker(URL.createObjectURL(new Blob( + ["self.onmessage = () => {}"], + { type: "application/javascript" } + ))) + JS + + target = wait_for_target(&:worker?) + wait_for { browser.workers }.to contain_exactly(target.worker) + page.command("Target.detachFromTarget", sessionId: target.session_id) + + wait_for { browser.workers }.to be_empty + end end describe "shared workers" do @@ -95,6 +112,15 @@ def wait_for_target(&block) expect(target.session_id).to be_nil end + it "raises when connected to without attaching first" do + page.go_to + page.evaluate_async(%(navigator.serviceWorker.register("/sw.js").then(arguments[0])), 5) + target = wait_for_target(&:service_worker?) + wait_for { target.session_id }.to be_nil + + expect { target.worker }.to raise_error(Ferrum::NoSuchTargetError, /attach_target/) + end + it "connects on demand through Context#attach_target, keeping it alive" do page.go_to page.evaluate_async(%(navigator.serviceWorker.register("/sw.js").then(arguments[0])), 5)