OLD | NEW |
---|---|
1 /** | 1 /** |
2 * Copyright 2016 The Chromium Authors. All rights reserved. | 2 * Copyright 2016 The Chromium Authors. All rights reserved. |
3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
5 */ | 5 */ |
6 | 6 |
7 /** | |
8 * Maps "RTCStats.type" values to descriptions of whitelisted (allowed to be | |
9 * exposed to the web) RTCStats-derived dictionaries described below. | |
10 * @private | |
11 */ | |
12 var gStatsWhitelist = new Map(); | |
13 | |
14 /** | |
15 * RTCRTPStreamStats | |
16 * https://w3c.github.io/webrtc-stats/#streamstats-dict* | |
17 * @private | |
18 */ | |
19 var kRTCRTPStreamStats = new RTCStats_(null, { | |
20 ssrc: 'string', | |
21 associateStatsId: 'string', | |
22 isRemote: 'boolean', | |
23 mediaType: 'string', | |
24 mediaTrackId: 'string', | |
25 transportId: 'string', | |
26 codecId: 'string', | |
27 firCount: 'number', | |
28 pliCount: 'number', | |
29 nackCount: 'number', | |
30 sliCount: 'number', | |
31 }); | |
32 | |
33 /* | |
34 * RTCCodecStats | |
35 * https://w3c.github.io/webrtc-stats/#codec-dict* | |
36 * @private | |
37 */ | |
38 var kRTCCodecStats = new RTCStats_(null, { | |
39 payloadType: 'number', | |
40 codec: 'string', | |
41 clockRate: 'number', | |
42 channels: 'number', | |
43 parameters: 'string', | |
44 implementation: 'string', | |
45 }); | |
46 gStatsWhitelist.set('codec', kRTCCodecStats); | |
47 | |
48 /* | |
49 * RTCInboundRTPStreamStats | |
50 * https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict* | |
51 * @private | |
52 */ | |
53 var kRTCInboundRTPStreamStats = new RTCStats_(kRTCRTPStreamStats, { | |
54 packetsReceived: 'number', | |
55 bytesReceived: 'number', | |
56 packetsLost: 'number', | |
57 jitter: 'number', | |
58 fractionLost: 'number', | |
59 packetsDiscarded: 'number', | |
60 packetsRepaired: 'number', | |
61 burstPacketsLost: 'number', | |
62 burstPacketsDiscarded: 'number', | |
63 burstLossCount: 'number', | |
64 burstDiscardCount: 'number', | |
65 burstLossRate: 'number', | |
66 burstDiscardRate: 'number', | |
67 gapLossRate: 'number', | |
68 gapDiscardRate: 'number', | |
69 }); | |
70 gStatsWhitelist.set('inbound-rtp', kRTCInboundRTPStreamStats); | |
71 | |
72 /* | |
73 * RTCOutboundRTPStreamStats | |
74 * https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict* | |
75 * @private | |
76 */ | |
77 var kRTCOutboundRTPStreamStats = new RTCStats_(kRTCRTPStreamStats, { | |
78 packetsSent: 'number', | |
79 bytesSent: 'number', | |
80 targetBitrate: 'number', | |
81 roundTripTime: 'number', | |
82 }); | |
83 gStatsWhitelist.set('outbound-rtp', kRTCOutboundRTPStreamStats); | |
84 | |
85 /* | |
86 * RTCPeerConnectionStats | |
87 * https://w3c.github.io/webrtc-stats/#pcstats-dict* | |
88 * @private | |
89 */ | |
90 var kRTCPeerConnectionStats = new RTCStats_(null, { | |
91 dataChannelsOpened: 'number', | |
92 dataChannelsClosed: 'number', | |
93 }); | |
94 gStatsWhitelist.set('peer-connection', kRTCPeerConnectionStats); | |
95 | |
96 /* | |
97 * RTCMediaStreamStats | |
98 * https://w3c.github.io/webrtc-stats/#msstats-dict* | |
99 * @private | |
100 */ | |
101 var kRTCMediaStreamStats = new RTCStats_(null, { | |
102 streamIdentifier: 'string', | |
103 trackIds: 'sequence_string', | |
104 }); | |
105 gStatsWhitelist.set('stream', kRTCMediaStreamStats); | |
106 | |
107 /* | |
108 * RTCMediaStreamTrackStats | |
109 * https://w3c.github.io/webrtc-stats/#mststats-dict* | |
110 * @private | |
111 */ | |
112 var kRTCMediaStreamTrackStats = new RTCStats_(null, { | |
113 trackIdentifier: 'string', | |
114 remoteSource: 'boolean', | |
115 ended: 'boolean', | |
116 detached: 'boolean', | |
117 ssrcIds: 'sequence_string', | |
118 frameWidth: 'number', | |
119 frameHeight: 'number', | |
120 framesPerSecond: 'number', | |
121 framesSent: 'number', | |
122 framesReceived: 'number', | |
123 framesDecoded: 'number', | |
124 framesDropped: 'number', | |
125 framesCorrupted: 'number', | |
126 partialFramesLost: 'number', | |
127 fullFramesLost: 'number', | |
128 audioLevel: 'number', | |
129 echoReturnLoss: 'number', | |
130 echoReturnLossEnhancement: 'number', | |
131 }); | |
132 gStatsWhitelist.set('track', kRTCMediaStreamTrackStats); | |
133 | |
134 /* | |
135 * RTCDataChannelStats | |
136 * https://w3c.github.io/webrtc-stats/#dcstats-dict* | |
137 * @private | |
138 */ | |
139 var kRTCDataChannelStats = new RTCStats_(null, { | |
140 label: 'string', | |
141 protocol: 'string', | |
142 datachannelid: 'number', | |
143 state: 'string', | |
144 messagesSent: 'number', | |
145 bytesSent: 'number', | |
146 messagesReceived: 'number', | |
147 bytesReceived: 'number', | |
148 }); | |
149 gStatsWhitelist.set('data-channel', kRTCDataChannelStats); | |
150 | |
151 /* | |
152 * RTCTransportStats | |
153 * https://w3c.github.io/webrtc-stats/#transportstats-dict* | |
154 * @private | |
155 */ | |
156 var kRTCTransportStats = new RTCStats_(null, { | |
157 bytesSent: 'number', | |
158 bytesReceived: 'number', | |
159 rtcpTransportStatsId: 'string', | |
160 activeConnection: 'boolean', | |
161 selectedCandidatePairId: 'string', | |
162 localCertificateId: 'string', | |
163 remoteCertificateId: 'string', | |
164 }); | |
165 gStatsWhitelist.set('transport', kRTCTransportStats); | |
166 | |
167 /* | |
168 * RTCIceCandidateStats | |
169 * https://w3c.github.io/webrtc-stats/#icecandidate-dict* | |
170 * @private | |
171 */ | |
172 var kRTCIceCandidateStats = new RTCStats_(null, { | |
173 ip: 'string', | |
174 port: 'number', | |
175 protocol: 'string', | |
176 candidateType: 'string', | |
177 priority: 'number', | |
178 url: 'string', | |
179 }); | |
180 gStatsWhitelist.set('local-candidate', kRTCIceCandidateStats); | |
181 gStatsWhitelist.set('remote-candidate', kRTCIceCandidateStats); | |
182 | |
183 /* | |
184 * RTCIceCandidatePairStats | |
185 * https://w3c.github.io/webrtc-stats/#candidatepair-dict* | |
186 * @private | |
187 */ | |
188 var kRTCIceCandidatePairStats = new RTCStats_(null, { | |
189 transportId: 'string', | |
190 localCandidateId: 'string', | |
191 remoteCandidateId: 'string', | |
192 state: 'string', | |
193 priority: 'number', | |
194 nominated: 'boolean', | |
195 writable: 'boolean', | |
196 readable: 'boolean', | |
197 bytesSent: 'number', | |
198 bytesReceived: 'number', | |
199 totalRtt: 'number', | |
200 currentRtt: 'number', | |
201 availableOutgoingBitrate: 'number', | |
202 availableIncomingBitrate: 'number', | |
203 requestsReceived: 'number', | |
204 requestsSent: 'number', | |
205 responsesReceived: 'number', | |
206 responsesSent: 'number', | |
207 retransmissionsReceived: 'number', | |
208 retransmissionsSent: 'number', | |
209 consentRequestsReceived: 'number', | |
210 consentRequestsSent: 'number', | |
211 consentResponsesReceived: 'number', | |
212 consentResponsesSent: 'number', | |
213 }); | |
214 gStatsWhitelist.set('candidate-pair', kRTCIceCandidatePairStats); | |
215 | |
216 /* | |
217 * RTCCertificateStats | |
218 * https://w3c.github.io/webrtc-stats/#certificatestats-dict* | |
219 * @private | |
220 */ | |
221 var kRTCCertificateStats = new RTCStats_(null, { | |
222 fingerprint: 'string', | |
223 fingerprintAlgorithm: 'string', | |
224 base64Certificate: 'string', | |
225 issuerCertificateId: 'string', | |
226 }); | |
227 gStatsWhitelist.set('certificate', kRTCCertificateStats); | |
228 | |
7 // Public interface to tests. These are expected to be called with | 229 // Public interface to tests. These are expected to be called with |
8 // ExecuteJavascript invocations from the browser tests and will return answers | 230 // ExecuteJavascript invocations from the browser tests and will return answers |
9 // through the DOM automation controller. | 231 // through the DOM automation controller. |
10 | 232 |
11 /** | 233 /** |
12 * Verifies that the promise-based |RTCPeerConnection.getStats| returns stats. | 234 * Verifies that the promise-based |RTCPeerConnection.getStats| returns stats. |
13 * | 235 * |
14 * Returns ok-got-stats on success. | 236 * Returns to test "ok-" followed by a list of "RTCStats.type" values as a |
237 * string of semicolon separated values, these being the different types of | |
238 * stats that was returned by getStats. | |
15 */ | 239 */ |
16 function verifyStatsGeneratedPromise() { | 240 function verifyStatsGeneratedPromise() { |
17 peerConnection_().getStats() | 241 peerConnection_().getStats() |
18 .then(function(report) { | 242 .then(function(report) { |
19 if (report == null || report.size == 0) | 243 if (report == null || report.size == 0) |
20 throw new failTest('report is null or empty.'); | 244 throw new failTest('report is null or empty.'); |
21 // Sanity check that applies to all stats. | 245 let statsTypes = new Set(); |
22 var ids = new Set(); | 246 let ids = new Set(); |
23 report.forEach(function(stats) { | 247 for (let stats of report.values()) { |
24 if (typeof(stats.id) !== 'string') | 248 verifyStatsIsWhitelisted_(stats); |
25 throw failTest('stats.id is not a string.'); | 249 statsTypes.add(stats.type); |
26 if (ids.has(stats.id)) | 250 if (ids.has(stats.id)) |
27 throw failTest('stats.id is not a unique identifier.'); | 251 throw failTest('stats.id is not a unique identifier.'); |
28 ids.add(stats.id); | 252 ids.add(stats.id); |
29 if (typeof(stats.timestamp) !== 'number' || stats.timestamp <= 0) | 253 } |
30 throw failTest('stats.timestamp is not a positive number.'); | 254 returnToTest('ok-' + iterableToSemicolonList(statsTypes.values())); |
phoglund_chromium
2016/11/09 15:50:10
I'd recommend using JSON.stringify here instead of
hbos_chromium
2016/11/09 17:24:32
Done. (The referenced JSON is being returned to C+
| |
31 if (typeof(stats.type) !== 'string') | |
32 throw failTest('stats.type is not a string.'); | |
33 }); | |
34 // TODO(hbos): When the new stats collection API is more mature (and | |
35 // certainly before unflagging the new stats API) add a whitelist of | |
36 // allowed stats to prevent accidentally exposing stats to the web that | |
37 // are not in the spec and that verifies type information. Status at | |
38 // crbug.com/627816. Stats collection is tested in the WebRTC repo and | |
39 // automatically surfaced to Blink, but there should be a process of | |
40 // having to land a Blink CL in order to expose a new RTCStats dictionary. | |
41 returnToTest('ok-got-stats'); | |
42 }, | 255 }, |
43 function(e) { | 256 function(e) { |
44 throw failTest('Promise was rejected: ' + e); | 257 throw failTest('Promise was rejected: ' + e); |
45 }); | 258 }); |
46 } | 259 } |
260 | |
261 /** | |
262 * Returns to test a complete list of whitelisted "RTCStats.type" values as a | |
263 * string of semicolon separated values. | |
264 */ | |
265 function getWhitelistedStatsTypes() { | |
266 returnToTest(iterableToSemicolonList(gStatsWhitelist.keys())); | |
267 } | |
268 | |
269 // Internals. | |
270 | |
271 /** @private */ | |
272 function RTCStats_(parent, membersObject) { | |
273 if (parent != null) { | |
274 for (let member in parent) { | |
275 this[member] = parent[member]; | |
276 } | |
277 } | |
278 for (let member in membersObject) { | |
279 this[member] = membersObject[member]; | |
280 } | |
281 } | |
282 | |
283 /** | |
284 * Checks if |stats| correctly maps to a a whitelisted RTCStats-derived | |
285 * dictionary, throwing |failTest| if it doesn't. See |gStatsWhitelist|. | |
286 * | |
287 * The "RTCStats.type" must map to a known dictionary description. Every member | |
288 * is optional, but if present it must be present in the whitelisted dictionary | |
289 * description and its type must match. | |
290 * @private | |
291 */ | |
292 function verifyStatsIsWhitelisted_(stats) { | |
293 if (stats == null) | |
294 throw failTest('stats is null or undefined: ' + stats); | |
295 if (typeof(stats.id) !== 'string') | |
296 throw failTest('stats.id is not a string:' + stats.id); | |
297 if (typeof(stats.timestamp) !== 'number' || !isFinite(stats.timestamp) || | |
298 stats.timestamp <= 0) { | |
299 throw failTest('stats.timestamp is not a positive finite number: ' + | |
300 stats.timestamp); | |
301 } | |
302 if (typeof(stats.type) !== 'string') | |
303 throw failTest('stats.type is not a string: ' + stats.type); | |
304 let whitelistedStats = gStatsWhitelist.get(stats.type); | |
305 if (whitelistedStats == null) | |
306 throw failTest('stats.type is not a whitelisted type: ' + stats.type); | |
307 for (let propertyName in stats) { | |
308 if (propertyName === 'id' || propertyName === 'timestamp' || | |
309 propertyName === 'type') { | |
310 continue; | |
311 } | |
312 if (!whitelistedStats.hasOwnProperty(propertyName)) { | |
313 throw failTest('stats.' + propertyName + ' is not a whitelisted ' + | |
314 'member: ' + stats[propertyName]); | |
315 } | |
316 if (!whitelistedStats[propertyName].startsWith('sequence_')) { | |
317 if (typeof(stats[propertyName]) !== whitelistedStats[propertyName]) { | |
318 throw failTest('stats.' + propertyName + ' should have a different ' + | |
319 'type according to the whitelist: ' + stats[propertyName] + ' vs ' + | |
320 whitelistedStats[propertyName]); | |
321 } | |
322 } else { | |
323 if (!Array.isArray(stats[propertyName])) { | |
324 throw failTest('stats.' + propertyName + ' should have a different ' + | |
325 'type according to the whitelist (should be an array): ' + | |
326 JSON.stringify(stats[propertyName]) + ' vs ' + | |
327 whitelistedStats[propertyName]); | |
328 } | |
329 let elementType = whitelistedStats[propertyName].substring(9); | |
330 for (let element in stats[propertyName]) { | |
331 if (typeof(element) !== elementType) { | |
332 throw failTest('stats.' + propertyName + ' should have a different ' + | |
333 'type according to the whitelist (an element of the array has ' + | |
334 'the incorrect type): ' + JSON.stringify(stats[propertyName]) + | |
335 ' vs ' + whitelistedStats[propertyName]); | |
336 } | |
337 } | |
338 } | |
339 } | |
340 } | |
341 | |
342 /** @private */ | |
343 function iterableToSemicolonList(iterable) { | |
344 let str = ''; | |
345 for (let element of iterable) { | |
346 if (str.length > 0) | |
347 str += ';'; | |
348 str += element; | |
349 } | |
350 return str; | |
351 } | |
OLD | NEW |