puppeteer-ruby 0.31.3 → 0.32.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c29d76a1dd9f52a59eecdb311ab8c110a3393bc1500aea3df7d5defdc469d4f
4
- data.tar.gz: 296188c1573813718e0f3d320ceff5bbd8f036f8326f04499e6d81a77ca92051
3
+ metadata.gz: 9f7d2798e0f07a51bd53367bcfc86ada958490b9122c44fb85c2b3732000d402
4
+ data.tar.gz: 7ded942ec52ace5eaed09898167e4bc46810d41b483c90331da849c4654486ee
5
5
  SHA512:
6
- metadata.gz: 25411b18a7d0279ebc736981cc47db542340d49c01429b8419e95e3f3d3c66ce633de11c8b645a0eb1643dc41adedb215522d0f2a06d173cd6140aeedb44156f
7
- data.tar.gz: e1cc9c01f114534c4c44df4f036a9c9743b7330c1cfee710799381d9cdafe736d2c04498c673e3d79cd64671d4f270fcd37fc7cd0cbebf07d423736a5705851f
6
+ metadata.gz: 974171fa712907022449619056dedf0560501b34a388db71a948b7e3bdbdd4c783201810563d0e72b121c076d613a7c380d774bf8e2fc93350a4d0c9136d9ce3
7
+ data.tar.gz: 360be9be4c17223a36812abdbaf1f1fe9fbd8f020d2a1aa079f4019ac4ac6be600cbeb6dd42c28764b64306f673aacd295bdcb585594511b5302ff66e954405d
data/CHANGELOG.md CHANGED
@@ -1,7 +1,42 @@
1
- ### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.3...master)]
1
+ ### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.32.1...master)]
2
2
 
3
3
  * xxx
4
4
 
5
+ ### 0.32.1 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.32.0...0.32.1)]
6
+
7
+ Bugfix:
8
+
9
+ * Fix WebSocket to work with `wss://...` endpoint (ex. browserless.io)
10
+
11
+ ### 0.32.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.6...0.32.0)]
12
+
13
+ New features:
14
+
15
+ * Tracing
16
+ * JS/CSS coverages
17
+
18
+ Improvement:
19
+
20
+ * Increase stability [#92](https://github.com/YusukeIwaki/puppeteer-ruby/pull/92)
21
+
22
+ ### 0.31.6 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.5...0.31.6)]
23
+
24
+ Improvement:
25
+
26
+ * Increase stability [#87](https://github.com/YusukeIwaki/puppeteer-ruby/pull/87)
27
+
28
+ ### 0.31.5 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.4...0.31.5)]
29
+
30
+ Bugfix:
31
+
32
+ * Fix file uploading to work without crash.
33
+
34
+ ### 0.31.4 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.3...0.31.4)]
35
+
36
+ Bugfix:
37
+
38
+ * Fix PDF options (format, margin, omit_background) to work.
39
+
5
40
  ### 0.31.3 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.31.1...0.31.3)]
6
41
 
7
42
  Bugfix:
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
data/docs/api_coverage.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # API coverages
2
2
  - Puppeteer version: v8.0.0
3
- - puppeteer-ruby version: 0.31.3
3
+ - puppeteer-ruby version: 0.32.1
4
4
 
5
5
  ## Puppeteer
6
6
 
@@ -124,7 +124,7 @@
124
124
  * target
125
125
  * title
126
126
  * ~~touchscreen~~
127
- * ~~tracing~~
127
+ * tracing
128
128
  * type => `#type_text`
129
129
  * url
130
130
  * viewport
@@ -170,10 +170,10 @@
170
170
 
171
171
  * ~~tap~~
172
172
 
173
- ## ~~Tracing~~
173
+ ## Tracing
174
174
 
175
- * ~~start~~
176
- * ~~stop~~
175
+ * start
176
+ * stop
177
177
 
178
178
  ## FileChooser
179
179
 
@@ -337,12 +337,12 @@
337
337
  * detach
338
338
  * send
339
339
 
340
- ## ~~Coverage~~
340
+ ## Coverage
341
341
 
342
- * ~~startCSSCoverage~~
343
- * ~~startJSCoverage~~
344
- * ~~stopCSSCoverage~~
345
- * ~~stopJSCoverage~~
342
+ * startCSSCoverage => `#start_css_coverage`
343
+ * startJSCoverage => `#start_js_coverage`
344
+ * stopCSSCoverage => `#stop_css_coverage`
345
+ * stopJSCoverage => `#stop_js_coverage`
346
346
 
347
347
  ## TimeoutError
348
348
 
data/lib/puppeteer.rb CHANGED
@@ -26,6 +26,8 @@ require 'puppeteer/browser_runner'
26
26
  require 'puppeteer/cdp_session'
27
27
  require 'puppeteer/connection'
28
28
  require 'puppeteer/console_message'
29
+ require 'puppeteer/coverage'
30
+ require 'puppeteer/css_coverage'
29
31
  require 'puppeteer/custom_query_handler'
30
32
  require 'puppeteer/devices'
31
33
  require 'puppeteer/dialog'
@@ -36,6 +38,7 @@ require 'puppeteer/execution_context'
36
38
  require 'puppeteer/file_chooser'
37
39
  require 'puppeteer/frame'
38
40
  require 'puppeteer/frame_manager'
41
+ require 'puppeteer/js_coverage'
39
42
  require 'puppeteer/js_handle'
40
43
  require 'puppeteer/keyboard'
41
44
  require 'puppeteer/launcher'
@@ -43,12 +46,14 @@ require 'puppeteer/lifecycle_watcher'
43
46
  require 'puppeteer/mouse'
44
47
  require 'puppeteer/network_manager'
45
48
  require 'puppeteer/page'
49
+ require 'puppeteer/protocol_stream_reader'
46
50
  require 'puppeteer/puppeteer'
47
51
  require 'puppeteer/query_handler_manager'
48
52
  require 'puppeteer/remote_object'
49
53
  require 'puppeteer/request'
50
54
  require 'puppeteer/response'
51
55
  require 'puppeteer/target'
56
+ require 'puppeteer/tracing'
52
57
  require 'puppeteer/timeout_settings'
53
58
  require 'puppeteer/touch_screen'
54
59
  require 'puppeteer/version'
@@ -45,6 +45,7 @@ class Puppeteer::Browser
45
45
  @contexts[context_id] = Puppeteer::BrowserContext.new(@connection, self, context_id)
46
46
  end
47
47
  @targets = {}
48
+ @wait_for_creating_targets = {}
48
49
  @connection.on_event(ConnectionEmittedEvents::Disconnected) do
49
50
  emit_event(BrowserEmittedEvents::Disconnected)
50
51
  end
@@ -125,8 +126,10 @@ class Puppeteer::Browser
125
126
  ignore_https_errors: @ignore_https_errors,
126
127
  default_viewport: @default_viewport,
127
128
  )
128
- # assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
129
129
  @targets[target_info.target_id] = target
130
+ if_present(@wait_for_creating_targets.delete(target_info.target_id)) do |promise|
131
+ promise.fulfill(target)
132
+ end
130
133
  if await target.initialized_promise
131
134
  emit_event(BrowserEmittedEvents::TargetCreated, target)
132
135
  context.emit_event(BrowserContextEmittedEvents::TargetCreated, target)
@@ -139,6 +142,9 @@ class Puppeteer::Browser
139
142
  target = @targets[target_id]
140
143
  target.ignore_initialize_callback_promise
141
144
  @targets.delete(target_id)
145
+ if_present(@wait_for_creating_targets.delete(target_id)) do |promise|
146
+ promise.reject('target destroyed')
147
+ end
142
148
  target.closed_callback
143
149
  if await target.initialized_promise
144
150
  emit_event(BrowserEmittedEvents::TargetDestroyed, target)
@@ -184,6 +190,16 @@ class Puppeteer::Browser
184
190
  result = @connection.send_message('Target.createTarget', **create_target_params)
185
191
  target_id = result['targetId']
186
192
  target = @targets[target_id]
193
+ unless target
194
+ # Target.targetCreated is often notified before the response of Target.createdTarget.
195
+ # https://github.com/YusukeIwaki/puppeteer-ruby/issues/91
196
+ # D, [2021-04-07T03:00:10.125241 #187] DEBUG -- : SEND >> {"method":"Target.createTarget","params":{"url":"about:blank","browserContextId":"56A86FC3391B50180CF9A6450A0D8C21"},"id":3}
197
+ # D, [2021-04-07T03:00:10.142396 #187] DEBUG -- : RECV << {"id"=>3, "result"=>{"targetId"=>"A518447C415A1A3E1A8979454A155632"}}
198
+ # D, [2021-04-07T03:00:10.145360 #187] DEBUG -- : RECV << {"method"=>"Target.targetCreated", "params"=>{"targetInfo"=>{"targetId"=>"A518447C415A1A3E1A8979454A155632", "type"=>"page", "title"=>"", "url"=>"", "attached"=>false, "canAccessOpener"=>false, "browserContextId"=>"56A86FC3391B50180CF9A6450A0D8C21"}}}
199
+ # This is just a workaround logic...
200
+ @wait_for_creating_targets[target_id] = resolvable_future
201
+ target = await @wait_for_creating_targets[target_id]
202
+ end
187
203
  await target.initialized_promise
188
204
  await target.page
189
205
  end
@@ -46,7 +46,11 @@ class Puppeteer::Connection
46
46
  @transport.on_message do |data|
47
47
  message = JSON.parse(data)
48
48
  sleep_before_handling_message(message)
49
- async_handle_message(message)
49
+ if should_handle_synchronously?(message)
50
+ handle_message(message)
51
+ else
52
+ async_handle_message(message)
53
+ end
50
54
  end
51
55
  @transport.on_close do |reason, code|
52
56
  handle_close
@@ -71,6 +75,40 @@ class Puppeteer::Connection
71
75
  sleep 0.004
72
76
  end
73
77
 
78
+ private def should_handle_synchronously?(message)
79
+ return true if message['id']
80
+
81
+ case message['method']
82
+ when nil
83
+ false
84
+ when /^Network\./
85
+ # Puppeteer doesn't handle any Network monitoring responses.
86
+ # So we don't care their handling order.
87
+ false
88
+ when /^Page\.frame/
89
+ # Page.frameAttached
90
+ # Page.frameNavigated
91
+ # Page.frameDetached
92
+ # Page.frameStoppedLoading
93
+ true
94
+ when 'Page.lifecycleEvent'
95
+ true
96
+ when /^Runtime\.executionContext/
97
+ # - Runtime.executionContextCreated
98
+ # - Runtime.executionContextDestroyed
99
+ # - Runtime.executionContextsCleared
100
+ # These events should be strictly ordered.
101
+ true
102
+ when 'Target.attachedToTarget', 'Target.detachedFromTarget'
103
+ true
104
+ when 'Target.targetCreated'
105
+ # type=page must be handled asynchronously for avoiding wait timeout...
106
+ message.dig('params', 'targetInfo', 'type') == 'browser'
107
+ else
108
+ false
109
+ end
110
+ end
111
+
74
112
  def self.from_session(session)
75
113
  session.connection
76
114
  end
@@ -273,16 +311,6 @@ class Puppeteer::Connection
273
311
  def create_session(target_info)
274
312
  result = send_message('Target.attachToTarget', targetId: target_info.target_id, flatten: true)
275
313
  session_id = result['sessionId']
276
-
277
- # Target.attachedToTarget is often notified after the result of Target.attachToTarget.
278
- # D, [2020-04-04T23:04:30.736311 #91875] DEBUG -- : RECV << {"id"=>2, "result"=>{"sessionId"=>"DA002F8A95B04710502CB40D8430B95A"}}
279
- # D, [2020-04-04T23:04:30.736649 #91875] DEBUG -- : RECV << {"method"=>"Target.attachedToTarget", "params"=>{"sessionId"=>"DA002F8A95B04710502CB40D8430B95A", "targetInfo"=>{"targetId"=>"EBAB949A7DE63F12CB94268AD3A9976B", "type"=>"page", "title"=>"about:blank", "url"=>"about:blank", "attached"=>true, "browserContextId"=>"46D23767E9B79DD9E589101121F6DADD"}, "waitingForDebugger"=>false}}
280
- # So we have to wait for "Target.attachedToTarget" a bit.
281
- 20.times do
282
- if @sessions[session_id]
283
- return @sessions[session_id]
284
- end
285
- sleep 0.1
286
- end
314
+ @sessions[session_id]
287
315
  end
288
316
  end
@@ -0,0 +1,106 @@
1
+ class Puppeteer::Coverage
2
+ # @param client [Puppeteer::CDPSession]
3
+ def initialize(client)
4
+ @js = Puppeteer::JSCoverage.new(client)
5
+ @css = Puppeteer::CSSCoverage.new(client)
6
+ end
7
+
8
+ def start_js_coverage(reset_on_navigation: nil, report_anonymous_scripts: nil)
9
+ @js.start(
10
+ reset_on_navigation: reset_on_navigation,
11
+ report_anonymous_scripts: report_anonymous_scripts,
12
+ )
13
+ end
14
+
15
+ def stop_js_coverage
16
+ @js.stop
17
+ end
18
+
19
+ def js_coverage(reset_on_navigation: nil, report_anonymous_scripts: nil, &block)
20
+ unless block
21
+ raise ArgumentError.new('Block must be given')
22
+ end
23
+
24
+ start_js_coverage(
25
+ reset_on_navigation: reset_on_navigation,
26
+ report_anonymous_scripts: report_anonymous_scripts,
27
+ )
28
+ block.call
29
+ stop_js_coverage
30
+ end
31
+
32
+ def start_css_coverage(reset_on_navigation: nil)
33
+ @css.start(reset_on_navigation: reset_on_navigation)
34
+ end
35
+
36
+ def stop_css_coverage
37
+ @css.stop
38
+ end
39
+
40
+ def css_coverage(reset_on_navigation: nil, &block)
41
+ unless block
42
+ raise ArgumentError.new('Block must be given')
43
+ end
44
+
45
+ start_css_coverage(reset_on_navigation: reset_on_navigation)
46
+ block.call
47
+ stop_css_coverage
48
+ end
49
+
50
+ module UtilFunctions
51
+ private def convert_to_disjoint_ranges(nested_ranges)
52
+ points = []
53
+ nested_ranges.each do |range|
54
+ points << { offset: range['startOffset'], type: 0, range: range }
55
+ points << { offset: range['endOffset'], type: 1, range: range }
56
+ end
57
+
58
+ # Sort points to form a valid parenthesis sequence.
59
+ points.sort! do |a, b|
60
+ if a[:offset] != b[:offset]
61
+ # Sort with increasing offsets.
62
+ a[:offset] <=> b[:offset]
63
+ elsif a[:type] != b[:type]
64
+ # All "end" points should go before "start" points.
65
+ b[:type] <=> a[:type]
66
+ else
67
+ alength = a[:range]['endOffset'] - a[:range]['startOffset']
68
+ blength = b[:range]['endOffset'] - b[:range]['startOffset']
69
+ if a[:type] == 0
70
+ # For two "start" points, the one with longer range goes first.
71
+ blength <=> alength
72
+ else
73
+ # For two "end" points, the one with shorter range goes first.
74
+ alength <=> blength
75
+ end
76
+ end
77
+ end
78
+
79
+ hit_count_stack = []
80
+ results = []
81
+ last_offset = 0
82
+ # Run scanning line to intersect all ranges.
83
+ points.each do |point|
84
+ if !hit_count_stack.empty? && last_offset < point[:offset] && hit_count_stack.last > 0
85
+ last_result = results.last
86
+ if last_result && last_result[:end] == last_offset
87
+ last_result[:end] = point[:offset]
88
+ else
89
+ results << { start: last_offset, end: point[:offset] }
90
+ end
91
+ end
92
+ last_offset = point[:offset]
93
+ if point[:type] == 0
94
+ hit_count_stack << point[:range]['count']
95
+ else
96
+ hit_count_stack.pop
97
+ end
98
+ end
99
+
100
+ # Filter out empty ranges.
101
+ results.select do |range|
102
+ range[:end] - range[:start] > 1
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,110 @@
1
+ require_relative './coverage'
2
+
3
+ class Puppeteer::CSSCoverage
4
+ include Puppeteer::Coverage::UtilFunctions
5
+
6
+ class Item
7
+ def initialize(url:, ranges:, text:)
8
+ @url = url
9
+ @ranges = ranges
10
+ @text = text
11
+ end
12
+ attr_reader :url, :ranges, :text
13
+ end
14
+
15
+ # @param client [Puppeteer::CDPSession]
16
+ def initialize(client)
17
+ @client = client
18
+ @enabled = false
19
+ @stylesheet_urls = {}
20
+ @stylesheet_sources = {}
21
+ end
22
+
23
+ def start(reset_on_navigation: nil)
24
+ raise 'CSSCoverage is already enabled' if @enabled
25
+
26
+ @reset_on_navigation =
27
+ if [true, false].include?(reset_on_navigation)
28
+ reset_on_navigation
29
+ else
30
+ true
31
+ end
32
+
33
+ @enabled = true
34
+ @stylesheet_urls.clear
35
+ @stylesheet_sources.clear
36
+ @event_listeners = []
37
+ @event_listeners << @client.add_event_listener('CSS.styleSheetAdded') do |event|
38
+ future { on_stylesheet(event) }
39
+ end
40
+ @event_listeners << @client.add_event_listener('Runtime.executionContextsCleared') do
41
+ on_execution_contexts_cleared
42
+ end
43
+ await_all(
44
+ @client.async_send_message('DOM.enable'),
45
+ @client.async_send_message('CSS.enable'),
46
+ @client.async_send_message('CSS.startRuleUsageTracking'),
47
+ )
48
+ end
49
+
50
+ private def on_execution_contexts_cleared
51
+ return unless @reset_on_navigation
52
+ @stylesheet_urls.clear
53
+ @stylesheet_sources.clear
54
+ end
55
+
56
+ private def on_stylesheet(event)
57
+ header = event['header']
58
+ source_url =
59
+ if header['sourceURL'] == ""
60
+ nil
61
+ else
62
+ header['sourceURL']
63
+ end
64
+
65
+ # Ignore anonymous scripts
66
+ return if !source_url
67
+
68
+ response = @client.send_message('CSS.getStyleSheetText', styleSheetId: header['styleSheetId'])
69
+ @stylesheet_urls[header['styleSheetId']] = source_url
70
+ @stylesheet_sources[header['styleSheetId']] = response['text']
71
+ end
72
+
73
+
74
+ def stop
75
+ raise 'CSSCoverage is not enabled' unless @enabled
76
+ @enabled = false
77
+
78
+ rule_tracking_response = @client.send_message('CSS.stopRuleUsageTracking')
79
+ await_all(
80
+ @client.async_send_message('CSS.disable'),
81
+ @client.async_send_message('DOM.disable'),
82
+ )
83
+ @client.remove_event_listener(*@event_listeners)
84
+
85
+ # aggregate by styleSheetId
86
+ stylesheet_id_to_coverage = {}
87
+ rule_tracking_response['ruleUsage'].each do |entry|
88
+ ranges = stylesheet_id_to_coverage[entry['styleSheetId']]
89
+ unless ranges
90
+ ranges = []
91
+ stylesheet_id_to_coverage[entry['styleSheetId']] = ranges
92
+ end
93
+
94
+ ranges << {
95
+ 'startOffset' => entry['startOffset'],
96
+ 'endOffset' => entry['endOffset'],
97
+ 'count' => entry['used'] ? 1 : 0,
98
+ }
99
+ end
100
+
101
+ coverage = []
102
+ @stylesheet_urls.each do |stylesheet_id, url|
103
+ text = @stylesheet_sources[stylesheet_id]
104
+ ranges = convert_to_disjoint_ranges(stylesheet_id_to_coverage[stylesheet_id] || [])
105
+ coverage << Item.new(url: url, ranges: ranges, text: text)
106
+ end
107
+
108
+ coverage
109
+ end
110
+ end
@@ -183,7 +183,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
183
183
  end
184
184
 
185
185
  if error_path = file_paths.find { |file_path| !File.exist?(file_path) }
186
- raise ArgmentError.new("#{error_path} does not exist or is not readable")
186
+ raise ArgumentError.new("#{error_path} does not exist or is not readable")
187
187
  end
188
188
 
189
189
  backend_node_id = @remote_object.node_info(@client)["node"]["backendNodeId"]
@@ -201,9 +201,9 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
201
201
  element.dispatchEvent(new Event('change', { bubbles: true }));
202
202
  }
203
203
  JAVASCRIPT
204
- await this.evaluate(fn)
204
+ await evaluate(fn)
205
205
  else
206
- @remote_object.set_file_input_files(@client, file_paths, backend_node_id)
206
+ @remote_object.set_file_input_files(@client, file_paths.map { |path| File.expand_path(path) }, backend_node_id)
207
207
  end
208
208
  end
209
209
 
@@ -0,0 +1,119 @@
1
+ require_relative './coverage'
2
+
3
+ class Puppeteer::JSCoverage
4
+ include Puppeteer::Coverage::UtilFunctions
5
+
6
+ class Item
7
+ def initialize(url:, ranges:, text:)
8
+ @url = url
9
+ @ranges = ranges
10
+ @text = text
11
+ end
12
+ attr_reader :url, :ranges, :text
13
+ end
14
+
15
+ # @param client [Puppeteer::CDPSession]
16
+ def initialize(client)
17
+ @client = client
18
+ @enabled = false
19
+ @script_urls = {}
20
+ @script_sources = {}
21
+ end
22
+
23
+ def start(reset_on_navigation: nil, report_anonymous_scripts: nil)
24
+ raise 'JSCoverage is already enabled' if @enabled
25
+
26
+ @reset_on_navigation =
27
+ if [true, false].include?(reset_on_navigation)
28
+ reset_on_navigation
29
+ else
30
+ true
31
+ end
32
+ @report_anonymous_scripts = report_anonymous_scripts || false
33
+ @enabled = true
34
+ @script_urls.clear
35
+ @script_sources.clear
36
+ @event_listeners = []
37
+ @event_listeners << @client.add_event_listener('Debugger.scriptParsed') do |event|
38
+ future { on_script_parsed(event) }
39
+ end
40
+ @event_listeners << @client.add_event_listener('Runtime.executionContextsCleared') do
41
+ on_execution_contexts_cleared
42
+ end
43
+ await_all(
44
+ @client.async_send_message('Profiler.enable'),
45
+ @client.async_send_message('Profiler.startPreciseCoverage',
46
+ callCount: false,
47
+ detailed: true,
48
+ ),
49
+ @client.async_send_message('Debugger.enable'),
50
+ @client.async_send_message('Debugger.setSkipAllPauses', skip: true),
51
+ )
52
+ end
53
+
54
+ private def on_execution_contexts_cleared
55
+ return unless @reset_on_navigation
56
+ @script_urls.clear
57
+ @script_sources.clear
58
+ end
59
+
60
+ private def on_script_parsed(event)
61
+ url =
62
+ if event['url'] == ""
63
+ nil
64
+ else
65
+ event['url']
66
+ end
67
+
68
+ # Ignore puppeteer-injected scripts
69
+ return if url == Puppeteer::ExecutionContext::EVALUATION_SCRIPT_URL
70
+
71
+ # Ignore other anonymous scripts unless the reportAnonymousScripts option is true.
72
+ return if !url && !@report_anonymous_scripts
73
+
74
+ response = @client.send_message('Debugger.getScriptSource', scriptId: event['scriptId'])
75
+ @script_urls[event['scriptId']] = url
76
+ @script_sources[event['scriptId']] = response['scriptSource']
77
+ end
78
+
79
+ def stop
80
+ raise 'JSCoverage is not enabled' unless @enabled
81
+ @enabled = false
82
+
83
+ results = await_all(
84
+ @client.async_send_message('Profiler.takePreciseCoverage'),
85
+ @client.async_send_message('Profiler.stopPreciseCoverage'),
86
+ @client.async_send_message('Profiler.disable'),
87
+ @client.async_send_message('Debugger.disable'),
88
+ )
89
+ @client.remove_event_listener(*@event_listeners)
90
+
91
+ coverage = []
92
+ profile_response = results.first
93
+ profile_response['result'].each do |entry|
94
+ url = @script_urls[entry['scriptId']]
95
+
96
+ if @report_anonymous_scripts
97
+ url ||= "debugger://VM#{entry['scriptId']}"
98
+ end
99
+
100
+ text = @script_sources[entry['scriptId']]
101
+ next if !text || !url
102
+
103
+ flatten_ranges = []
104
+ entry['functions'].each do |func|
105
+ func['ranges'].each do |range|
106
+ flatten_ranges << range
107
+ end
108
+ end
109
+
110
+ coverage << Item.new(
111
+ url: url,
112
+ ranges: convert_to_disjoint_ranges(flatten_ranges),
113
+ text: text,
114
+ )
115
+ end
116
+
117
+ coverage
118
+ end
119
+ end
@@ -39,9 +39,9 @@ class Puppeteer::Page
39
39
  # @accessibility = Accessibility.new(client)
40
40
  @frame_manager = Puppeteer::FrameManager.new(client, self, ignore_https_errors, @timeout_settings)
41
41
  @emulation_manager = Puppeteer::EmulationManager.new(client)
42
- # @tracing = Tracing.new(client)
42
+ @tracing = Puppeteer::Tracing.new(client)
43
43
  @page_bindings = {}
44
- # @coverage = Coverage.new(client)
44
+ @coverage = Puppeteer::Coverage.new(client)
45
45
  @javascript_enabled = true
46
46
  @screenshot_task_queue = ScreenshotTaskQueue.new
47
47
 
@@ -245,7 +245,7 @@ class Puppeteer::Page
245
245
  @frame_manager.main_frame
246
246
  end
247
247
 
248
- attr_reader :touch_screen, :coverage, :accessibility
248
+ attr_reader :touch_screen, :coverage, :tracing, :accessibility
249
249
 
250
250
  def keyboard(&block)
251
251
  @keyboard.instance_eval(&block) unless block.nil?
@@ -587,6 +587,16 @@ class Puppeteer::Page
587
587
  emit_event(PageEmittedEvents::Dialog, dialog)
588
588
  end
589
589
 
590
+ private def set_transparent_background_color(&block)
591
+ @client.send_message(
592
+ 'Emulation.setDefaultBackgroundColorOverride',
593
+ color: { r: 0, g: 0, b: 0, a: 0 })
594
+ end
595
+
596
+ private def reset_default_background_color(&block)
597
+ @client.send_message('Emulation.setDefaultBackgroundColorOverride')
598
+ end
599
+
590
600
  # @return [String]
591
601
  def url
592
602
  main_frame.url
@@ -949,18 +959,14 @@ class Puppeteer::Page
949
959
  end
950
960
 
951
961
  should_set_default_background = screenshot_options.omit_background? && format == 'png'
952
- if should_set_default_background
953
- @client.send_message('Emulation.setDefaultBackgroundColorOverride', color: { r: 0, g: 0, b: 0, a: 0 })
954
- end
962
+ set_transparent_background_color if should_set_default_background
955
963
  screenshot_params = {
956
964
  format: format,
957
965
  quality: screenshot_options.quality,
958
966
  clip: clip,
959
967
  }.compact
960
968
  result = @client.send_message('Page.captureScreenshot', screenshot_params)
961
- if should_set_default_background
962
- @client.send_message('Emulation.setDefaultBackgroundColorOverride')
963
- end
969
+ reset_default_background_color if should_set_default_background
964
970
 
965
971
  if screenshot_options.full_page? && @viewport
966
972
  self.viewport = @viewport
@@ -980,35 +986,6 @@ class Puppeteer::Page
980
986
  buffer
981
987
  end
982
988
 
983
- class ProtocolStreamReader
984
- def initialize(client:, handle:, path:)
985
- @client = client
986
- @handle = handle
987
- @path = path
988
- end
989
-
990
- def read
991
- out = StringIO.new
992
- File.open(@path, 'wb') do |file|
993
- eof = false
994
- until eof
995
- response = @client.send_message('IO.read', handle: @handle)
996
- eof = response['eof']
997
- data =
998
- if response['base64Encoded']
999
- Base64.decode64(response['data'])
1000
- else
1001
- response['data']
1002
- end
1003
- out.write(data)
1004
- file.write(data)
1005
- end
1006
- end
1007
- @client.send_message('IO.close', handle: @handle)
1008
- out.read
1009
- end
1010
- end
1011
-
1012
989
  class PrintToPdfIsNotImplementedError < StandardError
1013
990
  def initialize
1014
991
  super('pdf() is only available in headless mode. See https://github.com/puppeteer/puppeteer/issues/1829')
@@ -1018,8 +995,11 @@ class Puppeteer::Page
1018
995
  # @return [String]
1019
996
  def pdf(options = {})
1020
997
  pdf_options = PDFOptions.new(options)
998
+ omit_background = options[:omit_background]
999
+ set_transparent_background_color if omit_background
1021
1000
  result = @client.send_message('Page.printToPDF', pdf_options.page_print_args)
1022
- ProtocolStreamReader.new(client: @client, handle: result['stream'], path: pdf_options.path).read
1001
+ reset_default_background_color if omit_background
1002
+ Puppeteer::ProtocolStreamReader.new(client: @client, handle: result['stream'], path: pdf_options.path).read
1023
1003
  rescue => err
1024
1004
  if err.message.include?('PrintToPDF is not implemented')
1025
1005
  raise PrintToPdfIsNotImplementedError.new
@@ -52,24 +52,24 @@ class Puppeteer::Page
52
52
  end
53
53
 
54
54
  PAPER_FORMATS = {
55
- letter: PaperSize.new(width: 8.5, height: 11),
56
- legal: PaperSize.new(width: 8.5, height: 14),
57
- tabloid: PaperSize.new(width: 11, height: 17),
58
- ledger: PaperSize.new(width: 17, height: 11),
59
- a0: PaperSize.new(width: 33.1, height: 46.8),
60
- a1: PaperSize.new(width: 23.4, height: 33.1),
61
- a2: PaperSize.new(width: 16.54, height: 23.4),
62
- a3: PaperSize.new(width: 11.7, height: 16.54),
63
- a4: PaperSize.new(width: 8.27, height: 11.7),
64
- a5: PaperSize.new(width: 5.83, height: 8.27),
65
- a6: PaperSize.new(width: 4.13, height: 5.83),
55
+ 'letter' => PaperSize.new(width: 8.5, height: 11),
56
+ 'legal' => PaperSize.new(width: 8.5, height: 14),
57
+ 'tabloid' => PaperSize.new(width: 11, height: 17),
58
+ 'ledger' => PaperSize.new(width: 17, height: 11),
59
+ 'a0' => PaperSize.new(width: 33.1, height: 46.8),
60
+ 'a1' => PaperSize.new(width: 23.4, height: 33.1),
61
+ 'a2' => PaperSize.new(width: 16.54, height: 23.4),
62
+ 'a3' => PaperSize.new(width: 11.7, height: 16.54),
63
+ 'a4' => PaperSize.new(width: 8.27, height: 11.7),
64
+ 'a5' => PaperSize.new(width: 5.83, height: 8.27),
65
+ 'a6' => PaperSize.new(width: 4.13, height: 5.83),
66
66
  }
67
67
 
68
68
  UNIT_TO_PIXELS = {
69
- px: 1,
70
- in: 96,
71
- cm: 37.8,
72
- mm: 3.78,
69
+ 'px' => 1,
70
+ 'in' => 96,
71
+ 'cm' => 37.8,
72
+ 'mm' => 3.78,
73
73
  }
74
74
 
75
75
  # @param parameter [String|Integer|nil]
@@ -0,0 +1,45 @@
1
+ class Puppeteer::ProtocolStreamReader
2
+ def initialize(client:, handle:, path:)
3
+ @client = client
4
+ @handle = handle
5
+ @path = path
6
+ end
7
+
8
+ def read
9
+ StringIO.open do |out|
10
+ if @path
11
+ File.open(@path, 'wb') do |file|
12
+ io_read do |data|
13
+ out.write(data)
14
+ file.write(data)
15
+ end
16
+ end
17
+ else
18
+ io_read { |data| out.write(data) }
19
+ end
20
+ io_close
21
+
22
+ out.string
23
+ end
24
+ end
25
+
26
+ private def io_read(&block)
27
+ eof = false
28
+ until eof
29
+ response = @client.send_message('IO.read', handle: @handle)
30
+ eof = response['eof']
31
+ data =
32
+ if response['base64Encoded']
33
+ Base64.decode64(response['data'])
34
+ else
35
+ response['data']
36
+ end
37
+ block.call(data)
38
+ end
39
+ end
40
+
41
+
42
+ private def io_close
43
+ @client.send_message('IO.close', handle: @handle)
44
+ end
45
+ end
@@ -0,0 +1,50 @@
1
+ class Puppeteer::Tracing
2
+ # @param client [Puppeteer::CDPSession]
3
+ def initialize(client)
4
+ @client = client
5
+ @recording = false
6
+ end
7
+
8
+ DEFAULT_CATEGORIES = [
9
+ '-*',
10
+ 'devtools.timeline',
11
+ 'v8.execute',
12
+ 'disabled-by-default-devtools.timeline',
13
+ 'disabled-by-default-devtools.timeline.frame',
14
+ 'toplevel',
15
+ 'blink.console',
16
+ 'blink.user_timing',
17
+ 'latencyInfo',
18
+ 'disabled-by-default-devtools.timeline.stack',
19
+ 'disabled-by-default-v8.cpu_profiler',
20
+ 'disabled-by-default-v8.cpu_profiler.hires',
21
+ ].freeze
22
+
23
+ def start(path: nil, screenshots: nil, categories: nil)
24
+ option_categories = categories || DEFAULT_CATEGORIES.dup
25
+
26
+ if screenshots
27
+ option_categories << 'disabled-by-default-devtools.screenshot'
28
+ end
29
+
30
+ @path = path
31
+ @recording = true
32
+ @client.send_message('Tracing.start',
33
+ transferMode: 'ReturnAsStream',
34
+ categories: option_categories.join(','),
35
+ )
36
+ end
37
+
38
+ def stop
39
+ stream_promise = resolvable_future do |f|
40
+ @client.once('Tracing.tracingComplete') do |event|
41
+ f.fulfill(event['stream'])
42
+ end
43
+ end
44
+ @client.send_message('Tracing.end')
45
+ @recording = false
46
+
47
+ stream = await stream_promise
48
+ Puppeteer::ProtocolStreamReader.new(client: @client, handle: stream, path: @path).read
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module Puppeteer
2
- VERSION = '0.31.3'
2
+ VERSION = '0.32.1'
3
3
  end
@@ -1,3 +1,4 @@
1
+ require 'openssl'
1
2
  require 'socket'
2
3
  require 'websocket/driver'
3
4
 
@@ -5,11 +6,28 @@ require 'websocket/driver'
5
6
  # ref: https://github.com/cavalle/chrome_remote/blob/master/lib/chrome_remote/web_socket_client.rb
6
7
  class Puppeteer::WebSocket
7
8
  class DriverImpl # providing #url, #write(string)
9
+ class SecureSocketFactory
10
+ def initialize(host, port)
11
+ @host = host
12
+ @port = port || 443
13
+ end
14
+
15
+ def create
16
+ tcp_socket = TCPSocket.new(@host, @port)
17
+ OpenSSL::SSL::SSLSocket.new(tcp_socket).tap(&:connect)
18
+ end
19
+ end
20
+
8
21
  def initialize(url)
9
22
  @url = url
10
23
 
11
24
  endpoint = URI.parse(url)
12
- @socket = TCPSocket.new(endpoint.host, endpoint.port)
25
+ @socket =
26
+ if endpoint.scheme == 'wss'
27
+ SecureSocketFactory.new(endpoint.host, endpoint.port).create
28
+ else
29
+ TCPSocket.new(endpoint.host, endpoint.port)
30
+ end
13
31
  end
14
32
 
15
33
  attr_reader :url
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'dry-inflector'
29
29
  spec.add_development_dependency 'pry-byebug'
30
30
  spec.add_development_dependency 'rake', '~> 13.0.3'
31
+ spec.add_development_dependency 'rollbar'
31
32
  spec.add_development_dependency 'rspec', '~> 3.10.0 '
32
33
  spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
33
34
  spec.add_development_dependency 'rubocop', '~> 1.12.0'
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.31.3
4
+ version: 0.32.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-26 00:00:00.000000000 Z
11
+ date: 2021-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 13.0.3
125
+ - !ruby/object:Gem::Dependency
126
+ name: rollbar
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rspec
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -232,6 +246,7 @@ files:
232
246
  - CHANGELOG.md
233
247
  - Dockerfile
234
248
  - Gemfile
249
+ - LICENSE
235
250
  - README.md
236
251
  - Rakefile
237
252
  - bin/console
@@ -248,6 +263,8 @@ files:
248
263
  - lib/puppeteer/concurrent_ruby_utils.rb
249
264
  - lib/puppeteer/connection.rb
250
265
  - lib/puppeteer/console_message.rb
266
+ - lib/puppeteer/coverage.rb
267
+ - lib/puppeteer/css_coverage.rb
251
268
  - lib/puppeteer/custom_query_handler.rb
252
269
  - lib/puppeteer/debug_print.rb
253
270
  - lib/puppeteer/define_async_method.rb
@@ -271,6 +288,7 @@ files:
271
288
  - lib/puppeteer/frame_manager.rb
272
289
  - lib/puppeteer/geolocation.rb
273
290
  - lib/puppeteer/if_present.rb
291
+ - lib/puppeteer/js_coverage.rb
274
292
  - lib/puppeteer/js_handle.rb
275
293
  - lib/puppeteer/keyboard.rb
276
294
  - lib/puppeteer/keyboard/key_description.rb
@@ -289,6 +307,7 @@ files:
289
307
  - lib/puppeteer/page/pdf_options.rb
290
308
  - lib/puppeteer/page/screenshot_options.rb
291
309
  - lib/puppeteer/page/screenshot_task_queue.rb
310
+ - lib/puppeteer/protocol_stream_reader.rb
292
311
  - lib/puppeteer/puppeteer.rb
293
312
  - lib/puppeteer/query_handler_manager.rb
294
313
  - lib/puppeteer/remote_object.rb
@@ -297,6 +316,7 @@ files:
297
316
  - lib/puppeteer/target.rb
298
317
  - lib/puppeteer/timeout_settings.rb
299
318
  - lib/puppeteer/touch_screen.rb
319
+ - lib/puppeteer/tracing.rb
300
320
  - lib/puppeteer/version.rb
301
321
  - lib/puppeteer/viewport.rb
302
322
  - lib/puppeteer/wait_task.rb