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/domplate", 8 "firebug/lib/xpcom", 9 "firebug/lib/locale", 10 "firebug/lib/events", 11 "firebug/lib/options", 12 "firebug/lib/url", 13 "firebug/js/sourceLink", 14 "firebug/lib/http", 15 "firebug/lib/css", 16 "firebug/lib/dom", 17 "firebug/chrome/window", 18 "firebug/lib/search", 19 "firebug/lib/string", 20 "firebug/lib/array", 21 "firebug/lib/system", 22 "firebug/chrome/menu", 23 "firebug/net/netUtils", 24 "firebug/net/netProgress", 25 "firebug/css/cssReps", 26 "firebug/js/breakpoint", 27 "firebug/net/xmlViewer", 28 "firebug/net/svgViewer", 29 "firebug/net/jsonViewer", 30 "firebug/net/fontViewer", 31 "firebug/chrome/infotip", 32 "firebug/css/cssPanel", 33 "firebug/chrome/searchBox", 34 "firebug/console/errors", 35 "firebug/net/netMonitor", 36 "firebug/net/netReps" 37 ], 38 function(Obj, Firebug, Firefox, Domplate, Xpcom, Locale, 39 Events, Options, Url, SourceLink, Http, Css, Dom, Win, Search, Str, 40 Arr, System, Menu, NetUtils, NetProgress, CSSInfoTip) { 41 42 with (Domplate) { 43 44 // ********************************************************************************************* // 45 // Constants 46 47 const Cc = Components.classes; 48 const Ci = Components.interfaces; 49 const Cr = Components.results; 50 51 var layoutInterval = 300; 52 var panelName = "net"; 53 var NetRequestEntry = Firebug.NetMonitor.NetRequestEntry; 54 55 // ********************************************************************************************* // 56 57 /** 58 * @panel Represents a Firebug panel that displayes info about HTTP activity associated with 59 * the current page. This class is derived from <code>Firebug.ActivablePanel</code> in order 60 * to support activation (enable/disable). This allows to avoid (performance) expensive 61 * features if the functionality is not necessary for the user. 62 */ 63 function NetPanel() {} 64 NetPanel.prototype = Obj.extend(Firebug.ActivablePanel, 65 /** lends NetPanel */ 66 { 67 name: panelName, 68 searchable: true, 69 editable: true, 70 breakable: true, 71 enableA11y: true, 72 order: 60, 73 74 initialize: function(context, doc) 75 { 76 if (FBTrace.DBG_NET) 77 FBTrace.sysout("net.NetPanel.initialize; " + context.getName()); 78 79 this.queue = []; 80 this.onContextMenu = Obj.bind(this.onContextMenu, this); 81 82 Firebug.ActivablePanel.initialize.apply(this, arguments); 83 }, 84 85 destroy: function(state) 86 { 87 Firebug.ActivablePanel.destroy.apply(this, arguments); 88 }, 89 90 initializeNode : function() 91 { 92 Events.addEventListener(this.panelNode, "contextmenu", this.onContextMenu, false); 93 94 this.onResizer = Obj.bind(this.onResize, this); 95 this.resizeEventTarget = Firebug.chrome.$('fbContentBox'); 96 Events.addEventListener(this.resizeEventTarget, "resize", this.onResizer, true); 97 98 Firebug.ActivablePanel.initializeNode.apply(this, arguments); 99 }, 100 101 destroyNode : function() 102 { 103 Events.removeEventListener(this.panelNode, "contextmenu", this.onContextMenu, false); 104 Events.removeEventListener(this.resizeEventTarget, "resize", this.onResizer, true); 105 106 Firebug.ActivablePanel.destroyNode.apply(this, arguments); 107 }, 108 109 loadPersistedContent: function(state) 110 { 111 this.initLayout(); 112 113 var tbody = this.table.querySelector(".netTableBody"); 114 115 // Move all net-rows from the persistedState to this panel. 116 var prevTableBody = state.panelNode.getElementsByClassName("netTableBody").item(0); 117 if (!prevTableBody) 118 return; 119 120 var files = []; 121 122 // Iterate persisted content - table rows. These rows can represent various things 123 // 1) netPageRow - already persisted group 124 // 2) netRow - request entries from the previous session (page load) 125 while (prevTableBody.firstChild) 126 { 127 var row = prevTableBody.firstChild; 128 129 // Collect all entries that belongs to the current page load (not history) 130 if (Css.hasClass(row, "netRow") && 131 Css.hasClass(row, "hasHeaders") && 132 !Css.hasClass(row, "history")) 133 { 134 row.repObject.history = true; 135 files.push({ 136 file: row.repObject, 137 offset: 0 + "%", 138 width: 0 + "%", 139 elapsed: -1 140 }); 141 } 142 143 if (Css.hasClass(row, "netPageRow")) 144 { 145 Css.removeClass(row, "opened"); 146 147 // Insert the old page-load-history entry just before the summary-row, 148 // but after the limit row. 149 tbody.insertBefore(row, this.summaryRow); 150 } 151 else 152 { 153 prevTableBody.removeChild(row); 154 } 155 } 156 157 // New page-load-history entry is inserted just before summary row 158 // (at the end of page-load-history entry list) 159 var lastRow = this.summaryRow.previousSibling; 160 if (files.length) 161 { 162 var pageRow = Firebug.NetMonitor.NetPage.pageTag.insertRows({page: state}, lastRow)[0]; 163 pageRow.files = files; 164 165 lastRow = this.summaryRow.previousSibling; 166 } 167 168 // Insert a separator tag at the end of page-load-history entry list. 169 if (this.table.getElementsByClassName("netPageRow").item(0)) 170 Firebug.NetMonitor.NetPage.separatorTag.insertRows({}, lastRow); 171 172 Dom.scrollToBottom(this.panelNode); 173 }, 174 175 savePersistedContent: function(state) 176 { 177 Firebug.ActivablePanel.savePersistedContent.apply(this, arguments); 178 179 state.pageTitle = NetUtils.getPageTitle(this.context); 180 }, 181 182 show: function(state) 183 { 184 if (FBTrace.DBG_NET) 185 FBTrace.sysout("net.netPanel.show; " + this.context.getName(), state); 186 187 var enabled = Firebug.NetMonitor.isAlwaysEnabled(); 188 this.showToolbarButtons("fbNetButtons", enabled); 189 190 if (enabled) 191 Firebug.chrome.setGlobalAttribute("cmd_firebug_togglePersistNet", "checked", this.persistContent); 192 else 193 this.table = null; 194 195 if (!enabled) 196 return; 197 198 if (!this.filterCategory) 199 this.setFilter(Firebug.netFilterCategory); 200 201 this.layout(); 202 203 if (!this.layoutInterval) 204 this.layoutInterval = setInterval(Obj.bindFixed(this.updateLayout, this), layoutInterval); 205 206 if (this.wasScrolledToBottom) 207 Dom.scrollToBottom(this.panelNode); 208 }, 209 210 hide: function() 211 { 212 if (FBTrace.DBG_NET) 213 FBTrace.sysout("net.netPanel.hide; " + this.context.getName()); 214 215 delete this.infoTipURL; // clear the state that is tracking the infotip so it is reset after next show() 216 this.wasScrolledToBottom = Dom.isScrolledToBottom(this.panelNode); 217 218 clearInterval(this.layoutInterval); 219 delete this.layoutInterval; 220 }, 221 222 updateOption: function(name, value) 223 { 224 if (name == "netFilterCategory") 225 { 226 Firebug.NetMonitor.syncFilterButtons(Firebug.chrome); 227 Firebug.connection.eachContext(function syncFilters(context) 228 { 229 Firebug.NetMonitor.onToggleFilter(context, value); 230 }); 231 } 232 else if (name == "netShowBFCacheResponses") 233 { 234 this.updateBFCacheResponses(); 235 } 236 }, 237 238 updateBFCacheResponses: function() 239 { 240 if (this.table) 241 { 242 if (Firebug.netShowBFCacheResponses) 243 Css.setClass(this.table, "showBFCacheResponses"); 244 else 245 Css.removeClass(this.table, "showBFCacheResponses"); 246 247 // Recalculate the summary information since some requests doesn't have to 248 // be displayed now. 249 this.updateSummaries(NetUtils.now(), true); 250 } 251 }, 252 253 updateSelection: function(object) 254 { 255 if (!object) 256 return; 257 258 var netProgress = this.context.netProgress; 259 var file = netProgress.getRequestFile(object.request); 260 if (!file) 261 { 262 for (var i=0; i<netProgress.requests.length; i++) { 263 if (Http.safeGetRequestName(netProgress.requests[i]) == object.href) { 264 file = netProgress.files[i]; 265 break; 266 } 267 } 268 } 269 270 if (file) 271 { 272 Dom.scrollIntoCenterView(file.row); 273 if (!Css.hasClass(file.row, "opened")) 274 NetRequestEntry.toggleHeadersRow(file.row); 275 } 276 }, 277 278 getPopupObject: function(target) 279 { 280 var header = Dom.getAncestorByClass(target, "netHeaderRow"); 281 if (header) 282 return Firebug.NetMonitor.NetRequestTable; 283 284 return Firebug.ActivablePanel.getPopupObject.apply(this, arguments); 285 }, 286 287 supportsObject: function(object, type) 288 { 289 return ((object instanceof SourceLink.SourceLink && object.type == "net") ? 2 : 0); 290 }, 291 292 getOptionsMenuItems: function() 293 { 294 return [ 295 this.disableCacheOption(), 296 "-", 297 Menu.optionMenu("net.option.Show_Paint_Events", "netShowPaintEvents", 298 "net.option.tip.Show_Paint_Events"), 299 Menu.optionMenu("net.option.Show_BFCache_Responses", "netShowBFCacheResponses", 300 "net.option.tip.Show_BFCache_Responses") 301 ]; 302 }, 303 304 disableCacheOption: function() 305 { 306 var BrowserCache = Firebug.NetMonitor.BrowserCache; 307 var disabled = !BrowserCache.isEnabled(); 308 return { 309 label: "net.option.Disable_Browser_Cache", 310 type: "checkbox", 311 checked: disabled, 312 tooltiptext: "net.option.tip.Disable_Browser_Cache", 313 command: function() 314 { 315 BrowserCache.toggle(!this.hasAttribute("checked")); 316 } 317 }; 318 }, 319 320 getContextMenuItems: function(nada, target) 321 { 322 var items = []; 323 324 var file = Firebug.getRepObject(target); 325 if (!file || !(file instanceof Firebug.NetFile)) 326 return items; 327 328 var object = Firebug.getObjectByURL(this.context, file.href); 329 var isPost = NetUtils.isURLEncodedRequest(file, this.context); 330 var params = Url.parseURLParams(file.href); 331 332 items.push( 333 { 334 label: "CopyLocation", 335 tooltiptext: "clipboard.tip.Copy_Location", 336 command: Obj.bindFixed(System.copyToClipboard, System, file.href) 337 } 338 ); 339 340 if (params.length > 0) 341 { 342 items.push( 343 { 344 id: "fbCopyUrlParameters", 345 label: "CopyURLParameters", 346 tooltiptext: "net.tip.Copy_URL_Parameters", 347 command: Obj.bindFixed(this.copyURLParams, this, file) 348 } 349 ); 350 } 351 352 if (isPost) 353 { 354 items.push( 355 { 356 label: "CopyLocationParameters", 357 tooltiptext: "net.tip.Copy_Location_Parameters", 358 command: Obj.bindFixed(this.copyParams, this, file) 359 }, 360 { 361 id: "fbCopyPOSTParameters", 362 label: "CopyPOSTParameters", 363 tooltiptext: "net.tip.Copy_POST_Parameters", 364 command: Obj.bindFixed(this.copyPOSTParams, this, file) 365 } 366 ); 367 } 368 369 items.push( 370 { 371 label: "CopyRequestHeaders", 372 tooltiptext: "net.tip.Copy_Request_Headers", 373 command: Obj.bindFixed(this.copyRequestHeaders, this, file) 374 }, 375 { 376 label: "CopyResponseHeaders", 377 tooltiptext: "net.tip.Copy_Response_Headers", 378 command: Obj.bindFixed(this.copyResponseHeaders, this, file) 379 } 380 ); 381 382 if (NetUtils.textFileCategories.hasOwnProperty(file.category)) 383 { 384 items.push( 385 { 386 label: "CopyResponse", 387 tooltiptext: "net.tip.Copy_Response", 388 command: Obj.bindFixed(this.copyResponse, this, file) 389 } 390 ); 391 } 392 393 items.push( 394 "-", 395 { 396 label: "OpenInTab", 397 tooltiptext: "firebug.tip.Open_In_Tab", 398 command: Obj.bindFixed(this.openRequestInTab, this, file) 399 } 400 ); 401 402 if (NetUtils.textFileCategories.hasOwnProperty(file.category)) 403 { 404 items.push( 405 { 406 label: "Open_Response_In_New_Tab", 407 tooltiptext: "net.tip.Open_Response_In_New_Tab", 408 command: Obj.bindFixed(NetUtils.openResponseInTab, this, file) 409 } 410 ); 411 } 412 413 if (!file.loaded) 414 { 415 items.push( 416 "-", 417 { 418 label: "StopLoading", 419 tooltiptext: "net.tip.Stop_Loading", 420 command: Obj.bindFixed(this.stopLoading, this, file) 421 } 422 ); 423 } 424 425 if (object) 426 { 427 var subItems = Firebug.chrome.getInspectMenuItems(object); 428 if (subItems.length) 429 { 430 items.push("-"); 431 items.push.apply(items, subItems); 432 } 433 } 434 435 if (file.isXHR) 436 { 437 var bp = this.context.netProgress.breakpoints.findBreakpoint(file.getFileURL()); 438 439 items.push( 440 "-", 441 { 442 label: "net.label.Break_On_XHR", 443 tooltiptext: "net.tip.Break_On_XHR", 444 type: "checkbox", 445 checked: !!bp, 446 command: Obj.bindFixed(this.breakOnRequest, this, file) 447 } 448 ); 449 450 if (bp) 451 { 452 items.push( 453 { 454 label: "EditBreakpointCondition", 455 tooltiptext: "breakpoints.tip.Edit_Breakpoint_Condition", 456 command: Obj.bindFixed(this.editBreakpointCondition, this, file) 457 } 458 ); 459 } 460 } 461 462 items.push("-"); 463 items.push( 464 { 465 label: "net.label.Resend", 466 tooltiptext: "net.tip.Resend", 467 id: "fbNetResend", 468 command: Obj.bindFixed(Firebug.Spy.XHR.resend, Firebug.Spy.XHR, file, this.context) 469 } 470 ); 471 472 return items; 473 }, 474 475 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 476 // Context menu commands 477 478 copyURLParams: function(file) 479 { 480 var params = Url.parseURLParams(file.href); 481 var result = params.map(function(o) { return o.name + "=" + o.value; }); 482 System.copyToClipboard(result.join(Str.lineBreak())); 483 }, 484 485 copyPOSTParams: function(file) 486 { 487 if (!NetUtils.isURLEncodedRequest(file, this.context)) 488 return; 489 490 var text = NetUtils.getPostText(file, this.context, true); 491 if (text) 492 { 493 var lines = text.split("\n"); 494 var params = Url.parseURLEncodedText(lines[lines.length-1]); 495 var result = params.map(function(o) { return o.name + "=" + o.value; }); 496 System.copyToClipboard(result.join(Str.lineBreak())); 497 } 498 }, 499 500 copyParams: function(file) 501 { 502 var text = NetUtils.getPostText(file, this.context, true); 503 var url = Url.reEncodeURL(file, text, true); 504 System.copyToClipboard(url); 505 }, 506 507 copyRequestHeaders: function(file) 508 { 509 System.copyToClipboard(file.requestHeadersText); 510 }, 511 512 copyResponseHeaders: function(file) 513 { 514 System.copyToClipboard(file.responseHeadersText); 515 }, 516 517 copyResponse: function(file) 518 { 519 // Copy response to the clipboard 520 System.copyToClipboard(NetUtils.getResponseText(file, this.context)); 521 }, 522 523 openRequestInTab: function(file) 524 { 525 if (file.postText) 526 { 527 var lines = file.postText.split("\n"); 528 Win.openNewTab(file.href, lines[lines.length-1]); 529 } 530 else 531 { 532 Win.openNewTab(file.href, null); 533 } 534 }, 535 536 breakOnRequest: function(file) 537 { 538 if (!file.isXHR) 539 return; 540 541 // Create new or remove an existing breakpoint. 542 var breakpoints = this.context.netProgress.breakpoints; 543 var url = file.getFileURL(); 544 var bp = breakpoints.findBreakpoint(url); 545 if (bp) 546 breakpoints.removeBreakpoint(url); 547 else 548 breakpoints.addBreakpoint(url); 549 550 this.enumerateRequests(function(currFile) 551 { 552 if (url != currFile.getFileURL()) 553 return; 554 555 if (bp) 556 currFile.row.removeAttribute("breakpoint"); 557 else 558 currFile.row.setAttribute("breakpoint", "true"); 559 }) 560 }, 561 562 stopLoading: function(file) 563 { 564 const NS_BINDING_ABORTED = 0x804b0002; 565 566 file.request.cancel(NS_BINDING_ABORTED); 567 }, 568 569 // Support for xhr breakpoint conditions. 570 onContextMenu: function(event) 571 { 572 if (!Css.hasClass(event.target, "sourceLine")) 573 return; 574 575 var row = Dom.getAncestorByClass(event.target, "netRow"); 576 if (!row) 577 return; 578 579 var file = row.repObject; 580 var bp = this.context.netProgress.breakpoints.findBreakpoint(file.getFileURL()); 581 if (!bp) 582 return; 583 584 this.editBreakpointCondition(file); 585 Events.cancelEvent(event); 586 }, 587 588 editBreakpointCondition: function(file) 589 { 590 var bp = this.context.netProgress.breakpoints.findBreakpoint(file.getFileURL()); 591 if (!bp) 592 return; 593 594 var condition = bp ? bp.condition : ""; 595 596 this.selectedSourceBox = this.panelNode; 597 Firebug.Editor.startEditing(file.row, condition); 598 }, 599 600 getEditor: function(target, value) 601 { 602 if (!this.conditionEditor) 603 this.conditionEditor = new Firebug.NetMonitor.ConditionEditor(this.document); 604 605 return this.conditionEditor; 606 }, 607 608 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 609 // Activable Panel 610 611 /** 612 * Support for panel activation. 613 */ 614 onActivationChanged: function(enable) 615 { 616 if (FBTrace.DBG_NET || FBTrace.DBG_ACTIVATION) 617 FBTrace.sysout("net.NetPanel.onActivationChanged; enable: " + enable); 618 619 if (enable) 620 { 621 Firebug.NetMonitor.addObserver(this); 622 Firebug.TabCacheModel.addObserver(this); 623 } 624 else 625 { 626 Firebug.NetMonitor.removeObserver(this); 627 Firebug.TabCacheModel.removeObserver(this); 628 } 629 }, 630 631 breakOnNext: function(breaking) 632 { 633 this.context.breakOnXHR = breaking; 634 }, 635 636 shouldBreakOnNext: function() 637 { 638 return this.context.breakOnXHR; 639 }, 640 641 getBreakOnNextTooltip: function(enabled) 642 { 643 return (enabled ? Locale.$STR("net.Disable Break On XHR") : Locale.$STR("net.Break On XHR")); 644 }, 645 646 // Support for info tips. 647 showInfoTip: function(infoTip, target, x, y) 648 { 649 var row = Dom.getAncestorByClass(target, "netRow"); 650 if (row && row.repObject) 651 { 652 if (Dom.getAncestorByClass(target, "netTotalSizeCol")) 653 { 654 var infoTipURL = "netTotalSize"; 655 if (infoTipURL == this.infoTipURL) 656 return true; 657 658 this.infoTipURL = infoTipURL; 659 return this.populateTotalSizeInfoTip(infoTip, row); 660 } 661 else if (Dom.getAncestorByClass(target, "netSizeCol")) 662 { 663 var infoTipURL = row.repObject.href + "-netsize"; 664 if (infoTipURL == this.infoTipURL && row.repObject == this.infoTipFile) 665 return true; 666 667 this.infoTipURL = infoTipURL; 668 this.infoTipFile = row.repObject; 669 return this.populateSizeInfoTip(infoTip, row.repObject); 670 } 671 else if (Dom.getAncestorByClass(target, "netTimeCol")) 672 { 673 var infoTipURL = row.repObject.href + "-nettime"; 674 if (infoTipURL == this.infoTipURL && row.repObject == this.infoTipFile) 675 return true; 676 677 this.infoTipURL = infoTipURL; 678 this.infoTipFile = row.repObject; 679 return this.populateTimeInfoTip(infoTip, row.repObject); 680 } 681 else if (Css.hasClass(row, "category-image") && 682 !Dom.getAncestorByClass(target, "netRowHeader")) 683 { 684 var infoTipURL = row.repObject.href + "-image"; 685 if (infoTipURL == this.infoTipURL) 686 return true; 687 688 this.infoTipURL = infoTipURL; 689 return CSSInfoTip.populateImageInfoTip(infoTip, row.repObject.href); 690 } 691 } 692 693 delete this.infoTipURL; 694 return false; 695 }, 696 697 populateTimeInfoTip: function(infoTip, file) 698 { 699 Firebug.NetMonitor.TimeInfoTip.render(this.context, file, infoTip); 700 return true; 701 }, 702 703 populateSizeInfoTip: function(infoTip, file) 704 { 705 Firebug.NetMonitor.SizeInfoTip.render(file, infoTip); 706 return true; 707 }, 708 709 populateTotalSizeInfoTip: function(infoTip, row) 710 { 711 var totalSizeLabel = row.getElementsByClassName("netTotalSizeLabel").item(0); 712 var file = {size: totalSizeLabel.getAttribute("totalSize")}; 713 Firebug.NetMonitor.SizeInfoTip.tag.replace({file: file}, infoTip); 714 return true; 715 }, 716 717 // Support for search within the panel. 718 getSearchOptionsMenuItems: function() 719 { 720 return [ 721 Firebug.Search.searchOptionMenu("search.Case_Sensitive", "searchCaseSensitive", 722 "search.tip.Case_Sensitive"), 723 //Firebug.Search.searchOptionMenu("search.net.Headers", "netSearchHeaders"), 724 //Firebug.Search.searchOptionMenu("search.net.Parameters", "netSearchParameters"), 725 Firebug.Search.searchOptionMenu("search.Use_Regular_Expression", 726 "searchUseRegularExpression", "search.tip.Use_Regular_Expression"), 727 Firebug.Search.searchOptionMenu("search.net.Response_Bodies", "netSearchResponseBody", 728 "search.net.tip.Response_Bodies") 729 ]; 730 }, 731 732 search: function(text, reverse) 733 { 734 if (!text) 735 { 736 delete this.currentSearch; 737 this.highlightNode(null); 738 return false; 739 } 740 741 var row; 742 if (this.currentSearch && text == this.currentSearch.text) 743 { 744 row = this.currentSearch.findNext(true, false, reverse, Firebug.Search.isCaseSensitive(text)); 745 } 746 else 747 { 748 this.currentSearch = new NetPanelSearch(this); 749 row = this.currentSearch.find(text, reverse, Firebug.Search.isCaseSensitive(text)); 750 } 751 752 if (row) 753 { 754 var sel = this.document.defaultView.getSelection(); 755 sel.removeAllRanges(); 756 sel.addRange(this.currentSearch.range); 757 758 Dom.scrollIntoCenterView(row, this.panelNode); 759 if(this.currentSearch.shouldSearchResponses() && 760 Dom.getAncestorByClass(row, "netInfoResponseText")) 761 { 762 this.highlightNode(row) 763 } 764 else 765 { 766 this.highlightNode(Dom.getAncestorByClass(row, "netRow")); 767 } 768 Events.dispatch(this.fbListeners, 'onNetMatchFound', [this, text, row]); 769 return true; 770 } 771 else 772 { 773 Events.dispatch(this.fbListeners, 'onNetMatchFound', [this, text, null]); 774 return false; 775 } 776 }, 777 778 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 779 780 updateFile: function(file) 781 { 782 if (!file.invalid) 783 { 784 file.invalid = true; 785 this.queue.push(file); 786 } 787 }, 788 789 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 790 791 updateLayout: function() 792 { 793 if (!this.queue.length) 794 return; 795 796 var rightNow = NetUtils.now(); 797 var length = this.queue.length; 798 799 if (this.panelNode.offsetHeight) 800 this.wasScrolledToBottom = Dom.isScrolledToBottom(this.panelNode); 801 802 this.layout(); 803 804 if (this.wasScrolledToBottom) 805 Dom.scrollToBottom(this.panelNode); 806 807 this.updateHRefLabelWidth(); 808 809 if (FBTrace.DBG_NET) 810 FBTrace.sysout("net.updateLayout; Layout done, time elapsed: " + 811 Str.formatTime(NetUtils.now() - rightNow) + " (" + length + ")"); 812 }, 813 814 layout: function() 815 { 816 if (!this.queue.length || !this.context.netProgress || 817 !Firebug.NetMonitor.isAlwaysEnabled()) 818 return; 819 820 this.initLayout(); 821 822 var rightNow = NetUtils.now(); 823 this.updateRowData(rightNow); 824 this.updateLogLimit(Firebug.NetMonitor.maxQueueRequests); 825 this.updateTimeline(rightNow); 826 this.updateSummaries(rightNow); 827 }, 828 829 initLayout: function() 830 { 831 if (!this.table) 832 { 833 var limitInfo = { 834 totalCount: 0, 835 limitPrefsTitle: Locale.$STRF("LimitPrefsTitle", 836 [Options.prefDomain+".net.logLimit"]) 837 }; 838 839 this.table = Firebug.NetMonitor.NetRequestTable.tableTag.append({}, this.panelNode); 840 var tbody = this.table.querySelector(".netTableBody"); 841 this.limitRow = Firebug.NetMonitor.NetLimit.createRow(tbody, limitInfo); 842 this.summaryRow = NetRequestEntry.summaryTag.insertRows({}, this.table.lastChild.lastChild)[0]; 843 844 NetRequestEntry.footerTag.insertRows({}, this.summaryRow); 845 846 // Update visibility of columns according to the preferences 847 var hiddenCols = Options.get("net.hiddenColumns"); 848 if (hiddenCols) 849 this.table.setAttribute("hiddenCols", hiddenCols); 850 851 this.updateBFCacheResponses(); 852 } 853 }, 854 855 updateRowData: function(rightNow) 856 { 857 var queue = this.queue; 858 this.queue = []; 859 860 var phase; 861 var newFileData = []; 862 863 for (var i = 0; i < queue.length; ++i) 864 { 865 var file = queue[i]; 866 867 // xxxHonza: the entire phase management should ba part of NetPanel object 868 if (!file.phase && this.context.netProgress) 869 this.context.netProgress.extendPhase(file); 870 871 if (!file.phase) 872 continue; 873 874 file.invalid = false; 875 876 phase = this.calculateFileTimes(file, phase, rightNow); 877 878 this.updateFileRow(file, newFileData); 879 this.invalidatePhase(phase); 880 } 881 882 if (newFileData.length) 883 { 884 var tbody = this.table.querySelector(".netTableBody"); 885 var lastRow = this.summaryRow.previousSibling; 886 this.insertRows(newFileData, lastRow); 887 } 888 }, 889 890 insertRows: function(files, lastRow) 891 { 892 var row = NetRequestEntry.fileTag.insertRows({files: files}, lastRow)[0]; 893 894 for (var i = 0; i < files.length; ++i) 895 { 896 var file = files[i].file; 897 row.repObject = file; 898 file.row = row; 899 900 if (file.breakLayout) 901 row.setAttribute("breakLayout", "true"); 902 903 // Make sure a breakpoint is displayed. 904 var breakpoints = this.context.netProgress.breakpoints; 905 if (breakpoints && breakpoints.findBreakpoint(file.getFileURL())) 906 row.setAttribute("breakpoint", "true"); 907 908 // Allow customization of request entries in the list. A row is represented 909 // by <TR> HTML element. 910 Events.dispatch(Firebug.NetMonitor.NetRequestTable.fbListeners, 911 "onCreateRequestEntry", [this, row]); 912 913 row = row.nextSibling; 914 } 915 }, 916 917 invalidatePhase: function(phase) 918 { 919 if (phase && !phase.invalidPhase) 920 { 921 phase.invalidPhase = true; 922 this.invalidPhases = true; 923 } 924 }, 925 926 updateFileRow: function(file, newFileData) 927 { 928 var row = file.row; 929 if (!row) 930 { 931 newFileData.push({ 932 file: file, 933 offset: this.barOffset + "%", 934 width: this.barReceivingWidth + "%", 935 elapsed: file.loaded ? this.elapsed : -1 936 }); 937 } 938 else 939 { 940 var sizeLabel = row.getElementsByClassName("netSizeLabel").item(0); 941 942 var sizeText = NetRequestEntry.getSize(file); 943 944 // Show also total downloaded size for requests in progress. 945 if (file.totalReceived) 946 sizeText += " (" + Str.formatSize(file.totalReceived) + ")"; 947 948 sizeLabel.firstChild.nodeValue = sizeText; 949 950 var methodLabel = row.getElementsByClassName("netStatusLabel").item(0); 951 methodLabel.firstChild.nodeValue = NetRequestEntry.getStatus(file); 952 953 var hrefLabel = row.getElementsByClassName("netHrefLabel").item(0); 954 hrefLabel.firstChild.nodeValue = NetRequestEntry.getHref(file); 955 956 if (file.mimeType) 957 { 958 // Force update category. 959 file.category = null; 960 for (var category in NetUtils.fileCategories) 961 Css.removeClass(row, "category-" + category); 962 Css.setClass(row, "category-" + NetUtils.getFileCategory(file)); 963 } 964 965 var remoteIPLabel = row.querySelector(".netRemoteAddressCol .netAddressLabel"); 966 remoteIPLabel.textContent = NetRequestEntry.getRemoteAddress(file); 967 968 var localIPLabel = row.querySelector(".netLocalAddressCol .netAddressLabel"); 969 localIPLabel.textContent = NetRequestEntry.getLocalAddress(file); 970 971 if (file.requestHeaders) 972 Css.setClass(row, "hasHeaders"); 973 974 if (file.fromCache) 975 Css.setClass(row, "fromCache"); 976 else 977 Css.removeClass(row, "fromCache"); 978 979 if (file.fromBFCache) 980 Css.setClass(row, "fromBFCache"); 981 else 982 Css.removeClass(row, "fromBFCache"); 983 984 if (NetRequestEntry.isError(file)) 985 Css.setClass(row, "responseError"); 986 else 987 Css.removeClass(row, "responseError"); 988 989 var netBar = Dom.getChildByClass(row, "netTimeCol").childNodes[1]; 990 var timeLabel = Dom.getChildByClass(netBar, "netReceivingBar").firstChild; 991 timeLabel.textContent = NetRequestEntry.getElapsedTime({elapsed: this.elapsed}); 992 993 if (file.loaded) 994 Css.setClass(row, "loaded"); 995 else 996 Css.removeClass(row, "loaded"); 997 998 if (Css.hasClass(row, "opened")) 999 { 1000 var netInfoBox = row.nextSibling.getElementsByClassName("netInfoBody").item(0); 1001 Firebug.NetMonitor.NetInfoBody.updateInfo(netInfoBox, file, this.context); 1002 } 1003 } 1004 }, 1005 1006 updateTimeline: function(rightNow) 1007 { 1008 var tbody = this.table.querySelector(".netTableBody"); 1009 1010 // XXXjoe Don't update rows whose phase is done and layed out already 1011 var phase; 1012 for (var row = tbody.firstChild; row; row = row.nextSibling) 1013 { 1014 var file = row.repObject; 1015 1016 // Some rows aren't associated with a file (e.g. header, sumarry). 1017 if (!file) 1018 continue; 1019 1020 if (!file.loaded) 1021 continue; 1022 1023 phase = this.calculateFileTimes(file, phase, rightNow); 1024 1025 // Parent node for all timing bars. 1026 var netBar = row.querySelector(".netBar"); 1027 1028 // Get bar nodes 1029 var blockingBar = netBar.childNodes[1]; 1030 var resolvingBar = blockingBar.nextSibling; 1031 var connectingBar = resolvingBar.nextSibling; 1032 var sendingBar = connectingBar.nextSibling; 1033 var waitingBar = sendingBar.nextSibling; 1034 var receivingBar = waitingBar.nextSibling; 1035 1036 // All bars starts at the beginning 1037 resolvingBar.style.left = connectingBar.style.left = sendingBar.style.left = 1038 blockingBar.style.left = 1039 waitingBar.style.left = receivingBar.style.left = this.barOffset + "%"; 1040 1041 // Sets width of all bars (using style). The width is computed according to measured timing. 1042 blockingBar.style.width = this.barBlockingWidth + "%"; 1043 resolvingBar.style.width = this.barResolvingWidth + "%"; 1044 connectingBar.style.width = this.barConnectingWidth + "%"; 1045 sendingBar.style.width = this.barSendingWidth + "%"; 1046 waitingBar.style.width = this.barWaitingWidth + "%"; 1047 receivingBar.style.width = this.barReceivingWidth + "%"; 1048 1049 // Remove existing bars 1050 var bars = netBar.querySelectorAll(".netPageTimingBar"); 1051 for (var i=0; i<bars.length; i++) 1052 bars[i].parentNode.removeChild(bars[i]); 1053 1054 // Generate UI for page timings (vertical lines displayed for the first phase) 1055 for (var i=0; i<phase.timeStamps.length; i++) 1056 { 1057 var timing = phase.timeStamps[i]; 1058 if (!timing.offset) 1059 continue; 1060 1061 var bar = netBar.ownerDocument.createElement("DIV"); 1062 netBar.appendChild(bar); 1063 1064 if (timing.classes) 1065 Css.setClass(bar, timing.classes); 1066 1067 Css.setClass(bar, "netPageTimingBar"); 1068 1069 bar.style.left = timing.offset + "%"; 1070 bar.style.display = "block"; 1071 } 1072 } 1073 }, 1074 1075 calculateFileTimes: function(file, phase, rightNow) 1076 { 1077 var phases = this.context.netProgress.phases; 1078 1079 if (phase != file.phase) 1080 { 1081 phase = file.phase; 1082 this.phaseStartTime = phase.startTime; 1083 this.phaseEndTime = phase.endTime ? phase.endTime : rightNow; 1084 1085 // End of the first phase has to respect even the window "onload" event time, which 1086 // can occur after the last received file. This sets the extent of the timeline so, 1087 // the windowLoadBar is visible. 1088 if (phase.windowLoadTime && this.phaseEndTime < phase.windowLoadTime) 1089 this.phaseEndTime = phase.windowLoadTime; 1090 1091 this.phaseElapsed = this.phaseEndTime - phase.startTime; 1092 } 1093 1094 var elapsed = file.loaded ? file.endTime - file.startTime : 0; /*this.phaseEndTime - file.startTime*/ 1095 this.barOffset = Math.floor(((file.startTime-this.phaseStartTime)/this.phaseElapsed) * 100); 1096 1097 //Helper log for debugging timing problems. 1098 //NetUtils.traceRequestTiming("net.calculateFileTimes;", file); 1099 1100 var blockingEnd = NetUtils.getBlockingEndTime(file); 1101 this.barBlockingWidth = Math.round(((blockingEnd - file.startTime) / this.phaseElapsed) * 100); 1102 this.barResolvingWidth = Math.round(((file.connectingTime - file.startTime) / this.phaseElapsed) * 100); 1103 this.barConnectingWidth = Math.round(((file.sendingTime - file.startTime) / this.phaseElapsed) * 100); 1104 this.barSendingWidth = Math.round(((file.waitingForTime - file.startTime) / this.phaseElapsed) * 100); 1105 this.barWaitingWidth = Math.round(((file.respondedTime - file.startTime) / this.phaseElapsed) * 100); 1106 this.barReceivingWidth = Math.round((elapsed / this.phaseElapsed) * 100); 1107 1108 // Total request time doesn't include the time spent in queue. 1109 // xxxHonza: since all phases are now graphically distinguished it's easy to 1110 // see blocking requests. It's make sense to display the real total time now. 1111 this.elapsed = elapsed/* - (file.sendingTime - file.connectedTime)*/; 1112 1113 // The nspr timer doesn't have 1ms precision, so it can happen that entire 1114 // request is executed in l ms (so the total is zero). Let's display at least 1115 // one bar in such a case so the timeline is visible. 1116 if (this.elapsed <= 0) 1117 this.barReceivingWidth = "1"; 1118 1119 // Compute also offset for page timings, e.g.: contentLoadBar and windowLoadBar, 1120 // which are displayed for the first phase. This is done only if a page exists. 1121 this.calculateTimeStamps(file, phase); 1122 1123 return phase; 1124 }, 1125 1126 calculateTimeStamps: function(file, phase) 1127 { 1128 // Iterate all time stamps for the current phase and calculate offsets (from the 1129 // beginning of the waterfall graphs) for the vertical lines. 1130 for (var i=0; i<phase.timeStamps.length; i++) 1131 { 1132 var timeStamp = phase.timeStamps[i]; 1133 var time = timeStamp.time; 1134 1135 if (time > 0) 1136 { 1137 var offset = (((time - this.phaseStartTime)/this.phaseElapsed) * 100).toFixed(3); 1138 timeStamp.offset = offset; 1139 } 1140 } 1141 }, 1142 1143 updateSummaries: function(rightNow, updateAll) 1144 { 1145 if (!this.invalidPhases && !updateAll) 1146 return; 1147 1148 this.invalidPhases = false; 1149 1150 var phases = this.context.netProgress.phases; 1151 if (!phases.length) 1152 return; 1153 1154 var fileCount = 0, totalSize = 0, cachedSize = 0, totalTime = 0; 1155 for (var i = 0; i < phases.length; ++i) 1156 { 1157 var phase = phases[i]; 1158 phase.invalidPhase = false; 1159 1160 var summary = this.summarizePhase(phase, rightNow); 1161 fileCount += summary.fileCount; 1162 totalSize += summary.totalSize; 1163 cachedSize += summary.cachedSize; 1164 totalTime += summary.totalTime 1165 } 1166 1167 var row = this.summaryRow; 1168 if (!row) 1169 return; 1170 1171 var countLabel = row.getElementsByClassName("netCountLabel").item(0); //childNodes[1].firstChild; 1172 countLabel.firstChild.nodeValue = Locale.$STRP("plural.Request_Count2", [fileCount]); 1173 1174 var sizeLabel = row.getElementsByClassName("netTotalSizeLabel").item(0); //childNodes[4].firstChild; 1175 sizeLabel.setAttribute("totalSize", totalSize); 1176 sizeLabel.firstChild.nodeValue = NetRequestEntry.formatSize(totalSize); 1177 1178 var cacheSizeLabel = row.getElementsByClassName("netCacheSizeLabel").item(0); 1179 cacheSizeLabel.setAttribute("collapsed", cachedSize == 0); 1180 cacheSizeLabel.childNodes[1].firstChild.nodeValue = 1181 NetRequestEntry.formatSize(cachedSize); 1182 1183 var timeLabel = row.getElementsByClassName("netTotalTimeLabel").item(0); 1184 var timeText = NetRequestEntry.formatTime(totalTime); 1185 var firstPhase = phases[0]; 1186 if (firstPhase.windowLoadTime) 1187 { 1188 var loadTime = firstPhase.windowLoadTime - firstPhase.startTime; 1189 timeText += " (onload: " + NetRequestEntry.formatTime(loadTime) + ")"; 1190 } 1191 1192 timeLabel.textContent = timeText; 1193 }, 1194 1195 summarizePhase: function(phase, rightNow) 1196 { 1197 var cachedSize = 0, totalSize = 0; 1198 1199 var category = Firebug.netFilterCategory; 1200 if (category == "all") 1201 category = null; 1202 1203 var fileCount = 0; 1204 var minTime = 0, maxTime = 0; 1205 1206 for (var i=0; i<phase.files.length; i++) 1207 { 1208 var file = phase.files[i]; 1209 1210 // Do not count BFCache responses if the user says so. 1211 if (!Firebug.netShowBFCacheResponses && file.fromBFCache) 1212 continue; 1213 1214 if (!category || file.category == category) 1215 { 1216 if (file.loaded) 1217 { 1218 ++fileCount; 1219 1220 if (file.size > 0) 1221 { 1222 totalSize += file.size; 1223 if (file.fromCache) 1224 cachedSize += file.size; 1225 } 1226 1227 if (!minTime || file.startTime < minTime) 1228 minTime = file.startTime; 1229 if (file.endTime > maxTime) 1230 maxTime = file.endTime; 1231 } 1232 } 1233 } 1234 1235 var totalTime = maxTime - minTime; 1236 return {cachedSize: cachedSize, totalSize: totalSize, totalTime: totalTime, 1237 fileCount: fileCount} 1238 }, 1239 1240 updateLogLimit: function(limit) 1241 { 1242 var netProgress = this.context.netProgress; 1243 1244 if (!netProgress) // XXXjjb Honza, please check, I guess we are getting here with the context not setup 1245 { 1246 if (FBTrace.DBG_NET) 1247 FBTrace.sysout("net.updateLogLimit; NO NET CONTEXT for: " + this.context.getName()); 1248 return; 1249 } 1250 1251 // Must be positive number; 1252 limit = Math.max(0, limit); 1253 1254 var filesLength = netProgress.files.length; 1255 if (!filesLength || filesLength <= limit) 1256 return; 1257 1258 // Remove old requests. 1259 var removeCount = Math.max(0, filesLength - limit); 1260 for (var i=0; i<removeCount; i++) 1261 { 1262 var file = netProgress.files[0]; 1263 this.removeLogEntry(file); 1264 1265 // Remove the file occurrence from the queue. 1266 for (var j=0; j<this.queue.length; j++) 1267 { 1268 if (this.queue[j] == file) { 1269 this.queue.splice(j, 1); 1270 j--; 1271 } 1272 } 1273 } 1274 }, 1275 1276 removeLogEntry: function(file, noInfo) 1277 { 1278 // Remove associated row-entry from the UI before the removeFile method 1279 // is called (and file.row erased). 1280 if (this.table) 1281 { 1282 var tbody = this.table.querySelector(".netTableBody"); 1283 if (tbody && file.row) 1284 tbody.removeChild(file.row); 1285 } 1286 1287 if (!this.removeFile(file)) 1288 return; 1289 1290 if (!this.table) 1291 return; 1292 1293 var tbody = this.table.querySelector(".netTableBody"); 1294 if (!tbody) 1295 return; 1296 1297 if (noInfo || !this.limitRow) 1298 return; 1299 1300 this.limitRow.limitInfo.totalCount++; 1301 1302 Firebug.NetMonitor.NetLimit.updateCounter(this.limitRow); 1303 1304 //if (netProgress.currentPhase == file.phase) 1305 // netProgress.currentPhase = null; 1306 }, 1307 1308 removeFile: function(file) 1309 { 1310 var netProgress = this.context.netProgress; 1311 var index = netProgress.files.indexOf(file); 1312 if (index == -1) 1313 return false; 1314 1315 netProgress.files.splice(index, 1); 1316 netProgress.requests.splice(index, 1); 1317 1318 // Don't forget to remove the phase whose last file has been removed. 1319 var phase = file.phase; 1320 1321 // xxxHonza: This needs to be examined yet. Looks like the queue contains 1322 // requests from the previous page. When flushed the requestedFile isn't called 1323 // and the phase is not set. 1324 if (!phase) 1325 return true; 1326 1327 phase.removeFile(file); 1328 if (!phase.files.length) 1329 { 1330 Arr.remove(netProgress.phases, phase); 1331 1332 if (netProgress.currentPhase == phase) 1333 netProgress.currentPhase = null; 1334 } 1335 1336 file.clear(); 1337 1338 return true; 1339 }, 1340 1341 insertActivationMessage: function() 1342 { 1343 if (!Firebug.NetMonitor.isAlwaysEnabled()) 1344 return; 1345 1346 // Make sure the basic structure of the table panel is there. 1347 this.initLayout(); 1348 1349 // Get the last request row before summary row. 1350 var lastRow = this.summaryRow.previousSibling; 1351 1352 // Insert an activation message (if the last row isn't the message already); 1353 if (Css.hasClass(lastRow, "netActivationRow")) 1354 return; 1355 1356 var message = NetRequestEntry.activationTag.insertRows({}, lastRow)[0]; 1357 1358 if (FBTrace.DBG_NET) 1359 FBTrace.sysout("net.insertActivationMessage; " + this.context.getName(), message); 1360 }, 1361 1362 enumerateRequests: function(fn) 1363 { 1364 if (!this.table) 1365 return; 1366 1367 var rows = this.table.getElementsByClassName("netRow"); 1368 for (var i=0; i<rows.length; i++) 1369 { 1370 var row = rows[i]; 1371 var pageRow = Css.hasClass(row, "netPageRow"); 1372 1373 if (Css.hasClass(row, "collapsed") && !pageRow) 1374 continue; 1375 1376 if (Css.hasClass(row, "history")) 1377 continue; 1378 1379 // Export also history. These requests can be collapsed and so not visible. 1380 if (row.files) 1381 { 1382 for (var j=0; j<row.files.length; j++) 1383 fn(row.files[j].file); 1384 } 1385 1386 var file = Firebug.getRepObject(row); 1387 if (file) 1388 fn(file); 1389 } 1390 }, 1391 1392 setFilter: function(filterCategory) 1393 { 1394 this.filterCategory = filterCategory; 1395 1396 var panelNode = this.panelNode; 1397 for (var category in NetUtils.fileCategories) 1398 { 1399 if (filterCategory != "all" && category != filterCategory) 1400 Css.setClass(panelNode, "hideCategory-"+category); 1401 else 1402 Css.removeClass(panelNode, "hideCategory-"+category); 1403 } 1404 }, 1405 1406 clear: function() 1407 { 1408 Dom.clearNode(this.panelNode); 1409 1410 this.table = null; 1411 this.summaryRow = null; 1412 this.limitRow = null; 1413 1414 this.queue = []; 1415 this.invalidPhases = false; 1416 1417 if (this.context.netProgress) 1418 this.context.netProgress.clear(); 1419 1420 if (FBTrace.DBG_NET) 1421 FBTrace.sysout("net.panel.clear; " + this.context.getName()); 1422 }, 1423 1424 onResize: function() 1425 { 1426 this.updateHRefLabelWidth(); 1427 }, 1428 1429 updateHRefLabelWidth: function() 1430 { 1431 if (!this.table) 1432 return; 1433 1434 // Update max-width of the netHrefLabel according to the width of the parent column. 1435 // I don't know if there is a way to do this in Css. 1436 // See Issue 3633: Truncated URLs in net panel 1437 var netHrefCol = this.table.querySelector("#netHrefCol"); 1438 var hrefLabel = this.table.querySelector(".netHrefLabel"); 1439 1440 if (!hrefLabel) 1441 return; 1442 1443 if (!Firebug.currentContext) 1444 { 1445 if (FBTrace.DBG_ERRORS) 1446 FBTrace.sysout("net.updateHRefLabelWidth; Firebug.currentContext == NULL"); 1447 return; 1448 } 1449 1450 var maxWidth = netHrefCol.clientWidth; 1451 1452 // This call must precede all getCSSStyleRules calls FIXME not needed after 3.6 1453 Firebug.CSSModule.cleanupSheets(hrefLabel.ownerDocument, this.context); 1454 var rules = Dom.domUtils.getCSSStyleRules(hrefLabel); 1455 for (var i = 0; i < rules.Count(); ++i) 1456 { 1457 var rule = Xpcom.QI(rules.GetElementAt(i), Ci.nsIDOMCSSStyleRule); 1458 if (rule.selectorText == ".netHrefLabel") 1459 { 1460 var style = rule.style; 1461 var paddingLeft = parseInt(style.getPropertyValue("padding-left")); 1462 if (maxWidth == 0) 1463 style.setProperty("max-width", "15%", ""); 1464 else 1465 style.setProperty("max-width", (maxWidth - paddingLeft) + "px", ""); 1466 break; 1467 } 1468 } 1469 }, 1470 }); 1471 1472 // ********************************************************************************************* // 1473 1474 /* 1475 * Use this object to automatically select Net panel and inspect a network request. 1476 * Firebug.chrome.select(new Firebug.NetMonitor.NetFileLink(url [, request])); 1477 */ 1478 Firebug.NetMonitor.NetFileLink = function(href, request) 1479 { 1480 this.href = href; 1481 this.request = request; 1482 } 1483 1484 Firebug.NetMonitor.NetFileLink.prototype = 1485 { 1486 toString: function() 1487 { 1488 return this.message + this.href; 1489 } 1490 }; 1491 1492 // ********************************************************************************************* // 1493 1494 var NetPanelSearch = function(panel, rowFinder) 1495 { 1496 var panelNode = panel.panelNode; 1497 var doc = panelNode.ownerDocument; 1498 var searchRange, startPt; 1499 1500 // Common search object methods. 1501 this.find = function(text, reverse, caseSensitive) 1502 { 1503 this.text = text; 1504 1505 Search.finder.findBackwards = !!reverse; 1506 Search.finder.caseSensitive = !!caseSensitive; 1507 1508 this.currentRow = this.getFirstRow(); 1509 this.resetRange(); 1510 1511 return this.findNext(false, false, reverse, caseSensitive); 1512 }; 1513 1514 this.findNext = function(wrapAround, sameNode, reverse, caseSensitive) 1515 { 1516 while (this.currentRow) 1517 { 1518 var match = this.findNextInRange(reverse, caseSensitive); 1519 if (match) 1520 return match; 1521 1522 if (this.shouldSearchResponses()) 1523 this.findNextInResponse(reverse, caseSensitive); 1524 1525 this.currentRow = this.getNextRow(wrapAround, reverse); 1526 1527 if (this.currentRow) 1528 this.resetRange(); 1529 } 1530 }; 1531 1532 // Internal search helpers. 1533 this.findNextInRange = function(reverse, caseSensitive) 1534 { 1535 if (this.range) 1536 { 1537 startPt = doc.createRange(); 1538 if (reverse) 1539 startPt.setStartBefore(this.currentNode); 1540 else 1541 startPt.setStart(this.currentNode, this.range.endOffset); 1542 1543 this.range = Search.finder.Find(this.text, searchRange, startPt, searchRange); 1544 if (this.range) 1545 { 1546 this.currentNode = this.range ? this.range.startContainer : null; 1547 return this.currentNode ? this.currentNode.parentNode : null; 1548 } 1549 } 1550 1551 if (this.currentNode) 1552 { 1553 startPt = doc.createRange(); 1554 if (reverse) 1555 startPt.setStartBefore(this.currentNode); 1556 else 1557 startPt.setStartAfter(this.currentNode); 1558 } 1559 1560 this.range = Search.finder.Find(this.text, searchRange, startPt, searchRange); 1561 this.currentNode = this.range ? this.range.startContainer : null; 1562 return this.currentNode ? this.currentNode.parentNode : null; 1563 }, 1564 1565 this.findNextInResponse = function(reverse, caseSensitive) 1566 { 1567 var file = Firebug.getRepObject(this.currentRow); 1568 if (!file) 1569 return; 1570 1571 var scanRE = Firebug.Search.getTestingRegex(this.text); 1572 if (scanRE.test(file.responseText)) 1573 { 1574 if (!Css.hasClass(this.currentRow, "opened")) 1575 NetRequestEntry.toggleHeadersRow(this.currentRow); 1576 1577 var netInfoRow = this.currentRow.nextSibling; 1578 var netInfoBox = netInfoRow.getElementsByClassName("netInfoBody").item(0); 1579 Firebug.NetMonitor.NetInfoBody.selectTabByName(netInfoBox, "Response"); 1580 1581 // Before the search is started, the new content must be properly 1582 // layouted within the page. The layout is executed by reading 1583 // the following property. 1584 // xxxHonza: This workaround can be removed as soon as #488427 is fixed. 1585 doc.body.offsetWidth; 1586 } 1587 }, 1588 1589 // Helpers 1590 this.resetRange = function() 1591 { 1592 searchRange = doc.createRange(); 1593 searchRange.setStart(this.currentRow, 0); 1594 searchRange.setEnd(this.currentRow, this.currentRow.childNodes.length); 1595 1596 startPt = searchRange; 1597 } 1598 1599 this.getFirstRow = function() 1600 { 1601 var table = panelNode.getElementsByClassName("netTable").item(0); 1602 return table.querySelector(".netTableBody").firstChild; 1603 } 1604 1605 this.getNextRow = function(wrapAround, reverse) 1606 { 1607 // xxxHonza: reverse searching missing. 1608 for (var sib = this.currentRow.nextSibling; sib; sib = sib.nextSibling) 1609 { 1610 if (this.shouldSearchResponses()) 1611 return sib; 1612 else if (Css.hasClass(sib, "netRow")) 1613 return sib; 1614 } 1615 1616 return wrapAround ? this.getFirstRow() : null; 1617 } 1618 1619 this.shouldSearchResponses = function() 1620 { 1621 return Firebug["netSearchResponseBody"]; 1622 } 1623 }; 1624 1625 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1626 1627 Firebug.NetMonitor.ConditionEditor = function(doc) 1628 { 1629 Firebug.Breakpoint.ConditionEditor.apply(this, arguments); 1630 } 1631 1632 Firebug.NetMonitor.ConditionEditor.prototype = domplate(Firebug.Breakpoint.ConditionEditor.prototype, 1633 { 1634 endEditing: function(target, value, cancel) 1635 { 1636 if (cancel) 1637 return; 1638 1639 var file = target.repObject; 1640 var panel = Firebug.getElementPanel(target); 1641 var bp = panel.context.netProgress.breakpoints.findBreakpoint(file.getFileURL()); 1642 if (bp) 1643 bp.condition = value; 1644 } 1645 }); 1646 1647 // ********************************************************************************************* // 1648 // Browser Cache 1649 1650 Firebug.NetMonitor.BrowserCache = 1651 { 1652 cacheDomain: "browser.cache", 1653 1654 isEnabled: function() 1655 { 1656 var diskCache = Options.getPref(this.cacheDomain, "disk.enable"); 1657 var memoryCache = Options.getPref(this.cacheDomain, "memory.enable"); 1658 return diskCache && memoryCache; 1659 }, 1660 1661 toggle: function(state) 1662 { 1663 if (FBTrace.DBG_NET) 1664 FBTrace.sysout("net.BrowserCache.toggle; " + state); 1665 1666 Options.setPref(this.cacheDomain, "disk.enable", state); 1667 Options.setPref(this.cacheDomain, "memory.enable", state); 1668 } 1669 } 1670 1671 // ********************************************************************************************* // 1672 // Registration 1673 1674 Firebug.registerPanel(NetPanel); 1675 1676 return Firebug.NetMonitor; 1677 1678 // ********************************************************************************************* // 1679 }}); 1680