diff --git a/CHANGELOG.md b/CHANGELOG.md index a3206ce8..bfd8415c 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 +- 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 `Target.attachedToTarget`, so commands sent through it reached the page [#663] - `:extensions` run before the document's own scripts in popups and out-of-process iframes too. Popups used to get diff --git a/lib/ferrum/contexts.rb b/lib/ferrum/contexts.rb index 33138fb7..3cf79c85 100644 --- a/lib/ferrum/contexts.rb +++ b/lib/ferrum/contexts.rb @@ -159,14 +159,14 @@ def subscribe def subscribe_attached_target(client) client.on("Target.attachedToTarget") do |params| info, session_id = params.values_at("targetInfo", "sessionId") - next detach_untracked(session_id) unless ALLOWED_TARGET_TYPES.include?(info["type"]) + next detach_untracked(session_id, client) unless ALLOWED_TARGET_TYPES.include?(info["type"]) context_id = info["browserContextId"] add_context(context_id) target = @contexts[context_id]&.add_target(session_id: session_id, params: info, attached: true) rearm_auto_attach(session_id, info["type"]) - handle_attach(target, session_id, params) + handle_attach(target, session_id, params, client) end end @@ -218,11 +218,11 @@ def rearm_auto_attach(session_id, type) subscribe_attached_target(client) end - def handle_attach(target, session_id, params) + def handle_attach(target, session_id, params, parent) return unless target if target.service_worker? - detach_unless_manually_attached(target, session_id) + detach_unless_manually_attached(target, session_id, parent) elsif target.worker? || target.shared_worker? connect_worker(target) else @@ -247,21 +247,17 @@ def add_extensions_and_resume(session_id, waiting) # we don't track: Chrome's own `browser_ui` targets, for instance. We # never resume those, so left alone they stay attached and paused for # the lifetime of the browser. Resume them and drop the session. - def detach_untracked(session_id) - detach(session_id) - rescue BrowserError, TimeoutError - nil + def detach_untracked(session_id, parent) + silent_detach(session_id, parent) end # Attaching keeps a service worker alive forever, so unless the caller # explicitly asked to connect to it (via Context#attach_target), we # just resume it and let go. - def detach_unless_manually_attached(target, session_id) + def detach_unless_manually_attached(target, session_id, parent) return if @manually_attached.delete(target.id) - detach(session_id) - rescue BrowserError, TimeoutError - nil + silent_detach(session_id, parent) end # Workers have no events to notify us when they're ready, so we @@ -277,9 +273,13 @@ def resume(session_id) @client.session(session_id).command("Runtime.runIfWaitingForDebugger", async: true) end - def detach(session_id) - resume(session_id) - @client.command("Target.detachFromTarget", sessionId: session_id) + def silent_detach(session_id, parent) + Utils::Thread.spawn do + @client.session(session_id).command("Runtime.runIfWaitingForDebugger") + parent.command("Target.detachFromTarget", sessionId: session_id) + rescue Error + nil + end end def discover diff --git a/sig/ferrum/contexts.rbs b/sig/ferrum/contexts.rbs index 3da8c009..7e5a82ad 100644 --- a/sig/ferrum/contexts.rbs +++ b/sig/ferrum/contexts.rbs @@ -47,11 +47,11 @@ module Ferrum def rearm_auto_attach: (String session_id, String type) -> void - def handle_attach: (Target? target, String session_id, Hash[String, untyped] params) -> void + def handle_attach: (Target? target, String session_id, Hash[String, untyped] params, (Client | SessionClient) parent) -> void - def detach_untracked: (String session_id) -> void + def detach_untracked: (String session_id, (Client | SessionClient) parent) -> void - def detach_unless_manually_attached: (Target target, String session_id) -> void + def detach_unless_manually_attached: (Target target, String session_id, (Client | SessionClient) parent) -> void def connect_worker: (Target target) -> void @@ -59,7 +59,7 @@ module Ferrum def resume: (String session_id) -> void - def detach: (String session_id) -> void + def silent_detach: (String session_id, (Client | SessionClient) parent) -> Thread def discover: () -> void diff --git a/spec/worker_spec.rb b/spec/worker_spec.rb index 9c96fa91..26e3cce1 100644 --- a/spec/worker_spec.rb +++ b/spec/worker_spec.rb @@ -83,6 +83,17 @@ def wait_for_target(&block) expect(browser.service_workers).to contain_exactly(target) end + it "detaches from them so Chrome can terminate them" do + page.go_to + page.evaluate_async(%(navigator.serviceWorker.register("/sw.js").then(arguments[0])), 5) + wait_for_target(&:service_worker?) + + wait_for do + browser.client.command("Target.getTargets")["targetInfos"] + .select { |info| info["type"] == "service_worker" }.map { |info| info["attached"] } + end.to eq([false]) + 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)