1 /* See license.txt for terms of usage */
  2 
  3 // ************************************************************************************************
  4 // Shorcuts and Services
  5 
  6 const Cc = Components.classes;
  7 const Ci = Components.interfaces;
  8 
  9 const traceService = Cc["@joehewitt.com/firebug-trace-service;1"].getService(Ci.nsIObserverService);
 10 
 11 const PrefService = Cc["@mozilla.org/preferences-service;1"];
 12 const prefs = PrefService.getService(Ci.nsIPrefBranch2);
 13 const prefService = PrefService.getService(Ci.nsIPrefService);
 14 
 15 var gFindBar;
 16 
 17 const reDBG = /extensions\.([^\.]*)\.(DBG_.*)/;
 18 const reDBG_FBS = /DBG_FBS_(.*)/;
 19 const reEndings = /\r\n|\r|\n/;
 20 
 21 // The lib.js isn't included in this window so, define the global here.
 22 // It'll be initialized from window parameters (see initialize method).
 23 var FBL;
 24 
 25 // Cache messages that are fired before the content of the window is loaded.
 26 var queue = [];
 27 
 28 // ************************************************************************************************
 29 // Trace Window Implementation
 30 
 31 var TraceConsole =
 32 {
 33     modules: [],
 34 
 35     initialize: function()
 36     {
 37         var args = window.arguments[0];
 38         FBL = args.FBL;
 39         Firebug = args.Firebug;
 40 
 41         // Get pref domain is used for message filtering. Only logs that belong
 42         // to this pref-domain will be displayed.
 43         this.prefDomain = args.prefDomain;
 44         document.title = FBL.$STR("title.Tracing") + ": " + this.prefDomain;
 45 
 46         // Register listeners and observers
 47         traceService.addObserver(this, "firebug-trace-on-message", false);
 48         prefs.addObserver(this.prefDomain, this, false);
 49 
 50         // Initialize root node of the trace-console window.
 51         var consoleFrame = document.getElementById("consoleFrame");
 52         this.consoleNode = consoleFrame.contentDocument.getElementById("panelNode-traceConsole");
 53         Firebug.TraceModule.CommonBaseUI.initializeContent(this.consoleNode, this, this.prefDomain,
 54             FBL.bind(this.initializeContent, this));
 55 
 56         gFindBar = document.getElementById("FindToolbar");
 57     },
 58 
 59     initializeContent: function(logNode)
 60     {
 61         this.logs = logNode;
 62 
 63         // Notify listeners
 64         Firebug.TraceModule.onLoadConsole(window, logNode);
 65         this.registerModule(Firebug.TraceModule);
 66 
 67         // Make sure the UI is localized.
 68         this.internationalizeUI();
 69         this.updateTimeInfo();
 70 
 71         // If the opener is closed the console must be also closed.
 72         // (this console uses shared object from the opener (e.g. Firebug)
 73         window.opener.addEventListener("close", this.onCloseOpener, true);
 74         this.addedOnCloseOpener = true;
 75 
 76         // Fetch all cached messages.
 77         for (var i=0; i<queue.length; i++)
 78             this.dump(queue[i]);
 79     },
 80 
 81     internationalizeUI: function()
 82     {
 83         var buttons = ["clearConsole", "findConsole", "separateConsole",
 84             "restartFirefox", "closeFirefox", "saveToFile", "loadFromFile"];
 85 
 86         for (var i=0; i<buttons.length; i++)
 87         {
 88             var element = document.getElementById(buttons[i]);
 89             FBL.internationalize(element, "label");
 90             FBL.internationalize(element, "tooltiptext");
 91         }
 92     },
 93 
 94     updateTimeInfo: function()
 95     {
 96         var showTime = Firebug.getPref(this.prefDomain, "trace.showTime");
 97         if (showTime)
 98             FBL.setClass(this.logs.firstChild, "showTime");
 99         else
100             FBL.removeClass(this.logs.firstChild, "showTime");
101     },
102 
103     shutdown: function()
104     {
105         traceService.removeObserver(this, "firebug-trace-on-message");
106         prefs.removeObserver(this.prefDomain, this, false);
107 
108         // Notify listeners
109         for (var i=0; i<this.modules.length; ++i)
110             this.modules[i].onUnloadConsole(window);
111 
112         // Unregister from the opener
113         if (this.addedOnCloseOpener)
114         {
115             window.opener.removeEventListener("close", this.onCloseOpener, true);
116             delete this.addedOnCloseOpener;
117         }
118     },
119 
120     onCloseOpener: function()
121     {
122         if (FBTrace.DBG_INITIALIZE)
123             FBTrace.sysout("traceConsole.onCloseOpener closing window "+window.location);
124 
125         window.close();
126     },
127 
128     registerModule: function(traceModule)
129     {
130         this.modules.push(traceModule);
131     },
132 
133     unregisterModule: function(module)
134     {
135         for (var i=0; i<this.modules.length; ++i) {
136             if (this.modules[i] == module) {
137                 this.modules.splice(i, 1);
138                 break;
139             }
140         }
141     },
142 
143     // nsIObserver
144     observe: function(subject, topic, data)
145     {
146         if (topic == "firebug-trace-on-message")
147         {
148             // Display messages only with "firebug.extensions" type.
149             var messageInfo = subject.wrappedJSObject;
150 
151             // If the message type isn't specified, use Firebug's pref domain as the default.
152             if (!messageInfo.type)
153                 messageInfo.type = "extensions.firebug";
154 
155             if (messageInfo.type != this.prefDomain)
156                 return;
157 
158             var message = new Firebug.TraceModule.TraceMessage(
159                 messageInfo.type, data, messageInfo.obj, messageInfo.scope,
160                 messageInfo.time);
161 
162             // If the content isn't loaded yet, remember all messages and insert them later.
163             if (this.logs)
164                 this.dump(message);
165             else
166                 queue.push(message);
167 
168             return true;
169         }
170         else if (topic == "nsPref:changed")
171         {
172             if (data == this.prefDomain + ".trace.showTime")
173                 this.updateTimeInfo();
174         }
175     },
176 
177     // ********************************************************************************************
178     // Interface to the output nodes, going by the name outputNodes
179 
180     getScrollingNode: function()
181     {
182         //window.dump(FBL.getStackDump());
183         //window.dump("traceConsole getScrollingNode this.scrollingNode "+this.scrollingNode+"\n");
184 
185         return this.scrollingNode;
186     },
187 
188     setScrollingNode: function(node)
189     {
190         this.scrollingNode = node;
191     },
192 
193     getTargetNode: function()
194     {
195         //window.dump(FBL.getStackDump());
196         //window.dump("traceConsole getTargetgNode this.scrollingNode "+this.logs.firstChild+"\n");
197 
198         return this.logs.firstChild;
199     },
200 
201     // ********************************************************************************************
202     // Message dump
203 
204     dump: function(message)
205     {
206         // Notify listeners
207         for (var i=0; i<this.modules.length; ++i)
208             this.modules[i].onDump(message);
209 
210         Firebug.TraceModule.dump(message, this);
211     },
212 
213     dumpSeparator: function()
214     {
215         Firebug.TraceModule.MessageTemplate.dumpSeparator(this);
216     },
217 
218     // Trace console toolbar commands
219     onClearConsole: function()
220     {
221         FBL.clearNode(this.logs.firstChild);
222     },
223 
224     onSeparateConsole: function()
225     {
226         Firebug.TraceModule.MessageTemplate.dumpSeparator(this);
227     },
228 
229     onSaveToFile: function()
230     {
231         try
232         {
233             var nsIFilePicker = Ci.nsIFilePicker;
234             var fp = Cc["@mozilla.org/filepicker;1"].getService(nsIFilePicker);
235             fp.init(window, null, nsIFilePicker.modeSave);
236             fp.appendFilter("Firebug Tracing Logs","*.ftl;");
237             fp.appendFilters(nsIFilePicker.filterAll);
238             fp.filterIndex = 1;
239             fp.defaultString = "firebug-tracing-logs.ftl";
240 
241             var rv = fp.show();
242             if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace)
243             {
244                 var foStream = Cc["@mozilla.org/network/file-output-stream;1"]
245                     .createInstance(Ci.nsIFileOutputStream);
246                 foStream.init(fp.file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate
247 
248                 var appInfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
249                 var currLocale = Firebug.getPref("general.useragent", "locale");
250                 var systemInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag); 
251 
252                 var log = { version: "1.0" };
253 
254                 // Firebug info version
255                 log.firebug = Firebug.version;
256                 log.app = {
257                     name: appInfo.name,
258                     version: appInfo.version,
259                     platformVersion: appInfo.platformVersion,
260                     buildID: appInfo.appBuildID,
261                     locale: currLocale
262                 };
263                 log.os = {
264                     name: systemInfo.getProperty("name"),
265                     version: systemInfo.getProperty("version")
266                 };
267                 log.date = (new Date()).toGMTString();
268                 log.messages = [];
269 
270                 // Iterate over all logs and store it into a file.
271                 var tbody = this.logs.firstChild;
272                 for (var row = tbody.firstChild; row; row = row.nextSibling)
273                     this.saveMessage(log, row.repObject);
274 
275                 var jsonString = JSON.stringify(log, null, "  ");
276                 foStream.write(jsonString, jsonString.length);
277                 foStream.close();
278             }
279         }
280         catch (err)
281         {
282             alert(err.toString());
283         }
284     },
285 
286     onLoadFromFile: function()
287     {
288         try
289         {
290             var nsIFilePicker = Ci.nsIFilePicker;
291             var fp = Cc["@mozilla.org/filepicker;1"].getService(nsIFilePicker);
292             fp.init(window, null, nsIFilePicker.modeOpen);
293             fp.appendFilters(nsIFilePicker.filterAll);
294             fp.appendFilter("Firebug Tracing Logs", "*.ftl;");
295             fp.filterIndex = 1;
296 
297             var rv = fp.show();
298             if (rv != nsIFilePicker.returnOK)
299                 return;
300 
301             var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
302                 .createInstance(Ci.nsIFileInputStream);
303             inputStream.init(fp.file, -1, -1, 0); // read-only
304 
305             // Read and parset the content
306             var jsonString = FBL.readFromStream(inputStream)
307             var log = JSON.parse(jsonString);
308             if (!log)
309             {
310                 alert("No log data available.");
311                 return;
312             }
313 
314             log.filePath = fp.file.path;
315 
316             var MessageTemplate = Firebug.TraceModule.MessageTemplate;
317             var TraceModule = Firebug.TraceModule;
318 
319             // Create header, dump all logs and create footer.
320             MessageTemplate.dumpSeparator(this, MessageTemplate.importHeaderTag, log);
321             for (var i=0; i<log.messages.length; i++)
322             {
323                 var logMsg = log.messages[i];
324                 if (!logMsg.type)
325                     continue;
326                 else if (logMsg.type == "separator")
327                     MessageTemplate.dumpSeparator(this);
328                 else
329                     MessageTemplate.dump(new TraceModule.ImportedMessage(logMsg), this);
330             }
331             MessageTemplate.dumpSeparator(this, MessageTemplate.importFooterTag);
332         }
333         catch (err)
334         {
335             alert(err.toString());
336         }
337     },
338 
339     saveMessage: function(log, message)
340     {
341         if (!message)
342             return;
343 
344         var text = message.text;
345         text = text ? text.replace(reEndings, "") : "---";
346         text = text.replace(/"|'/g, "");
347 
348         var msgLog = {
349             index: message.index,
350             text: message.text,
351             type: message.type ? message.type : "",
352             time: message.time,
353             stack: []
354         };
355 
356         var stack = message.stack;
357         for (var i=0; stack && i<stack.length; i++)
358         {
359             var frame = stack[i];
360             msgLog.stack.push({
361                 fileName: frame.fileName,
362                 lineNumber: frame.lineNumber,
363                 funcName: frame.funcName,
364             });
365         }
366 
367         log.messages.push(msgLog);
368     },
369 
370     onRestartFirefox: function()
371     {
372         Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup).
373             quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
374     },
375 
376     onExitFirefox: function()
377     {
378         goQuitApplication();
379     },
380 };
381 
382 // ************************************************************************************************
383