puppeteer-ruby 0.0.23 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,10 +12,21 @@ class Puppeteer::ExecutionContext
12
12
  @client = client
13
13
  @world = world
14
14
  @context_id = context_payload['id']
15
+ @context_name = context_payload['name']
15
16
  end
16
17
 
17
18
  attr_reader :client, :world
18
19
 
20
+ # only used in DOMWorld
21
+ private def _context_id
22
+ @context_id
23
+ end
24
+
25
+ # only used in DOMWorld::BindingFunction#add_binding_to_context
26
+ private def _context_name
27
+ @context_name
28
+ end
29
+
19
30
  # @return [Puppeteer::Frame]
20
31
  def frame
21
32
  if_present(@world) do |world|
@@ -223,6 +234,7 @@ class Puppeteer::ExecutionContext
223
234
  remote_object: Puppeteer::RemoteObject.new(response["object"]),
224
235
  )
225
236
  end
237
+ private define_async_method :async_adopt_backend_node_id
226
238
 
227
239
  # @param element_handle [Puppeteer::ElementHandle]
228
240
  # @return [Puppeteer::ElementHandle]
@@ -92,7 +92,7 @@ module Puppeteer::Launcher
92
92
  '--disable-default-apps',
93
93
  '--disable-dev-shm-usage',
94
94
  '--disable-extensions',
95
- '--disable-features=TranslateUI',
95
+ '--disable-features=Translate',
96
96
  '--disable-hang-monitor',
97
97
  '--disable-ipc-flooding-protection',
98
98
  '--disable-popup-blocking',
@@ -105,6 +105,9 @@ module Puppeteer::Launcher
105
105
  '--enable-automation',
106
106
  '--password-store=basic',
107
107
  '--use-mock-keychain',
108
+ # TODO(sadym): remove '--enable-blink-features=IdleDetection'
109
+ # once IdleDetection is turned on by default.
110
+ '--enable-blink-features=IdleDetection',
108
111
  ]
109
112
 
110
113
  if chrome_arg_options.user_data_dir
@@ -3,6 +3,7 @@ require "stringio"
3
3
 
4
4
  require_relative './page/pdf_options'
5
5
  require_relative './page/screenshot_options'
6
+ require_relative './page/screenshot_task_queue'
6
7
 
7
8
  class Puppeteer::Page
8
9
  include Puppeteer::EventCallbackable
@@ -13,10 +14,9 @@ class Puppeteer::Page
13
14
  # @param {!Puppeteer.Target} target
14
15
  # @param {boolean} ignoreHTTPSErrors
15
16
  # @param {?Puppeteer.Viewport} defaultViewport
16
- # @param {!Puppeteer.TaskQueue} screenshotTaskQueue
17
17
  # @return {!Promise<!Page>}
18
- def self.create(client, target, ignore_https_errors, default_viewport, screenshot_task_queue)
19
- page = Puppeteer::Page.new(client, target, ignore_https_errors, screenshot_task_queue)
18
+ def self.create(client, target, ignore_https_errors, default_viewport)
19
+ page = Puppeteer::Page.new(client, target, ignore_https_errors)
20
20
  page.init
21
21
  if default_viewport
22
22
  page.viewport = default_viewport
@@ -27,8 +27,7 @@ class Puppeteer::Page
27
27
  # @param {!Puppeteer.CDPSession} client
28
28
  # @param {!Puppeteer.Target} target
29
29
  # @param {boolean} ignoreHTTPSErrors
30
- # @param {!Puppeteer.TaskQueue} screenshotTaskQueue
31
- def initialize(client, target, ignore_https_errors, screenshot_task_queue)
30
+ def initialize(client, target, ignore_https_errors)
32
31
  @closed = false
33
32
  @client = client
34
33
  @target = target
@@ -43,7 +42,7 @@ class Puppeteer::Page
43
42
  @page_bindings = {}
44
43
  # @coverage = Coverage.new(client)
45
44
  @javascript_enabled = true
46
- @screenshot_task_queue = screenshot_task_queue
45
+ @screenshot_task_queue = ScreenshotTaskQueue.new
47
46
 
48
47
  @workers = {}
49
48
  @client.on_event('Target.attachedToTarget') do |event|
@@ -341,47 +340,34 @@ class Puppeteer::Page
341
340
 
342
341
  define_async_method :async_Sx
343
342
 
344
- # /**
345
- # * @param {!Array<string>} urls
346
- # * @return {!Promise<!Array<Network.Cookie>>}
347
- # */
348
- # async cookies(...urls) {
349
- # return (await this._client.send('Network.getCookies', {
350
- # urls: urls.length ? urls : [this.url()]
351
- # })).cookies;
352
- # }
343
+ # @return [Array<Hash>]
344
+ def cookies(*urls)
345
+ @client.send_message('Network.getCookies', urls: (urls.empty? ? [url] : urls))['cookies']
346
+ end
353
347
 
354
- # /**
355
- # * @param {Array<Protocol.Network.deleteCookiesParameters>} cookies
356
- # */
357
- # async deleteCookie(...cookies) {
358
- # const pageURL = this.url();
359
- # for (const cookie of cookies) {
360
- # const item = Object.assign({}, cookie);
361
- # if (!cookie.url && pageURL.startsWith('http'))
362
- # item.url = pageURL;
363
- # await this._client.send('Network.deleteCookies', item);
364
- # }
365
- # }
348
+ def delete_cookie(*cookies)
349
+ page_url = url
350
+ starts_with_http = page_url.start_with?("http")
351
+ cookies.each do |cookie|
352
+ item = (starts_with_http ? { url: page_url } : {}).merge(cookie)
353
+ @client.send_message("Network.deleteCookies", item)
354
+ end
355
+ end
366
356
 
367
- # /**
368
- # * @param {Array<Network.CookieParam>} cookies
369
- # */
370
- # async setCookie(...cookies) {
371
- # const pageURL = this.url();
372
- # const startsWithHTTP = pageURL.startsWith('http');
373
- # const items = cookies.map(cookie => {
374
- # const item = Object.assign({}, cookie);
375
- # if (!item.url && startsWithHTTP)
376
- # item.url = pageURL;
377
- # assert(item.url !== 'about:blank', `Blank page can not have cookie "${item.name}"`);
378
- # assert(!String.prototype.startsWith.call(item.url || '', 'data:'), `Data URL page can not have cookie "${item.name}"`);
379
- # return item;
380
- # });
381
- # await this.deleteCookie(...items);
382
- # if (items.length)
383
- # await this._client.send('Network.setCookies', { cookies: items });
384
- # }
357
+ def set_cookie(*cookies)
358
+ page_url = url
359
+ starts_with_http = page_url.start_with?("http")
360
+ items = cookies.map do |cookie|
361
+ (starts_with_http ? { url: page_url } : {}).merge(cookie).tap do |item|
362
+ raise ArgumentError.new("Blank page can not have cookie \"#{item[:name]}\"") if item[:url] == "about:blank"
363
+ raise ArgumetnError.new("Data URL page can not have cookie \"#{item[:name]}\"") if item[:url]&.start_with?("data:")
364
+ end
365
+ end
366
+ delete_cookie(*items)
367
+ unless items.empty?
368
+ @client.send_message("Network.setCookies", cookies: items)
369
+ end
370
+ end
385
371
 
386
372
  class ScriptTag
387
373
  # @param {!{content?: string, path?: string, type?: string, url?: string}} options
@@ -843,6 +829,21 @@ class Puppeteer::Page
843
829
  end
844
830
  end
845
831
 
832
+ # @param is_user_active [Boolean]
833
+ # @param is_screen_unlocked [Boolean]
834
+ def emulate_idle_state(is_user_active: nil, is_screen_unlocked: nil)
835
+ overrides = {
836
+ isUserActive: is_user_active,
837
+ isScreenUnlocked: is_screen_unlocked,
838
+ }.compact
839
+
840
+ if overrides.empty?
841
+ @client.send_message('Emulation.clearIdleOverride')
842
+ else
843
+ @client.send_message('Emulation.setIdleOverride', overrides)
844
+ end
845
+ end
846
+
846
847
  # @param viewport [Viewport]
847
848
  def viewport=(viewport)
848
849
  needs_reload = @emulation_manager.emulate_viewport(viewport)
@@ -880,15 +881,28 @@ class Puppeteer::Page
880
881
  main_frame.title
881
882
  end
882
883
 
883
- # /**
884
- # * @param {!ScreenshotOptions=} options
885
- # * @return {!Promise<!Buffer|!String>}
886
- # */
887
- def screenshot(options = {})
884
+ # @param type [String] "png"|"jpeg"
885
+ # @param path [String]
886
+ # @param full_page [Boolean]
887
+ # @param clip [Hash]
888
+ # @param quality [Integer]
889
+ # @param omit_background [Boolean]
890
+ # @param encoding [String]
891
+ def screenshot(type: nil, path: nil, full_page: nil, clip: nil, quality: nil, omit_background: nil, encoding: nil)
892
+ options = {
893
+ type: type,
894
+ path: path,
895
+ full_page: full_page,
896
+ clip: clip,
897
+ quality: quality,
898
+ omit_background: omit_background,
899
+ encoding: encoding,
900
+ }.compact
888
901
  screenshot_options = ScreenshotOptions.new(options)
889
902
 
890
- # @screenshot_task_queue.post_task(-> { screenshot_task(screenshot_options.type, screenshot_options) })
891
- screenshot_task(screenshot_options.type, screenshot_options)
903
+ @screenshot_task_queue.post_task do
904
+ screenshot_task(screenshot_options.type, screenshot_options)
905
+ end
892
906
  end
893
907
 
894
908
  # @param {"png"|"jpeg"} format
@@ -32,14 +32,14 @@ class Puppeteer::Page
32
32
  @type ||= 'png'
33
33
 
34
34
  if options[:quality]
35
- unless @type == 'png'
35
+ unless @type == 'jpeg'
36
36
  raise ArgumentError.new("options.quality is unsupported for the #{@type} screenshots")
37
37
  end
38
38
  unless options[:quality].is_a?(Numeric)
39
39
  raise ArgumentError.new("Expected options.quality to be a number but found #{options[:quality].class}")
40
40
  end
41
41
  quality = options[:quality].to_i
42
- unless (0..100).include?(qualizy)
42
+ unless (0..100).include?(quality)
43
43
  raise ArgumentError.new("Expected options.quality to be between 0 and 100 (inclusive), got #{quality}")
44
44
  end
45
45
  @quality = quality
@@ -0,0 +1,13 @@
1
+ class Puppeteer::Page
2
+ class ScreenshotTaskQueue
3
+ def initialize
4
+ @chain = Concurrent::Promises.fulfilled_future(nil)
5
+ end
6
+
7
+ def post_task(&block)
8
+ result = @chain.then { block.call }
9
+ @chain = result.rescue { nil }
10
+ result.value!
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,65 @@
1
+ require 'singleton'
2
+
3
+ class Puppeteer::QueryHandlerManager
4
+ include Singleton
5
+
6
+ def query_handlers
7
+ @query_handlers ||= {
8
+ aria: Puppeteer::AriaQueryHandler.new,
9
+ }
10
+ end
11
+
12
+ private def default_handler
13
+ @default_handler ||= Puppeteer::CustomQueryHandler.new(
14
+ query_one: '(element, selector) => element.querySelector(selector)',
15
+ query_all: '(element, selector) => element.querySelectorAll(selector)',
16
+ )
17
+ end
18
+
19
+ class Result
20
+ def initialize(query_handler:, selector:)
21
+ @query_handler = query_handler
22
+ @selector = selector
23
+ end
24
+
25
+ def query_one(element_handle)
26
+ @query_handler.query_one(element_handle, @selector)
27
+ end
28
+
29
+ def wait_for(dom_world, visible:, hidden:, timeout:)
30
+ @query_handler.wait_for(dom_world, @selector, visible: visible, hidden: hidden, timeout: timeout)
31
+ end
32
+
33
+ def query_all(element_handle)
34
+ @query_handler.query_all(element_handle, @selector)
35
+ end
36
+
37
+ def query_all_array(element_handle)
38
+ @query_handler.query_all_array(element_handle, @selector)
39
+ end
40
+ end
41
+
42
+ def detect_query_handler(selector)
43
+ unless /^[a-zA-Z]+\// =~ selector
44
+ return Result.new(
45
+ query_handler: default_handler,
46
+ selector: selector,
47
+ )
48
+ end
49
+
50
+ chunk = selector.split("/")
51
+ name = chunk.shift
52
+ updated_selector = chunk.join("/")
53
+
54
+ query_handler = query_handlers[name.to_sym]
55
+
56
+ unless query_handler
57
+ raise ArgumentError.new("Query set to use \"#{name}\", but no query handler of that name was found")
58
+ end
59
+
60
+ Result.new(
61
+ query_handler: query_handler,
62
+ selector: updated_selector,
63
+ )
64
+ end
65
+ end
@@ -97,6 +97,18 @@ class Puppeteer::RemoteObject
97
97
  nil
98
98
  end
99
99
 
100
+ # used in ElementHandle#query_ax_tree
101
+ def query_ax_tree(client, accessible_name: nil, role: nil)
102
+ result = client.send_message('Accessibility.queryAXTree', {
103
+ objectId: @object_id,
104
+ accessibleName: accessible_name,
105
+ role: role,
106
+ }.compact)
107
+
108
+ result['nodes'].reject do |node|
109
+ node['role']['value'] == 'text'
110
+ end
111
+ end
100
112
 
101
113
  # helper#valueFromRemoteObject
102
114
  def value
@@ -18,15 +18,13 @@ class Puppeteer::Target
18
18
  # @param {!function():!Promise<!Puppeteer.CDPSession>} sessionFactory
19
19
  # @param {boolean} ignoreHTTPSErrors
20
20
  # @param {?Puppeteer.Viewport} defaultViewport
21
- # @param {!Puppeteer.TaskQueue} screenshotTaskQueue
22
- def initialize(target_info:, browser_context:, session_factory:, ignore_https_errors:, default_viewport:, screenshot_task_queue:)
21
+ def initialize(target_info:, browser_context:, session_factory:, ignore_https_errors:, default_viewport:)
23
22
  @target_info = target_info
24
23
  @browser_context = browser_context
25
24
  @target_id = target_info.target_id
26
25
  @session_factory = session_factory
27
26
  @ignore_https_errors = ignore_https_errors
28
27
  @default_viewport = default_viewport
29
- @screenshot_task_queue = screenshot_task_queue
30
28
 
31
29
 
32
30
  # /** @type {?Promise<!Puppeteer.Page>} */
@@ -87,7 +85,7 @@ class Puppeteer::Target
87
85
  def page
88
86
  if ['page', 'background_page', 'webview'].include?(@target_info.type) && @page.nil?
89
87
  client = @session_factory.call
90
- @page = Puppeteer::Page.create(client, self, @ignore_https_errors, @default_viewport, @screenshot_task_queue)
88
+ @page = Puppeteer::Page.create(client, self, @ignore_https_errors, @default_viewport)
91
89
  end
92
90
  @page
93
91
  end
@@ -1,3 +1,3 @@
1
1
  class Puppeteer
2
- VERSION = '0.0.23'
2
+ VERSION = '0.29.0'
3
3
  end
@@ -9,7 +9,7 @@ class Puppeteer::WaitTask
9
9
  end
10
10
  end
11
11
 
12
- def initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: [])
12
+ def initialize(dom_world:, predicate_body:, title:, polling:, timeout:, args: [], binding_function: nil)
13
13
  if polling.is_a?(String)
14
14
  if polling != 'raf' && polling != 'mutation'
15
15
  raise ArgumentError.new("Unknown polling option: #{polling}")
@@ -27,8 +27,12 @@ class Puppeteer::WaitTask
27
27
  @timeout = timeout
28
28
  @predicate_body = "return (#{predicate_body})(...args);"
29
29
  @args = args
30
+ @binding_function = binding_function
30
31
  @run_count = 0
31
- @dom_world._wait_tasks.add(self)
32
+ @dom_world.send(:_wait_tasks).add(self)
33
+ if binding_function
34
+ @dom_world.send(:_bound_functions)[binding_function.name] = binding_function
35
+ end
32
36
  @promise = resolvable_future
33
37
 
34
38
  # Since page navigation requires us to re-install the pageScript, we should track
@@ -53,8 +57,16 @@ class Puppeteer::WaitTask
53
57
 
54
58
  def rerun
55
59
  run_count = (@run_count += 1)
60
+ context = @dom_world.execution_context
61
+
62
+ return if @terminated || run_count != @run_count
63
+ if @binding_function
64
+ @dom_world.add_binding_to_context(context, @binding_function)
65
+ end
66
+ return if @terminated || run_count != @run_count
67
+
56
68
  begin
57
- success = @dom_world.execution_context.evaluate_handle(
69
+ success = context.evaluate_handle(
58
70
  WAIT_FOR_PREDICATE_PAGE_FUNCTION,
59
71
  @predicate_body,
60
72
  @polling,
@@ -103,7 +115,7 @@ class Puppeteer::WaitTask
103
115
 
104
116
  private def cleanup
105
117
  @timeout_cleared = true
106
- @dom_world._wait_tasks.delete(self)
118
+ @dom_world.send(:_wait_tasks).delete(self)
107
119
  end
108
120
 
109
121
  private define_async_method :async_rerun
@@ -21,13 +21,15 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency 'concurrent-ruby', '~> 1.1.0'
22
22
  spec.add_dependency 'websocket-driver', '>= 0.6.0'
23
23
  spec.add_dependency 'mime-types', '>= 3.0'
24
- spec.add_development_dependency 'bundler', '~> 1.17'
24
+ spec.add_development_dependency 'bundler', '~> 2.2.3'
25
+ spec.add_development_dependency 'chunky_png'
25
26
  spec.add_development_dependency 'pry-byebug'
26
- spec.add_development_dependency 'rake', '~> 10.0'
27
- spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'rake', '~> 13.0.3'
28
+ spec.add_development_dependency 'rspec', '~> 3.10.0 '
28
29
  spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
29
- spec.add_development_dependency 'rubocop', '~> 0.90.0'
30
+ spec.add_development_dependency 'rubocop', '~> 1.10.0'
30
31
  spec.add_development_dependency 'rubocop-rspec'
31
32
  spec.add_development_dependency 'sinatra'
33
+ spec.add_development_dependency 'webrick'
32
34
  spec.add_development_dependency 'yard'
33
35
  end
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.0.23
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-08 00:00:00.000000000 Z
11
+ date: 2021-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -58,14 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.17'
61
+ version: 2.2.3
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.17'
68
+ version: 2.2.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: chunky_png
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: pry-byebug
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -86,28 +100,28 @@ dependencies:
86
100
  requirements:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: '10.0'
103
+ version: 13.0.3
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: '10.0'
110
+ version: 13.0.3
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rspec
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '3.0'
117
+ version: 3.10.0
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '3.0'
124
+ version: 3.10.0
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec_junit_formatter
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +142,14 @@ dependencies:
128
142
  requirements:
129
143
  - - "~>"
130
144
  - !ruby/object:Gem::Version
131
- version: 0.90.0
145
+ version: 1.10.0
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
- version: 0.90.0
152
+ version: 1.10.0
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: rubocop-rspec
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +178,20 @@ dependencies:
164
178
  - - ">="
165
179
  - !ruby/object:Gem::Version
166
180
  version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: webrick
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
167
195
  - !ruby/object:Gem::Dependency
168
196
  name: yard
169
197
  requirement: !ruby/object:Gem::Requirement
@@ -186,6 +214,8 @@ extensions: []
186
214
  extra_rdoc_files: []
187
215
  files:
188
216
  - ".circleci/config.yml"
217
+ - ".github/ISSUE_TEMPLATE/bug_report.md"
218
+ - ".github/ISSUE_TEMPLATE/feature_request.md"
189
219
  - ".github/stale.yml"
190
220
  - ".github/workflows/docs.yml"
191
221
  - ".github/workflows/reviewdog.yml"
@@ -193,6 +223,7 @@ files:
193
223
  - ".rspec"
194
224
  - ".rubocop.yml"
195
225
  - ".travis.yml"
226
+ - CHANGELOG.md
196
227
  - Dockerfile
197
228
  - Gemfile
198
229
  - README.md
@@ -201,6 +232,7 @@ files:
201
232
  - bin/setup
202
233
  - docker-compose.yml
203
234
  - lib/puppeteer.rb
235
+ - lib/puppeteer/aria_query_handler.rb
204
236
  - lib/puppeteer/browser.rb
205
237
  - lib/puppeteer/browser_context.rb
206
238
  - lib/puppeteer/browser_fetcher.rb
@@ -209,6 +241,7 @@ files:
209
241
  - lib/puppeteer/concurrent_ruby_utils.rb
210
242
  - lib/puppeteer/connection.rb
211
243
  - lib/puppeteer/console_message.rb
244
+ - lib/puppeteer/custom_query_handler.rb
212
245
  - lib/puppeteer/debug_print.rb
213
246
  - lib/puppeteer/define_async_method.rb
214
247
  - lib/puppeteer/device.rb
@@ -248,6 +281,8 @@ files:
248
281
  - lib/puppeteer/page.rb
249
282
  - lib/puppeteer/page/pdf_options.rb
250
283
  - lib/puppeteer/page/screenshot_options.rb
284
+ - lib/puppeteer/page/screenshot_task_queue.rb
285
+ - lib/puppeteer/query_handler_manager.rb
251
286
  - lib/puppeteer/remote_object.rb
252
287
  - lib/puppeteer/request.rb
253
288
  - lib/puppeteer/response.rb