puppeteer-ruby 0.50.0.alpha8 → 0.50.0.alpha10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc07137db57af2ee149486e37d5ee9268baf3f928cd2e51e8e2bfb733bf3aaf3
4
- data.tar.gz: 36f6b3d362e99c2c5b4a52a2039264b078f605e0ba249acbf8116b1706ec724a
3
+ metadata.gz: 6ad92e4a471db943a70ea1f6e928ba29e611cba0fc4b62b001bafb0cf444c797
4
+ data.tar.gz: 226103f1934651f038099e58a7b94414e248db9290f928c4fc2d4b262df59807
5
5
  SHA512:
6
- metadata.gz: 1e89b2eed4753830edb5dcd8c54e6d923a24fd802c2e57bf4c7c92ebf9e5e620d4fda0a1096511177e5ba689785aa0ff8d56d2c024d8469bd75a729df738e8fe
7
- data.tar.gz: 32cd0c2eb2e677c661b74801d6922a3876a3454557f0d12f7468708951f44e789404d389d041dfed17cada032101e5b4efc513fb9cf41d93ee1a6b130975b4b9
6
+ metadata.gz: 1ba290b15ba557824715b8aa82ad8695cdc9d08200381c7e004cb3c3fe63e041bc732e5e27f904e6bf75ff4eef51e6fdb905972d4225cfa495bbde9637a5f2ac
7
+ data.tar.gz: c7a208b38765265b77cef6f81dc1945daa6b4f4c09957c0b9b8e20484630a20a0359bbd3bbde26d5d88c1b55ea5c17cb02d522e358e294aa3db814685559a736
data/docs/api_coverage.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # API coverages
2
2
  - Puppeteer version: v24.35.0
3
- - puppeteer-ruby version: 0.50.0.alpha4
3
+ - puppeteer-ruby version: 0.50.0.alpha10
4
4
 
5
5
  ## Puppeteer
6
6
 
@@ -1,4 +1,5 @@
1
1
  require 'json'
2
+ require 'async/queue'
2
3
 
3
4
  class Puppeteer::Connection
4
5
  include Puppeteer::DebugPrint
@@ -44,11 +45,16 @@ class Puppeteer::Connection
44
45
  @delay = delay
45
46
  @protocol_timeout = protocol_timeout
46
47
 
48
+ @network_message_queue = Async::Queue.new
49
+ @network_message_task = nil
50
+
47
51
  @transport = transport
48
52
  @transport.on_message do |data|
49
53
  message = JSON.parse(data)
50
54
  sleep_before_handling_message(message)
51
- if should_handle_synchronously?(message)
55
+ if network_event_message?(message)
56
+ enqueue_network_message(message)
57
+ elsif should_handle_synchronously?(message)
52
58
  handle_message(message)
53
59
  else
54
60
  async_handle_message(message)
@@ -87,7 +93,8 @@ class Puppeteer::Connection
87
93
  when nil
88
94
  false
89
95
  when /^Network\./
90
- true
96
+ # Network events are queued for ordered async processing.
97
+ false
91
98
  when /^Page\.frame/
92
99
  # Page.frameAttached
93
100
  # Page.frameNavigated
@@ -112,6 +119,34 @@ class Puppeteer::Connection
112
119
  end
113
120
  end
114
121
 
122
+ private def network_event_message?(message)
123
+ message['method']&.start_with?('Network.')
124
+ end
125
+
126
+ private def ensure_network_message_task
127
+ return if @network_message_task&.alive?
128
+ return unless Async::Task.current?
129
+
130
+ @network_message_task = Async do
131
+ while (queued = @network_message_queue.dequeue)
132
+ handle_message(queued)
133
+ end
134
+ end
135
+ end
136
+
137
+ private def enqueue_network_message(message)
138
+ if Async::Task.current?
139
+ ensure_network_message_task
140
+ begin
141
+ @network_message_queue.enqueue(message)
142
+ rescue Async::Queue::ClosedError
143
+ # Connection is closing; ignore late network events.
144
+ end
145
+ else
146
+ handle_message(message)
147
+ end
148
+ end
149
+
115
150
  def self.from_session(session)
116
151
  session.connection
117
152
  end
@@ -310,6 +345,12 @@ class Puppeteer::Connection
310
345
  @closed = true
311
346
  @transport.on_message
312
347
  @transport.on_close
348
+ @network_message_queue.close
349
+ begin
350
+ @network_message_task&.stop
351
+ rescue Async::Stop
352
+ # Task already stopping; ignore.
353
+ end
313
354
  callbacks = @callbacks_mutex.synchronize do
314
355
  @callbacks.values.tap { @callbacks.clear }
315
356
  end
@@ -34,32 +34,49 @@ class Puppeteer::FrameManager
34
34
 
35
35
  # Keeps track of frames that are in the process of being attached in #onFrameAttached.
36
36
  @frames_pending_attachment = {}
37
+ @frame_tree_handled_promise = nil
38
+ @queued_frame_events = []
39
+ @frame_tree_mutex = Mutex.new
37
40
 
38
41
  setup_listeners(@client)
39
42
  end
40
43
 
41
44
  private def setup_listeners(client)
42
45
  client.on_event('Page.frameAttached') do |event|
43
- handle_frame_attached(client, event['frameId'], event['parentFrameId'])
46
+ with_frame_tree_handled do
47
+ handle_frame_attached(client, event['frameId'], event['parentFrameId'])
48
+ end
44
49
  end
45
50
  client.on_event('Page.frameNavigated') do |event|
46
- @frame_naviigated_received << event['frame']['id']
47
- handle_frame_navigated(event['frame'])
51
+ with_frame_tree_handled do
52
+ @frame_naviigated_received << event['frame']['id']
53
+ handle_frame_navigated(event['frame'])
54
+ end
48
55
  end
49
56
  client.on_event('Page.navigatedWithinDocument') do |event|
50
- handle_frame_navigated_within_document(event['frameId'], event['url'])
57
+ with_frame_tree_handled do
58
+ handle_frame_navigated_within_document(event['frameId'], event['url'])
59
+ end
51
60
  end
52
61
  client.on_event('Page.frameDetached') do |event|
53
- handle_frame_detached(event['frameId'], event['reason'])
62
+ with_frame_tree_handled do
63
+ handle_frame_detached(event['frameId'], event['reason'])
64
+ end
54
65
  end
55
66
  client.on_event('Page.frameStartedLoading') do |event|
56
- handle_frame_started_loading(event['frameId'])
67
+ with_frame_tree_handled do
68
+ handle_frame_started_loading(event['frameId'])
69
+ end
57
70
  end
58
71
  client.on_event('Page.frameStoppedLoading') do |event|
59
- handle_frame_stopped_loading(event['frameId'])
72
+ with_frame_tree_handled do
73
+ handle_frame_stopped_loading(event['frameId'])
74
+ end
60
75
  end
61
76
  client.on_event('Runtime.executionContextCreated') do |event|
62
- handle_execution_context_created(event['context'], client)
77
+ with_frame_tree_handled do
78
+ handle_execution_context_created(event['context'], client)
79
+ end
63
80
  end
64
81
  client.on_event('Runtime.executionContextDestroyed') do |event|
65
82
  handle_execution_context_destroyed(event['executionContextId'], client)
@@ -68,7 +85,9 @@ class Puppeteer::FrameManager
68
85
  handle_execution_contexts_cleared(client)
69
86
  end
70
87
  client.on_event('Page.lifecycleEvent') do |event|
71
- handle_lifecycle_event(event)
88
+ with_frame_tree_handled do
89
+ handle_lifecycle_event(event)
90
+ end
72
91
  end
73
92
  end
74
93
 
@@ -78,6 +97,7 @@ class Puppeteer::FrameManager
78
97
  @frames_pending_target_init[target_id] ||= Async::Promise.new
79
98
  client = cdp_session || @client
80
99
 
100
+ prepare_frame_tree_handling
81
101
  promises = [
82
102
  client.async_send_message('Page.enable'),
83
103
  client.async_send_message('Page.getFrameTree'),
@@ -85,6 +105,7 @@ class Puppeteer::FrameManager
85
105
  results = Puppeteer::AsyncUtils.await_promise_all(*promises)
86
106
  frame_tree = results[1]['frameTree']
87
107
  handle_frame_tree(client, frame_tree)
108
+ finish_frame_tree_handling
88
109
  Puppeteer::AsyncUtils.await_promise_all(
89
110
  client.async_send_message('Page.setLifecycleEventsEnabled', enabled: true),
90
111
  client.async_send_message('Runtime.enable'),
@@ -93,10 +114,14 @@ class Puppeteer::FrameManager
93
114
  @network_manager.init unless cdp_session
94
115
  rescue => err
95
116
  # The target might have been closed before the initialization finished.
96
- return if err.message.include?('Target closed') || err.message.include?('Session closed')
117
+ if err.message.include?('Target closed') || err.message.include?('Session closed')
118
+ finish_frame_tree_handling
119
+ return
120
+ end
97
121
 
98
122
  raise
99
123
  ensure
124
+ finish_frame_tree_handling if @frame_tree_handled_promise && !@frame_tree_handled_promise.resolved?
100
125
  @frames_pending_target_init.delete(target_id)&.resolve(nil)
101
126
  end
102
127
 
@@ -220,6 +245,46 @@ class Puppeteer::FrameManager
220
245
  emit_event(FrameManagerEmittedEvents::LifecycleEvent, frame)
221
246
  end
222
247
 
248
+ private def with_frame_tree_handled(&block)
249
+ promise = @frame_tree_handled_promise
250
+ if promise && !promise.resolved?
251
+ queued = false
252
+ @frame_tree_mutex.synchronize do
253
+ promise = @frame_tree_handled_promise
254
+ if promise && !promise.resolved?
255
+ @queued_frame_events << block
256
+ queued = true
257
+ end
258
+ end
259
+ return if queued
260
+ end
261
+ block.call
262
+ end
263
+
264
+ private def prepare_frame_tree_handling
265
+ queued = nil
266
+ @frame_tree_mutex.synchronize do
267
+ if @frame_tree_handled_promise && !@frame_tree_handled_promise.resolved?
268
+ @frame_tree_handled_promise.resolve(nil)
269
+ queued = @queued_frame_events
270
+ end
271
+ @queued_frame_events = []
272
+ @frame_tree_handled_promise = Async::Promise.new
273
+ end
274
+ queued&.each(&:call)
275
+ end
276
+
277
+ private def finish_frame_tree_handling
278
+ queued = nil
279
+ @frame_tree_mutex.synchronize do
280
+ return unless @frame_tree_handled_promise
281
+ @frame_tree_handled_promise.resolve(nil) unless @frame_tree_handled_promise.resolved?
282
+ queued = @queued_frame_events
283
+ @queued_frame_events = []
284
+ end
285
+ queued&.each(&:call)
286
+ end
287
+
223
288
  # @param frame_id [String]
224
289
  def handle_frame_started_loading(frame_id)
225
290
  frame = @frames[frame_id]
@@ -352,8 +417,11 @@ class Puppeteer::FrameManager
352
417
  if is_main_frame
353
418
  if frame
354
419
  # Update frame id to retain frame identity on cross-process navigation.
355
- @frames.delete(frame.id)
356
- frame.id = frame_id
420
+ if frame.id != frame_id
421
+ old_frame_id = frame.id
422
+ @frames.delete(old_frame_id)
423
+ frame.id = frame_id
424
+ end
357
425
  else
358
426
  # Initial main frame navigation.
359
427
  frame = Puppeteer::Frame.new(self, nil, frame_id, @client)
@@ -1,3 +1,3 @@
1
1
  module Puppeteer
2
- VERSION = '0.50.0.alpha8'
2
+ VERSION = '0.50.0.alpha10'
3
3
  end
@@ -178,6 +178,9 @@ class Puppeteer::FrameManager
178
178
 
179
179
  def setup_listeners: (Puppeteer::CDPSession client) -> void
180
180
  def init: (String target_id, ?Puppeteer::CDPSession cdp_session) -> void
181
+ def with_frame_tree_handled: () { () -> void } -> void
182
+ def prepare_frame_tree_handling: () -> void
183
+ def finish_frame_tree_handling: () -> void
181
184
  def attach_child_frame: (Puppeteer::Frame parent_frame, String parent_frame_id, String frame_id, Puppeteer::CDPSession? session) -> void
182
185
  def reattach_frame: (Puppeteer::Frame frame, String frame_id, bool is_main_frame, Hash[String, untyped] frame_payload) -> void
183
186
  def ensure_isolated_world: (Puppeteer::CDPSession session, String name) -> void
@@ -286,6 +289,9 @@ class Puppeteer::Connection
286
289
 
287
290
  def sleep_before_handling_message: (Hash[String, untyped] message) -> void
288
291
  def should_handle_synchronously?: (Hash[String, untyped] message) -> bool
292
+ def network_event_message?: (Hash[String, untyped] message) -> bool
293
+ def ensure_network_message_task: () -> void
294
+ def enqueue_network_message: (Hash[String, untyped] message) -> void
289
295
  def request_debug_printer: () -> untyped
290
296
  def response_debug_printer: () -> untyped
291
297
  def handle_message: (Hash[String, untyped] message) -> void
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppeteer-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.50.0.alpha8
4
+ version: 0.50.0.alpha10
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-01-24 00:00:00.000000000 Z
11
+ date: 2026-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async