puppeteer-ruby 0.37.0 → 0.37.4

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: 9e46056e6227e597554a407d47c132610af952d42b1cd7c2149e2046e0d8f1bb
4
- data.tar.gz: 99a5c6fd6885181e32c72086a768c60583738974a3c86a05a693af2b97b51bc3
3
+ metadata.gz: '000206932f5d3d4626602e984cff4015039644eea2ef89ff139b239fffb9f673'
4
+ data.tar.gz: 0e560f862a3317a42c1e0e2907c599bc654d05e3f17fd5a650126b73bfd6948e
5
5
  SHA512:
6
- metadata.gz: d4b1032b52bb5a0ad92e57f0bac5fe5208b785679f1d7efff0ab335cb28478f6110979102d0ec4759ef2f86654b4d231e018aa9cdb3982dc1ffa2e985fa4c55f
7
- data.tar.gz: 42f81fe580fcb85b5c57397962362c3c6b0ba7462fe210c65ee017b9e73c0a3d9d091c03a7b18f6e6d78f3a3c52572a5d0bb8284f1087412344a184e8624170d
6
+ metadata.gz: aac4be4a26f3116eee198ea65c0017359589f2ad11cc95125497e3f84ea1277d9bbb8d1071b57fb42812bb12afd4370534c48586fbb79897acafb54d03222159
7
+ data.tar.gz: de51d1ca30e26e13346532118df225370fad4b1967d205dad0c150574c4fa60a649973aab83e24b2140b8f98f05626eb106a9141f04b94fe5d3927b1231737d4
data/CHANGELOG.md CHANGED
@@ -1,7 +1,31 @@
1
- ### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.0...master)]
1
+ ### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.4...main)]
2
2
 
3
3
  - xxx
4
4
 
5
+ ### 0.37.4 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.3...0.37.4)]
6
+
7
+ Bugfix:
8
+
9
+ - Fix ElementHandle#screenshot to work.
10
+
11
+ ### 0.37.3 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.2...0.37.3)]
12
+
13
+ Improvement:
14
+
15
+ - Improve the logic of detection of Chrome/Firefox executable path on Linux.
16
+
17
+ ### 0.37.2 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.1...0.37.2)]
18
+
19
+ Bugfix:
20
+
21
+ - `timeout: 0` did set the timeout value to zero. Now it disables the timeout as expected.
22
+
23
+ ### 0.37.1 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.37.0...0.37.1)]
24
+
25
+ Bugfix:
26
+
27
+ - Fix handle_SIGINT, handle_SIGTERM, handle_SIGHUP options to work with `false` value specified.
28
+
5
29
  ### 0.37.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.36.0...0.37.0)]
6
30
 
7
31
  New features:
data/README.md CHANGED
@@ -41,7 +41,7 @@ NOTE: `require 'puppeteer-ruby'` is not necessary in Rails.
41
41
  ```ruby
42
42
  require 'puppeteer-ruby'
43
43
 
44
- Puppeteer.launch(headless: false, slow_mo: 50, args: ['--guest', '--window-size=1280,800']) do |browser|
44
+ Puppeteer.launch(headless: false, slow_mo: 50, args: ['--window-size=1280,800']) do |browser|
45
45
  page = browser.new_page
46
46
  page.viewport = Puppeteer::Viewport.new(width: 1280, height: 800)
47
47
  page.goto("https://github.com/", wait_until: 'domcontentloaded')
data/docs/api_coverage.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # API coverages
2
- - Puppeteer version: v10.4.0
3
- - puppeteer-ruby version: 0.37.0
2
+ - Puppeteer version: v12.0.0
3
+ - puppeteer-ruby version: 0.37.4
4
4
 
5
5
  ## Puppeteer
6
6
 
@@ -134,6 +134,7 @@
134
134
  * viewport
135
135
  * ~~waitFor~~
136
136
  * waitForFileChooser => `#wait_for_file_chooser`
137
+ * ~~waitForFrame~~
137
138
  * waitForFunction => `#wait_for_function`
138
139
  * waitForNavigation => `#wait_for_navigation`
139
140
  * ~~waitForNetworkIdle~~
@@ -226,6 +227,7 @@
226
227
  * goto
227
228
  * hover
228
229
  * isDetached => `#detached?`
230
+ * ~~isOOPFrame~~
229
231
  * name
230
232
  * parentFrame => `#parent_frame`
231
233
  * select
@@ -295,43 +297,44 @@
295
297
  * type => `#type_text`
296
298
  * uploadFile => `#upload_file`
297
299
 
298
- ## ~~HTTPRequest~~
299
-
300
- * ~~abort~~
301
- * ~~abortErrorReason~~
302
- * ~~continue~~
303
- * ~~continueRequestOverrides~~
304
- * ~~enqueueInterceptAction~~
305
- * ~~failure~~
306
- * ~~finalizeInterceptions~~
307
- * ~~frame~~
308
- * ~~headers~~
309
- * ~~isNavigationRequest~~
310
- * ~~method~~
311
- * ~~postData~~
312
- * ~~redirectChain~~
313
- * ~~resourceType~~
314
- * ~~respond~~
315
- * ~~response~~
316
- * ~~responseForRequest~~
317
- * ~~url~~
300
+ ## HTTPRequest
301
+
302
+ * abort
303
+ * abortErrorReason => `#abort_error_reason`
304
+ * continue
305
+ * continueRequestOverrides => `#continue_request_overrides`
306
+ * enqueueInterceptAction => `#enqueue_intercept_action`
307
+ * failure
308
+ * finalizeInterceptions => `#finalize_interceptions`
309
+ * frame
310
+ * headers
311
+ * ~~initiator~~
312
+ * isNavigationRequest => `#navigation_request?`
313
+ * method
314
+ * postData => `#post_data`
315
+ * redirectChain => `#redirect_chain`
316
+ * resourceType => `#resource_type`
317
+ * respond
318
+ * response
319
+ * responseForRequest => `#response_for_request`
320
+ * url
318
321
 
319
- ## ~~HTTPResponse~~
322
+ ## HTTPResponse
320
323
 
321
- * ~~buffer~~
322
- * ~~frame~~
324
+ * buffer
325
+ * frame
323
326
  * ~~fromCache~~
324
327
  * ~~fromServiceWorker~~
325
- * ~~headers~~
326
- * ~~json~~
328
+ * headers
329
+ * json
327
330
  * ~~ok~~
328
- * ~~remoteAddress~~
329
- * ~~request~~
330
- * ~~securityDetails~~
331
- * ~~status~~
332
- * ~~statusText~~
333
- * ~~text~~
334
- * ~~url~~
331
+ * remoteAddress => `#remote_address`
332
+ * request
333
+ * securityDetails => `#security_details`
334
+ * status
335
+ * statusText => `#status_text`
336
+ * text
337
+ * url
335
338
 
336
339
  ## ~~SecurityDetails~~
337
340
 
@@ -357,6 +360,7 @@
357
360
 
358
361
  * connection
359
362
  * detach
363
+ * ~~id~~
360
364
  * send
361
365
 
362
366
  ## Coverage
@@ -94,7 +94,7 @@ class Puppeteer::BrowserRunner
94
94
  FileUtils.rm_rf(@temp_directory)
95
95
  end
96
96
  }
97
- trap(:EXIT) do
97
+ at_exit do
98
98
  kill
99
99
  end
100
100
 
@@ -323,7 +323,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
323
323
  end
324
324
  end
325
325
 
326
- def screenshot(options = {})
326
+ def screenshot(type: nil, path: nil, full_page: nil, clip: nil, quality: nil, omit_background: nil, encoding: nil)
327
327
  needs_viewport_reset = false
328
328
 
329
329
  box = bounding_box
@@ -358,14 +358,16 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
358
358
  page_x = layout_metrics["layoutViewport"]["pageX"]
359
359
  page_y = layout_metrics["layoutViewport"]["pageY"]
360
360
 
361
- clip = {
362
- x: page_x + box.x,
363
- y: page_y + box.y,
364
- width: box.width,
365
- height: box.height,
366
- }
361
+ if clip.nil?
362
+ clip = {
363
+ x: page_x + box.x,
364
+ y: page_y + box.y,
365
+ width: box.width,
366
+ height: box.height,
367
+ }
368
+ end
367
369
 
368
- @page.screenshot({ clip: clip }.merge(options))
370
+ @page.screenshot(type: type, path: path, full_page: full_page, clip: clip, quality: quality, omit_background: omit_background, encoding: encoding)
369
371
  ensure
370
372
  if needs_viewport_reset
371
373
  @page.viewport = viewport
@@ -0,0 +1,28 @@
1
+ class Puppeteer::ExecutablePathFinder
2
+ # @param executable_names [Array<String>] executable file names to find.
3
+ def initialize(*executable_names)
4
+ @executable_names = executable_names
5
+ end
6
+
7
+ def find_executables_in_path
8
+ Enumerator.new do |result|
9
+ @executable_names.each do |name|
10
+ # Find the first existing path.
11
+ paths.each do |path|
12
+ candidate = File.join(path, name)
13
+ next unless File.exist?(candidate)
14
+ result << candidate
15
+ break
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ def find_first
22
+ find_executables_in_path.first
23
+ end
24
+
25
+ private def paths
26
+ ENV['PATH'].split(File::PATH_SEPARATOR)
27
+ end
28
+ end
@@ -28,7 +28,7 @@ class Puppeteer::Frame
28
28
  # @param rederer [String]
29
29
  # @param timeout [number|nil]
30
30
  # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
31
- # @return [Puppeteer::Response]
31
+ # @return [Puppeteer::HTTPResponse]
32
32
  def goto(url, referer: nil, timeout: nil, wait_until: nil)
33
33
  @frame_manager.navigate_frame(self, url, referer: referer, timeout: timeout, wait_until: wait_until)
34
34
  end
@@ -82,7 +82,7 @@ class Puppeteer::FrameManager
82
82
  # @param frame [Puppeteer::Frame]
83
83
  # @param url [String]
84
84
  # @param {!{referer?: string, timeout?: number, waitUntil?: string|!Array<string>}=} options
85
- # @return [Puppeteer::Response]
85
+ # @return [Puppeteer::HTTPResponse]
86
86
  def navigate_frame(frame, url, referer: nil, timeout: nil, wait_until: nil)
87
87
  assert_no_legacy_navigation_options(wait_until: wait_until)
88
88
 
@@ -132,7 +132,7 @@ class Puppeteer::FrameManager
132
132
 
133
133
  # @param timeout [number|nil]
134
134
  # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
135
- # @return [Puppeteer::Response]
135
+ # @return [Puppeteer::HTTPResponse]
136
136
  def wait_for_frame_navigation(frame, timeout: nil, wait_until: nil)
137
137
  assert_no_legacy_navigation_options(wait_until: wait_until)
138
138
 
@@ -1,4 +1,4 @@
1
- class Puppeteer::Request
1
+ class Puppeteer::HTTPRequest
2
2
  include Puppeteer::DebugPrint
3
3
  include Puppeteer::IfPresent
4
4
 
@@ -16,7 +16,7 @@ class Puppeteer::Request
16
16
  @request.instance_variable_get(:@interception_id)
17
17
  end
18
18
 
19
- # @param response [Puppeteer::Response]
19
+ # @param response [Puppeteer::HTTPResponse]
20
20
  def response=(response)
21
21
  @request.instance_variable_set(:@response, response)
22
22
  end
@@ -56,6 +56,12 @@ class Puppeteer::Request
56
56
  @post_data = event['request']['postData']
57
57
  @frame = frame
58
58
  @redirect_chain = redirect_chain
59
+ @continue_request_overrides = {}
60
+ @current_strategy = 'none'
61
+ @current_priority = nil
62
+ @intercept_actions = []
63
+ @initiator = event['initiator']
64
+
59
65
  @headers = {}
60
66
  event['request']['headers'].each do |key, value|
61
67
  @headers[key.downcase] = value
@@ -68,6 +74,76 @@ class Puppeteer::Request
68
74
  attr_reader :internal
69
75
  attr_reader :url, :resource_type, :method, :post_data, :headers, :response, :frame
70
76
 
77
+ private def assert_interception_allowed
78
+ unless @allow_interception
79
+ raise InterceptionNotEnabledError.new
80
+ end
81
+ end
82
+
83
+ private def assert_interception_not_handled
84
+ if @interception_handled
85
+ raise AlreadyHandledError.new
86
+ end
87
+ end
88
+
89
+ # @returns the `ContinueRequestOverrides` that will be used
90
+ # if the interception is allowed to continue (ie, `abort()` and
91
+ # `respond()` aren't called).
92
+ def continue_request_overrides
93
+ assert_interception_allowed
94
+ @continue_request_overrides
95
+ end
96
+
97
+ # @returns The `ResponseForRequest` that gets used if the
98
+ # interception is allowed to respond (ie, `abort()` is not called).
99
+ def response_for_request
100
+ assert_interception_allowed
101
+ @response_for_request
102
+ end
103
+
104
+ # @returns the most recent reason for aborting the request
105
+ def abort_error_reason
106
+ assert_interception_allowed
107
+ @abort_error_reason
108
+ end
109
+
110
+ # @returns An array of the current intercept resolution strategy and priority
111
+ # `[strategy,priority]`. Strategy is one of: `abort`, `respond`, `continue`,
112
+ # `disabled`, `none`, or `already-handled`.
113
+ def intercept_resolution
114
+ if !@allow_interception
115
+ ['disabled']
116
+ elsif @interception_handled
117
+ ['already-handled']
118
+ else
119
+ [@current_strategy, @current_priority]
120
+ end
121
+ end
122
+
123
+ # Adds an async request handler to the processing queue.
124
+ # Deferred handlers are not guaranteed to execute in any particular order,
125
+ # but they are guarnateed to resolve before the request interception
126
+ # is finalized.
127
+ #
128
+ # @param pending_handler [Proc]
129
+ def enqueue_intercept_action(pending_handler)
130
+ @intercept_actions << pending_handler
131
+ end
132
+
133
+ # Awaits pending interception handlers and then decides how to fulfill
134
+ # the request interception.
135
+ def finalize_interceptions
136
+ @intercept_actions.each(&:call)
137
+ case @intercept_resolution
138
+ when :abort
139
+ abort_impl(**@abort_error_reason)
140
+ when :respond
141
+ respond_impl(**@response_for_request)
142
+ when :continue
143
+ continue_impl(@continue_request_overrides)
144
+ end
145
+ end
146
+
71
147
  def navigation_request?
72
148
  @is_navigation_request
73
149
  end
@@ -116,24 +192,43 @@ class Puppeteer::Request
116
192
  # end
117
193
  #
118
194
  # @param error_code [String|Symbol]
119
- def continue(url: nil, method: nil, post_data: nil, headers: nil)
195
+ def continue(url: nil, method: nil, post_data: nil, headers: nil, priority: nil)
120
196
  # Request interception is not supported for data: urls.
121
197
  return if @url.start_with?('data:')
122
198
 
123
- unless @allow_interception
124
- raise InterceptionNotEnabledError.new
125
- end
126
- if @interception_handled
127
- raise AlreadyHandledError.new
128
- end
129
- @interception_handled = true
199
+ assert_interception_allowed
200
+ assert_interception_not_handled
130
201
 
131
202
  overrides = {
132
203
  url: url,
133
204
  method: method,
134
- post_data: post_data,
205
+ postData: post_data,
135
206
  headers: headers_to_array(headers),
136
207
  }.compact
208
+
209
+ if priority.nil?
210
+ continue_impl(overrides)
211
+ return
212
+ end
213
+
214
+ @continue_request_overrides = overrides
215
+ if @current_priority.nil? || priority > @current_priority
216
+ @current_strategy = :continue
217
+ @current_priority = priority
218
+ return
219
+ end
220
+
221
+ if priority == @current_priority
222
+ if @current_strategy == :abort || @current_strategy == :respond
223
+ return
224
+ end
225
+ @current_strategy = :continue
226
+ end
227
+ end
228
+
229
+ private def continue_impl(overrides)
230
+ @interception_handled = true
231
+
137
232
  begin
138
233
  @client.send_message('Fetch.continueRequest',
139
234
  requestId: @interception_id,
@@ -162,16 +257,40 @@ class Puppeteer::Request
162
257
  # @param headers [Hash<String, String>]
163
258
  # @param content_type [String]
164
259
  # @param body [String]
165
- def respond(status: nil, headers: nil, content_type: nil, body: nil)
260
+ def respond(status: nil, headers: nil, content_type: nil, body: nil, priority: nil)
166
261
  # Mocking responses for dataURL requests is not currently supported.
167
262
  return if @url.start_with?('data:')
168
263
 
169
- unless @allow_interception
170
- raise InterceptionNotEnabledError.new
264
+ assert_interception_allowed
265
+ assert_interception_not_handled
266
+
267
+ if priority.nil?
268
+ respond_impl(status: status, headers: headers, content_type: content_type, body: body)
269
+ return
171
270
  end
172
- if @interception_handled
173
- raise AlreadyHandledError.new
271
+
272
+ @response_for_request = {
273
+ status: status,
274
+ headers: headers,
275
+ content_type: content_type,
276
+ body: body,
277
+ }
278
+
279
+ if @current_priority.nil? || priority > @current_priority
280
+ @current_strategy = :respond
281
+ @current_priority = priority
282
+ return
283
+ end
284
+
285
+ if priority == @current_priority
286
+ if @current_strategy == :abort
287
+ return
288
+ end
289
+ @current_strategy = :respond
174
290
  end
291
+ end
292
+
293
+ private def respond_impl(status: nil, headers: nil, content_type: nil, body: nil)
175
294
  @interception_handled = true
176
295
 
177
296
  mock_response_headers = {}
@@ -191,6 +310,7 @@ class Puppeteer::Request
191
310
  responseHeaders: headers_to_array(mock_response_headers),
192
311
  body: if_present(body) { |mock_body| Base64.strict_encode64(mock_body) },
193
312
  }.compact
313
+
194
314
  begin
195
315
  @client.send_message('Fetch.fulfillRequest',
196
316
  requestId: @interception_id,
@@ -216,7 +336,7 @@ class Puppeteer::Request
216
336
  # end
217
337
  #
218
338
  # @param error_code [String|Symbol]
219
- def abort(error_code: :failed)
339
+ def abort(error_code: :failed, priority: nil)
220
340
  # Request interception is not supported for data: urls.
221
341
  return if @url.start_with?('data:')
222
342
 
@@ -224,12 +344,21 @@ class Puppeteer::Request
224
344
  unless error_reason
225
345
  raise ArgumentError.new("Unknown error code: #{error_code}")
226
346
  end
227
- unless @allow_interception
228
- raise InterceptionNotEnabledError.new
347
+ assert_interception_allowed
348
+ assert_interception_not_handled
349
+
350
+ if priority.nil?
351
+ abort_impl(error_reason)
229
352
  end
230
- if @interception_handled
231
- raise AlreadyHandledError.new
353
+ @abort_error_reason = error_reason
354
+
355
+ if @current_priority.nil? || priority > @current_priority
356
+ @current_strategy = :abort
357
+ @current_priority = priority
232
358
  end
359
+ end
360
+
361
+ private def abort_impl(error_reason)
233
362
  @interception_handled = true
234
363
 
235
364
  begin
@@ -1,6 +1,6 @@
1
1
  require 'json'
2
2
 
3
- class Puppeteer::Response
3
+ class Puppeteer::HTTPResponse
4
4
  include Puppeteer::IfPresent
5
5
 
6
6
  class Redirected < StandardError
@@ -29,7 +29,7 @@ class Puppeteer::Response
29
29
  end
30
30
 
31
31
  # @param client [Puppeteer::CDPSession]
32
- # @param request [Puppeteer::Request]
32
+ # @param request [Puppeteer::HTTPRequest]
33
33
  # @param response_payload [Hash]
34
34
  def initialize(client, request, response_payload)
35
35
  @client = client
@@ -48,7 +48,7 @@ module Puppeteer::Launcher
48
48
  if @launch_options.channel
49
49
  executable_path_for_channel(@launch_options.channel.to_s)
50
50
  else
51
- @launch_options.executable_path || executable_path_for_channel('chrome')
51
+ @launch_options.executable_path || fallback_executable_path
52
52
  end
53
53
  use_pipe = chrome_arguments.include?('--remote-debugging-pipe')
54
54
  runner = Puppeteer::BrowserRunner.new(chrome_executable, chrome_arguments, temporary_user_data_dir)
@@ -216,10 +216,14 @@ module Puppeteer::Launcher
216
216
  if channel
217
217
  executable_path_for_channel(channel.to_s)
218
218
  else
219
- executable_path_for_channel('chrome')
219
+ fallback_executable_path
220
220
  end
221
221
  end
222
222
 
223
+ private def fallback_executable_path
224
+ executable_path_for_channel('chrome')
225
+ end
226
+
223
227
  CHROMIUM_CHANNELS = {
224
228
  windows: {
225
229
  'chrome' => "#{ENV['PROGRAMFILES']}\\Google\\Chrome\\Application\\chrome.exe",
@@ -236,7 +240,16 @@ module Puppeteer::Launcher
236
240
  'msedge' => '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
237
241
  },
238
242
  linux: {
239
- 'chrome' => '/opt/google/chrome/chrome',
243
+ 'chrome' => -> {
244
+ Puppeteer::ExecutablePathFinder.new(
245
+ 'google-chrome-stable',
246
+ 'google-chrome',
247
+ 'chrome',
248
+ 'chromium-freeworld',
249
+ 'chromium-browser',
250
+ 'chromium',
251
+ ).find_first
252
+ },
240
253
  'chrome-beta' => '/opt/google/chrome-beta/chrome',
241
254
  'chrome-dev' => '/opt/google/chrome-unstable/chrome',
242
255
  },
@@ -254,6 +267,10 @@ module Puppeteer::Launcher
254
267
  end
255
268
 
256
269
  chrome_path = chrome_path_map[channel]
270
+ if chrome_path.is_a?(Proc)
271
+ chrome_path = chrome_path.call
272
+ end
273
+
257
274
  unless chrome_path
258
275
  raise ArgumentError.new("Invalid channel: '#{channel}'. Allowed channel is #{chrome_path_map.keys}")
259
276
  end
@@ -42,7 +42,7 @@ module Puppeteer::Launcher
42
42
  if @launch_options.channel
43
43
  executable_path_for_channel(@launch_options.channel.to_s)
44
44
  else
45
- @launch_options.executable_path || executable_path_for_channel('nightly')
45
+ @launch_options.executable_path || fallback_executable_path
46
46
  end
47
47
  runner = Puppeteer::BrowserRunner.new(firefox_executable, firefox_arguments, temporary_user_data_dir)
48
48
  runner.start(
@@ -138,14 +138,18 @@ module Puppeteer::Launcher
138
138
  if channel
139
139
  executable_path_for_channel(channel.to_s)
140
140
  else
141
- executable_path_for_channel('firefox')
141
+ fallback_executable_path
142
142
  end
143
143
  end
144
144
 
145
+ private def fallback_executable_path
146
+ executable_path_for_channel('firefox')
147
+ end
148
+
145
149
  FIREFOX_EXECUTABLE_PATHS = {
146
150
  windows: "#{ENV['PROGRAMFILES']}\\Firefox Nightly\\firefox.exe",
147
151
  darwin: '/Applications/Firefox Nightly.app/Contents/MacOS/firefox',
148
- linux: '/usr/bin/firefox',
152
+ linux: -> { Puppeteer::ExecutablePathFinder.new('firefox').find_first },
149
153
  }.freeze
150
154
 
151
155
  # @param channel [String]
@@ -163,6 +167,9 @@ module Puppeteer::Launcher
163
167
  else
164
168
  FIREFOX_EXECUTABLE_PATHS[:linux]
165
169
  end
170
+ if firefox_path.is_a?(Proc)
171
+ firefox_path = firefox_path.call
172
+ end
166
173
 
167
174
  unless File.exist?(firefox_path)
168
175
  raise "Nightly version of Firefox is not installed on this system.\nExpected path: #{firefox_path}"
@@ -35,9 +35,9 @@ module Puppeteer::Launcher
35
35
  @channel = options[:channel]
36
36
  @executable_path = options[:executable_path]
37
37
  @ignore_default_args = options[:ignore_default_args] || false
38
- @handle_SIGINT = options[:handle_SIGINT] || true
39
- @handle_SIGTERM = options[:handle_SIGTERM] || true
40
- @handle_SIGHUP = options[:handle_SIGHUP] || true
38
+ @handle_SIGINT = options.fetch(:handle_SIGINT, true)
39
+ @handle_SIGTERM = options.fetch(:handle_SIGTERM, true)
40
+ @handle_SIGHUP = options.fetch(:handle_SIGHUP, true)
41
41
  @timeout = options[:timeout] || 30000
42
42
  @dumpio = options[:dumpio] || false
43
43
  @env = options[:env] || ENV
@@ -88,7 +88,7 @@ class Puppeteer::LifecycleWatcher
88
88
  check_lifecycle_complete
89
89
  end
90
90
 
91
- # @param [Puppeteer::Request] request
91
+ # @param [Puppeteer::HTTPRequest] request
92
92
  def handle_request(request)
93
93
  return if request.frame != @frame || !request.navigation_request?
94
94
  @navigation_request = request
@@ -103,7 +103,7 @@ class Puppeteer::LifecycleWatcher
103
103
  check_lifecycle_complete
104
104
  end
105
105
 
106
- # @return [Puppeteer::Response]
106
+ # @return [Puppeteer::HTTPResponse]
107
107
  def navigation_response
108
108
  if_present(@navigation_request) do |request|
109
109
  request.response
@@ -142,9 +142,14 @@ class Puppeteer::NetworkManager
142
142
  end
143
143
 
144
144
  # @param user_agent [String]
145
- def user_agent=(user_agent)
146
- @client.send_message('Network.setUserAgentOverride', userAgent: user_agent)
145
+ # @param user_agent_metadata [Hash]
146
+ def set_user_agent(user_agent, user_agent_metadata = nil)
147
+ @client.send_message('Network.setUserAgentOverride', {
148
+ userAgent: user_agent,
149
+ userAgentMetadata: user_agent_metadata,
150
+ }.compact)
147
151
  end
152
+ alias_method :user_agent=, :set_user_agent
148
153
 
149
154
  def cache_enabled=(enabled)
150
155
  @user_cache_disabled = !enabled
@@ -246,9 +251,14 @@ class Puppeteer::NetworkManager
246
251
  end
247
252
  end
248
253
  frame = if_present(event['frameId']) { |frame_id| @frame_manager.frame(frame_id) }
249
- request = Puppeteer::Request.new(@client, frame, interception_id, @user_request_interception_enabled, event, redirect_chain)
254
+ request = Puppeteer::HTTPRequest.new(@client, frame, interception_id, @user_request_interception_enabled, event, redirect_chain)
250
255
  @request_id_to_request[event['requestId']] = request
251
256
  emit_event(NetworkManagerEmittedEvents::Request, request)
257
+ begin
258
+ request.finalize_interceptions
259
+ rescue => err
260
+ debug_puts(err)
261
+ end
252
262
  end
253
263
 
254
264
  private def handle_request_served_from_cache(event)
@@ -257,13 +267,13 @@ class Puppeteer::NetworkManager
257
267
  end
258
268
  end
259
269
 
260
- # @param request [Puppeteer::Request]
270
+ # @param request [Puppeteer::HTTPRequest]
261
271
  # @param response_payload [Hash]
262
272
  private def handle_request_redirect(request, response_payload)
263
- response = Puppeteer::Response.new(@client, request, response_payload)
273
+ response = Puppeteer::HTTPResponse.new(@client, request, response_payload)
264
274
  request.internal.response = response
265
275
  request.internal.redirect_chain << request
266
- response.internal.body_loaded_promise.reject(Puppeteer::Response::Redirected.new)
276
+ response.internal.body_loaded_promise.reject(Puppeteer::HTTPResponse::Redirected.new)
267
277
  @request_id_to_request.delete(request.internal.request_id)
268
278
  @attempted_authentications.delete(request.internal.interception_id)
269
279
  emit_event(NetworkManagerEmittedEvents::Response, response)
@@ -276,7 +286,7 @@ class Puppeteer::NetworkManager
276
286
  # FileUpload sends a response without a matching request.
277
287
  return unless request
278
288
 
279
- response = Puppeteer::Response.new(@client, request, event['response'])
289
+ response = Puppeteer::HTTPResponse.new(@client, request, event['response'])
280
290
  request.internal.response = response
281
291
  emit_event(NetworkManagerEmittedEvents::Response, response)
282
292
  end
@@ -153,6 +153,12 @@ class Puppeteer::Page
153
153
  raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{PageEmittedEvents.values.to_a.join(", ")}")
154
154
  end
155
155
 
156
+ if event_name.to_s == 'request'
157
+ super('request') do |req|
158
+ req.enqueue_intercept_action(-> { block.call(req) })
159
+ end
160
+ end
161
+
156
162
  super(event_name.to_s, &block)
157
163
  end
158
164
 
@@ -461,37 +467,6 @@ class Puppeteer::Page
461
467
 
462
468
  nil
463
469
  end
464
- # /**
465
- # * @param {string} name
466
- # * @param {Function} puppeteerFunction
467
- # */
468
- # async exposeFunction(name, puppeteerFunction) {
469
- # if (this._pageBindings.has(name))
470
- # throw new Error(`Failed to add page binding with name ${name}: window['${name}'] already exists!`);
471
- # this._pageBindings.set(name, puppeteerFunction);
472
-
473
- # const expression = helper.evaluationString(addPageBinding, name);
474
- # await this._client.send('Runtime.addBinding', {name: name});
475
- # await this._client.send('Page.addScriptToEvaluateOnNewDocument', {source: expression});
476
- # await Promise.all(this.frames().map(frame => frame.evaluate(expression).catch(debugError)));
477
-
478
- # function addPageBinding(bindingName) {
479
- # const binding = window[bindingName];
480
- # window[bindingName] = (...args) => {
481
- # const me = window[bindingName];
482
- # let callbacks = me['callbacks'];
483
- # if (!callbacks) {
484
- # callbacks = new Map();
485
- # me['callbacks'] = callbacks;
486
- # }
487
- # const seq = (me['lastSeq'] || 0) + 1;
488
- # me['lastSeq'] = seq;
489
- # const promise = new Promise((resolve, reject) => callbacks.set(seq, {resolve, reject}));
490
- # binding(JSON.stringify({name: bindingName, seq, args}));
491
- # return promise;
492
- # };
493
- # }
494
- # }
495
470
 
496
471
  # @param username [String?]
497
472
  # @param password [String?]
@@ -505,9 +480,11 @@ class Puppeteer::Page
505
480
  end
506
481
 
507
482
  # @param user_agent [String]
508
- def user_agent=(user_agent)
509
- @frame_manager.network_manager.user_agent = user_agent
483
+ # @param user_agent_metadata [Hash]
484
+ def set_user_agent(user_agent, user_agent_metadata = nil)
485
+ @frame_manager.network_manager.set_user_agent(user_agent, user_agent_metadata)
510
486
  end
487
+ alias_method :user_agent=, :set_user_agent
511
488
 
512
489
  def metrics
513
490
  response = @client.send_message('Performance.getMetrics')
@@ -667,7 +644,7 @@ class Puppeteer::Page
667
644
 
668
645
  # @param timeout [number|nil]
669
646
  # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
670
- # @return [Puppeteer::Response]
647
+ # @return [Puppeteer::HTTPResponse]
671
648
  def reload(timeout: nil, wait_until: nil)
672
649
  wait_for_navigation(timeout: timeout, wait_until: wait_until) do
673
650
  @client.send_message('Page.reload')
@@ -697,11 +674,12 @@ class Puppeteer::Page
697
674
  @wait_for_network_manager_event_listener_ids[event_name] =
698
675
  @frame_manager.network_manager.add_event_listener(event_name) do |event_target|
699
676
  if predicate.call(event_target)
700
- promise.fulfill(nil)
677
+ promise.fulfill(event_target)
701
678
  end
702
679
  end
703
680
 
704
681
  begin
682
+ # Timeout.timeout(0) means "no limit" for timeout.
705
683
  Timeout.timeout(option_timeout / 1000.0) do
706
684
  await_any(promise, session_close_promise)
707
685
  end
@@ -751,7 +729,7 @@ class Puppeteer::Page
751
729
  # wait_for_request(predicate: -> (req){ req.url.start_with?('https://example.com/search') })
752
730
  #
753
731
  # @param url [String]
754
- # @param predicate [Proc(Puppeteer::Request -> Boolean)]
732
+ # @param predicate [Proc(Puppeteer::HTTPRequest -> Boolean)]
755
733
  define_async_method :async_wait_for_request
756
734
 
757
735
  def wait_for_response(url: nil, predicate: nil, timeout: nil)
@@ -777,7 +755,7 @@ class Puppeteer::Page
777
755
  # @!method async_wait_for_response(url: nil, predicate: nil, timeout: nil)
778
756
  #
779
757
  # @param url [String]
780
- # @param predicate [Proc(Puppeteer::Request -> Boolean)]
758
+ # @param predicate [Proc(Puppeteer::HTTPRequest -> Boolean)]
781
759
  define_async_method :async_wait_for_response
782
760
 
783
761
  # @param timeout [number|nil]
@@ -1,3 +1,3 @@
1
1
  module Puppeteer
2
- VERSION = '0.37.0'
2
+ VERSION = '0.37.4'
3
3
  end
@@ -37,7 +37,7 @@ class Puppeteer::WaitTask
37
37
 
38
38
  # Since page navigation requires us to re-install the pageScript, we should track
39
39
  # timeout on our end.
40
- if timeout
40
+ if timeout && timeout > 0
41
41
  timeout_error = TimeoutError.new(title: title, timeout: timeout)
42
42
  Concurrent::Promises.schedule(timeout / 1000.0) { terminate(timeout_error) unless @timeout_cleared }
43
43
  end
data/lib/puppeteer.rb CHANGED
@@ -33,10 +33,13 @@ require 'puppeteer/dialog'
33
33
  require 'puppeteer/dom_world'
34
34
  require 'puppeteer/emulation_manager'
35
35
  require 'puppeteer/exception_details'
36
+ require 'puppeteer/executable_path_finder'
36
37
  require 'puppeteer/execution_context'
37
38
  require 'puppeteer/file_chooser'
38
39
  require 'puppeteer/frame'
39
40
  require 'puppeteer/frame_manager'
41
+ require 'puppeteer/http_request'
42
+ require 'puppeteer/http_response'
40
43
  require 'puppeteer/js_coverage'
41
44
  require 'puppeteer/js_handle'
42
45
  require 'puppeteer/keyboard'
@@ -50,8 +53,6 @@ require 'puppeteer/protocol_stream_reader'
50
53
  require 'puppeteer/puppeteer'
51
54
  require 'puppeteer/query_handler_manager'
52
55
  require 'puppeteer/remote_object'
53
- require 'puppeteer/request'
54
- require 'puppeteer/response'
55
56
  require 'puppeteer/target'
56
57
  require 'puppeteer/tracing'
57
58
  require 'puppeteer/timeout_helper'
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency 'rollbar'
33
33
  spec.add_development_dependency 'rspec', '~> 3.10.0 '
34
34
  spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
35
- spec.add_development_dependency 'rubocop', '~> 1.21.0'
35
+ spec.add_development_dependency 'rubocop', '~> 1.23.0'
36
36
  spec.add_development_dependency 'rubocop-rspec'
37
37
  spec.add_development_dependency 'sinatra'
38
38
  spec.add_development_dependency 'webrick'
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.37.0
4
+ version: 0.37.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-09-29 00:00:00.000000000 Z
11
+ date: 2021-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -170,14 +170,14 @@ dependencies:
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: 1.21.0
173
+ version: 1.23.0
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: 1.21.0
180
+ version: 1.23.0
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: rubocop-rspec
183
183
  requirement: !ruby/object:Gem::Requirement
@@ -282,11 +282,14 @@ files:
282
282
  - lib/puppeteer/event_callbackable.rb
283
283
  - lib/puppeteer/events.rb
284
284
  - lib/puppeteer/exception_details.rb
285
+ - lib/puppeteer/executable_path_finder.rb
285
286
  - lib/puppeteer/execution_context.rb
286
287
  - lib/puppeteer/file_chooser.rb
287
288
  - lib/puppeteer/frame.rb
288
289
  - lib/puppeteer/frame_manager.rb
289
290
  - lib/puppeteer/geolocation.rb
291
+ - lib/puppeteer/http_request.rb
292
+ - lib/puppeteer/http_response.rb
290
293
  - lib/puppeteer/if_present.rb
291
294
  - lib/puppeteer/js_coverage.rb
292
295
  - lib/puppeteer/js_handle.rb
@@ -313,8 +316,6 @@ files:
313
316
  - lib/puppeteer/puppeteer.rb
314
317
  - lib/puppeteer/query_handler_manager.rb
315
318
  - lib/puppeteer/remote_object.rb
316
- - lib/puppeteer/request.rb
317
- - lib/puppeteer/response.rb
318
319
  - lib/puppeteer/target.rb
319
320
  - lib/puppeteer/timeout_helper.rb
320
321
  - lib/puppeteer/timeout_settings.rb