Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 15 additions & 15 deletions lib/ferrum/contexts.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
8 changes: 4 additions & 4 deletions sig/ferrum/contexts.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,19 @@ 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

def add_extensions_and_resume: (String session_id, bool? waiting) -> void

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

Expand Down
11 changes: 11 additions & 0 deletions spec/worker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading