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