1
/* See license.txt for terms of usage */
3
FBL.ns(function() { with (FBL) {
5
// ************************************************************************************************
8
const nsIWebProgressListener = CI("nsIWebProgressListener")
9
const nsIWebProgress = CI("nsIWebProgress")
10
const nsIRequest = CI("nsIRequest")
11
const nsIChannel = CI("nsIChannel")
12
const nsIHttpChannel = CI("nsIHttpChannel")
13
const nsICacheService = CI("nsICacheService")
14
const nsICache = CI("nsICache")
15
const nsIObserverService = CI("nsIObserverService")
16
const nsISupportsWeakReference = CI("nsISupportsWeakReference")
17
const nsISupports = CI("nsISupports")
18
const nsIIOService = CI("nsIIOService")
19
const imgIRequest = CI("imgIRequest");
21
const CacheService = CC("@mozilla.org/network/cache-service;1");
22
const ImgCache = CC("@mozilla.org/image/cache;1");
23
const IOService = CC("@mozilla.org/network/io-service;1");
25
const NOTIFY_ALL = nsIWebProgress.NOTIFY_ALL;
27
const STATE_IS_WINDOW = nsIWebProgressListener.STATE_IS_WINDOW;
28
const STATE_IS_DOCUMENT = nsIWebProgressListener.STATE_IS_DOCUMENT;
29
const STATE_IS_NETWORK = nsIWebProgressListener.STATE_IS_NETWORK;
30
const STATE_IS_REQUEST = nsIWebProgressListener.STATE_IS_REQUEST;
32
const STATE_START = nsIWebProgressListener.STATE_START;
33
const STATE_STOP = nsIWebProgressListener.STATE_STOP;
34
const STATE_TRANSFERRING = nsIWebProgressListener.STATE_TRANSFERRING;
36
const LOAD_BACKGROUND = nsIRequest.LOAD_BACKGROUND;
37
const LOAD_FROM_CACHE = nsIRequest.LOAD_FROM_CACHE;
38
const LOAD_DOCUMENT_URI = nsIChannel.LOAD_DOCUMENT_URI;
40
const ACCESS_READ = nsICache.ACCESS_READ;
41
const STORE_ANYWHERE = nsICache.STORE_ANYWHERE;
43
const NS_ERROR_CACHE_KEY_NOT_FOUND = 0x804B003D;
44
const NS_ERROR_CACHE_WAIT_FOR_VALIDATION = 0x804B0040;
47
const observerService = CCSV("@mozilla.org/observer-service;1", "nsIObserverService");
49
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
51
const maxPendingCheck = 200;
52
const maxQueueRequests = 50;
54
const mimeExtensionMap =
62
"js": "application/x-javascript",
63
"jss": "application/x-javascript",
69
"swf": "application/x-shockwave-flash"
72
const fileCategories =
85
const textFileCategories =
94
const binaryFileCategories =
100
const mimeCategoryMap =
103
"application/octet-stream": "bin",
107
"application/x-javascript": "js",
108
"text/javascript": "js",
109
"image/jpeg": "image",
110
"image/gif": "image",
111
"image/png": "image",
112
"image/bmp": "image",
113
"application/x-shockwave-flash": "flash"
116
const binaryCategoryMap =
122
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
124
const reIgnore = /about:|javascript:|resource:|chrome:|jar:/;
126
const layoutInterval = 300;
127
const phaseInterval = 1000;
128
const indentWidth = 18;
130
var cacheSession = null;
132
// ************************************************************************************************
134
Firebug.NetMonitor = extend(Firebug.Module,
136
clear: function(context)
138
var panel = context.getPanel("net", true);
142
if (context.netProgress)
143
context.netProgress.clear();
146
onToggleFilter: function(context, filterCategory)
148
Firebug.setPref("netFilterCategory", filterCategory);
150
var panel = context.getPanel("net", true);
153
panel.setFilter(filterCategory);
154
panel.updateSummaries(now(), true);
158
syncFilterButtons: function(chrome)
160
var button = chrome.$("fbNetFilter-"+Firebug.netFilterCategory);
161
button.checked = true;
164
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
167
initialize: function()
169
this.syncFilterButtons(FirebugChrome);
172
initContext: function(context)
174
if (!Firebug.disableNetMonitor)
175
monitorContext(context);
178
reattachContext: function(browser, context)
180
var chrome = context ? context.chrome : FirebugChrome;
181
this.syncFilterButtons(chrome);
184
destroyContext: function(context)
186
if (context.netProgress)
187
unmonitorContext(context);
190
showContext: function(browser, context)
194
var panel = context.chrome.getSelectedPanel();
195
if (panel && panel.name == "net")
196
context.netProgress.panel = panel;
200
loadedContext: function(context)
202
if (context.netProgress)
203
context.netProgress.loaded = true;
206
showPanel: function(browser, panel)
208
var netButtons = browser.chrome.$("fbNetButtons");
209
collapse(netButtons, !panel || panel.name != "net");
211
if (panel && panel.context.netProgress)
213
if (panel.name == "net")
214
panel.context.netProgress.activate(panel);
216
panel.context.netProgress.activate(null);
221
// ************************************************************************************************
223
function NetPanel() {}
225
NetPanel.prototype = domplate(Firebug.Panel,
228
TABLE({class: "netTable", cellpadding: 0, cellspacing: 0, onclick: "$onClick"},
240
FOR("file", "$files",
241
TR({class: "netRow $file.file|getCategory",
242
$hasHeaders: "$file.file|hasResponseHeaders",
243
$loaded: "$file.file.loaded", $responseError: "$file.file|isError",
244
$fromCache: "$file.file.fromCache", $inFrame: "$file.file|getInFrame"},
245
TD({class: "netHrefCol netCol"},
246
DIV({class: "netHrefLabel netLabel",
247
style: "margin-left: $file.file|getIndent\\px"},
250
DIV({class: "netFullHrefLabel netHrefLabel netLabel",
251
style: "margin-left: $file.file|getIndent\\px"},
255
TD({class: "netDomainCol netCol"},
256
DIV({class: "netDomainLabel netLabel"}, "$file.file|getDomain")
258
TD({class: "netSizeCol netCol"},
259
DIV({class: "netSizeLabel netLabel"}, "$file.file|getSize")
261
TD({class: "netTimeCol netCol"},
262
DIV({class: "netBar"},
264
DIV({class: "netTotalBar", style: "left: $file.offset"}),
265
DIV({class: "netTimeBar", style: "left: $file.offset; width: $file.width"},
266
SPAN({class: "netTimeLabel"}, "$file.elapsed|formatTime")
274
TR({class: "netHeadRow"},
275
TD({class: "netHeadCol", colspan: 4},
276
DIV({class: "netHeadLabel"}, "$doc.rootFile.href")
281
TR({class: "netInfoRow"},
282
TD({class: "netInfoCol", colspan: 4})
286
TR({class: "netRow netPhaseRow"},
287
TD({class: "netPhaseCol", colspan: 4})
291
TR({class: "netRow netSummaryRow"},
292
TD({class: "netCol"},
293
DIV({class: "netCountLabel netSummaryLabel"}, "-")
295
TD({class: "netCol"}),
296
TD({class: "netTotalSizeCol netCol"},
297
DIV({class: "netTotalSizeLabel netSummaryLabel"}, "0KB")
299
TD({class: "netTotalTimeCol netCol"},
300
DIV({class: "netBar"},
301
DIV({class: "netCacheSizeLabel netSummaryLabel"},
304
SPAN(" " + $STR("FromCache")),
307
DIV({class: "netTotalBar"}),
308
DIV({class: "netTimeBar", style: "width: 100%"},
309
SPAN({class: "netTotalTimeLabel netSummaryLabel"}, "0ms")
315
getCategory: function(file)
317
return "category-"+getFileCategory(file);
320
getInFrame: function(file)
322
return !!file.document.parent;
325
getIndent: function(file)
327
// XXXjoe Turn off indenting for now, it's confusing since we don't
328
// actually place nested files directly below their parent
329
//return file.document.level * indentWidth;
333
isError: function(file)
335
var errorRange = Math.floor(file.status/100);
336
return errorRange == 4 || errorRange == 5;
339
summarizePhase: function(phase, rightNow)
341
var cachedSize = 0, totalSize = 0;
343
var category = Firebug.netFilterCategory;
344
if (category == "all")
348
var minTime = 0, maxTime = 0;
349
for (var file = phase.phaseLastStart; file; file = file.previousFile)
351
if (!category || file.category == category)
357
totalSize += file.size;
359
cachedSize += file.size;
362
if (!minTime || file.startTime < minTime)
363
minTime = file.startTime;
364
if (file.endTime > maxTime)
365
maxTime = file.endTime;
372
var totalTime = maxTime - minTime;
373
return {cachedSize: cachedSize, totalSize: totalSize, totalTime: totalTime,
374
fileCount: fileCount}
377
getHref: function(file)
379
if (file.status && file.status != 200)
380
return getFileName(file.href) + " (" + file.status + ")";
382
return getFileName(file.href);
385
getDomain: function(file)
387
return getPrettyDomain(file.href);
390
getSize: function(file)
392
return this.formatSize(file.size);
395
hasResponseHeaders: function(file)
397
return !!file.responseHeaders;
400
formatSize: function(bytes)
402
if (bytes == -1 || bytes == undefined)
404
else if (bytes < 1000)
406
else if (bytes < 1000000)
407
return Math.ceil(bytes/1000) + " KB";
409
return (Math.ceil(bytes/10000)/100) + " MB";
412
formatTime: function(elapsed)
415
return "_"; // should be but this will be escaped so we need something that is no whitespace
416
else if (elapsed < 1000)
417
return elapsed + "ms";
418
else if (elapsed < 60000)
419
return (Math.ceil(elapsed/10) / 100) + "s";
421
return (Math.ceil((elapsed/60000)*100)/100) + "m";
424
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
426
onClick: function(event)
428
if (isLeftClick(event))
430
var row = getAncestorByClass(event.target, "netRow");
433
this.toggleHeadersRow(row);
439
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
443
this.panelNode.innerHTML = "";
445
this.summaryRow = null;
448
this.invalidPhases = false;
451
setFilter: function(filterCategory)
453
this.filterCategory = filterCategory;
455
var panelNode = this.panelNode;
456
for (var category in fileCategories)
458
if (filterCategory != "all" && category != filterCategory)
459
setClass(panelNode, "hideCategory-"+category);
461
removeClass(panelNode, "hideCategory-"+category);
465
toggleHeadersRow: function(row)
467
if (!hasClass(row, "hasHeaders"))
470
toggleClass(row, "opened");
472
if (hasClass(row, "opened"))
474
var template = Firebug.NetMonitor.NetInfoBody;
476
var netInfoRow = this.netInfoTag.insertRows({}, row)[0];
477
var netInfo = template.tag.replace({file: row.repObject}, netInfoRow.firstChild);
478
template.selectTabByName(netInfo, "Headers");
480
setClass(netInfo, "category-"+getFileCategory(row.repObject));
484
row.parentNode.removeChild(row.nextSibling);
488
copyParams: function(file)
490
var text = getPostText(file, this.context);
492
var lines = text.split("\n");
493
var params = parseURLEncodedText(lines[lines.length-1]);
496
for (var i = 0; i < params.length; ++i)
497
args.push(escape(params[i].name)+"="+escape(params[i].value));
500
url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&");
502
copyToClipboard(url);
505
copyHeaders: function(headers)
510
for (var i = 0; i < headers.length; ++i)
512
var header = headers[i];
513
lines.push(header.name + ": " + header.value);
517
var text = lines.join("\r\n");
518
copyToClipboard(text);
521
copyResponse: function(file)
523
var text = file.responseText
525
: this.context.sourceCache.loadText(file.href);
527
copyToClipboard(text);
530
stopLoading: function(file)
532
const NS_BINDING_ABORTED = 0x804b0002;
534
file.request.cancel(NS_BINDING_ABORTED);
537
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
544
initialize: function()
548
Firebug.Panel.initialize.apply(this, arguments);
551
destroy: function(state)
553
if (this.pendingInterval)
555
this.context.clearInterval(this.pendingInterval);
556
delete this.pendingInterval;
559
Firebug.Panel.destroy.apply(this, arguments);
562
show: function(state)
564
if (!this.filterCategory)
565
this.setFilter(Firebug.netFilterCategory);
568
this.layoutInterval = setInterval(bindFixed(this.updateLayout, this), layoutInterval);
570
if (this.wasScrolledToBottom)
571
scrollToBottom(this.panelNode);
576
this.wasScrolledToBottom = isScrolledToBottom(this.panelNode);
578
clearInterval(this.layoutInterval);
579
delete this.layoutInterval;
582
updateOption: function(name, value)
584
if (name == "disableNetMonitor")
585
TabWatcher.iterateContexts(value ? monitorContext : unmonitorContext);
586
else if (name == "netFilterCategory")
588
Firebug.NetMonitor.syncFilterButtons(this.context.chrome);
589
for (var i = 0; i < TabWatcher.contexts.length; ++i)
591
var context = TabWatcher.contexts[i];
592
Firebug.NetMonitor.onToggleFilter(context, value);
597
getOptionsMenuItems: function()
600
optionMenu("DisableNetMonitor", "disableNetMonitor")
604
getContextMenuItems: function(nada, target)
608
var file = Firebug.getRepObject(target);
612
var object = Firebug.getObjectByURL(this.context, file.href);
613
var isPost = isURLEncodedFile(file, getPostText(file, this.context));
616
{label: "CopyLocation", command: bindFixed(copyToClipboard, FBL, file.href) }
622
{label: "CopyLocationParameters", command: bindFixed(this.copyParams, this, file) }
627
{label: "CopyRequestHeaders",
628
command: bindFixed(this.copyHeaders, this, file.requestHeaders) },
629
{label: "CopyResponseHeaders",
630
command: bindFixed(this.copyHeaders, this, file.responseHeaders) }
633
if (file.category in textFileCategories)
636
{label: "CopyResponse", command: bindFixed(this.copyResponse, this, file) }
642
{label: "OpenInTab", command: bindFixed(openNewTab, FBL, file.href) }
649
{label: "StopLoading", command: bindFixed(this.stopLoading, this, file) }
655
var subItems = FirebugChrome.getInspectMenuItems(object);
659
items.push.apply(items, subItems);
666
showInfoTip: function(infoTip, target, x, y)
668
var row = getAncestorByClass(target, "netRow");
671
if (hasClass(row, "category-image"))
673
var url = row.repObject.href;
674
if (url == this.infoTipURL)
677
this.infoTipURL = url;
678
return Firebug.InfoTip.populateImageInfoTip(infoTip, url);
683
search: function(text)
687
delete this.currentSearch;
692
if (this.currentSearch && text == this.currentSearch.text)
693
row = this.currentSearch.findNext(true);
696
function findRow(node) { return getAncestorByClass(node, "netRow"); }
697
this.currentSearch = new TextSearch(this.panelNode, findRow);
698
row = this.currentSearch.find(text);
703
var sel = this.document.defaultView.getSelection();
704
sel.removeAllRanges();
705
sel.addRange(this.currentSearch.range);
707
scrollIntoCenterView(row, this.panelNode);
714
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
716
updateFile: function(file)
721
this.queue.push(file);
725
invalidatePhase: function(phase)
727
if (!phase.invalidPhase)
729
phase.invalidPhase = true;
730
this.invalidPhases = true;
734
updateLayout: function()
736
if (!this.queue.length)
739
var scrolledToBottom = isScrolledToBottom(this.panelNode);
743
if (scrolledToBottom)
744
scrollToBottom(this.panelNode);
747
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
751
if (!this.queue.length || !this.context.netProgress)
756
this.table = this.tableTag.replace({}, this.panelNode, this);
757
this.summaryRow = this.summaryTag.insertRows({}, this.table.lastChild.lastChild)[0];
760
var rightNow = now();
761
this.updateRowData(rightNow);
762
this.updateTimeline(rightNow);
763
this.updateSummaries(rightNow);
766
updateRowData: function(rightNow)
768
var queue = this.queue;
772
var newFileData = [];
774
for (var i = 0; i < queue.length; ++i)
778
phase = this.calculateFileTimes(file, phase, rightNow);
779
this.updateFileRow(file, newFileData);
781
file.invalid = false;
782
this.invalidatePhase(file.phase);
785
if (newFileData.length)
787
var tbody = this.table.firstChild;
788
var lastRow = tbody.lastChild.previousSibling;
789
var row = this.fileTag.insertRows({files: newFileData}, lastRow)[0];
791
for (var i = 0; i < newFileData.length; ++i)
793
var file = newFileData[i].file;
794
row.repObject = file;
796
row = row.nextSibling;
801
updateFileRow: function(file, newFileData)
810
offset: this.barOffset + "%",
811
width: this.barWidth + "%",
812
elapsed: file.loaded ? this.elapsed : -1
816
else if (file.invalid)
818
var sizeLabel = row.childNodes[2].firstChild;
819
sizeLabel.firstChild.nodeValue = this.getSize(file);
821
if (file.mimeType && !file.category)
823
removeClass(row, "category-undefined");
824
setClass(row, "category-"+getFileCategory(file));
827
if (file.responseHeaders)
828
setClass(row, "hasHeaders");
831
setClass(row, "fromCache");
833
removeClass(row, "fromCache");
835
if (this.isError(file))
837
setClass(row, "responseError");
839
var hrefLabel = row.firstChild.firstChild.firstChild;
840
hrefLabel.nodeValue = this.getHref(file);
843
var timeLabel = row.childNodes[3].firstChild.lastChild.firstChild;
847
setClass(row, "loaded");
848
timeLabel.innerHTML = this.formatTime(this.elapsed);
852
removeClass(row, "loaded");
853
timeLabel.innerHTML = " ";
856
if (hasClass(row, "opened"))
858
var netInfoBox = row.nextSibling.firstChild.firstChild;
859
Firebug.NetMonitor.NetInfoBody.updateInfo(netInfoBox, file, this.context);
864
updateTimeline: function(rightNow)
866
var rootFile = this.context.netProgress.rootFile; // XXXjjb never read?
867
var tbody = this.table.firstChild;
869
// XXXjoe Don't update rows whose phase is done and layed out already
871
for (var row = tbody.firstChild; row; row = row.nextSibling)
873
var file = row.repObject;
877
phase = this.calculateFileTimes(file, phase, rightNow);
879
var totalBar = row.childNodes[3].firstChild.childNodes[1];
880
var timeBar = totalBar.nextSibling;
882
totalBar.style.left = timeBar.style.left = this.barOffset + "%";
883
timeBar.style.width = this.barWidth + "%";
885
if (file.phase.phaseLastEnd && !file.phase.summaryRow)
887
var previousPhase = this.summaryRow.phase;
890
var lastRow = previousPhase.phaseLastStart.row;
891
previousPhase.summaryRow = this.phaseTag.insertRows({}, lastRow)[0];
892
this.invalidatePhase(previousPhase);
895
this.summaryRow.phase = file.phase;
896
file.phase.summaryRow = this.summaryRow;
901
updateSummaries: function(rightNow, updateAll)
903
if (!this.invalidPhases && !updateAll)
906
this.invalidPhases = false;
908
var phases = this.context.netProgress.phases;
912
var fileCount = 0, totalSize = 0, cachedSize = 0, totalTime = 0;
913
for (var i = 0; i < phases.length; ++i)
915
var phase = phases[i];
916
phase.invalidPhase = false;
918
var summary = this.summarizePhase(phase, rightNow);
919
fileCount += summary.fileCount;
920
totalSize += summary.totalSize;
921
cachedSize += summary.cachedSize;
922
totalTime += summary.totalTime
925
var row = this.summaryRow;
929
var countLabel = row.firstChild.firstChild;
930
countLabel.firstChild.nodeValue = fileCount == 1
932
: $STRF("RequestCount", [fileCount]);
934
var sizeLabel = row.childNodes[2].firstChild;
935
sizeLabel.firstChild.nodeValue = this.formatSize(totalSize);
937
var cacheSizeLabel = row.lastChild.firstChild.firstChild;
938
cacheSizeLabel.setAttribute("collapsed", cachedSize == 0);
939
cacheSizeLabel.childNodes[1].firstChild.nodeValue = this.formatSize(cachedSize);
941
var timeLabel = row.lastChild.firstChild.lastChild.firstChild;
942
timeLabel.innerHTML = this.formatTime(totalTime);
945
calculateFileTimes: function(file, phase, rightNow)
947
if (phase != file.phase)
950
this.phaseStartTime = phase.startTime;
951
this.phaseEndTime = phase.phaseLastEndTime ? phase.phaseLastEndTime : rightNow;
952
this.phaseElapsed = this.phaseEndTime - phase.startTime;
955
this.elapsed = file.loaded ? file.endTime - file.startTime : this.phaseEndTime - file.startTime;
956
this.barWidth = Math.floor((this.elapsed/this.phaseElapsed) * 100);
957
this.barOffset = Math.floor(((file.startTime-this.phaseStartTime)/this.phaseElapsed) * 100);
963
// ************************************************************************************************
965
function NetProgress(context)
967
this.context = context;
972
this.post = function(handler, args)
976
var file = handler.apply(this, args);
979
panel.updateFile(file);
985
if (queue.length/2 >= maxQueueRequests)
987
queue.push(handler, args);
991
this.flush = function()
993
for (var i = 0; i < queue.length; i += 2)
995
var file = queue[i].apply(this, queue[i+1]);
997
panel.updateFile(file);
1003
this.activate = function(activePanel)
1005
this.panel = panel = activePanel;
1010
this.update = function(file)
1013
panel.updateFile(file);
1016
this.clear = function()
1019
this.requestMap = {};
1022
this.documents = [];
1031
NetProgress.prototype =
1035
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1037
respondedTopWindow: function(request, time, webProgress)
1039
var win = webProgress ? safeGetWindow(webProgress) : null;
1040
this.requestedFile(request, time, win);
1041
return this.respondedFile(request, time);
1044
requestedFile: function(request, time, win, category) // XXXjjb 3rd arg was webProgress, pulled safeGetWindow up
1046
// XXXjjb to allow spy to pass win. var win = webProgress ? safeGetWindow(webProgress) : null;
1047
var file = this.getRequestFile(request, win);
1050
// For cached image files, we may never hear another peep from any observers
1051
// after this point, so we have to assume that the file is cached and loaded
1052
// until we get a respondedFile call later
1053
file.startTime = file.endTime = time;
1054
//file.fromCache = true;
1055
//file.loaded = true;
1056
if (category && !file.category)
1057
file.category = category;
1058
file.isBackground = request.loadFlags & LOAD_BACKGROUND;
1060
this.awaitFile(request, file);
1061
this.extendPhase(file);
1067
respondedFile: function(request, time)
1069
var file = this.getRequestFile(request);
1072
var endedAlready = !!file.endTime;
1074
file.respondedTime = time;
1075
file.endTime = time;
1077
if (request.contentLength > 0)
1078
file.size = request.contentLength;
1080
if (request.responseStatus == 304)
1081
file.fromCache = true;
1082
else if (!file.fromCache)
1083
file.fromCache = false;
1085
getHttpHeaders(request, file);
1087
// This is a strange but effective tactic for simulating the
1088
// load of background images, which we can't actually track.
1089
// If endTime was set before this, that means the cache request
1090
// came back, which only seems to happen for background images.
1091
// We thus end the load now, since we know we'll never hear
1092
// from these requests again.
1100
progressFile: function(request, progress, expectedSize)
1102
var file = this.getRequestFile(request);
1105
file.size = progress;
1106
file.expectedSize = expectedSize;
1108
this.arriveFile(file, request);
1114
stopFile: function(request, time, postText, responseText)
1116
var file = this.getRequestFile(request);
1119
file.endTime = time;
1120
file.postText = postText;
1121
file.responseText = responseText;
1123
// XXXjoe Nice work, pavlov. This crashes randomly when it access decoderObserver.
1124
//file.sourceObject = getRequestElement(request);
1126
getHttpHeaders(request, file);
1128
this.arriveFile(file, request);
1131
getCacheEntry(file, this);
1137
cacheEntryReady: function(request, file, size)
1143
getHttpHeaders(request, file);
1145
this.arriveFile(file, request);
1151
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1153
getRequestFile: function(request, win)
1155
var name = safeGetName(request);
1156
if (!name || reIgnore.exec(name))
1159
var index = this.requests.indexOf(request);
1162
var file = this.requestMap[name];
1166
if (!win || getRootWindow(win) != this.context.window)
1169
var fileDoc = this.getRequestDocument(win);
1170
var isDocument = request.loadFlags & LOAD_DOCUMENT_URI && fileDoc.parent;
1171
var doc = isDocument ? fileDoc.parent : fileDoc;
1173
file = doc.addFile(request);
1176
fileDoc.documentFile = file;
1177
file.ownDocument = fileDoc;
1181
this.rootFile = file; // don't set file.previousFile
1183
file.previousFile = this.files[this.files.length-1];
1185
file.request = request;
1186
file.index = this.files.length;
1187
this.requestMap[name] = file;
1188
this.requests.push(request);
1189
this.files.push(file);
1194
return this.files[index];
1197
getRequestDocument: function(win)
1201
var index = this.windows.indexOf(win);
1204
var doc = new NetDocument(win); // XXXjjb arg ignored
1205
if (win.parent != win)
1206
doc.parent = this.getRequestDocument(win.parent);
1208
doc.index = this.documents.length;
1209
doc.level = getFrameLevel(win);
1211
this.documents.push(doc);
1212
this.windows.push(win);
1217
return this.documents[index];
1220
return this.documents[0];
1223
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1225
awaitFile: function(request, file)
1230
// XXXjoe Remove files after they have been checked N times
1231
if (!this.pendingInterval)
1233
this.pendingInterval = this.context.setInterval(bindFixed(function()
1235
for (var i = 0; i < this.pending.length; ++i)
1237
var file = this.pending[i];
1238
if (file.pendingCount > maxPendingCheck)
1240
this.post(cacheEntryReady, [request, file, 0]);
1241
this.pending.splice(i, 0);
1245
waitForCacheCompletion(request, file, this);
1250
file.pendingIndex = this.pending.length;
1251
this.pending.push(file);
1254
arriveFile: function(file, request)
1256
delete this.requestMap[file.href];
1258
var index = this.pending.indexOf(file);
1260
this.pending.splice(index, 1);
1262
if (!this.pending.length)
1264
this.context.clearInterval(this.pendingInterval);
1265
delete this.pendingInterval;
1269
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1271
endLoad: function(file)
1275
file.phase.phaseLastEnd = file;
1276
if (!file.phase.phaseLastEndTime || file.endTime > file.phase.phaseLastEndTime)
1277
file.phase.phaseLastEndTime = file.endTime;
1280
extendPhase: function(file)
1282
if (this.currentPhase)
1284
var phaseLastStart = this.currentPhase.phaseLastStart;
1286
if (this.loaded && file.startTime - phaseLastStart.startTime >= phaseInterval)
1287
this.startPhase(file, phaseLastStart);
1289
file.phase = this.currentPhase;
1292
this.startPhase(file, null);
1294
file.phase.phaseLastStart = file;
1297
startPhase: function(file, phaseLastStart)
1300
phaseLastStart.endPhase = true;
1302
file.phase = this.currentPhase = file;
1303
file.startPhase = true;
1305
this.phases.push(file);
1308
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1311
QueryInterface: function(iid)
1313
if (iid.equals(nsIWebProgressListener)
1314
|| iid.equals(nsISupportsWeakReference)
1315
|| iid.equals(nsISupports))
1320
throw Components.results.NS_NOINTERFACE;
1323
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1326
observe: function(request, topic, data)
1328
request = QI(request, nsIHttpChannel);
1329
if (topic == "http-on-modify-request")
1331
var webProgress = getRequestWebProgress(request, this);
1332
var category = getRequestCategory(request);
1333
var win = webProgress ? safeGetWindow(webProgress) : null;
1334
this.post(requestedFile, [request, now(), win, category]);
1338
this.post(respondedFile, [request, now()]);
1342
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1343
// nsIWebProgressListener
1345
onStateChange: function(progress, request, flag, status)
1347
if (flag & STATE_TRANSFERRING && flag & STATE_IS_DOCUMENT)
1349
var win = progress.DOMWindow;
1350
if (win == win.parent)
1351
this.post(respondedTopWindow, [request, now(), progress]);
1353
else if (flag & STATE_STOP && flag & STATE_IS_REQUEST)
1355
if (this.getRequestFile(request))
1356
this.post(stopFile, [request, now()]);
1360
onProgressChange : function(progress, request, current, max, total, maxTotal)
1362
this.post(progressFile, [request, current, max]);
1365
stateIsRequest: false,
1366
onLocationChange: function() {},
1367
onStatusChange : function() {},
1368
onSecurityChange : function() {},
1369
onLinkIconAvailable : function() {}
1372
var requestedFile = NetProgress.prototype.requestedFile;
1373
var respondedTopWindow = NetProgress.prototype.respondedTopWindow;
1374
var respondedFile = NetProgress.prototype.respondedFile;
1375
var progressFile = NetProgress.prototype.progressFile;
1376
var stopFile = NetProgress.prototype.stopFile;
1377
var cacheEntryReady = NetProgress.prototype.cacheEntryReady;
1379
// ************************************************************************************************
1381
function NetDocument()
1386
NetDocument.prototype =
1390
addFile: function(request)
1392
var file = new NetFile(request.name, this);
1393
this.files.push(file);
1395
if (this.files.length == 1)
1396
this.rootFile = file;
1402
// ************************************************************************************************
1404
function NetFile(href, document)
1407
this.document = document
1421
Firebug.NetFile = NetFile;
1423
// ************************************************************************************************
1426
function monitorContext(context)
1428
if (!context.netProgress)
1430
var listener = context.netProgress = new NetProgress(context);
1432
context.browser.addProgressListener(listener, NOTIFY_ALL);
1434
observerService.addObserver(listener, "http-on-modify-request", false);
1435
observerService.addObserver(listener, "http-on-examine-response", false);
1439
function unmonitorContext(context)
1441
if (context.netProgress)
1443
if (context.browser.docShell)
1444
context.browser.removeProgressListener(context.netProgress, NOTIFY_ALL);
1446
// XXXjoe We also want to do this when the context is hidden, so that
1447
// background files are only logged in the currently visible context
1448
observerService.removeObserver(context.netProgress, "http-on-modify-request", false);
1449
observerService.removeObserver(context.netProgress, "http-on-examine-response", false);
1451
delete context.netProgress;
1455
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1457
function initCacheSession()
1461
var cacheService = CacheService.getService(nsICacheService);
1462
cacheSession = cacheService.createSession("HTTP", STORE_ANYWHERE, true);
1463
cacheSession.doomEntriesIfExpired = false;
1467
function waitForCacheCompletion(request, file, netProgress)
1472
var descriptor = cacheSession.openCacheEntry(file.href, ACCESS_READ, false);
1474
netProgress.post(cacheEntryReady, [request, file, descriptor.dataSize]);
1478
if (exc.result != NS_ERROR_CACHE_WAIT_FOR_VALIDATION
1479
&& exc.result != NS_ERROR_CACHE_KEY_NOT_FOUND)
1482
netProgress.post(cacheEntryReady, [request, file, -1]);
1487
function getCacheEntry(file, netProgress)
1489
// Pause first because this is usually called from stopFile, at which point
1490
// the file's cache entry is locked
1491
setTimeout(function()
1496
cacheSession.asyncOpenCacheEntry(file.href, ACCESS_READ, {
1497
onCacheEntryAvailable: function(descriptor, accessGranted, status)
1503
file.size = descriptor.dataSize;
1505
if(descriptor.lastModified && descriptor.lastFetched &&
1506
descriptor.lastModified < Math.floor(file.startTime/1000)) {
1507
file.fromCache = true;
1510
{ name: "Last Modified",
1511
value: getDateFromSeconds(descriptor.lastModified)
1513
{ name: "Last Fetched",
1514
value: getDateFromSeconds(descriptor.lastFetched)
1516
{ name: "Data Size",
1517
value: descriptor.dataSize
1519
{ name: "Fetch Count",
1520
value: descriptor.fetchCount
1523
value: descriptor.deviceID
1526
netProgress.update(file);
1538
function getDateFromSeconds(s)
1545
function getHttpHeaders(request, file)
1549
var http = QI(request, nsIHttpChannel);
1550
file.method = http.requestMethod;
1551
file.status = request.responseStatus;
1552
file.urlParams = parseURLParams(file.href);
1555
file.mimeType = getMimeType(request);
1557
// Disable temporarily
1558
if (!file.responseHeaders && Firebug.collectHttpHeaders)
1560
var requestHeaders = [], responseHeaders = [];
1562
http.visitRequestHeaders({
1563
visitHeader: function(name, value)
1565
requestHeaders.push({name: name, value: value});
1568
http.visitResponseHeaders({
1569
visitHeader: function(name, value)
1571
responseHeaders.push({name: name, value: value});
1575
file.requestHeaders = requestHeaders;
1576
file.responseHeaders = responseHeaders;
1584
function getRequestWebProgress(request, netProgress)
1588
if (request.notificationCallbacks)
1591
if (request.notificationCallbacks instanceof XMLHttpRequest)
1593
request.notificationCallbacks.channel.visitRequestHeaders(
1595
visitHeader: function(header, value)
1597
if (header == "X-Moz" && value == "microsummary")
1602
// XXXjjb Joe review: code above sets bypass, so this stmt should be in if (gives exceptions otherwise)
1603
// The instanceof can't be used here. Fix for #434 [Honza]
1605
return request.notificationCallbacks.getInterface(nsIWebProgress);
1612
if (request.loadGroup && request.loadGroup.groupObserver)
1613
return QI(request.loadGroup.groupObserver, nsIWebProgress);
1618
function getRequestCategory(request)
1622
if (request.notificationCallbacks)
1624
if (request.notificationCallbacks instanceof XMLHttpRequest)
1631
function getRequestElement(request)
1633
if (request instanceof imgIRequest)
1635
if (request.decoderObserver && request.decoderObserver instanceof Element)
1637
return request.decoderObserver;
1642
function safeGetWindow(webProgress)
1646
return webProgress.DOMWindow;
1654
function safeGetName(request)
1658
return request.name;
1666
function getFileCategory(file)
1669
return file.category;
1673
var ext = getFileExtension(file.href);
1675
file.mimeType = mimeExtensionMap[ext.toLowerCase()];
1678
return (file.category = mimeCategoryMap[file.mimeType]);
1681
function getMimeType(request)
1683
var mimeType = request.contentType;
1684
if (!mimeType || !(mimeType in mimeCategoryMap))
1686
var ext = getFileExtension(request.name);
1691
var extMimeType = mimeExtensionMap[ext.toLowerCase()];
1692
return extMimeType ? extMimeType : mimeType;
1699
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1703
return (new Date()).getTime();
1706
function getFrameLevel(win)
1710
for (; win && win != win.parent; win = win.parent)
1716
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1718
Firebug.NetMonitor.NetInfoBody = domplate(Firebug.Rep,
1721
DIV({class: "netInfoBody", _repObject: "$file"},
1722
DIV({class: "netInfoTabs"},
1723
A({class: "netInfoParamsTab netInfoTab", onclick: "$onClickTab",
1725
$collapsed: "$file|hideParams"},
1726
$STR("URLParameters")
1728
A({class: "netInfoHeadersTab netInfoTab", onclick: "$onClickTab",
1732
A({class: "netInfoPostTab netInfoTab", onclick: "$onClickTab",
1734
$collapsed: "$file|hidePost"},
1737
A({class: "netInfoResponseTab netInfoTab", onclick: "$onClickTab",
1739
$collapsed: "$file|hideResponse"},
1742
A({class: "netInfoCacheTab netInfoTab", onclick: "$onClickTab",
1744
$collapsed: "$file|hideCache"},
1745
"Cache" // todo: Localization
1748
TABLE({class: "netInfoParamsText netInfoText netInfoParamsTable",
1749
cellpadding: 0, cellspacing: 0}, TBODY()),
1750
TABLE({class: "netInfoHeadersText netInfoText netInfoHeadersTable",
1751
cellpadding: 0, cellspacing: 0},
1753
TR({class: "netInfoResponseHeadersTitle"},
1755
DIV({class: "netInfoHeadersGroup"}, $STR("ResponseHeaders"))
1758
TR({class: "netInfoRequestHeadersTitle"},
1760
DIV({class: "netInfoHeadersGroup"}, $STR("RequestHeaders"))
1765
DIV({class: "netInfoPostText netInfoText"},
1766
TABLE({class: "netInfoPostTable", cellpadding: 0, cellspacing: 0},
1770
DIV({class: "netInfoResponseText netInfoText"},
1773
DIV({class: "netInfoCacheText netInfoText"},
1774
TABLE({class: "netInfoCacheTable", cellpadding: 0, cellspacing: 0},
1781
FOR("param", "$headers",
1783
TD({class: "netInfoParamName"}, "$param.name"),
1784
TD({class: "netInfoParamValue"}, "$param.value")
1788
hideParams: function(file)
1790
return !file.urlParams || !file.urlParams.length;
1793
hidePost: function(file)
1795
return file.method.toUpperCase() != "POST";
1798
hideResponse: function(file)
1800
return file.category in binaryFileCategories;
1803
hideCache: function(file)
1805
return !file.cacheEntry || file.category=="image";
1808
onClickTab: function(event)
1810
this.selectTab(event.currentTarget);
1813
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1815
selectTabByName: function(netInfoBox, tabName)
1817
var tab = getChildByClass(netInfoBox, "netInfoTabs", "netInfo"+tabName+"Tab");
1819
this.selectTab(tab);
1822
selectTab: function(tab)
1824
var netInfoBox = tab.parentNode.parentNode;
1826
var view = tab.getAttribute("view");
1827
if (netInfoBox.selectedTab)
1829
netInfoBox.selectedTab.removeAttribute("selected");
1830
netInfoBox.selectedText.removeAttribute("selected");
1833
var textBodyName = "netInfo" + view + "Text";
1835
netInfoBox.selectedTab = tab;
1836
netInfoBox.selectedText = getChildByClass(netInfoBox, textBodyName);
1838
netInfoBox.selectedTab.setAttribute("selected", "true");
1839
netInfoBox.selectedText.setAttribute("selected", "true");
1841
var file = Firebug.getRepObject(netInfoBox);
1842
var context = Firebug.getElementPanel(netInfoBox).context;
1843
this.updateInfo(netInfoBox, file, context);
1846
updateInfo: function(netInfoBox, file, context)
1848
var tab = netInfoBox.selectedTab;
1849
if (hasClass(tab, "netInfoParamsTab"))
1851
if (file.urlParams && !netInfoBox.urlParamsPresented)
1853
netInfoBox.urlParamsPresented = true;
1854
this.insertHeaderRows(netInfoBox, file.urlParams, "Params");
1858
if (hasClass(tab, "netInfoHeadersTab"))
1860
if (file.responseHeaders && !netInfoBox.responseHeadersPresented)
1862
netInfoBox.responseHeadersPresented = true;
1863
this.insertHeaderRows(netInfoBox, file.responseHeaders, "Headers", "ResponseHeaders");
1866
if (file.requestHeaders && !netInfoBox.requestHeadersPresented)
1868
netInfoBox.requestHeadersPresented = true;
1869
this.insertHeaderRows(netInfoBox, file.requestHeaders, "Headers", "RequestHeaders");
1873
if (hasClass(tab, "netInfoPostTab"))
1875
var postTextBox = getChildByClass(netInfoBox, "netInfoPostText");
1876
if (!netInfoBox.postPresented)
1878
netInfoBox.postPresented = true;
1880
var text = getPostText(file, context);
1881
if (text != undefined)
1883
if (isURLEncodedFile(file, text))
1885
var lines = text.split("\n");
1886
var params = parseURLEncodedText(lines[lines.length-1]);
1887
this.insertHeaderRows(netInfoBox, params, "Post");
1891
var postText = formatPostText(text);
1893
insertWrappedText(postText, postTextBox);
1899
if (hasClass(tab, "netInfoResponseTab") && file.loaded && !netInfoBox.responsePresented)
1901
netInfoBox.responsePresented = true;
1903
var responseTextBox = getChildByClass(netInfoBox, "netInfoResponseText");
1904
if (file.category == "image")
1906
var responseImage = netInfoBox.ownerDocument.createElement("img");
1907
responseImage.src = file.href;
1908
responseTextBox.replaceChild(responseImage, responseTextBox.firstChild);
1910
else if (!(file.category in binaryCategoryMap))
1912
var text = file.responseText
1914
: context.sourceCache.loadText(file.href);
1917
insertWrappedText(text, responseTextBox);
1919
insertWrappedText("", responseTextBox);
1923
if (hasClass(tab, "netInfoCacheTab") && file.loaded && !netInfoBox.cachePresented)
1925
netInfoBox.cachePresented = true;
1927
var responseTextBox = getChildByClass(netInfoBox, "netInfoCacheText");
1928
if(file.cacheEntry) {
1929
this.insertHeaderRows(netInfoBox, file.cacheEntry, "Cache");
1934
insertHeaderRows: function(netInfoBox, headers, tableName, rowName)
1936
var headersTable = getElementByClass(netInfoBox, "netInfo"+tableName+"Table");
1937
var tbody = headersTable.firstChild;
1938
var titleRow = getChildByClass(tbody, "netInfo" + rowName + "Title");
1942
this.headerDataTag.insertRows({headers: headers}, titleRow ? titleRow : tbody);
1943
removeClass(titleRow, "collapsed");
1946
setClass(titleRow, "collapsed");
1950
// ************************************************************************************************
1952
function findHeader(headers, name)
1954
for (var i = 0; i < headers.length; ++i)
1956
if (headers[i].name == name)
1957
return headers[i].value;
1961
function formatPostText(text)
1963
if (text instanceof XMLDocument)
1964
return getElementXML(text.documentElement);
1969
function getPostText(file, context)
1972
file.postText = readPostText(file.href, context);
1974
return file.postText;
1977
function insertWrappedText(text, textBox)
1979
var reNonAlphaNumeric = /[^A-Za-z_$0-9'"-]/;
1982
var wrapWidth = Firebug.textWrapWidth;
1984
var lines = splitLines(text);
1985
for (var i = 0; i < lines.length; ++i)
1987
var line = lines[i];
1988
while (line.length > wrapWidth)
1990
var m = reNonAlphaNumeric.exec(line.substr(wrapWidth, 100));
1991
var wrapIndex = wrapWidth+ (m ? m.index : 0);
1992
var subLine = line.substr(0, wrapIndex);
1993
line = line.substr(wrapIndex);
1996
html.push(escapeHTML(subLine));
1997
html.push("</pre>");
2001
html.push(escapeHTML(line));
2002
html.push("</pre>");
2005
textBox.innerHTML = html.join("");
2008
function isURLEncodedFile(file, text)
2010
return (text && text.indexOf("Content-Type: application/x-www-form-urlencoded") != -1)
2011
|| findHeader(file.requestHeaders, "Content-Type") == "application/x-www-form-urlencoded";
2014
// ************************************************************************************************
2016
Firebug.registerModule(Firebug.NetMonitor);
2017
Firebug.registerPanel(NetPanel);
2019
// ************************************************************************************************