Home | History | Annotate | Download | only in chrome-firephp
      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 function Console() {
      6 }
      7 
      8 Console.Type = {
      9   LOG: "log",
     10   DEBUG: "debug",
     11   INFO: "info",
     12   WARN: "warn",
     13   ERROR: "error",
     14   GROUP: "group",
     15   GROUP_COLLAPSED: "groupCollapsed",
     16   GROUP_END: "groupEnd"
     17 };
     18 
     19 Console.addMessage = function(type, format, args) {
     20   chrome.extension.sendRequest({
     21       command: "sendToConsole",
     22       tabId: chrome.devtools.tabId,
     23       args: escape(JSON.stringify(Array.prototype.slice.call(arguments, 0)))
     24   });
     25 };
     26 
     27 // Generate Console output methods, i.e. Console.log(), Console.debug() etc.
     28 (function() {
     29   var console_types = Object.getOwnPropertyNames(Console.Type);
     30   for (var type = 0; type < console_types.length; ++type) {
     31     var method_name = Console.Type[console_types[type]];
     32     Console[method_name] = Console.addMessage.bind(Console, method_name);
     33   }
     34 })();
     35 
     36 function ChromeFirePHP() {
     37 };
     38 
     39 ChromeFirePHP.handleFirePhpHeaders = function(har_entry) {
     40   var response_headers = har_entry.response.headers;
     41   var wf_header_map = {};
     42   var had_wf_headers = false;
     43 
     44   for (var i = 0; i < response_headers.length; ++i) {
     45     var header = response_headers[i];
     46     if (/^X-Wf-/.test(header.name)) {
     47       wf_header_map[header.name] = header.value;
     48       had_wf_headers = true;
     49     }
     50   }
     51 
     52   var proto_header = wf_header_map["X-Wf-Protocol-1"];
     53   if (!had_wf_headers || !this._checkProtoVersion(proto_header))
     54     return;
     55 
     56   var message_objects = this._buildMessageObjects(wf_header_map);
     57   message_objects.sort(function(a, b) {
     58       var aFile = a.File || "";
     59       var bFile = b.File || "";
     60       if (aFile !== bFile)
     61         return aFile.localeCompare(bFile);
     62       var aLine = a.Line !== undefined ? a.Line : -1;
     63       var bLine = b.Line !== undefined ? b.Line : -1;
     64       return aLine - bLine;
     65   });
     66 
     67   var context = { pageRef: har_entry.pageref };
     68   for (var i = 0; i < message_objects.length; ++i)
     69     this._processLogMessage(message_objects[i], context);
     70   if (context.groupStarted)
     71     Console.groupEnd();
     72 };
     73 
     74 ChromeFirePHP._processLogMessage = function(message, context) {
     75   var meta = message[0];
     76   if (!meta) {
     77     Console.error("No Meta in FirePHP message");
     78     return;
     79   }
     80 
     81   var body = message[1];
     82   var type = meta.Type;
     83   if (!type) {
     84     Console.error("No Type for FirePHP message");
     85     return;
     86   }
     87 
     88   switch (type) {
     89     case "LOG":
     90     case "INFO":
     91     case "WARN":
     92     case "ERROR":
     93       if (!context.groupStarted) {
     94         context.groupStarted = true;
     95         Console.groupCollapsed(context.pageRef || "");
     96       }
     97       Console.addMessage(Console.Type[type], "%s%o",
     98           (meta.Label ? meta.Label + ": " : ""), body);
     99       break;
    100     case "EXCEPTION":
    101     case "TABLE":
    102     case "TRACE":
    103     case "GROUP_START":
    104     case "GROUP_END":
    105      // FIXME: implement
    106      break;
    107   }
    108 };
    109 
    110 ChromeFirePHP._buildMessageObjects = function(header_map)
    111 {
    112   const normal_header_prefix = "X-Wf-1-1-1-";
    113 
    114   return this._collectMessageObjectsForPrefix(header_map, normal_header_prefix);
    115 };
    116 
    117 ChromeFirePHP._collectMessageObjectsForPrefix = function(header_map, prefix) {
    118   var results = [];
    119   const header_regexp = /(?:\d+)?\|(.+)/;
    120   var json = "";
    121   for (var i = 1; ; ++i) {
    122     var name = prefix + i;
    123     var value = header_map[name];
    124     if (!value)
    125       break;
    126 
    127     var match = value.match(header_regexp);
    128     if (!match) {
    129       Console.error("Failed to parse FirePHP log message: " + value);
    130       break;
    131     }
    132     var json_part = match[1];
    133     json += json_part.substring(0, json_part.lastIndexOf("|"));
    134     if (json_part.charAt(json_part.length - 1) === "\\")
    135       continue;
    136     try {
    137       var message = JSON.parse(json);
    138       results.push(message);
    139     } catch(e) {
    140       Console.error("Failed to parse FirePHP log message: " + json);
    141     }
    142     json = "";
    143   }
    144   return results;
    145 };
    146 
    147 ChromeFirePHP._checkProtoVersion = function(proto_header) {
    148   if (!proto_header) {
    149     Console.warn("WildFire protocol header not found");
    150     return;
    151   }
    152 
    153   var match = /http:\/\/meta\.wildfirehq\.org\/Protocol\/([^\/]+)\/(.+)/.exec(
    154       proto_header);
    155   if (!match) {
    156     Console.warn("Invalid WildFire protocol header");
    157     return;
    158   }
    159   var proto_name = match[1];
    160   var proto_version = match[2];
    161   if (proto_name !== "JsonStream" || proto_version !== "0.2") {
    162     Console.warn(
    163         "Unknown FirePHP protocol version: %s (expecting JsonStream/0.2)",
    164         proto_name + "/" + proto_version);
    165     return false;
    166   }
    167   return true;
    168 };
    169 
    170 chrome.devtools.network.addRequestHeaders({
    171     "X-FirePHP-Version": "0.0.6"
    172 });
    173 
    174 chrome.devtools.network.getHAR(function(result) {
    175   var entries = result.entries;
    176   if (!entries.length) {
    177     Console.warn("ChromeFirePHP suggests that you reload the page to track" +
    178         " FirePHP messages for all the requests");
    179   }
    180   for (var i = 0; i < entries.length; ++i)
    181     ChromeFirePHP.handleFirePhp_headers(entries[i]);
    182 
    183   chrome.devtools.network.onRequestFinished.addListener(
    184       ChromeFirePHP.handleFirePhpHeaders.bind(ChromeFirePHP));
    185 });
    186