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