OLD | NEW |
1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** @typedef {{startOffset: number, endOffset: number, count: number}} */ | 5 /** @typedef {{startOffset: number, endOffset: number, count: number}} */ |
6 Coverage.RangeUseCount; | 6 Coverage.RangeUseCount; |
7 | 7 |
8 /** @typedef {{end: number, count: (number|undefined)}} */ | 8 /** @typedef {{end: number, count: (number|undefined)}} */ |
9 Coverage.CoverageSegment; | 9 Coverage.CoverageSegment; |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... |
21 * @param {!SDK.Target} target | 21 * @param {!SDK.Target} target |
22 */ | 22 */ |
23 constructor(target) { | 23 constructor(target) { |
24 super(target); | 24 super(target); |
25 this._cpuProfilerModel = target.model(SDK.CPUProfilerModel); | 25 this._cpuProfilerModel = target.model(SDK.CPUProfilerModel); |
26 this._cssModel = target.model(SDK.CSSModel); | 26 this._cssModel = target.model(SDK.CSSModel); |
27 this._debuggerModel = target.model(SDK.DebuggerModel); | 27 this._debuggerModel = target.model(SDK.DebuggerModel); |
28 | 28 |
29 /** @type {!Map<string, !Coverage.URLCoverageInfo>} */ | 29 /** @type {!Map<string, !Coverage.URLCoverageInfo>} */ |
30 this._coverageByURL = new Map(); | 30 this._coverageByURL = new Map(); |
| 31 /** @type {!Map<!Common.ContentProvider, !Coverage.CoverageInfo>} */ |
| 32 this._coverageByContentProvider = new Map(); |
31 } | 33 } |
32 | 34 |
33 /** | 35 /** |
34 * @return {boolean} | 36 * @return {boolean} |
35 */ | 37 */ |
36 start() { | 38 start() { |
37 this._coverageByURL.clear(); | 39 this._coverageByURL.clear(); |
38 if (this._cssModel) | 40 if (this._cssModel) |
39 this._cssModel.startRuleUsageTracking(); | 41 this._cssModel.startCoverage(); |
40 if (this._cpuProfilerModel) | 42 if (this._cpuProfilerModel) |
41 this._cpuProfilerModel.startPreciseCoverage(); | 43 this._cpuProfilerModel.startPreciseCoverage(); |
42 return !!(this._cssModel || this._cpuProfilerModel); | 44 return !!(this._cssModel || this._cpuProfilerModel); |
43 } | 45 } |
44 | 46 |
45 /** | 47 /** |
46 * @return {!Promise<!Array<!Coverage.URLCoverageInfo>>} | 48 * @return {!Promise<!Array<!Coverage.CoverageInfo>>} |
47 */ | 49 */ |
48 async stop() { | 50 stop() { |
49 await Promise.all([this._stopCSSCoverage(), this._stopJSCoverage()]); | 51 var pollPromise = this.poll(); |
| 52 if (this._cpuProfilerModel) |
| 53 this._cpuProfilerModel.stopPreciseCoverage(); |
| 54 if (this._cssModel) |
| 55 this._cssModel.stopCoverage(); |
| 56 return pollPromise; |
| 57 } |
| 58 |
| 59 /** |
| 60 * @return {!Promise<!Array<!Coverage.CoverageInfo>>} |
| 61 */ |
| 62 async poll() { |
| 63 var updates = await Promise.all([this._takeCSSCoverage(), this._takeJSCovera
ge()]); |
| 64 return updates[0].concat(updates[1]); |
| 65 } |
| 66 |
| 67 /** |
| 68 * @return {!Array<!Coverage.URLCoverageInfo>} |
| 69 */ |
| 70 entries() { |
50 return Array.from(this._coverageByURL.values()); | 71 return Array.from(this._coverageByURL.values()); |
51 } | 72 } |
52 | 73 |
53 async _stopJSCoverage() { | 74 /** |
| 75 * @param {!Common.ContentProvider} contentProvider |
| 76 * @param {number} startOffset |
| 77 * @param {number} endOffset |
| 78 * @return {boolean|undefined} |
| 79 */ |
| 80 usageForRange(contentProvider, startOffset, endOffset) { |
| 81 var coverageInfo = this._coverageByContentProvider.get(contentProvider); |
| 82 return coverageInfo && coverageInfo.usageForRange(startOffset, endOffset); |
| 83 } |
| 84 |
| 85 /** |
| 86 * @return {!Promise<!Array<!Coverage.CoverageInfo>>} |
| 87 */ |
| 88 async _takeJSCoverage() { |
54 if (!this._cpuProfilerModel) | 89 if (!this._cpuProfilerModel) |
55 return; | 90 return []; |
56 var coveragePromise = this._cpuProfilerModel.takePreciseCoverage(); | 91 var rawCoverageData = await this._cpuProfilerModel.takePreciseCoverage(); |
57 this._cpuProfilerModel.stopPreciseCoverage(); | 92 return this._processJSCoverage(rawCoverageData); |
58 var rawCoverageData = await coveragePromise; | |
59 this._processJSCoverage(rawCoverageData); | |
60 } | 93 } |
61 | 94 |
62 /** | 95 /** |
63 * @param {!Array<!Protocol.Profiler.ScriptCoverage>} scriptsCoverage | 96 * @param {!Array<!Protocol.Profiler.ScriptCoverage>} scriptsCoverage |
| 97 * @return {!Array<!Coverage.CoverageInfo>} |
64 */ | 98 */ |
65 _processJSCoverage(scriptsCoverage) { | 99 _processJSCoverage(scriptsCoverage) { |
| 100 var updatedEntries = []; |
66 for (var entry of scriptsCoverage) { | 101 for (var entry of scriptsCoverage) { |
67 var script = this._debuggerModel.scriptForId(entry.scriptId); | 102 var script = this._debuggerModel.scriptForId(entry.scriptId); |
68 if (!script) | 103 if (!script) |
69 continue; | 104 continue; |
70 var ranges = []; | 105 var ranges = []; |
71 for (var func of entry.functions) { | 106 for (var func of entry.functions) { |
72 for (var range of func.ranges) | 107 for (var range of func.ranges) |
73 ranges.push(range); | 108 ranges.push(range); |
74 } | 109 } |
75 this._addCoverage(script, script.contentLength, script.lineOffset, script.
columnOffset, ranges); | 110 var entry = this._addCoverage(script, script.contentLength, script.lineOff
set, script.columnOffset, ranges); |
| 111 if (entry) |
| 112 updatedEntries.push(entry); |
76 } | 113 } |
| 114 return updatedEntries; |
77 } | 115 } |
78 | 116 |
79 /** | 117 /** |
| 118 * @return {!Promise<!Array<!Coverage.CoverageInfo>>} |
| 119 */ |
| 120 async _takeCSSCoverage() { |
| 121 if (!this._cssModel) |
| 122 return []; |
| 123 var rawCoverageData = await this._cssModel.takeCoverageDelta(); |
| 124 return this._processCSSCoverage(rawCoverageData); |
| 125 } |
| 126 |
| 127 /** |
| 128 * @param {!Array<!Protocol.CSS.RuleUsage>} ruleUsageList |
| 129 * @return {!Array<!Coverage.CoverageInfo>} |
| 130 */ |
| 131 _processCSSCoverage(ruleUsageList) { |
| 132 var updatedEntries = []; |
| 133 /** @type {!Map<!SDK.CSSStyleSheetHeader, !Array<!Coverage.RangeUseCount>>}
*/ |
| 134 var rulesByStyleSheet = new Map(); |
| 135 for (var rule of ruleUsageList) { |
| 136 var styleSheetHeader = this._cssModel.styleSheetHeaderForId(rule.styleShee
tId); |
| 137 if (!styleSheetHeader) |
| 138 continue; |
| 139 var ranges = rulesByStyleSheet.get(styleSheetHeader); |
| 140 if (!ranges) { |
| 141 ranges = []; |
| 142 rulesByStyleSheet.set(styleSheetHeader, ranges); |
| 143 } |
| 144 ranges.push({startOffset: rule.startOffset, endOffset: rule.endOffset, cou
nt: Number(rule.used)}); |
| 145 } |
| 146 for (var entry of rulesByStyleSheet) { |
| 147 var styleSheetHeader = /** @type {!SDK.CSSStyleSheetHeader} */ (entry[0]); |
| 148 var ranges = /** @type {!Array<!Coverage.RangeUseCount>} */ (entry[1]); |
| 149 var entry = this._addCoverage( |
| 150 styleSheetHeader, styleSheetHeader.contentLength, styleSheetHeader.sta
rtLine, styleSheetHeader.startColumn, |
| 151 ranges); |
| 152 if (entry) |
| 153 updatedEntries.push(entry); |
| 154 } |
| 155 return updatedEntries; |
| 156 } |
| 157 |
| 158 /** |
80 * @param {!Array<!Coverage.RangeUseCount>} ranges | 159 * @param {!Array<!Coverage.RangeUseCount>} ranges |
81 * @return {!Array<!Coverage.CoverageSegment>} | 160 * @return {!Array<!Coverage.CoverageSegment>} |
82 */ | 161 */ |
83 static _convertToDisjointSegments(ranges) { | 162 static _convertToDisjointSegments(ranges) { |
84 ranges.sort((a, b) => a.startOffset - b.startOffset); | 163 ranges.sort((a, b) => a.startOffset - b.startOffset); |
85 | 164 |
86 var result = []; | 165 var result = []; |
87 var stack = []; | 166 var stack = []; |
88 for (var entry of ranges) { | 167 for (var entry of ranges) { |
89 var top = stack.peekLast(); | 168 var top = stack.peekLast(); |
(...skipping 24 matching lines...) Expand all Loading... |
114 last.end = end; | 193 last.end = end; |
115 return; | 194 return; |
116 } | 195 } |
117 } | 196 } |
118 result.push({end: end, count: count}); | 197 result.push({end: end, count: count}); |
119 } | 198 } |
120 | 199 |
121 return result; | 200 return result; |
122 } | 201 } |
123 | 202 |
124 async _stopCSSCoverage() { | |
125 if (!this._cssModel) | |
126 return []; | |
127 | |
128 var rawCoverageData = await this._cssModel.ruleListPromise(); | |
129 this._processCSSCoverage(rawCoverageData); | |
130 } | |
131 | |
132 /** | |
133 * @param {!Array<!Protocol.CSS.RuleUsage>} ruleUsageList | |
134 */ | |
135 _processCSSCoverage(ruleUsageList) { | |
136 /** @type {!Map<!SDK.CSSStyleSheetHeader, !Array<!Coverage.RangeUseCount>>}
*/ | |
137 var rulesByStyleSheet = new Map(); | |
138 for (var rule of ruleUsageList) { | |
139 var styleSheetHeader = this._cssModel.styleSheetHeaderForId(rule.styleShee
tId); | |
140 if (!styleSheetHeader) | |
141 continue; | |
142 var ranges = rulesByStyleSheet.get(styleSheetHeader); | |
143 if (!ranges) { | |
144 ranges = []; | |
145 rulesByStyleSheet.set(styleSheetHeader, ranges); | |
146 } | |
147 ranges.push({startOffset: rule.startOffset, endOffset: rule.endOffset, cou
nt: Number(rule.used)}); | |
148 } | |
149 for (var entry of rulesByStyleSheet) { | |
150 var styleSheetHeader = /** @type {!SDK.CSSStyleSheetHeader} */ (entry[0]); | |
151 var ranges = /** @type {!Array<!Coverage.RangeUseCount>} */ (entry[1]); | |
152 this._addCoverage( | |
153 styleSheetHeader, styleSheetHeader.contentLength, styleSheetHeader.sta
rtLine, styleSheetHeader.startColumn, | |
154 ranges); | |
155 } | |
156 } | |
157 | |
158 /** | 203 /** |
159 * @param {!Common.ContentProvider} contentProvider | 204 * @param {!Common.ContentProvider} contentProvider |
160 * @param {number} contentLength | 205 * @param {number} contentLength |
161 * @param {number} startLine | 206 * @param {number} startLine |
162 * @param {number} startColumn | 207 * @param {number} startColumn |
163 * @param {!Array<!Coverage.RangeUseCount>} ranges | 208 * @param {!Array<!Coverage.RangeUseCount>} ranges |
| 209 * @return {?Coverage.CoverageInfo} |
164 */ | 210 */ |
165 _addCoverage(contentProvider, contentLength, startLine, startColumn, ranges) { | 211 _addCoverage(contentProvider, contentLength, startLine, startColumn, ranges) { |
166 var url = contentProvider.contentURL(); | 212 var url = contentProvider.contentURL(); |
167 if (!url) | 213 if (!url) |
168 return; | 214 return null; |
169 var entry = this._coverageByURL.get(url); | 215 var urlCoverage = this._coverageByURL.get(url); |
170 if (!entry) { | 216 if (!urlCoverage) { |
171 entry = new Coverage.URLCoverageInfo(url); | 217 urlCoverage = new Coverage.URLCoverageInfo(url); |
172 this._coverageByURL.set(url, entry); | 218 this._coverageByURL.set(url, urlCoverage); |
173 } | 219 } |
| 220 var coverageInfo = urlCoverage._ensureEntry(contentProvider, contentLength,
startLine, startColumn); |
| 221 this._coverageByContentProvider.set(contentProvider, coverageInfo); |
174 var segments = Coverage.CoverageModel._convertToDisjointSegments(ranges); | 222 var segments = Coverage.CoverageModel._convertToDisjointSegments(ranges); |
175 if (segments.length && segments.peekLast().end < contentLength) | 223 if (segments.length && segments.peekLast().end < contentLength) |
176 segments.push({end: contentLength}); | 224 segments.push({end: contentLength}); |
177 entry.update(contentProvider, contentLength, startLine, startColumn, segment
s); | 225 var oldUsedSize = coverageInfo._usedSize; |
| 226 coverageInfo.mergeCoverage(segments); |
| 227 if (coverageInfo._usedSize === oldUsedSize) |
| 228 return null; |
| 229 urlCoverage._usedSize += coverageInfo._usedSize - oldUsedSize; |
| 230 return coverageInfo; |
178 } | 231 } |
179 }; | 232 }; |
180 | 233 |
181 Coverage.URLCoverageInfo = class { | 234 Coverage.URLCoverageInfo = class { |
182 /** | 235 /** |
183 * @param {string} url | 236 * @param {string} url |
184 */ | 237 */ |
185 constructor(url) { | 238 constructor(url) { |
186 this._url = url; | 239 this._url = url; |
187 /** @type {!Map<string, !Coverage.CoverageInfo>} */ | 240 /** @type {!Map<string, !Coverage.CoverageInfo>} */ |
188 this._coverageInfoByLocation = new Map(); | 241 this._coverageInfoByLocation = new Map(); |
189 this._size = 0; | 242 this._size = 0; |
190 this._usedSize = 0; | 243 this._usedSize = 0; |
191 /** @type {!Coverage.CoverageType} */ | 244 /** @type {!Coverage.CoverageType} */ |
192 this._type; | 245 this._type; |
193 } | 246 } |
194 | 247 |
195 /** | 248 /** |
196 * @param {!Common.ContentProvider} contentProvider | |
197 * @param {number} contentLength | |
198 * @param {number} lineOffset | |
199 * @param {number} columnOffset | |
200 * @param {!Array<!Coverage.CoverageSegment>} segments | |
201 */ | |
202 update(contentProvider, contentLength, lineOffset, columnOffset, segments) { | |
203 var key = `${lineOffset}:${columnOffset}`; | |
204 var entry = this._coverageInfoByLocation.get(key); | |
205 | |
206 if (!entry) { | |
207 entry = new Coverage.CoverageInfo(contentProvider, lineOffset, columnOffse
t); | |
208 this._coverageInfoByLocation.set(key, entry); | |
209 this._size += contentLength; | |
210 this._type |= entry.type(); | |
211 } | |
212 this._usedSize -= entry._usedSize; | |
213 entry.mergeCoverage(segments); | |
214 this._usedSize += entry._usedSize; | |
215 } | |
216 | |
217 /** | |
218 * @return {string} | 249 * @return {string} |
219 */ | 250 */ |
220 url() { | 251 url() { |
221 return this._url; | 252 return this._url; |
222 } | 253 } |
223 | 254 |
224 /** | 255 /** |
225 * @return {!Coverage.CoverageType} | 256 * @return {!Coverage.CoverageType} |
226 */ | 257 */ |
227 type() { | 258 type() { |
(...skipping 15 matching lines...) Expand all Loading... |
243 } | 274 } |
244 | 275 |
245 /** | 276 /** |
246 * @return {number} | 277 * @return {number} |
247 */ | 278 */ |
248 unusedSize() { | 279 unusedSize() { |
249 return this._size - this._usedSize; | 280 return this._size - this._usedSize; |
250 } | 281 } |
251 | 282 |
252 /** | 283 /** |
253 * @return {!Promise<!Array<!{range: !TextUtils.TextRange, count: number}>>} | 284 * @param {!Common.ContentProvider} contentProvider |
| 285 * @param {number} contentLength |
| 286 * @param {number} lineOffset |
| 287 * @param {number} columnOffset |
| 288 * @return {!Coverage.CoverageInfo} |
254 */ | 289 */ |
255 async buildTextRanges() { | 290 _ensureEntry(contentProvider, contentLength, lineOffset, columnOffset) { |
256 var textRangePromises = []; | 291 var key = `${lineOffset}:${columnOffset}`; |
257 for (var coverageInfo of this._coverageInfoByLocation.values()) | 292 var entry = this._coverageInfoByLocation.get(key); |
258 textRangePromises.push(coverageInfo.buildTextRanges()); | 293 |
259 var allTextRanges = await Promise.all(textRangePromises); | 294 if (!entry) { |
260 return [].concat(...allTextRanges); | 295 entry = new Coverage.CoverageInfo(contentProvider, lineOffset, columnOffse
t); |
| 296 this._coverageInfoByLocation.set(key, entry); |
| 297 this._size += contentLength; |
| 298 this._type |= entry.type(); |
| 299 } |
| 300 return entry; |
261 } | 301 } |
262 }; | 302 }; |
263 | 303 |
264 Coverage.CoverageInfo = class { | 304 Coverage.CoverageInfo = class { |
265 /** | 305 /** |
266 * @param {!Common.ContentProvider} contentProvider | 306 * @param {!Common.ContentProvider} contentProvider |
267 * @param {number} lineOffset | 307 * @param {number} lineOffset |
268 * @param {number} columnOffset | 308 * @param {number} columnOffset |
269 */ | 309 */ |
270 constructor(contentProvider, lineOffset, columnOffset) { | 310 constructor(contentProvider, lineOffset, columnOffset) { |
271 this._contentProvider = contentProvider; | 311 this._contentProvider = contentProvider; |
272 this._lineOffset = lineOffset; | 312 this._lineOffset = lineOffset; |
273 this._columnOffset = columnOffset; | 313 this._columnOffset = columnOffset; |
274 this._usedSize = 0; | 314 this._usedSize = 0; |
275 | 315 |
276 if (contentProvider.contentType().isScript()) { | 316 if (contentProvider.contentType().isScript()) { |
277 this._coverageType = Coverage.CoverageType.JavaScript; | 317 this._coverageType = Coverage.CoverageType.JavaScript; |
278 } else if (contentProvider.contentType().isStyleSheet()) { | 318 } else if (contentProvider.contentType().isStyleSheet()) { |
279 this._coverageType = Coverage.CoverageType.CSS; | 319 this._coverageType = Coverage.CoverageType.CSS; |
280 } else { | 320 } else { |
281 console.assert( | 321 console.assert( |
282 false, `Unexpected resource type ${contentProvider.contentType().name}
for ${contentProvider.contentURL()}`); | 322 false, `Unexpected resource type ${contentProvider.contentType().name}
for ${contentProvider.contentURL()}`); |
283 } | 323 } |
284 /** !Array<!Coverage.CoverageSegment> */ | 324 /** !Array<!Coverage.CoverageSegment> */ |
285 this._segments = []; | 325 this._segments = []; |
286 } | 326 } |
287 | 327 |
288 /** | 328 /** |
| 329 * @return {!Common.ContentProvider} |
| 330 */ |
| 331 contentProvider() { |
| 332 return this._contentProvider; |
| 333 } |
| 334 |
| 335 /** |
| 336 * @return {string} |
| 337 */ |
| 338 url() { |
| 339 return this._contentProvider.contentURL(); |
| 340 } |
| 341 |
| 342 /** |
289 * @return {!Coverage.CoverageType} | 343 * @return {!Coverage.CoverageType} |
290 */ | 344 */ |
291 type() { | 345 type() { |
292 return this._coverageType; | 346 return this._coverageType; |
293 } | 347 } |
294 | 348 |
295 /** | 349 /** |
296 * @param {!Array<!Coverage.CoverageSegment>} segments | 350 * @param {!Array<!Coverage.CoverageSegment>} segments |
297 */ | 351 */ |
298 mergeCoverage(segments) { | 352 mergeCoverage(segments) { |
299 this._segments = Coverage.CoverageInfo._mergeCoverage(this._segments, segmen
ts); | 353 this._segments = Coverage.CoverageInfo._mergeCoverage(this._segments, segmen
ts); |
300 this._updateStats(); | 354 this._updateStats(); |
301 } | 355 } |
302 | 356 |
303 /** | 357 /** |
| 358 * @param {number} start |
| 359 * @param {number} end |
| 360 * @return {boolean} |
| 361 */ |
| 362 usageForRange(start, end) { |
| 363 var index = this._segments.upperBound(start, (position, segment) => position
- segment.end); |
| 364 for (; index < this._segments.length && this._segments[index].end < end; ++i
ndex) { |
| 365 if (this._segments[index].count) |
| 366 return true; |
| 367 } |
| 368 return index < this._segments.length && this._segments[index].count; |
| 369 } |
| 370 |
| 371 /** |
304 * @param {!Array<!Coverage.CoverageSegment>} segmentsA | 372 * @param {!Array<!Coverage.CoverageSegment>} segmentsA |
305 * @param {!Array<!Coverage.CoverageSegment>} segmentsB | 373 * @param {!Array<!Coverage.CoverageSegment>} segmentsB |
306 */ | 374 */ |
307 static _mergeCoverage(segmentsA, segmentsB) { | 375 static _mergeCoverage(segmentsA, segmentsB) { |
308 var result = []; | 376 var result = []; |
309 | 377 |
310 var indexA = 0; | 378 var indexA = 0; |
311 var indexB = 0; | 379 var indexB = 0; |
312 while (indexA < segmentsA.length && indexB < segmentsB.length) { | 380 while (indexA < segmentsA.length && indexB < segmentsB.length) { |
313 var a = segmentsA[indexA]; | 381 var a = segmentsA[indexA]; |
(...skipping 12 matching lines...) Expand all Loading... |
326 indexB++; | 394 indexB++; |
327 } | 395 } |
328 | 396 |
329 for (; indexA < segmentsA.length; indexA++) | 397 for (; indexA < segmentsA.length; indexA++) |
330 result.push(segmentsA[indexA]); | 398 result.push(segmentsA[indexA]); |
331 for (; indexB < segmentsB.length; indexB++) | 399 for (; indexB < segmentsB.length; indexB++) |
332 result.push(segmentsB[indexB]); | 400 result.push(segmentsB[indexB]); |
333 return result; | 401 return result; |
334 } | 402 } |
335 | 403 |
336 /** | |
337 * @return {!Promise<!Array<!{range: !TextUtils.TextRange, count: number}>>} | |
338 */ | |
339 async buildTextRanges() { | |
340 var contents = await this._contentProvider.requestContent(); | |
341 if (!contents) | |
342 return []; | |
343 var text = new TextUtils.Text(contents); | |
344 var lastOffset = 0; | |
345 var result = []; | |
346 for (var segment of this._segments) { | |
347 if (!segment.end) | |
348 continue; | |
349 var startPosition = text.positionFromOffset(lastOffset); | |
350 var endPosition = text.positionFromOffset(segment.end); | |
351 if (!startPosition.lineNumber) | |
352 startPosition.columnNumber += this._columnOffset; | |
353 startPosition.lineNumber += this._lineOffset; | |
354 if (!endPosition.lineNumber) | |
355 endPosition.columnNumber += this._columnOffset; | |
356 endPosition.lineNumber += this._lineOffset; | |
357 var range = new TextUtils.TextRange( | |
358 startPosition.lineNumber, startPosition.columnNumber, endPosition.line
Number, endPosition.columnNumber); | |
359 result.push({count: segment.count || 0, range: range}); | |
360 lastOffset = segment.end; | |
361 } | |
362 return result; | |
363 } | |
364 | |
365 _updateStats() { | 404 _updateStats() { |
366 this._usedSize = 0; | 405 this._usedSize = 0; |
367 | 406 |
368 var last = 0; | 407 var last = 0; |
369 for (var segment of this._segments) { | 408 for (var segment of this._segments) { |
370 if (segment.count) | 409 if (segment.count) |
371 this._usedSize += segment.end - last; | 410 this._usedSize += segment.end - last; |
372 last = segment.end; | 411 last = segment.end; |
373 } | 412 } |
374 } | 413 } |
375 }; | 414 }; |
OLD | NEW |