puppeteer-bidi 0.0.5 → 0.0.6

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/API_COVERAGE.md +26 -9
  3. data/lib/puppeteer/bidi/async_utils.rb +9 -4
  4. data/lib/puppeteer/bidi/browser.rb +28 -13
  5. data/lib/puppeteer/bidi/browser_context.rb +36 -1
  6. data/lib/puppeteer/bidi/console_message.rb +25 -0
  7. data/lib/puppeteer/bidi/cookie_utils.rb +2 -2
  8. data/lib/puppeteer/bidi/core/README.md +7 -8
  9. data/lib/puppeteer/bidi/core/browser.rb +6 -6
  10. data/lib/puppeteer/bidi/core/browsing_context.rb +66 -42
  11. data/lib/puppeteer/bidi/core/navigation.rb +6 -3
  12. data/lib/puppeteer/bidi/core/realm.rb +16 -6
  13. data/lib/puppeteer/bidi/core/request.rb +2 -2
  14. data/lib/puppeteer/bidi/core/session.rb +4 -3
  15. data/lib/puppeteer/bidi/core/user_context.rb +7 -7
  16. data/lib/puppeteer/bidi/core/user_prompt.rb +4 -4
  17. data/lib/puppeteer/bidi/frame.rb +106 -31
  18. data/lib/puppeteer/bidi/http_response.rb +8 -2
  19. data/lib/puppeteer/bidi/http_utils.rb +20 -0
  20. data/lib/puppeteer/bidi/injected.js +1 -1
  21. data/lib/puppeteer/bidi/injected_source.rb +3 -3
  22. data/lib/puppeteer/bidi/js_handle.rb +11 -71
  23. data/lib/puppeteer/bidi/locator.rb +97 -49
  24. data/lib/puppeteer/bidi/page.rb +20 -6
  25. data/lib/puppeteer/bidi/realm.rb +4 -3
  26. data/lib/puppeteer/bidi/target.rb +16 -1
  27. data/lib/puppeteer/bidi/version.rb +2 -2
  28. data/lib/puppeteer/bidi.rb +2 -0
  29. data/scripts/update_injected_source.rb +12 -7
  30. data/sig/puppeteer/bidi/browser.rbs +19 -4
  31. data/sig/puppeteer/bidi/browser_context.rbs +14 -0
  32. data/sig/puppeteer/bidi/console_message.rbs +26 -0
  33. data/sig/puppeteer/bidi/core/browsing_context.rbs +11 -0
  34. data/sig/puppeteer/bidi/core/realm.rbs +5 -0
  35. data/sig/puppeteer/bidi/frame.rbs +13 -2
  36. data/sig/puppeteer/bidi/http_utils.rbs +14 -0
  37. data/sig/puppeteer/bidi/injected_source.rbs +2 -2
  38. data/sig/puppeteer/bidi/locator.rbs +5 -2
  39. data/sig/puppeteer/bidi/page.rbs +12 -2
  40. data/sig/puppeteer/bidi/target.rbs +9 -0
  41. metadata +5 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93bdca661a74b8ebba846818445d6e319c829fcbd16de7a40319350177a9b058
4
- data.tar.gz: 0cd0a68461ca4e9546a2f8ef653e87a1caf0c380e65cc653e6d1bf000f23ec91
3
+ metadata.gz: 8321a46efce20f517001a8cf7e19d849120dc7139ab3ee324d1b70eb26c9e5de
4
+ data.tar.gz: 3b2fac88bdaf8248396608e68cf4302547cde9364ec93288a69c14293ad4019f
5
5
  SHA512:
6
- metadata.gz: c38900fa8c8af9996c38b91f1a5cefc63e2797c609ef57b240877f04f907a78287df9ed261114cfdac0f0fb0e6033261fdc644df01b2f61dbca3ee287a10ab11
7
- data.tar.gz: caf3af388d8a7fa1c3e178cbd326fda3797cc0c4b3ed765952c79472c2c4fe73fbc50618860d7b23f819a21b8ea483df5d800b198507d812fab18fdfd4a7fa04
6
+ metadata.gz: 70abd79d8bc6208fd115ad7324eece4c942e0b697ec5ca1a1fa2dc0eba5721d3abb933cde32df0bfb36dad22fcc86e59ebc2ecb16bb5a03f39dc97a1870976fa
7
+ data.tar.gz: 346f958a1b13fa51a0b0387230274f55f0f1f0f8e403605649ba41b29226a4d3af5a4a5e59ce1cd06d1a0e0468dea795e9ce99e9b7b022d978fd588db14ec512
data/API_COVERAGE.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # API Coverage
2
2
 
3
- - Puppeteer commit: `5dc0f3c7e67da8d4f965795dd2bd64396e00d6fd`
3
+ - Puppeteer version: `25.3.0`
4
+ - Puppeteer commit: `58d9b023ed74c3a1f14ec5fd9315872602270c72`
4
5
  - Generated by: `development/generate_api_coverage.rb`
5
- - Coverage: `188/268` (`70.15%`)
6
+ - Coverage: `195/280` (`69.64%`)
6
7
 
7
8
  ## Browser (Puppeteer::Bidi::Browser)
8
9
 
@@ -17,19 +18,19 @@
17
18
  | `Browser.deleteCookie` | `Puppeteer::Bidi::Browser#delete_cookie` | ✅ |
18
19
  | `Browser.deleteMatchingCookies` | `Puppeteer::Bidi::Browser#delete_matching_cookies` | ✅ |
19
20
  | `Browser.disconnect` | `Puppeteer::Bidi::Browser#disconnect` | ✅ |
21
+ | `Browser.extensions` | `Puppeteer::Bidi::Browser#extensions` | ❌ |
20
22
  | `Browser.getWindowBounds` | `Puppeteer::Bidi::Browser#get_window_bounds` | ✅ |
21
23
  | `Browser.installExtension` | `Puppeteer::Bidi::Browser#install_extension` | ❌ |
22
- | `Browser.isConnected` | `Puppeteer::Bidi::Browser#is_connected` | ❌ |
23
24
  | `Browser.newPage` | `Puppeteer::Bidi::Browser#new_page` | ✅ |
24
25
  | `Browser.pages` | `Puppeteer::Bidi::Browser#pages` | ✅ |
25
26
  | `Browser.process` | `Puppeteer::Bidi::Browser#process` | ✅ |
26
27
  | `Browser.removeScreen` | `Puppeteer::Bidi::Browser#remove_screen` | ❌ |
27
28
  | `Browser.screens` | `Puppeteer::Bidi::Browser#screens` | ❌ |
28
29
  | `Browser.setCookie` | `Puppeteer::Bidi::Browser#set_cookie` | ✅ |
29
- | `Browser.setPermission` | `Puppeteer::Bidi::Browser#set_permission` | |
30
+ | `Browser.setPermission` | `Puppeteer::Bidi::Browser#set_permission` | |
30
31
  | `Browser.setWindowBounds` | `Puppeteer::Bidi::Browser#set_window_bounds` | ✅ |
31
- | `Browser.target` | `Puppeteer::Bidi::Browser#target` | |
32
- | `Browser.targets` | `Puppeteer::Bidi::Browser#targets` | |
32
+ | `Browser.target` | `Puppeteer::Bidi::Browser#target` | |
33
+ | `Browser.targets` | `Puppeteer::Bidi::Browser#targets` | |
33
34
  | `Browser.uninstallExtension` | `Puppeteer::Bidi::Browser#uninstall_extension` | ❌ |
34
35
  | `Browser.userAgent` | `Puppeteer::Bidi::Browser#user_agent` | ✅ |
35
36
  | `Browser.version` | `Puppeteer::Bidi::Browser#version` | ❌ |
@@ -51,8 +52,8 @@
51
52
  | `BrowserContext.pages` | `Puppeteer::Bidi::BrowserContext#pages` | ✅ |
52
53
  | `BrowserContext.setCookie` | `Puppeteer::Bidi::BrowserContext#set_cookie` | ✅ |
53
54
  | `BrowserContext.setPermission` | `Puppeteer::Bidi::BrowserContext#set_permission` | ✅ |
54
- | `BrowserContext.targets` | `Puppeteer::Bidi::BrowserContext#targets` | |
55
- | `BrowserContext.waitForTarget` | `Puppeteer::Bidi::BrowserContext#wait_for_target` | |
55
+ | `BrowserContext.targets` | `Puppeteer::Bidi::BrowserContext#targets` | |
56
+ | `BrowserContext.waitForTarget` | `Puppeteer::Bidi::BrowserContext#wait_for_target` | |
56
57
 
57
58
  ## ElementHandle (Puppeteer::Bidi::ElementHandle)
58
59
 
@@ -116,6 +117,7 @@
116
117
  | `Frame.content` | `Puppeteer::Bidi::Frame#content` | ✅ |
117
118
  | `Frame.evaluate` | `Puppeteer::Bidi::Frame#evaluate` | ✅ |
118
119
  | `Frame.evaluateHandle` | `Puppeteer::Bidi::Frame#evaluate_handle` | ✅ |
120
+ | `Frame.extensionRealms` | `Puppeteer::Bidi::Frame#extension_realms` | ❌ |
119
121
  | `Frame.focus` | `Puppeteer::Bidi::Frame#focus` | ❌ |
120
122
  | `Frame.frameElement` | `Puppeteer::Bidi::Frame#frame_element` | ✅ |
121
123
  | `Frame.goto` | `Puppeteer::Bidi::Frame#goto` | ✅ |
@@ -250,6 +252,7 @@
250
252
  | `Page.emulateCPUThrottling` | `Puppeteer::Bidi::Page#emulate_cpu_throttling` | ❌ |
251
253
  | `Page.emulateFocusedPage` | `Puppeteer::Bidi::Page#emulate_focused_page` | ❌ |
252
254
  | `Page.emulateIdleState` | `Puppeteer::Bidi::Page#emulate_idle_state` | ❌ |
255
+ | `Page.emulateLocale` | `Puppeteer::Bidi::Page#emulate_locale` | ✅ |
253
256
  | `Page.emulateMediaFeatures` | `Puppeteer::Bidi::Page#emulate_media_features` | ❌ |
254
257
  | `Page.emulateMediaType` | `Puppeteer::Bidi::Page#emulate_media_type` | ❌ |
255
258
  | `Page.emulateNetworkConditions` | `Puppeteer::Bidi::Page#emulate_network_conditions` | ❌ |
@@ -259,6 +262,7 @@
259
262
  | `Page.evaluateHandle` | `Puppeteer::Bidi::Page#evaluate_handle` | ✅ |
260
263
  | `Page.evaluateOnNewDocument` | `Puppeteer::Bidi::Page#evaluate_on_new_document` | ✅ |
261
264
  | `Page.exposeFunction` | `Puppeteer::Bidi::Page#expose_function` | ✅ |
265
+ | `Page.extensionRealms` | `Puppeteer::Bidi::Page#extension_realms` | ❌ |
262
266
  | `Page.focus` | `Puppeteer::Bidi::Page#focus` | ✅ |
263
267
  | `Page.frames` | `Puppeteer::Bidi::Page#frames` | ✅ |
264
268
  | `Page.getDefaultNavigationTimeout` | `Puppeteer::Bidi::Page#get_default_navigation_timeout` | ✅ |
@@ -266,6 +270,7 @@
266
270
  | `Page.goBack` | `Puppeteer::Bidi::Page#go_back` | ✅ |
267
271
  | `Page.goForward` | `Puppeteer::Bidi::Page#go_forward` | ✅ |
268
272
  | `Page.goto` | `Puppeteer::Bidi::Page#goto` | ✅ |
273
+ | `Page.hasDevTools` | `Puppeteer::Bidi::Page#has_dev_tools` | ❌ |
269
274
  | `Page.hover` | `Puppeteer::Bidi::Page#hover` | ✅ |
270
275
  | `Page.isClosed` | `Puppeteer::Bidi::Page#closed?` | ✅ |
271
276
  | `Page.isDragInterceptionEnabled` | `Puppeteer::Bidi::Page#is_drag_interception_enabled` | ❌ |
@@ -300,8 +305,9 @@
300
305
  | `Page.setUserAgent` | `Puppeteer::Bidi::Page#set_user_agent` | ✅ |
301
306
  | `Page.setViewport` | `Puppeteer::Bidi::Page#set_viewport` | ✅ |
302
307
  | `Page.tap` | `Puppeteer::Bidi::Page#tap` | ❌ |
303
- | `Page.target` | `Puppeteer::Bidi::Page#target` | |
308
+ | `Page.target` | `Puppeteer::Bidi::Page#target` | |
304
309
  | `Page.title` | `Puppeteer::Bidi::Page#title` | ✅ |
310
+ | `Page.triggerExtensionAction` | `Puppeteer::Bidi::Page#trigger_extension_action` | ❌ |
305
311
  | `Page.type` | `Puppeteer::Bidi::Page#type` | ✅ |
306
312
  | `Page.url` | `Puppeteer::Bidi::Page#url` | ✅ |
307
313
  | `Page.viewport` | `Puppeteer::Bidi::Page#viewport` | ✅ |
@@ -326,9 +332,20 @@
326
332
  | `PuppeteerNode.connect` | `Puppeteer::Bidi.connect` | ✅ |
327
333
  | `Puppeteer.customQueryHandlerNames` | `Puppeteer::Bidi.custom_query_handler_names` | ❌ |
328
334
  | `PuppeteerNode.defaultArgs` | `Puppeteer::Bidi.default_args` | ❌ |
335
+ | `PuppeteerNode.defaultBrowser` | `Puppeteer::Bidi.default_browser` | ❌ |
329
336
  | `PuppeteerNode.executablePath` | `Puppeteer::Bidi.executable_path` | ❌ |
337
+ | `PuppeteerNode.lastLaunchedBrowser` | `Puppeteer::Bidi.last_launched_browser` | ❌ |
330
338
  | `PuppeteerNode.launch` | `Puppeteer::Bidi.launch` | ✅ |
331
339
  | `Puppeteer.registerCustomQueryHandler` | `Puppeteer::Bidi.register_custom_query_handler` | ❌ |
332
340
  | `PuppeteerNode.trimCache` | `Puppeteer::Bidi.trim_cache` | ❌ |
333
341
  | `Puppeteer.unregisterCustomQueryHandler` | `Puppeteer::Bidi.unregister_custom_query_handler` | ❌ |
334
342
 
343
+ ## WebWorker (Puppeteer::Bidi::WebWorker)
344
+
345
+ | Node.js | Ruby | Supported |
346
+ | --- | --- | :---: |
347
+ | `WebWorker.close` | `Puppeteer::Bidi::WebWorker#close` | ❌ |
348
+ | `WebWorker.evaluate` | `Puppeteer::Bidi::WebWorker#evaluate` | ❌ |
349
+ | `WebWorker.evaluateHandle` | `Puppeteer::Bidi::WebWorker#evaluate_handle` | ❌ |
350
+ | `WebWorker.url` | `Puppeteer::Bidi::WebWorker#url` | ❌ |
351
+ | `WebWorker.waitForFunction` | `Puppeteer::Bidi::WebWorker#wait_for_function` | ❌ |
@@ -134,10 +134,15 @@ module Puppeteer
134
134
  end
135
135
  end
136
136
 
137
- # Wait for all tasks to complete
138
- barrier.wait
139
-
140
- results
137
+ begin
138
+ # Wait for all tasks to complete
139
+ barrier.wait
140
+ results
141
+ ensure
142
+ # Promise.all rejects as soon as a task fails. Stop pending siblings so
143
+ # callers that time out do not leave fibers waiting on unresolved promises.
144
+ barrier.stop
145
+ end
141
146
  end
142
147
 
143
148
  def first(*tasks)
@@ -162,6 +162,18 @@ module Puppeteer
162
162
  @default_browser_context.pages
163
163
  end
164
164
 
165
+ # Get all known targets.
166
+ # @rbs return: Array[BrowserTarget | PageTarget | FrameTarget] -- Known targets
167
+ def targets
168
+ each_target.to_a
169
+ end
170
+
171
+ # Get the browser target.
172
+ # @rbs return: BrowserTarget -- Browser target
173
+ def target
174
+ @target ||= BrowserTarget.new(self)
175
+ end
176
+
165
177
  # Get all cookies in the default browser context.
166
178
  # @rbs return: Array[Hash[String, untyped]] -- Cookies
167
179
  def cookies
@@ -192,6 +204,14 @@ module Puppeteer
192
204
  @default_browser_context.delete_matching_cookies(*filters, **filter)
193
205
  end
194
206
 
207
+ # Set permission states in the default browser context.
208
+ # @rbs origin: String | Symbol -- Origin URL (must not be '*')
209
+ # @rbs *permissions: Hash[Symbol | String, untyped] -- Permission descriptors with states
210
+ # @rbs return: void
211
+ def set_permission(origin, *permissions)
212
+ @default_browser_context.set_permission(origin, *permissions)
213
+ end
214
+
195
215
  # Get browser window bounds for a given window ID.
196
216
  # @rbs window_id: String -- Window ID
197
217
  # @rbs return: Hash[Symbol, untyped] -- Window bounds hash
@@ -296,7 +316,7 @@ module Puppeteer
296
316
 
297
317
  # Wait until a target (top-level browsing context) satisfies the predicate.
298
318
  # @rbs timeout: Integer? -- Timeout in milliseconds (default: 30000)
299
- # @rbs &predicate: (BrowserTarget | PageTarget | FrameTarget) -> boolish -- Predicate evaluated against each Target
319
+ # @rbs &predicate: (BrowserTarget | PageTarget | FrameTarget) -> boolish -- Predicate for each Target
300
320
  # @rbs return: BrowserTarget | PageTarget | FrameTarget -- Matching target
301
321
  def wait_for_target(timeout: nil, &predicate)
302
322
  predicate ||= ->(_target) { true }
@@ -341,6 +361,7 @@ module Puppeteer
341
361
  session_events = [
342
362
  :'browsingContext.contextCreated',
343
363
  :'browsingContext.navigationStarted',
364
+ :'browsingContext.navigationCommitted',
344
365
  :'browsingContext.historyUpdated',
345
366
  :'browsingContext.fragmentNavigated',
346
367
  :'browsingContext.domContentLoaded',
@@ -352,11 +373,10 @@ module Puppeteer
352
373
  session_listeners << [event, session_listener]
353
374
  end
354
375
 
355
- browser_disconnect_listener = proc do |data|
376
+ browser_disconnect_listener = proc do |reason|
356
377
  next if promise.resolved?
357
378
 
358
- reason = data[:reason] || 'Browser disconnected'
359
- promise.reject(Core::BrowserDisconnectedError.new(reason))
379
+ promise.reject(Core::BrowserDisconnectedError.new(reason || 'Browser disconnected'))
360
380
  cleanup.call
361
381
  end
362
382
 
@@ -408,13 +428,13 @@ module Puppeteer
408
428
  warn(error.full_message)
409
429
  end
410
430
 
411
- # @rbs &block: (BrowserTarget | PageTarget | FrameTarget) -> void -- Block to yield each target to
412
- # @rbs return: Enumerator[BrowserTarget | PageTarget | FrameTarget, void] -- Enumerator of targets
431
+ # @rbs () -> Enumerator[BrowserTarget | PageTarget | FrameTarget, void]
432
+ # | () { (BrowserTarget | PageTarget | FrameTarget) -> void } -> void
413
433
  def each_target(&block)
414
434
  return enum_for(:each_target) unless block_given?
415
435
  return unless @core_browser
416
436
 
417
- yield BrowserTarget.new(self)
437
+ yield target
418
438
 
419
439
  @core_browser.user_contexts.each do |user_context|
420
440
  next if user_context.disposed?
@@ -422,12 +442,7 @@ module Puppeteer
422
442
  browser_context = browser_context_for(user_context)
423
443
  next unless browser_context
424
444
 
425
- user_context.browsing_contexts.each do |browsing_context|
426
- next if browsing_context.disposed?
427
-
428
- page = browser_context.page_for(browsing_context)
429
- yield PageTarget.new(page) if page
430
- end
445
+ browser_context.targets.each { |target| yield target }
431
446
  end
432
447
  end
433
448
 
@@ -42,6 +42,7 @@ module Puppeteer
42
42
  @browser = browser
43
43
  @user_context = user_context
44
44
  @pages = {}
45
+ @frame_targets = {}
45
46
  @overrides = []
46
47
  end
47
48
 
@@ -80,6 +81,28 @@ module Puppeteer
80
81
  .map { |browsing_context| page_for(browsing_context) }
81
82
  end
82
83
 
84
+ # Get all known targets in this browser context.
85
+ # @rbs return: Array[PageTarget | FrameTarget] -- Known targets
86
+ def targets
87
+ pages.flat_map do |page|
88
+ [page.target] + page.frames.drop(1).map { |frame| target_for_frame(frame) }
89
+ end
90
+ end
91
+
92
+ # Wait until a target in this context satisfies the predicate.
93
+ # @rbs timeout: Integer? -- Timeout in milliseconds (default: 30000)
94
+ # @rbs &predicate: (PageTarget | FrameTarget) -> boolish -- Predicate evaluated against each target
95
+ # @rbs return: PageTarget | FrameTarget -- Matching target
96
+ def wait_for_target(timeout: nil, &predicate)
97
+ predicate ||= ->(_target) { true }
98
+
99
+ browser.wait_for_target(timeout: timeout) do |target|
100
+ (target.is_a?(PageTarget) || target.is_a?(FrameTarget)) &&
101
+ target.browser_context == self &&
102
+ predicate.call(target)
103
+ end #: PageTarget | FrameTarget
104
+ end
105
+
83
106
  # Get all cookies in this context.
84
107
  # @rbs return: Array[Hash[String, untyped]] -- Cookies
85
108
  def cookies
@@ -122,7 +145,6 @@ module Puppeteer
122
145
  bidi_cookie["expiry"] = expiry unless expiry.nil?
123
146
  bidi_cookie.merge!(CookieUtils.cdp_specific_cookie_properties_from_puppeteer_to_bidi(
124
147
  normalized_cookie,
125
- "sameParty",
126
148
  "sourceScheme",
127
149
  "priority",
128
150
  "url"
@@ -275,6 +297,19 @@ module Puppeteer
275
297
 
276
298
  private
277
299
 
300
+ # @rbs frame: Frame -- Frame to get target for
301
+ # @rbs return: FrameTarget -- Frame target
302
+ def target_for_frame(frame)
303
+ context_id = frame.browsing_context.id
304
+ @frame_targets[context_id] ||= begin
305
+ target = FrameTarget.new(frame)
306
+ frame.browsing_context.once(:closed) do
307
+ @frame_targets.delete(context_id)
308
+ end
309
+ target
310
+ end
311
+ end
312
+
278
313
  def cookie_matches_filter?(cookie, raw_filter)
279
314
  filter = CookieUtils.normalize_cookie_input(raw_filter)
280
315
  return false unless filter["name"] == cookie["name"]
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+ # rbs_inline: enabled
3
+
4
+ module Puppeteer
5
+ module Bidi
6
+ # ConsoleMessage represents a browser console/log entry.
7
+ class ConsoleMessage
8
+ attr_reader :type, :text, :args, :location, :stack_trace
9
+
10
+ # @rbs type: String -- Console message type
11
+ # @rbs text: String -- Console text
12
+ # @rbs args: Array[JSHandle] -- Console argument handles
13
+ # @rbs location: Hash[Symbol, untyped]? -- Source location
14
+ # @rbs stack_trace: Array[Hash[Symbol, untyped]] -- Stack trace frames
15
+ # @rbs return: void
16
+ def initialize(type:, text:, args: [], location: nil, stack_trace: [])
17
+ @type = type
18
+ @text = text
19
+ @args = args
20
+ @location = location
21
+ @stack_trace = stack_trace
22
+ end
23
+ end
24
+ end
25
+ end
@@ -35,8 +35,8 @@ module Puppeteer
35
35
  "session" => expiry.nil? || expiry <= 0,
36
36
  }.compact
37
37
 
38
- cookie.merge!(cdp_specific_cookie_properties_from_bidi(bidi_cookie, "sameParty", "sourceScheme",
39
- "partitionKeyOpaque", "priority"))
38
+ cookie.merge!(cdp_specific_cookie_properties_from_bidi(bidi_cookie, "sourceScheme", "partitionKeyOpaque",
39
+ "priority"))
40
40
  cookie.merge!(partition_key_from_bidi(partition_key, return_composite_partition_key))
41
41
 
42
42
  cookie
@@ -101,8 +101,7 @@ context = browser.default_user_context
101
101
  browsing_context = context.create_browsing_context('tab')
102
102
 
103
103
  # Listen for navigation events
104
- browsing_context.on(:navigation) do |data|
105
- navigation = data[:navigation]
104
+ browsing_context.on(:navigation) do |navigation|
106
105
  puts "Navigation started to #{browsing_context.url}"
107
106
  end
108
107
 
@@ -123,12 +122,12 @@ All core classes extend `EventEmitter` and emit various events:
123
122
 
124
123
  ```ruby
125
124
  # Browser events
126
- browser.on(:closed) { |data| puts "Browser closed: #{data[:reason]}" }
127
- browser.on(:disconnected) { |data| puts "Browser disconnected: #{data[:reason]}" }
125
+ browser.on(:closed) { |reason| puts "Browser closed: #{reason}" }
126
+ browser.on(:disconnected) { |reason| puts "Browser disconnected: #{reason}" }
128
127
 
129
128
  # BrowsingContext events
130
- browsing_context.on(:navigation) { |data| puts "Navigation: #{data[:navigation]}" }
131
- browsing_context.on(:request) { |data| puts "Request: #{data[:request].url}" }
129
+ browsing_context.on(:navigation) { |navigation| puts "Navigation: #{navigation}" }
130
+ browsing_context.on(:request) { |request| puts "Request: #{request.url}" }
132
131
  browsing_context.on(:load) { puts "Page loaded" }
133
132
  browsing_context.on(:dom_content_loaded) { puts "DOM ready" }
134
133
 
@@ -147,8 +146,8 @@ Core classes implement the `Disposable` pattern for proper resource cleanup:
147
146
  browser.close # Disposes all user contexts, browsing contexts, etc.
148
147
 
149
148
  # Disposal triggers appropriate events
150
- browsing_context.on(:closed) do |data|
151
- puts "Context closed: #{data[:reason]}"
149
+ browsing_context.on(:closed) do |reason|
150
+ puts "Context closed: #{reason}"
152
151
  end
153
152
 
154
153
  # Check disposal status
@@ -65,7 +65,7 @@ module Puppeteer
65
65
  begin
66
66
  @session.async_send_command('browser.close', {})
67
67
  ensure
68
- dispose_browser('Browser closed', closed: true)
68
+ dispose_browser('Browser already closed.', closed: true)
69
69
  end
70
70
  end
71
71
  end
@@ -159,9 +159,9 @@ module Puppeteer
159
159
  protected
160
160
 
161
161
  def perform_dispose
162
- @reason ||= 'Browser was disconnected, probably because the session ended'
163
- emit(:closed, { reason: @reason }) if @closed
164
- emit(:disconnected, { reason: @reason })
162
+ @reason ||= 'Browser was disconnected, probably because the session ended.'
163
+ emit(:closed, @reason) if @closed
164
+ emit(:disconnected, @reason)
165
165
  @disposables.dispose
166
166
  super
167
167
  end
@@ -171,8 +171,8 @@ module Puppeteer
171
171
  def initialize_browser
172
172
  Async do
173
173
  # Listen for session end
174
- @session.on(:ended) do |data|
175
- dispose_browser(data[:reason])
174
+ @session.on(:ended) do |reason|
175
+ dispose_browser(reason)
176
176
  end
177
177
 
178
178
  # Listen for shared worker creation
@@ -37,7 +37,6 @@ module Puppeteer
37
37
  @disposables = Disposable::DisposableStack.new
38
38
  @children = {}
39
39
  @realms = {}
40
- @requests = {}
41
40
  @inflight_request_ids = {}
42
41
  @navigation = nil
43
42
  @emulation_state = { javascript_enabled: true }
@@ -218,7 +217,7 @@ module Puppeteer
218
217
  raise BrowsingContextClosedError, @reason if closed?
219
218
  realm = WindowRealm.from(self, sandbox)
220
219
  realm.on(:worker) do |worker_realm|
221
- emit(:worker, { realm: worker_realm })
220
+ emit(:worker, worker_realm)
222
221
  end
223
222
  realm
224
223
  end
@@ -360,7 +359,7 @@ module Puppeteer
360
359
  end
361
360
 
362
361
  @client_hints_are_set = true
363
- session.async_send_command('emulation.setClientHintsOverride', {
362
+ session.async_send_command('userAgentClientHints.setClientHintsOverride', {
364
363
  clientHints: client_hints,
365
364
  contexts: [@id]
366
365
  }).wait
@@ -382,6 +381,18 @@ module Puppeteer
382
381
  })
383
382
  end
384
383
 
384
+ # Set locale override
385
+ # @rbs locale: String? -- Locale, or nil to restore the default
386
+ # @rbs return: Async::Task[void]
387
+ def set_locale_override(locale = nil)
388
+ raise BrowsingContextClosedError, @reason if closed?
389
+
390
+ session.async_send_command("emulation.setLocaleOverride", {
391
+ locale: locale,
392
+ contexts: [@id]
393
+ })
394
+ end
395
+
385
396
  # Set files on an input element
386
397
  # @rbs element: Hash[String, untyped] -- Element reference
387
398
  # @rbs files: Array[String] -- File paths
@@ -454,8 +465,8 @@ module Puppeteer
454
465
  def dispose
455
466
  return if disposed?
456
467
 
457
- @reason ||= 'Browsing context closed, probably because the user context closed'
458
- emit(:closed, { reason: @reason })
468
+ @reason ||= 'Browsing context already closed, probably because the user context closed.'
469
+ emit(:closed, @reason)
459
470
 
460
471
  super # This sets @disposed = true and calls perform_dispose
461
472
  end
@@ -492,8 +503,8 @@ module Puppeteer
492
503
 
493
504
  def initialize_context
494
505
  # Listen for user context closure
495
- @user_context.once(:closed) do |data|
496
- dispose_context("Browsing context closed: #{data[:reason]}")
506
+ @user_context.once(:closed) do |reason|
507
+ dispose_context("Browsing context already closed: #{reason}")
497
508
  end
498
509
 
499
510
  # Listen for various browsing context events
@@ -505,7 +516,7 @@ module Puppeteer
505
516
  session.on('browsingContext.contextDestroyed') do |info|
506
517
  next unless info['context'] == @id
507
518
  dispose_children('Parent browsing context was disposed')
508
- dispose_context('Browsing context already closed')
519
+ dispose_context('Browsing context already closed.')
509
520
  end
510
521
 
511
522
  # Child context created
@@ -527,7 +538,7 @@ module Puppeteer
527
538
  @children.delete(child_context.id)
528
539
  end
529
540
 
530
- emit(:browsingcontext, { browsing_context: child_context })
541
+ emit(:browsingcontext, child_context)
531
542
  end
532
543
 
533
544
  # History updated
@@ -562,9 +573,6 @@ module Puppeteer
562
573
  session.on('browsingContext.navigationStarted') do |info|
563
574
  next unless info['context'] == @id
564
575
 
565
- # Clean up disposed requests
566
- @requests.delete_if { |_, request| request.disposed? }
567
-
568
576
  # Skip if navigation hasn't finished
569
577
  next if @navigation && !@navigation.disposed?
570
578
 
@@ -586,65 +594,44 @@ module Puppeteer
586
594
  end
587
595
 
588
596
  # Emit navigation event for subscribers (e.g., Frame#wait_for_navigation)
589
- emit(:navigation, { navigation: @navigation })
597
+ emit(:navigation, @navigation)
590
598
  end
591
599
 
592
600
  # Network events
593
601
  session.on('network.beforeRequestSent') do |event|
594
602
  next unless event['context'] == @id
595
603
 
596
- request_id = event.dig('request', 'request')
597
- next if @requests.key?(request_id)
604
+ first_event_for_request = track_inflight_request(event)
605
+ next if event['redirectCount'].to_i > 0
606
+ next unless first_event_for_request
598
607
 
599
608
  request = Request.from(self, event)
600
- @requests[request_id] = request
601
- emit(:request, { request: request })
602
-
603
- # Increment inflight requests counter
604
- @inflight_mutex.synchronize do
605
- @inflight_request_ids[request_id] = true
606
- @inflight_requests += 1
607
- emit(:inflight_changed, { inflight: @inflight_requests })
608
- end
609
+ emit(:request, request)
609
610
  end
610
611
 
611
612
  session.on('network.responseCompleted') do |event|
612
613
  next unless event['context'] == @id
613
614
 
614
- request_id = event.dig('request', 'request')
615
- next unless @inflight_request_ids.delete(request_id)
616
-
617
- # Decrement inflight requests counter
618
- @inflight_mutex.synchronize do
619
- @inflight_requests -= 1
620
- emit(:inflight_changed, { inflight: @inflight_requests })
621
- end
615
+ finish_inflight_request(event)
622
616
  end
623
617
 
624
618
  session.on('network.fetchError') do |event|
625
619
  next unless event['context'] == @id
626
620
 
627
- request_id = event.dig('request', 'request')
628
- next unless @inflight_request_ids.delete(request_id)
629
-
630
- # Decrement inflight requests counter
631
- @inflight_mutex.synchronize do
632
- @inflight_requests -= 1
633
- emit(:inflight_changed, { inflight: @inflight_requests })
634
- end
621
+ finish_inflight_request(event)
635
622
  end
636
623
 
637
624
  # Log entries
638
625
  session.on('log.entryAdded') do |entry|
639
626
  next unless entry.dig('source', 'context') == @id
640
- emit(:log, { entry: entry })
627
+ emit(:log, entry)
641
628
  end
642
629
 
643
630
  # User prompts
644
631
  session.on('browsingContext.userPromptOpened') do |info|
645
632
  next unless info['context'] == @id
646
633
  # user_prompt = UserPrompt.from(self, info)
647
- # emit(:userprompt, { user_prompt: user_prompt })
634
+ # emit(:userprompt, user_prompt)
648
635
  end
649
636
 
650
637
  # File dialog
@@ -654,6 +641,43 @@ module Puppeteer
654
641
  end
655
642
  end
656
643
 
644
+ def track_inflight_request(event)
645
+ key = inflight_request_key(event)
646
+ return false unless key
647
+
648
+ inflight = nil
649
+ @inflight_mutex.synchronize do
650
+ unless @inflight_request_ids.key?(key)
651
+ @inflight_request_ids[key] = true
652
+ @inflight_requests += 1
653
+ inflight = @inflight_requests
654
+ end
655
+ end
656
+ emit(:inflight_changed, { inflight: inflight }) if inflight
657
+ !inflight.nil?
658
+ end
659
+
660
+ def finish_inflight_request(event)
661
+ key = inflight_request_key(event)
662
+ return unless key
663
+
664
+ inflight = nil
665
+ @inflight_mutex.synchronize do
666
+ if @inflight_request_ids.delete(key)
667
+ @inflight_requests -= 1
668
+ inflight = @inflight_requests
669
+ end
670
+ end
671
+ emit(:inflight_changed, { inflight: inflight }) if inflight
672
+ end
673
+
674
+ def inflight_request_key(event)
675
+ request_id = event.dig('request', 'request')
676
+ return nil unless request_id
677
+
678
+ [request_id, event['redirectCount'].to_i]
679
+ end
680
+
657
681
  def dispose_children(reason)
658
682
  @children.values.each do |child|
659
683
  next if child.closed?
@@ -58,8 +58,7 @@ module Puppeteer
58
58
  end
59
59
 
60
60
  # Listen for requests with navigation ID
61
- @browsing_context.on(:request) do |data|
62
- request = data[:request]
61
+ @browsing_context.on(:request) do |request|
63
62
  next unless request.navigation
64
63
  next unless matches?(request.navigation)
65
64
 
@@ -81,7 +80,11 @@ module Puppeteer
81
80
  end
82
81
 
83
82
  # Listen for navigation completion events
84
- %w[browsingContext.domContentLoaded browsingContext.load].each do |event_name|
83
+ %w[
84
+ browsingContext.domContentLoaded
85
+ browsingContext.load
86
+ browsingContext.navigationCommitted
87
+ ].each do |event_name|
85
88
  session.on(event_name) do |info|
86
89
  next unless info['context'] == @browsing_context.id
87
90
  next if info['navigation'].nil?