1 /* See license.txt for terms of usage */ 2 3 // ********************************************************************************************* // 4 // Constants 5 6 const Cc = Components.classes; 7 const Ci = Components.interfaces; 8 const Cr = Components.results; 9 const Cu = Components.utils; 10 11 var EXPORTED_SYMBOLS = ["traceConsoleService"]; 12 13 const PrefService = Cc["@mozilla.org/preferences-service;1"]; 14 const prefs = PrefService.getService(Ci.nsIPrefBranch); 15 const prefService = PrefService.getService(Ci.nsIPrefService); 16 17 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 18 Cu.import("resource://gre/modules/Services.jsm"); 19 Cu.import("resource://gre/modules/AddonManager.jsm"); 20 21 // xxxHonza: could we remove some of them? 22 var TraceAPI = ["dump", "sysout", "setScope", "matchesNode", "time", "timeEnd"]; 23 24 // ********************************************************************************************* // 25 // Service Implementation 26 27 try 28 { 29 Cu["import"]("resource://fbtrace/firebug-trace-service.js"); 30 } 31 catch (err) 32 { 33 // Tracing Console is not available yet, let's use a fake one. 34 var traceConsoleService = 35 { 36 tracers: {}, 37 38 getTracer: function(prefDomain) 39 { 40 var tracer = this.tracers[prefDomain]; 41 if (tracer) 42 return tracer; 43 44 var enabledAddons = decodeURIComponent(getCharPref("extensions", "enabledAddons")); 45 if (enabledAddons.indexOf("fbtrace@getfirebug.com:") >= 0) 46 { 47 // Solution with built-in buffer for logs created before console is ready. 48 var wrapper = new TracerWrapper(prefDomain); 49 tracer = wrapper.createTracer(); 50 } 51 else 52 { 53 // Simple empty implementation for cases where Firebug Tracing Console 54 // is not even installed or 'alwaysOpenTraceConsole' is set to false. 55 tracer = {}; 56 for (var i=0; i<TraceAPI.length; i++) 57 tracer[TraceAPI[i]] = function() {}; 58 } 59 60 this.tracers[prefDomain] = tracer; 61 return tracer; 62 }, 63 } 64 } 65 66 // ********************************************************************************************* // 67 // Tracer Wrapper 68 69 /** 70 * Trace Wrapper represents a temporary Trace object that is used till the real Tracing 71 * Console is opened and available to use. Trace Wrapper implements a buffer that 72 * collects all logs and flushes them as intot the real console as soon as it's ready. 73 * 74 * In order to use this functionality, you need to set: 75 * 'extensions.firebug.alwaysOpenTraceConsole' to true 76 * 77 * @param {Object} prefDomain Associated pref domain. Usually 'extensions.firebug' 78 */ 79 function TracerWrapper(prefDomain) 80 { 81 this.prefDomain = prefDomain; 82 } 83 84 TracerWrapper.prototype = 85 { 86 // Temporary trace object 87 tracer: null, 88 89 // Buffer with logs used till the Tracing Console UI is available 90 queue: [], 91 92 // The real tracing console tracer object. 93 FBTrace: null, 94 95 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 96 // Tracer 97 98 createTracer: function() 99 { 100 var self = this; 101 102 this.addObserver(); 103 104 // Default FBTrace implementation puts all calls in a buffer. 105 // It'll be used as soon as the console is ready. 106 function createHandler(method) 107 { 108 return function() { 109 self.push(method, arguments); 110 } 111 }; 112 113 // Fake FBTrace object 114 this.tracer = {}; 115 116 // Dynamically create APIs. 117 for (var i=0; i<TraceAPI.length; i++) 118 { 119 var method = TraceAPI[i]; 120 this.tracer[method] = createHandler(method); 121 } 122 123 var branch = prefService.getBranch(this.prefDomain); 124 var arrayDesc = {}; 125 126 // Set options from preferences. 127 var children = branch.getChildList("", arrayDesc); 128 for (var i=0; i<children.length; i++) 129 { 130 var name = children[i]; 131 var m = name.indexOf("DBG_"); 132 if (m != -1) 133 { 134 var optionName = name.substr(1); // drop leading . 135 this.tracer[optionName] = getBoolPref(this.prefDomain, optionName); 136 } 137 } 138 139 // Create FBTrace proxy. As soon as FBTrace console is available it'll forward 140 // all calls to it. 141 return Proxy.create( 142 { 143 get: function(target, name) 144 { 145 return self.FBTrace ? self.FBTrace[name] : self.tracer[name]; 146 }, 147 148 set: function(target, name, value) 149 { 150 if (self.FBTrace) 151 self.FBTrace[name] = value; 152 else 153 self.tracer[name] = value; 154 155 return true; 156 }, 157 }); 158 }, 159 160 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 161 // Buffer 162 163 push: function(method, args) 164 { 165 if (!this.queue) 166 return; 167 168 this.queue.push({ 169 method: method, 170 args: args, 171 }); 172 173 // Size of the buffer is limited. 174 while (this.queue.length > 1000) 175 this.queue.pop(); 176 }, 177 178 clearBuffer: function() 179 { 180 this.queue = null; 181 }, 182 183 flush: function() 184 { 185 if (!this.FBTrace || !this.queue) 186 return; 187 188 if (this.queue.length > 0) 189 this.FBTrace.sysout("FBTrace: flush " + this.queue.length + " buffered logs:"); 190 191 for (var i=0; i<this.queue.length; i++) 192 { 193 var call = this.queue[i]; 194 this.FBTrace[call.method].apply(this.FBTrace, call.args); 195 } 196 197 this.clearBuffer(); 198 }, 199 200 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 201 // FBTrace Console Observer 202 203 addObserver: function() 204 { 205 // Listen for new windows, Firebug must be loaded into them too. 206 Services.obs.addObserver(this, "chrome-document-global-created", false); 207 }, 208 209 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), 210 observe: function windowWatcher(win, topic, data) 211 { 212 // xxxHonza: the window should be associated with the same prefDomain. 213 if (win.location.href == "chrome://fbtrace/content/traceLogFrame.html") 214 { 215 var self = this; 216 217 // https://bugzil.la/795961 ? 218 win.addEventListener("load", function onLoad(evt) 219 { 220 // load listener not necessary once https://bugzil.la/800677 is fixed 221 var win = evt.currentTarget; 222 win.removeEventListener("load", onLoad, false); 223 224 self.initFBTrace(self.prefDomain); 225 }, false); 226 } 227 }, 228 229 initFBTrace: function() 230 { 231 if (this.FBTrace) 232 return this.FBTrace; 233 234 try 235 { 236 var scope = {}; 237 Cu.import("resource://fbtrace/firebug-trace-service.js", scope); 238 this.FBTrace = scope.traceConsoleService.getTracer(this.prefDomain); 239 240 // FBTrace Console is ready let's flush the log buffer. 241 this.flush(); 242 } 243 catch (err) 244 { 245 } 246 } 247 } 248 249 // ********************************************************************************************* // 250 // Helpers 251 252 function getStackDump() 253 { 254 var lines = []; 255 for (var frame = Components.stack; frame; frame = frame.caller) 256 lines.push(frame.filename + " (" + frame.lineNumber + ")"); 257 258 return lines.join("\n"); 259 }; 260 261 function getBoolPref(prefDomain, name) 262 { 263 try 264 { 265 var prefName = prefDomain + "." + name; 266 return prefs.getBoolPref(prefName); 267 } 268 catch (err) 269 { 270 } 271 272 return false; 273 } 274 275 function getCharPref(prefDomain, name) 276 { 277 try 278 { 279 var prefName = prefDomain + "." + name; 280 return prefs.getCharPref(prefName); 281 } 282 catch (err) 283 { 284 } 285 286 return false; 287 } 288 289 // ********************************************************************************************* // 290