Home | History | Annotate | Download | only in media
      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 
      5 cr.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