1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5cr.define('media', function() { 6 'use strict'; 7 8 /** 9 * This class represents a file cached by net. 10 */ 11 function CacheEntry() { 12 this.read_ = new media.DisjointRangeSet; 13 this.written_ = new media.DisjointRangeSet; 14 this.available_ = new media.DisjointRangeSet; 15 16 // Set to true when we know the entry is sparse. 17 this.sparse = false; 18 this.key = null; 19 this.size = null; 20 21 // The <details> element representing this CacheEntry. 22 this.details_ = document.createElement('details'); 23 this.details_.className = 'cache-entry'; 24 this.details_.open = false; 25 26 // The <details> summary line. It contains a chart of requested file ranges 27 // and the url if we know it. 28 var summary = document.createElement('summary'); 29 30 this.summaryText_ = document.createTextNode(''); 31 summary.appendChild(this.summaryText_); 32 33 summary.appendChild(document.createTextNode(' ')); 34 35 // Controls to modify this CacheEntry. 36 var controls = document.createElement('span'); 37 controls.className = 'cache-entry-controls'; 38 summary.appendChild(controls); 39 summary.appendChild(document.createElement('br')); 40 41 // A link to clear recorded data from this CacheEntry. 42 var clearControl = document.createElement('a'); 43 clearControl.href = 'javascript:void(0)'; 44 clearControl.onclick = this.clear.bind(this); 45 clearControl.textContent = '(clear entry)'; 46 controls.appendChild(clearControl); 47 48 this.details_.appendChild(summary); 49 50 // The canvas for drawing cache writes. 51 this.writeCanvas = document.createElement('canvas'); 52 this.writeCanvas.width = media.BAR_WIDTH; 53 this.writeCanvas.height = media.BAR_HEIGHT; 54 this.details_.appendChild(this.writeCanvas); 55 56 // The canvas for drawing cache reads. 57 this.readCanvas = document.createElement('canvas'); 58 this.readCanvas.width = media.BAR_WIDTH; 59 this.readCanvas.height = media.BAR_HEIGHT; 60 this.details_.appendChild(this.readCanvas); 61 62 // A tabular representation of the data in the above canvas. 63 this.detailTable_ = document.createElement('table'); 64 this.detailTable_.className = 'cache-table'; 65 this.details_.appendChild(this.detailTable_); 66 } 67 68 CacheEntry.prototype = { 69 /** 70 * Mark a range of bytes as read from the cache. 71 * @param {int} start The first byte read. 72 * @param {int} length The number of bytes read. 73 */ 74 readBytes: function(start, length) { 75 start = parseInt(start); 76 length = parseInt(length); 77 this.read_.add(start, start + length); 78 this.available_.add(start, start + length); 79 this.sparse = true; 80 }, 81 82 /** 83 * Mark a range of bytes as written to the cache. 84 * @param {int} start The first byte written. 85 * @param {int} length The number of bytes written. 86 */ 87 writeBytes: function(start, length) { 88 start = parseInt(start); 89 length = parseInt(length); 90 this.written_.add(start, start + length); 91 this.available_.add(start, start + length); 92 this.sparse = true; 93 }, 94 95 /** 96 * Merge this CacheEntry with another, merging recorded ranges and flags. 97 * @param {CacheEntry} other The CacheEntry to merge into this one. 98 */ 99 merge: function(other) { 100 this.read_.merge(other.read_); 101 this.written_.merge(other.written_); 102 this.available_.merge(other.available_); 103 this.sparse = this.sparse || other.sparse; 104 this.key = this.key || other.key; 105 this.size = this.size || other.size; 106 }, 107 108 /** 109 * Clear all recorded ranges from this CacheEntry and redraw this.details_. 110 */ 111 clear: function() { 112 this.read_ = new media.DisjointRangeSet; 113 this.written_ = new media.DisjointRangeSet; 114 this.available_ = new media.DisjointRangeSet; 115 this.generateDetails(); 116 }, 117 118 /** 119 * Helper for drawCacheReadsToCanvas() and drawCacheWritesToCanvas(). 120 * 121 * Accepts the entries to draw, a canvas fill style, and the canvas to 122 * draw on. 123 */ 124 drawCacheEntriesToCanvas: function(entries, fillStyle, canvas) { 125 // Don't bother drawing anything if we don't know the total size. 126 if (!this.size) { 127 return; 128 } 129 130 var width = canvas.width; 131 var height = canvas.height; 132 var context = canvas.getContext('2d'); 133 var fileSize = this.size; 134 135 context.fillStyle = '#aaa'; 136 context.fillRect(0, 0, width, height); 137 138 function drawRange(start, end) { 139 var left = start / fileSize * width; 140 var right = end / fileSize * width; 141 context.fillRect(left, 0, right - left, height); 142 } 143 144 context.fillStyle = fillStyle; 145 entries.map(function(start, end) { 146 drawRange(start, end); 147 }); 148 }, 149 150 /** 151 * Draw cache writes to the given canvas. 152 * 153 * It should consist of a horizontal bar with highlighted sections to 154 * represent which parts of a file have been written to the cache. 155 * 156 * e.g. |xxxxxx----------x| 157 */ 158 drawCacheWritesToCanvas: function(canvas) { 159 this.drawCacheEntriesToCanvas(this.written_, '#00a', canvas); 160 }, 161 162 /** 163 * Draw cache reads to the given canvas. 164 * 165 * It should consist of a horizontal bar with highlighted sections to 166 * represent which parts of a file have been read from the cache. 167 * 168 * e.g. |xxxxxx----------x| 169 */ 170 drawCacheReadsToCanvas: function(canvas) { 171 this.drawCacheEntriesToCanvas(this.read_, '#0a0', canvas); 172 }, 173 174 /** 175 * Update this.details_ to contain everything we currently know about 176 * this file. 177 */ 178 generateDetails: function() { 179 function makeElement(tag, content) { 180 var toReturn = document.createElement(tag); 181 toReturn.textContent = content; 182 return toReturn; 183 } 184 185 this.details_.id = this.key; 186 this.summaryText_.textContent = this.key || 'Unknown File'; 187 188 this.detailTable_.textContent = ''; 189 var header = document.createElement('thead'); 190 var footer = document.createElement('tfoot'); 191 var body = document.createElement('tbody'); 192 this.detailTable_.appendChild(header); 193 this.detailTable_.appendChild(footer); 194 this.detailTable_.appendChild(body); 195 196 var headerRow = document.createElement('tr'); 197 headerRow.appendChild(makeElement('th', 'Read From Cache')); 198 headerRow.appendChild(makeElement('th', 'Written To Cache')); 199 header.appendChild(headerRow); 200 201 var footerRow = document.createElement('tr'); 202 var footerCell = document.createElement('td'); 203 footerCell.textContent = 'Out of ' + (this.size || 'unkown size'); 204 footerCell.setAttribute('colspan', 2); 205 footerRow.appendChild(footerCell); 206 footer.appendChild(footerRow); 207 208 var read = this.read_.map(function(start, end) { 209 return start + ' - ' + end; 210 }); 211 var written = this.written_.map(function(start, end) { 212 return start + ' - ' + end; 213 }); 214 215 var length = Math.max(read.length, written.length); 216 for (var i = 0; i < length; i++) { 217 var row = document.createElement('tr'); 218 row.appendChild(makeElement('td', read[i] || '')); 219 row.appendChild(makeElement('td', written[i] || '')); 220 body.appendChild(row); 221 } 222 223 this.drawCacheWritesToCanvas(this.writeCanvas); 224 this.drawCacheReadsToCanvas(this.readCanvas); 225 }, 226 227 /** 228 * Render this CacheEntry as a <li>. 229 * @return {HTMLElement} A <li> representing this CacheEntry. 230 */ 231 toListItem: function() { 232 this.generateDetails(); 233 234 var result = document.createElement('li'); 235 result.appendChild(this.details_); 236 return result; 237 } 238 }; 239 240 return { 241 CacheEntry: CacheEntry 242 }; 243}); 244