1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/object",
  5     "firebug/firebug",
  6     "firebug/chrome/firefox",
  7     "firebug/lib/options",
  8     "firebug/chrome/window",
  9     "firebug/lib/string",
 10     "firebug/lib/persist",
 11     "firebug/net/httpActivityObserver",
 12     "firebug/net/requestObserver",
 13     "firebug/net/netProgress",
 14     "firebug/lib/http",
 15     "firebug/net/netUtils",
 16     "firebug/net/netDebugger",
 17     "firebug/lib/events",
 18     "firebug/trace/traceListener",
 19     "firebug/trace/traceModule"
 20 ],
 21 function(Obj, Firebug, Firefox, Options, Win, Str, Persist, NetHttpActivityObserver,
 22     HttpRequestObserver, NetProgress, Http, NetUtils, NetDebugger, Events,
 23     TraceListener, TraceModule) {
 24 
 25 // ********************************************************************************************* //
 26 // Constants
 27 
 28 const Cc = Components.classes;
 29 const Ci = Components.interfaces;
 30 const Cr = Components.results;
 31 
 32 var panelName = "net";
 33 
 34 var startFile = NetProgress.prototype.startFile;
 35 var openingFile = NetProgress.prototype.openingFile;
 36 var requestedFile = NetProgress.prototype.requestedFile;
 37 var respondedFile = NetProgress.prototype.respondedFile;
 38 var respondedCacheFile = NetProgress.prototype.respondedCacheFile;
 39 var windowPaint = NetProgress.prototype.windowPaint;
 40 var timeStamp = NetProgress.prototype.timeStamp;
 41 var windowLoad = NetProgress.prototype.windowLoad;
 42 var contentLoad = NetProgress.prototype.contentLoad;
 43 
 44 // ********************************************************************************************* //
 45 
 46 /**
 47  * @module Represents a module object for the Net panel. This object is derived
 48  * from <code>Firebug.ActivableModule</code> in order to support activation (enable/disable).
 49  * This allows to avoid (performance) expensive features if the functionality is not necessary
 50  * for the user.
 51  */
 52 Firebug.NetMonitor = Obj.extend(Firebug.ActivableModule,
 53 {
 54     dispatchName: "netMonitor",
 55     maxQueueRequests: 500,
 56     contexts: new Array(),
 57 
 58     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 59     // Module
 60 
 61     initialize: function()
 62     {
 63         Firebug.ActivableModule.initialize.apply(this, arguments);
 64 
 65         this.traceNetListener = new TraceListener("net.", "DBG_NET", true);
 66         this.traceActivityListener = new TraceListener("activityObserver.",
 67             "DBG_ACTIVITYOBSERVER", true);
 68 
 69         TraceModule.addListener(this.traceNetListener);
 70         TraceModule.addListener(this.traceActivityListener);
 71 
 72         Firebug.connection.addListener(this.DebuggerListener);
 73 
 74         NetHttpObserver.registerObserver();
 75     },
 76 
 77     initializeUI: function()
 78     {
 79         Firebug.ActivableModule.initializeUI.apply(this, arguments);
 80 
 81         // Initialize max limit for logged requests.
 82         Firebug.NetMonitor.updateMaxLimit();
 83 
 84         // Synchronize UI buttons with the current filter.
 85         this.syncFilterButtons(Firebug.chrome);
 86 
 87         if (FBTrace.DBG_NET)
 88             FBTrace.sysout("net.NetMonitor.initializeUI; enabled: " + this.isAlwaysEnabled());
 89     },
 90 
 91     shutdown: function()
 92     {
 93         Firebug.ActivableModule.shutdown.apply(this, arguments);
 94 
 95         TraceModule.removeListener(this.traceNetListener);
 96         TraceModule.removeListener(this.traceActivityListener);
 97 
 98         Firebug.connection.removeListener(this.DebuggerListener);
 99 
100         NetHttpObserver.unregisterObserver();
101     },
102 
103     initContext: function(context, persistedState)
104     {
105         Firebug.ActivableModule.initContext.apply(this, arguments);
106 
107         if (FBTrace.DBG_NET)
108             FBTrace.sysout("net.initContext for: " + context.getName());
109 
110         // XXXjjb changed test to instanceof because jetpack uses fake window objects
111         if (context.window && context.window instanceof Window)
112         {
113             var win = context.window;
114 
115             var onWindowPaintHandler = function()
116             {
117                 if (context.netProgress)
118                     context.netProgress.post(windowPaint, [win, NetUtils.now()]);
119             }
120 
121             if (Options.get("netShowPaintEvents"))
122             {
123                 context.addEventListener(win, "MozAfterPaint", onWindowPaintHandler, false);
124             }
125 
126             // Register "load" listener in order to track window load time.
127             var onWindowLoadHandler = function()
128             {
129                 if (context.netProgress)
130                     context.netProgress.post(windowLoad, [win, NetUtils.now()]);
131                 context.removeEventListener(win, "load", onWindowLoadHandler, true);
132 
133                 context.setTimeout(function()
134                 {
135                     if (win && !win.closed)
136                     {
137                         context.removeEventListener(win, "MozAfterPaint", onWindowPaintHandler, false);
138                     }
139                 }, 2000); //xxxHonza: this should be customizable using preferences.
140             }
141             context.addEventListener(win, "load", onWindowLoadHandler, true);
142 
143             // Register "DOMContentLoaded" listener to track timing.
144             var onContentLoadHandler = function()
145             {
146                 if (context.netProgress)
147                     context.netProgress.post(contentLoad, [win, NetUtils.now()]);
148                 context.removeEventListener(win, "DOMContentLoaded", onContentLoadHandler, true);
149             }
150 
151             context.addEventListener(win, "DOMContentLoaded", onContentLoadHandler, true);
152         }
153 
154         if (Firebug.NetMonitor.isAlwaysEnabled())
155             monitorContext(context);
156 
157         if (context.netProgress)
158         {
159             // Load existing breakpoints
160             var persistedPanelState = Persist.getPersistedState(context, panelName);
161             if (persistedPanelState.breakpoints)
162                 context.netProgress.breakpoints = persistedPanelState.breakpoints;
163         }
164     },
165 
166     showContext: function(browser, context)
167     {
168         Firebug.ActivableModule.showContext.apply(this, arguments);
169 
170         if (FBTrace.DBG_NET)
171             FBTrace.sysout("net.showContext; " + (context ? context.getName() : "NULL") +
172                 ", temp contexts: " + getTempContextCount());
173     },
174 
175     loadedContext: function(context)
176     {
177         var tabId = Win.getWindowProxyIdForWindow(context.browser.contentWindow);
178         delete this.contexts[tabId];
179 
180         if (FBTrace.DBG_NET)
181             FBTrace.sysout("net.loadedContext; temp contexts (" + getTempContextCount() + ")");
182 
183         var netProgress = context.netProgress;
184         if (netProgress)
185         {
186             netProgress.loaded = true;
187 
188             // Set Page title and id into all document objects.
189             for (var i=0; i<netProgress.documents.length; i++)
190             {
191                 var doc = netProgress.documents[i];
192                 doc.id = context.uid;
193                 doc.title = NetUtils.getPageTitle(context);
194             }
195         }
196     },
197 
198     destroyContext: function(context, persistedState)
199     {
200         Firebug.ActivableModule.destroyContext.apply(this, arguments);
201 
202         if (FBTrace.DBG_NET)
203             FBTrace.sysout("net.destroyContext for: " +
204                 (context ? context.getName() : "No context"));
205 
206         if (context.netProgress)
207         {
208             // Remember existing breakpoints.
209             var persistedPanelState = Persist.getPersistedState(context, panelName);
210             persistedPanelState.breakpoints = context.netProgress.breakpoints;
211         }
212 
213         if (Firebug.NetMonitor.isAlwaysEnabled())
214             unmonitorContext(context);
215     },
216 
217     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
218     // Activable Module
219 
220     onObserverChange: function(observer)
221     {
222         if (FBTrace.DBG_NET)
223             FBTrace.sysout("net.onObserverChange; hasObservers: " + this.hasObservers() +
224                 ", Firebug suspended: " + Firebug.getSuspended());
225 
226         if (!Firebug.getSuspended())  // then Firebug is in action
227             this.onResumeFirebug();   // and we need to test to see if we need to addObserver
228     },
229 
230     onResumeFirebug: function()
231     {
232         if (FBTrace.DBG_NET)
233             FBTrace.sysout("net.onResumeFirebug; enabled: " + Firebug.NetMonitor.isAlwaysEnabled());
234 
235         // Resume only if NetPanel is enabled and so, observing NetMonitor module.
236         if (Firebug.NetMonitor.isAlwaysEnabled())
237         {
238             NetHttpActivityObserver.registerObserver();
239             Firebug.connection.eachContext(monitorContext);
240         }
241         else
242         {
243             // If the Net panel is not enabled, we needto make sure the unmonitorContext
244             // is executed and so, the start button (aka firebug status bar icons) is
245             // properly updated.
246             NetHttpActivityObserver.unregisterObserver();
247             Firebug.connection.eachContext(unmonitorContext);
248         }
249     },
250 
251     onSuspendFirebug: function()
252     {
253         if (FBTrace.DBG_NET)
254             FBTrace.sysout("net.onSuspendFirebug; enabled: " + Firebug.NetMonitor.isAlwaysEnabled());
255 
256         NetHttpActivityObserver.unregisterObserver();
257         Firebug.connection.eachContext(unmonitorContext);
258     },
259 
260     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
261     // User Actions
262 
263     clear: function(context)
264     {
265         // The user pressed a Clear button so, remove content of the panel...
266         var panel = context.getPanel(panelName, true);
267         if (panel)
268             panel.clear();
269     },
270 
271     onToggleFilter: function(context, filterCategory)
272     {
273         if (!context.netProgress)
274             return;
275 
276         Options.set("netFilterCategory", filterCategory);
277 
278         // The content filter has been changed. Make sure that the content
279         // of the panel is updated (CSS is used to hide or show individual files).
280         var panel = context.getPanel(panelName, true);
281         if (panel)
282         {
283             panel.setFilter(filterCategory);
284             panel.updateSummaries(NetUtils.now(), true);
285         }
286     },
287 
288     syncFilterButtons: function(chrome)
289     {
290         var button = chrome.$("fbNetFilter-" + Firebug.netFilterCategory);
291         button.checked = true;
292     },
293 
294     togglePersist: function(context)
295     {
296         var panel = context.getPanel(panelName);
297         panel.persistContent = panel.persistContent ? false : true;
298         Firebug.chrome.setGlobalAttribute("cmd_firebug_togglePersistNet", "checked", panel.persistContent);
299     },
300 
301     updateOption: function(name, value)
302     {
303         if (name == "net.logLimit")
304             this.updateMaxLimit();
305     },
306 
307     updateMaxLimit: function()
308     {
309         var value = Options.get("net.logLimit");
310         this.maxQueueRequests = value ? value : this.maxQueueRequests;
311     },
312 
313     addTimeStamp: function(context, time, label, color)
314     {
315         if (context.netProgress)
316             context.netProgress.post(timeStamp, [context.window, time, label, color]);
317     }
318 });
319 
320 // ********************************************************************************************* //
321 
322 // HTTP Observer
323 
324 // HTTP listener - based on HttpRequestObserver module
325 // This observer is used for observing the first document http-on-modify-request
326 // and http-on-examine-response events, which are fired before the context
327 // is initialized (initContext method call). Without this observer this events
328 // would be lost and the time measuring would be wrong.
329 //
330 // This observer stores these early requests in helper array (contexts) and maps
331 // them to appropriate tab - initContext then uses the array in order to access it.
332 
333 var NetHttpObserver =
334 {
335     dispatchName: "NetHttpObserver",
336     registered: false,
337 
338     registerObserver: function()
339     {
340         if (this.registered)
341             return;
342 
343         if (FBTrace.DBG_NET)
344             FBTrace.sysout("net.NetHttpObserver.register;");
345 
346         HttpRequestObserver.addObserver(this, "firebug-http-event", false);
347         this.registered = true;
348     },
349 
350     unregisterObserver: function()
351     {
352         if (!this.registered)
353             return;
354 
355         if (FBTrace.DBG_NET)
356             FBTrace.sysout("net.NetHttpObserver.unregister;");
357 
358         HttpRequestObserver.removeObserver(this, "firebug-http-event");
359         this.registered = false;
360     },
361 
362     /* nsIObserve */
363     observe: function(subject, topic, data)
364     {
365         if (!Firebug.NetMonitor.isAlwaysEnabled())
366             return;
367 
368         try
369         {
370             if (FBTrace.DBG_NET_EVENTS)
371             {
372                 FBTrace.sysout("net.events.observe " + (topic ? topic.toUpperCase() : topic) +
373                     ", " + ((subject instanceof Ci.nsIRequest) ? Http.safeGetRequestName(subject) : "") +
374                     ", Browser: " + Firebug.chrome.window.document.title);
375             }
376 
377             if (!(subject instanceof Ci.nsIHttpChannel))
378                 return;
379 
380             var win = Http.getWindowForRequest(subject);
381             var context = Firebug.connection.getContextByWindow(win);
382 
383             // The context doesn't have to exist yet. In such cases a temp Net context is
384             // created within onModifyRequest.
385 
386             // Some requests are not associated with any page (e.g. favicon).
387             // These are ignored as Net panel shows only page requests.
388             var tabId = win ? Win.getWindowProxyIdForWindow(win) : null;
389             if (!tabId)
390             {
391                 if (FBTrace.DBG_NET)
392                     FBTrace.sysout("net.observe NO TAB " + Http.safeGetRequestName(subject) +
393                         ", " + tabId + ", " + win);
394                 return;
395             }
396 
397             if (topic == "http-on-modify-request")
398                 this.onModifyRequest(subject, win, tabId, context);
399             else if (topic == "http-on-examine-response")
400                 this.onExamineResponse(subject, win, tabId, context);
401             else if (topic == "http-on-examine-cached-response")
402                 this.onExamineCachedResponse(subject, win, tabId, context);
403             else if (topic == "http-on-opening-request")
404                 this.openingFile(subject, win, tabId, context);
405         }
406         catch (err)
407         {
408             if (FBTrace.DBG_ERRORS)
409                 FBTrace.sysout("net.observe EXCEPTION", err);
410         }
411     },
412 
413     onModifyRequest: function(request, win, tabId, context)
414     {
415         var name = request.URI.asciiSpec;
416         var origName = request.originalURI.asciiSpec;
417         var isRedirect = (name != origName);
418 
419         // We only need to create a new context if this is a top document uri (not frames).
420         if ((request.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI) &&
421             request.loadGroup && request.loadGroup.groupObserver &&
422             win == win.parent && !isRedirect)
423         {
424             var browser = Firefox.getBrowserForWindow(win);
425 
426             if (!Firebug.TabWatcher.shouldCreateContext(browser, name, null))
427             {
428                 if (FBTrace.DBG_NET)
429                     FBTrace.sysout("net.onModifyRequest; Activation logic says don't create " +
430                         "temp context for: " + name);
431                 return;
432             }
433 
434             // Create a new network context prematurely.
435             if (!Firebug.NetMonitor.contexts[tabId])
436             {
437                 Firebug.NetMonitor.contexts[tabId] = createNetProgress(null);
438 
439                 // OK, we definitelly want to watch this page load, temp context is created
440                 // so, make sure the activity-observer is registered and we have detailed
441                 // timing info for this first document request.
442                 NetHttpActivityObserver.registerObserver();
443 
444                 if (FBTrace.DBG_NET)
445                     FBTrace.sysout("net.onModifyRequest; Temp Context created (" +
446                         getTempContextCount() + "), " + tabId);
447             }
448         }
449 
450         var networkContext = Firebug.NetMonitor.contexts[tabId];
451         if (!networkContext)
452             networkContext = context ? context.netProgress : null;
453 
454         if (networkContext)
455         {
456             networkContext.post(startFile, [request, win]);
457 
458             // We need to track the request now since the activity observer is not used in case
459             // the response comes from BF cache. If it's a regular HTTP request the timing
460             // is properly overridden by the activity observer (ACTIVITY_SUBTYPE_REQUEST_HEADER).
461             // Even if the Firebug.netShowBFCacheResponses is false now, the user could
462             // switch it on later.
463             var xhr = Http.isXHR(request);
464             networkContext.post(requestedFile, [request, NetUtils.now(), win, xhr]);
465         }
466     },
467 
468     onExamineResponse: function(request, win, tabId, context)
469     {
470         var networkContext = Firebug.NetMonitor.contexts[tabId];
471         if (!networkContext)
472             networkContext = context ? context.netProgress : null;
473 
474         if (!networkContext)
475             return;
476 
477         var info = new Object();
478         info.responseStatus = request.responseStatus;
479         info.responseStatusText = request.responseStatusText;
480 
481         // Initialize info.postText property.
482         info.request = request;
483         NetUtils.getPostText(info, context);
484 
485         // Get response headers now. They could be replaced by cached headers later
486         // (if the response is coming from the cache).
487         NetUtils.getHttpHeaders(request, info, context);
488 
489         if (FBTrace.DBG_NET && info.postText)
490             FBTrace.sysout("net.onExamineResponse, POST data: " + info.postText, info);
491 
492         networkContext.post(respondedFile, [request, NetUtils.now(), info]);
493 
494         // Make sure to track the first document response.
495         //Firebug.TabCacheModel.registerStreamListener(request, win, true);
496     },
497 
498     onExamineCachedResponse: function(request, win, tabId, context)
499     {
500         var networkContext = Firebug.NetMonitor.contexts[tabId];
501         if (!networkContext)
502             networkContext = context ? context.netProgress : null;
503 
504         if (!networkContext)
505         {
506             if (FBTrace.DBG_NET_EVENTS)
507                 FBTrace.sysout("net.onExamineCachedResponse; No CONTEXT for:" +
508                     Http.safeGetRequestName(request));
509             return;
510         }
511 
512         var info = new Object();
513         info.responseStatus = request.responseStatus;
514         info.responseStatusText = request.responseStatusText;
515 
516         // Initialize info.postText property.
517         info.request = request;
518         NetUtils.getPostText(info, context);
519 
520         networkContext.post(respondedCacheFile, [request, NetUtils.now(), info]);
521     },
522 
523     openingFile: function(request, win, tabId, context)
524     {
525         var networkContext = Firebug.NetMonitor.contexts[tabId];
526         if (!networkContext)
527             networkContext = context ? context.netProgress : null;
528 
529         if (!networkContext)
530             return;
531 
532         networkContext.post(openingFile, [request, win]);
533     },
534 
535     /* nsISupports */
536     QueryInterface: function(iid)
537     {
538         if (iid.equals(Ci.nsISupports) ||
539             iid.equals(Ci.nsIObserver)) {
540              return this;
541          }
542 
543         throw Cr.NS_ERROR_NO_INTERFACE;
544     }
545 }
546 
547 // ********************************************************************************************* //
548 // Monitoring start/stop
549 
550 function monitorContext(context)
551 {
552     if (context.netProgress)
553         return;
554 
555     var networkContext = null;
556 
557     // Use an existing context associated with the browser tab if any
558     // or create a pure new network context.
559     if (context.window)
560     {
561         var tabId = Win.getWindowProxyIdForWindow(context.window);
562         networkContext = Firebug.NetMonitor.contexts[tabId];
563     }
564 
565     if (FBTrace.DBG_NET)
566         FBTrace.sysout("net.monitorContext; (" + networkContext + ") " +
567             tabId + ", " + context.getName());
568 
569     if (networkContext)
570     {
571         if (FBTrace.DBG_NET)
572             FBTrace.sysout("net.monitorContext; Use temporary context: " + tabId);
573 
574         networkContext.context = context;
575         delete Firebug.NetMonitor.contexts[tabId];
576     }
577     else
578     {
579         if (FBTrace.DBG_NET)
580             FBTrace.sysout("net.monitorContext; create network monitor context object for: " +
581                 tabId);
582 
583         networkContext = createNetProgress(context);
584     }
585 
586     // Register activity-distributor observer if available (#488270)
587     //NetHttpActivityObserver.registerObserver();
588 
589     context.netProgress = networkContext;
590 
591     // Add cache listener so, net panel has always fresh responses.
592     // Safe to call multiple times.
593     networkContext.cacheListener.register(context.sourceCache);
594 
595     // Activate net panel sub-context.
596     var panel = context.getPanel(panelName);
597     context.netProgress.activate(panel);
598 
599     // Display info message, but only if the panel isn't just reloaded or Persist == true.
600     if (!context.persistedState)
601         panel.insertActivationMessage();
602 
603     updateStartButton(true);
604 }
605 
606 function unmonitorContext(context)
607 {
608     if (FBTrace.DBG_NET)
609         FBTrace.sysout("net.unmonitorContext; (" +
610             (context ? context.netProgress : "netProgress == NULL") + ") " +
611             (context ? context.getName() : "no context"));
612 
613     var netProgress = context ? context.netProgress : null;
614     if (!netProgress)
615         return;
616 
617     // Since the print into the UI is done by timeout asynchronously,
618     // make sure there are no requests left.
619     var panel = context.getPanel(panelName, true);
620     if (panel)
621         panel.updateLayout();
622 
623     //NetHttpActivityObserver.unregisterObserver();
624 
625     // Remove cache listener. Safe to call multiple times.
626     netProgress.cacheListener.unregister();
627 
628     // Deactivate net sub-context.
629     context.netProgress.activate(null);
630 
631     updateStartButton(false);
632 
633     // And finaly destroy the net panel sub context.
634     delete context.netProgress;
635 }
636 
637 function updateStartButton(enabled)
638 {
639     if (FBTrace.DBG_NET)
640         FBTrace.sysout("net.updateStartButton; update start button, enabled: " + enabled);
641 
642     var firebugStatus = Firefox.getElementById("firebugStatus");
643 
644     // Update status
645     if (enabled)
646         firebugStatus.setAttribute("net", "on");
647     else
648         firebugStatus.removeAttribute("net");
649 
650     // Update start button tooltip
651     if (Firebug.StartButton)
652         Firebug.StartButton.resetTooltip();
653     else
654         FBTrace.sysout("net.updateStartButton; ERROR No Firebug.StartButton ?");
655 }
656 
657 function createNetProgress(context)
658 {
659     var netProgress = new NetProgress(context);
660     netProgress.cacheListener = new NetCacheListener(netProgress);
661     netProgress.breakpoints = new NetDebugger.NetBreakpointGroup();
662     return netProgress;
663 }
664 
665 // ********************************************************************************************* //
666 // TabCache Listener
667 
668 /**
669  * TabCache listner implementation. Net panel uses this listner to remember all
670  * responses stored into the cache. There can be more requests to the same URL that
671  * returns different responses. The Net panels must remember all of them (tab cache
672  * remembers only the last one)
673  */
674 function NetCacheListener(netProgress)
675 {
676     this.netProgress = netProgress;
677     this.cache = null;
678 }
679 
680 NetCacheListener.prototype =
681 {
682     dispatchName: "NetCacheListener",
683 
684     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
685     // Registration
686 
687     register: function(cache)
688     {
689         if (this.cache)
690             return;
691 
692         this.cache = cache;
693         this.cache.addListener(this);
694     },
695 
696     unregister: function()
697     {
698         if (!this.cache)
699             return;
700 
701         this.cache.removeListener(this);
702         this.cache = null;
703     },
704 
705     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
706     // Cache Listener
707 
708     onStartRequest: function(context, request)
709     {
710         // Keep in mind that the file object (representing the request) doesn't have to be
711         // created at this moment (top document request).
712     },
713 
714     onStopRequest: function(context, request, responseText)
715     {
716         // Remember the response for this request.
717         var file = this.netProgress.getRequestFile(request, null, true);
718         if (file)
719             file.responseText = responseText;
720 
721         Events.dispatch(Firebug.NetMonitor.fbListeners, "onResponseBody", [context, file]);
722     }
723 }
724 
725 // ********************************************************************************************* //
726 // Debugger Listener
727 
728 Firebug.NetMonitor.DebuggerListener =
729 {
730     getBreakpoints: function(context, groups)
731     {
732         if (context.netProgress && !context.netProgress.breakpoints.isEmpty())
733             groups.push(context.netProgress.breakpoints);
734     },
735 };
736 
737 // ********************************************************************************************* //
738 // Tracing support
739 
740 function getTempContextCount()
741 {
742     var counter = 0;
743     for (var p in Firebug.NetMonitor.contexts)
744         counter++;
745     return counter;
746 }
747 
748 // ********************************************************************************************* //
749 // Registration
750 
751 // Keep compatibility with existing XUL based extensions
752 // deprecated
753 Firebug.NetMonitor.Utils = NetUtils;
754 
755 Firebug.registerActivableModule(Firebug.NetMonitor);
756 
757 return Firebug.NetMonitor;
758 
759 // ********************************************************************************************* //
760 });
761