Home | History | Annotate | Download | only in importer
      1 <!DOCTYPE html>
      2 <!--
      3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
      4 Use of this source code is governed by a BSD-style license that can be
      5 found in the LICENSE file.
      6 -->
      7 
      8 <link rel="import" href="/extras/importer/jszip.html">
      9 <link rel="import" href="/importer/importer.html">
     10 <link rel="import" href="/model/model.html">
     11 
     12 <script>
     13 'use strict';
     14 
     15 /**
     16  * @fileoverview GzipImporter inflates gzip compressed data and passes it along
     17  * to an actual importer.
     18  */
     19 tr.exportTo('tr.e.importer', function() {
     20   var Importer = tr.importer.Importer;
     21 
     22   var GZIP_MEMBER_HEADER_ID_SIZE = 3;
     23 
     24   var GZIP_HEADER_ID1 = 0x1f;
     25   var GZIP_HEADER_ID2 = 0x8b;
     26   var GZIP_DEFLATE_COMPRESSION = 8;
     27 
     28   function GzipImporter(model, eventData) {
     29     // Normalize the data into an Uint8Array.
     30     if (typeof(eventData) === 'string' || eventData instanceof String) {
     31       eventData = JSZip.utils.transformTo('uint8array', eventData);
     32     } else if (eventData instanceof ArrayBuffer) {
     33       eventData = new Uint8Array(eventData);
     34     } else
     35       throw new Error('Unknown gzip data format');
     36     this.model_ = model;
     37     this.gzipData_ = eventData;
     38   }
     39 
     40   /**
     41    * @param {eventData} Possibly gzip compressed data as a string or an
     42    *                    ArrayBuffer.
     43    * @return {boolean} Whether obj looks like gzip compressed data.
     44    */
     45   GzipImporter.canImport = function(eventData) {
     46     var header;
     47     if (eventData instanceof ArrayBuffer)
     48       header = new Uint8Array(eventData.slice(0, GZIP_MEMBER_HEADER_ID_SIZE));
     49     else if (typeof(eventData) === 'string' || eventData instanceof String) {
     50       header = eventData.substring(0, GZIP_MEMBER_HEADER_ID_SIZE);
     51       // Convert the string to a byteArray for correct value comparison.
     52       header = JSZip.utils.transformTo('uint8array', header);
     53     } else
     54       return false;
     55     return header[0] == GZIP_HEADER_ID1 &&
     56         header[1] == GZIP_HEADER_ID2 &&
     57         header[2] == GZIP_DEFLATE_COMPRESSION;
     58   };
     59 
     60   /**
     61    * Inflates (decompresses) the data stored in the given gzip bitstream.
     62    * @return {string} Inflated data.
     63    */
     64   GzipImporter.inflateGzipData_ = function(data) {
     65     var position = 0;
     66 
     67     function getByte() {
     68       if (position >= data.length)
     69         throw new Error('Unexpected end of gzip data');
     70       return data[position++];
     71     }
     72 
     73     function getWord() {
     74       var low = getByte();
     75       var high = getByte();
     76       return (high << 8) + low;
     77     }
     78 
     79     function skipBytes(amount) {
     80       position += amount;
     81     }
     82 
     83     function skipZeroTerminatedString() {
     84       while (getByte() != 0) {}
     85     }
     86 
     87     var id1 = getByte();
     88     var id2 = getByte();
     89     if (id1 !== GZIP_HEADER_ID1 || id2 !== GZIP_HEADER_ID2)
     90       throw new Error('Not gzip data');
     91     var compression_method = getByte();
     92     if (compression_method !== GZIP_DEFLATE_COMPRESSION)
     93       throw new Error('Unsupported compression method: ' + compression_method);
     94     var flags = getByte();
     95     var have_header_crc = flags & (1 << 1);
     96     var have_extra_fields = flags & (1 << 2);
     97     var have_file_name = flags & (1 << 3);
     98     var have_comment = flags & (1 << 4);
     99 
    100     // Skip modification time, extra flags and OS.
    101     skipBytes(4 + 1 + 1);
    102 
    103     // Skip remaining fields before compressed data.
    104     if (have_extra_fields) {
    105       var bytes_to_skip = getWord();
    106       skipBytes(bytes_to_skip);
    107     }
    108     if (have_file_name)
    109       skipZeroTerminatedString();
    110     if (have_comment)
    111       skipZeroTerminatedString();
    112     if (have_header_crc)
    113       getWord();
    114 
    115     // Inflate the data using jszip.
    116     var inflated_data =
    117         JSZip.compressions['DEFLATE'].uncompress(data.subarray(position));
    118     return JSZip.utils.transformTo('string', inflated_data);
    119   },
    120 
    121   GzipImporter.prototype = {
    122     __proto__: Importer.prototype,
    123 
    124     /**
    125      * Called by the Model to check whether the importer just encapsulates
    126      * the actual trace data which needs to be imported by another importer.
    127      */
    128     isTraceDataContainer: function() {
    129       return true;
    130     },
    131 
    132     /**
    133      * Called by the Model to extract subtraces from the event data. The
    134      * subtraces are passed on to other importers that can recognize them.
    135      */
    136     extractSubtraces: function() {
    137       var eventData = GzipImporter.inflateGzipData_(this.gzipData_);
    138       return eventData ? [eventData] : [];
    139     }
    140   };
    141 
    142   tr.importer.Importer.register(GzipImporter);
    143 
    144   return {
    145     GzipImporter: GzipImporter
    146   };
    147 });
    148 </script>
    149