Bug 859046 - Implement filtering out certain types of requests, r=rcampbell

* * *
Bug 873761 - Several "XUL box for hbox element contained an inline #text child, forcing all its children to be wrapped in a block." warnings for netmonitor.xul, r=rcampbell
This commit is contained in:
Victor Porof 2013-05-24 20:53:10 +03:00
parent d025dfdef0
commit 1bcfae93c8
23 changed files with 1249 additions and 145 deletions

View File

@ -243,13 +243,23 @@ TargetEventsHandler.prototype = {
* Packet received from the server.
*/
_onTabNavigated: function(aType, aPacket) {
if (aType == "will-navigate") {
NetMonitorView.RequestsMenu.reset();
NetMonitorView.NetworkDetails.reset();
window.emit("NetMonitor:TargetWillNavigate");
}
if (aType == "navigate") {
window.emit("NetMonitor:TargetNavigate");
switch (aType) {
case "will-navigate": {
// Reset UI.
NetMonitorView.RequestsMenu.reset();
NetMonitorView.NetworkDetails.reset();
// Reset global helpers cache.
nsIURL.store.clear();
drain.store.clear();
window.emit("NetMonitor:TargetWillNavigate");
break;
}
case "navigate": {
window.emit("NetMonitor:TargetNavigate");
break;
}
}
},
@ -310,8 +320,8 @@ NetworkEventsHandler.prototype = {
* The message received from the server.
*/
_onNetworkEvent: function(aType, aPacket) {
let { actor, startedDateTime, method, url } = aPacket.eventActor;
NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url);
let { actor, startedDateTime, method, url, isXHR } = aPacket.eventActor;
NetMonitorView.RequestsMenu.addRequest(actor, startedDateTime, method, url, isXHR);
window.emit("NetMonitor:NetworkEvent");
},

View File

@ -103,6 +103,7 @@ let NetMonitorView = {
_initializePanes: function() {
dumpn("Initializing the NetMonitorView panes");
this._body = $("#body");
this._detailsPane = $("#details-pane");
this._detailsPaneToggleButton = $("#details-pane-toggle");
@ -153,9 +154,11 @@ let NetMonitorView = {
ViewHelpers.togglePane(aFlags, pane);
if (aFlags.visible) {
this._body.removeAttribute("pane-collapsed");
button.removeAttribute("pane-collapsed");
button.setAttribute("tooltiptext", this._collapsePaneString);
} else {
this._body.setAttribute("pane-collapsed", "");
button.setAttribute("pane-collapsed", "");
button.setAttribute("tooltiptext", this._expandPaneString);
}
@ -190,9 +193,12 @@ let NetMonitorView = {
return deferred.promise;
},
_editorPromises: new Map(),
_body: null,
_detailsPane: null,
_detailsPaneToggleButton: null,
_collapsePaneString: "",
_expandPaneString: "",
_editorPromises: new Map(),
_isInitialized: false,
_isDestroyed: false
};
@ -259,6 +265,9 @@ function RequestsMenuView() {
this._onMouseDown = this._onMouseDown.bind(this);
this._onSelect = this._onSelect.bind(this);
this._onResize = this._onResize.bind(this);
this._byFile = this._byFile.bind(this);
this._byDomain = this._byDomain.bind(this);
this._byType = this._byType.bind(this);
}
create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
@ -314,8 +323,10 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
* Specifies the request method (e.g. "GET", "POST", etc.)
* @param string aUrl
* Specifies the request's url.
* @param boolean aIsXHR
* True if this request was initiated via XHR.
*/
addRequest: function(aId, aStartedDateTime, aMethod, aUrl) {
addRequest: function(aId, aStartedDateTime, aMethod, aUrl, aIsXHR) {
// Convert the received date/time string to a unix timestamp.
let unixTime = Date.parse(aStartedDateTime);
@ -333,22 +344,75 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
startedDeltaMillis: unixTime - this._firstRequestStartedMillis,
startedMillis: unixTime,
method: aMethod,
url: aUrl
url: aUrl,
isXHR: aIsXHR
},
finalize: this._onRequestItemRemoved
});
$("#details-pane-toggle").disabled = false;
$(".requests-menu-empty-notice").hidden = true;
$("#requests-menu-empty-notice").hidden = true;
this._cache.set(aId, requestItem);
},
/**
* Filters all network requests in this container by a specified type.
*
* @param string aType
* Either null, "html", "css", "js", "xhr", "fonts", "images", "media"
* or "flash".
*/
filterOn: function(aType) {
let target = $("#requests-menu-filter-" + aType + "-button");
let buttons = document.querySelectorAll(".requests-menu-footer-button");
for (let button of buttons) {
if (button != target) {
button.removeAttribute("checked");
} else {
button.setAttribute("checked", "");
}
}
// Filter on nothing.
if (!target) {
this.filterContents(() => true);
}
// Filter on whatever was requested.
else switch (aType) {
case "html":
this.filterContents(this._onHtml);
break;
case "css":
this.filterContents(this._onCss);
break;
case "js":
this.filterContents(this._onJs);
break;
case "xhr":
this.filterContents(this._onXhr);
break;
case "fonts":
this.filterContents(this._onFonts);
break;
case "images":
this.filterContents(this._onImages);
break;
case "media":
this.filterContents(this._onMedia);
break;
case "flash":
this.filterContents(this._onFlash);
break;
}
},
/**
* Sorts all network requests in this container by a specified detail.
*
* @param string aType
* Either "status", "method", "file", "domain", "type" or "size".
* Either null, "status", "method", "file", "domain", "type" or "size".
*/
sortBy: function(aType) {
let target = $("#requests-menu-" + aType + "-button");
@ -426,6 +490,54 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
}
},
/**
* Predicates used when filtering items.
*
* @param MenuItem aItem
* The filtered menu item.
* @return boolean
* True if the menu item should be visible, false otherwise.
*/
_onHtml: function({ attachment: { mimeType } })
mimeType && mimeType.contains("/html"),
_onCss: function({ attachment: { mimeType } })
mimeType && mimeType.contains("/css"),
_onJs: function({ attachment: { mimeType } })
mimeType && (
mimeType.contains("/ecmascript") ||
mimeType.contains("/javascript") ||
mimeType.contains("/x-javascript")),
_onXhr: function({ attachment: { isXHR } })
isXHR,
_onFonts: function({ attachment: { url, mimeType } }) // Fonts are a mess.
(mimeType && (
mimeType.contains("font/") ||
mimeType.contains("/font"))) ||
url.contains(".eot") ||
url.contains(".ttf") ||
url.contains(".otf") ||
url.contains(".woff"),
_onImages: function({ attachment: { mimeType } })
mimeType && mimeType.contains("image/"),
_onMedia: function({ attachment: { mimeType } }) // Not including images.
mimeType && (
mimeType.contains("audio/") ||
mimeType.contains("video/") ||
mimeType.contains("model/")),
_onFlash: function({ attachment: { url, mimeType } }) // Flash is a mess.
(mimeType && (
mimeType.contains("/x-flv") ||
mimeType.contains("/x-shockwave-flash"))) ||
url.contains(".swf") ||
url.contains(".flv"),
/**
* Predicates used when sorting items.
*
@ -438,32 +550,29 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
* 0 to leave aFirst and aSecond unchanged with respect to each other
* 1 to sort aSecond to a lower index than aFirst
*/
_byTiming: (aFirst, aSecond) =>
aFirst.attachment.startedMillis > aSecond.attachment.startedMillis,
_byTiming: function({ attachment: first }, { attachment: second })
first.startedMillis > second.startedMillis,
_byStatus: (aFirst, aSecond) =>
aFirst.attachment.status > aSecond.attachment.status,
_byStatus: function({ attachment: first }, { attachment: second })
first.status > second.status,
_byMethod: (aFirst, aSecond) =>
aFirst.attachment.method > aSecond.attachment.method,
_byMethod: function({ attachment: first }, { attachment: second })
first.method > second.method,
_byFile: (aFirst, aSecond) =>
!aFirst.target || !aSecond.target ? -1 :
$(".requests-menu-file", aFirst.target).getAttribute("value").toLowerCase() >
$(".requests-menu-file", aSecond.target).getAttribute("value").toLowerCase(),
_byFile: function({ attachment: first }, { attachment: second })
this._getUriNameWithQuery(first.url).toLowerCase() >
this._getUriNameWithQuery(second.url).toLowerCase(),
_byDomain: (aFirst, aSecond) =>
!aFirst.target || !aSecond.target ? -1 :
$(".requests-menu-domain", aFirst.target).getAttribute("value").toLowerCase() >
$(".requests-menu-domain", aSecond.target).getAttribute("value").toLowerCase(),
_byDomain: function({ attachment: first }, { attachment: second })
this._getUriHostPort(first.url).toLowerCase() >
this._getUriHostPort(second.url).toLowerCase(),
_byType: (aFirst, aSecond) =>
!aFirst.target || !aSecond.target ? -1 :
$(".requests-menu-type", aFirst.target).getAttribute("value").toLowerCase() >
$(".requests-menu-type", aSecond.target).getAttribute("value").toLowerCase(),
_byType: function({ attachment: first }, { attachment: second })
this._getAbbreviatedMimeType(first.mimeType).toLowerCase() >
this._getAbbreviatedMimeType(second.mimeType).toLowerCase(),
_bySize: (aFirst, aSecond) =>
aFirst.attachment.contentSize > aSecond.attachment.contentSize,
_bySize: function({ attachment: first }, { attachment: second })
first.contentSize > second.contentSize,
/**
* Schedules adding additional information to a network request.
@ -577,8 +686,13 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
// We're done flushing all the requests, clear the update queue.
this._updateQueue = [];
// Make sure all the requests are sorted.
// Make sure all the requests are sorted and filtered.
// Freshly added requests may not yet contain all the information required
// for sorting and filtering predicates, so this is done each time the
// network requests table is flushed (don't worry, events are drained first
// so this doesn't happen once per network event update).
this.sortContents();
this.filterContents();
},
/**
@ -592,19 +706,19 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
* The network request view.
*/
_createMenuView: function(aMethod, aUrl) {
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
let name = NetworkHelper.convertToUnicode(unescape(uri.fileName)) || "/";
let query = NetworkHelper.convertToUnicode(unescape(uri.query));
let hostPort = NetworkHelper.convertToUnicode(unescape(uri.hostPort));
let uri = nsIURL(aUrl);
let nameWithQuery = this._getUriNameWithQuery(uri);
let hostPort = this._getUriHostPort(uri);
let template = $("#requests-menu-item-template");
let fragment = document.createDocumentFragment();
$(".requests-menu-method", template).setAttribute("value", aMethod);
let method = $(".requests-menu-method", template);
method.setAttribute("value", aMethod);
let file = $(".requests-menu-file", template);
file.setAttribute("value", name + (query ? "?" + query : ""));
file.setAttribute("tooltiptext", name + (query ? "?" + query : ""));
file.setAttribute("value", nameWithQuery);
file.setAttribute("tooltiptext", nameWithQuery);
let domain = $(".requests-menu-domain", template);
domain.setAttribute("value", hostPort);
@ -653,7 +767,7 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
break;
}
case "mimeType": {
let type = aValue.split(";")[0].split("/")[1] || "?";
let type = this._getAbbreviatedMimeType(aValue);
let node = $(".requests-menu-type", aItem.target);
let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type;
node.setAttribute("value", text);
@ -981,6 +1095,40 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, {
}
},
/**
* Helpers for getting details about an nsIURL.
*
* @param nsIURL | string aUrl
* @return string
*/
_getUriNameWithQuery: function(aUrl) {
if (!(aUrl instanceof Ci.nsIURL)) {
aUrl = nsIURL(aUrl);
}
let name = NetworkHelper.convertToUnicode(unescape(aUrl.fileName)) || "/";
let query = NetworkHelper.convertToUnicode(unescape(aUrl.query));
return name + (query ? "?" + query : "");
},
_getUriHostPort: function(aUrl) {
if (!(aUrl instanceof Ci.nsIURL)) {
aUrl = nsIURL(aUrl);
}
return NetworkHelper.convertToUnicode(unescape(aUrl.hostPort));
},
/**
* Helper for getting an abbreviated string for a mime type.
*
* @param string aMimeType
* @return string
*/
_getAbbreviatedMimeType: function(aMimeType) {
if (!aMimeType) {
return "";
}
return (aMimeType.split(";")[0].split("/")[1] || "").split("+")[0];
},
/**
* Gets the available waterfall width in this container.
* @return number
@ -1296,8 +1444,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
* The request's url.
*/
_setRequestGetParams: function(aUrl) {
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
let query = uri.query;
let query = nsIURL(aUrl).query;
if (query) {
this._addParams(this._paramsQueryString, query);
}
@ -1379,7 +1526,6 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
if (!aResponse) {
return;
}
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
let { mimeType, text, encoding } = aResponse.content;
gNetwork.getString(text).then((aString) => {
@ -1408,7 +1554,7 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
// Immediately display additional information about the image:
// file name, mime type and encoding.
$("#response-content-image-name-value").setAttribute("value", uri.fileName);
$("#response-content-image-name-value").setAttribute("value", nsIURL(aUrl).fileName);
$("#response-content-image-mime-value").setAttribute("value", mimeType);
$("#response-content-image-encoding-value").setAttribute("value", encoding);
@ -1530,15 +1676,27 @@ create({ constructor: NetworkDetailsView, proto: MenuContainer.prototype }, {
*/
function $(aSelector, aTarget = document) aTarget.querySelector(aSelector);
/**
* Helper for getting an nsIURL instance out of a string.
*/
function nsIURL(aUrl, aStore = nsIURL.store) {
if (aStore.has(aUrl)) {
return aStore.get(aUrl);
}
let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
aStore.set(aUrl, uri);
return uri;
}
nsIURL.store = new Map();
/**
* Helper for draining a rapid succession of events and invoking a callback
* once everything settles down.
*/
function drain(aId, aWait, aCallback) {
window.clearTimeout(drain.store.get(aId));
drain.store.set(aId, window.setTimeout(aCallback, aWait));
function drain(aId, aWait, aCallback, aStore = drain.store) {
window.clearTimeout(aStore.get(aId));
aStore.set(aId, window.setTimeout(aCallback, aWait));
}
drain.store = new Map();
/**

View File

@ -20,12 +20,23 @@
#toolbar-spacer,
#details-pane-toggle,
#details-pane[pane-collapsed],
.requests-menu-waterfall {
.requests-menu-waterfall,
.requests-menu-footer-label {
display: none;
}
}
@media (min-width: 701px) and (max-width: 1024px) {
#body:not([pane-collapsed]) #requests-menu-footer {
display: none;
}
}
@media (min-width: 701px) {
#requests-menu-spacer-start {
display: none;
}
#network-table[waterfall-overflows] .requests-menu-waterfall {
display: none;
}

View File

@ -17,7 +17,9 @@
<script type="text/javascript" src="netmonitor-controller.js"/>
<script type="text/javascript" src="netmonitor-view.js"/>
<box id="body" flex="1" class="devtools-responsive-container">
<box id="body"
class="devtools-responsive-container"
flex="1">
<vbox id="network-table" flex="1">
<toolbar id="requests-menu-toolbar"
class="devtools-toolbar"
@ -28,14 +30,14 @@
align="center">
<button id="requests-menu-status-button"
class="requests-menu-header-button requests-menu-status"
onclick="NetMonitorView.RequestsMenu.sortBy('status')">
&netmonitorUI.toolbar.status;
onclick="NetMonitorView.RequestsMenu.sortBy('status')"
label="&netmonitorUI.toolbar.status;">
</button>
<button id="requests-menu-method-button"
class="requests-menu-header-button requests-menu-method"
onclick="NetMonitorView.RequestsMenu.sortBy('method')"
label="&netmonitorUI.toolbar.method;"
flex="1">
&netmonitorUI.toolbar.method;
</button>
</hbox>
<hbox id="requests-menu-file-header-box"
@ -44,8 +46,8 @@
<button id="requests-menu-file-button"
class="requests-menu-header-button requests-menu-file"
onclick="NetMonitorView.RequestsMenu.sortBy('file')"
label="&netmonitorUI.toolbar.file;"
flex="1">
&netmonitorUI.toolbar.file;
</button>
</hbox>
<hbox id="requests-menu-domain-header-box"
@ -54,8 +56,8 @@
<button id="requests-menu-domain-button"
class="requests-menu-header-button requests-menu-domain"
onclick="NetMonitorView.RequestsMenu.sortBy('domain')"
label="&netmonitorUI.toolbar.domain;"
flex="1">
&netmonitorUI.toolbar.domain;
</button>
</hbox>
<hbox id="requests-menu-type-header-box"
@ -64,8 +66,8 @@
<button id="requests-menu-type-button"
class="requests-menu-header-button requests-menu-type"
onclick="NetMonitorView.RequestsMenu.sortBy('type')"
label="&netmonitorUI.toolbar.type;"
flex="1">
&netmonitorUI.toolbar.type;
</button>
</hbox>
<hbox id="requests-menu-size-header-box"
@ -74,8 +76,8 @@
<button id="requests-menu-size-button"
class="requests-menu-header-button requests-menu-size"
onclick="NetMonitorView.RequestsMenu.sortBy('size')"
label="&netmonitorUI.toolbar.size;"
flex="1">
&netmonitorUI.toolbar.size;
</button>
</hbox>
<hbox id="requests-menu-waterfall-header-box"
@ -93,7 +95,7 @@
disabled="true"
tabindex="0"/>
</toolbar>
<label class="plain requests-menu-empty-notice"
<label id="requests-menu-empty-notice"
value="&netmonitorUI.emptyNotice;"/>
<vbox id="requests-menu-contents" flex="1">
<hbox id="requests-menu-item-template" hidden="true">
@ -124,11 +126,66 @@
</hbox>
</hbox>
</vbox>
<hbox id="requests-menu-footer">
<spacer id="requests-menu-spacer-start"
class="requests-menu-footer-spacer"
flex="100"/>
<button id="requests-menu-filter-all-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn(null)"
label="&netmonitorUI.footer.filterAll;">
</button>
<button id="requests-menu-filter-html-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('html')"
label="&netmonitorUI.footer.filterHTML;">
</button>
<button id="requests-menu-filter-css-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('css')"
label="&netmonitorUI.footer.filterCSS;">
</button>
<button id="requests-menu-filter-js-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('js')"
label="&netmonitorUI.footer.filterJS;">
</button>
<button id="requests-menu-filter-xhr-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('xhr')"
label="&netmonitorUI.footer.filterXHR;">
</button>
<button id="requests-menu-filter-fonts-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('fonts')"
label="&netmonitorUI.footer.filterFonts;">
</button>
<button id="requests-menu-filter-images-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('images')"
label="&netmonitorUI.footer.filterImages;">
</button>
<button id="requests-menu-filter-media-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('media')"
label="&netmonitorUI.footer.filterMedia;">
</button>
<button id="requests-menu-filter-flash-button"
class="requests-menu-footer-button"
onclick="NetMonitorView.RequestsMenu.filterOn('flash')"
label="&netmonitorUI.footer.filterFlash;">
</button>
<spacer id="requests-menu-spacer-end"
class="requests-menu-footer-spacer"
flex="100"/>
</hbox>
</vbox>
<splitter class="devtools-side-splitter"/>
<tabbox id="details-pane" class="devtools-sidebar-tabs" hidden="true">
<tabbox id="details-pane"
class="devtools-sidebar-tabs"
hidden="true">
<tabs>
<tab label="&netmonitorUI.tab.headers;"/>
<tab label="&netmonitorUI.tab.cookies;"/>
@ -166,7 +223,7 @@
<label class="plain tabpanel-summary-label"
value="&netmonitorUI.summary.status;"/>
<box id="headers-summary-status-circle"
class="requests-menu-status"/>
class="requests-menu-status"/>
<label id="headers-summary-status-value"
class="plain tabpanel-summary-value"
crop="end"

View File

@ -33,6 +33,9 @@ MOCHITEST_BROWSER_TESTS = \
browser_net_sort-01.js \
browser_net_sort-02.js \
browser_net_sort-03.js \
browser_net_filter-01.js \
browser_net_filter-02.js \
browser_net_filter-03.js \
head.js \
$(NULL)
@ -47,6 +50,7 @@ MOCHITEST_BROWSER_PAGES = \
html_jsonp-test-page.html \
html_json-long-test-page.html \
html_sorting-test-page.html \
html_filter-test-page.html \
html_infinite-get-page.html \
sjs_simple-test-server.sjs \
sjs_content-type-test-server.sjs \

View File

@ -0,0 +1,184 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test if filtering items in the network table works correctly.
*/
function test() {
initNetMonitor(FILTERING_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { $, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 8).then(() => {
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
testButtons();
testContents([1, 1, 1, 1, 1, 1, 1, 1])
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
testButtons("html");
return testContents([1, 0, 0, 0, 0, 0, 0, 0]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button"));
testButtons("css");
return testContents([0, 1, 0, 0, 0, 0, 0, 0]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-js-button"));
testButtons("js");
return testContents([0, 0, 1, 0, 0, 0, 0, 0]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-xhr-button"));
testButtons("xhr");
return testContents([1, 1, 1, 1, 1, 1, 1, 1]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-fonts-button"));
testButtons("fonts");
return testContents([0, 0, 0, 1, 0, 0, 0, 0]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-images-button"));
testButtons("images");
return testContents([0, 0, 0, 0, 1, 0, 0, 0]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-media-button"));
testButtons("media");
return testContents([0, 0, 0, 0, 0, 1, 1, 0]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-flash-button"));
testButtons("flash");
return testContents([0, 0, 0, 0, 0, 0, 0, 1]);
})
.then(() => {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-all-button"));
testButtons();
return testContents([1, 1, 1, 1, 1, 1, 1, 1]);
})
.then(() => {
return teardown(aMonitor);
})
.then(finish);
});
function testButtons(aFilterType) {
let doc = aMonitor.panelWin.document;
let target = doc.querySelector("#requests-menu-filter-" + aFilterType + "-button");
let buttons = doc.querySelectorAll(".requests-menu-footer-button");
for (let button of buttons) {
if (button != target) {
is(button.hasAttribute("checked"), false,
"The " + button.id + " button should not have a 'checked' attribute.");
} else {
is(button.hasAttribute("checked"), true,
"The " + button.id + " button should have a 'checked' attribute.");
}
}
}
function testContents(aVisibility) {
isnot(RequestsMenu.selectedItem, null,
"There should still be a selected item after filtering.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be still selected after filtering.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after filtering.");
is(RequestsMenu.allItems.length, aVisibility.length,
"There should be a specific amount of items in the requests menu.");
is(RequestsMenu.visibleItems.length, aVisibility.filter(e => e).length,
"There should be a specific amount of visbile items in the requests menu.");
for (let i = 0; i < aVisibility.length; i++) {
is(RequestsMenu.getItemAtIndex(i).target.hidden, !aVisibility[i],
"The item at index " + i + " doesn't have the correct hidden state.");
}
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "html",
fullMimeType: "text/html; charset=utf-8"
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "css",
fullMimeType: "text/css; charset=utf-8"
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2),
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "js",
fullMimeType: "application/javascript; charset=utf-8"
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3),
"GET", CONTENT_TYPE_SJS + "?fmt=font", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "woff",
fullMimeType: "font/woff"
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
"GET", CONTENT_TYPE_SJS + "?fmt=image", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "png",
fullMimeType: "image/png"
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5),
"GET", CONTENT_TYPE_SJS + "?fmt=audio", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "ogg",
fullMimeType: "audio/ogg"
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6),
"GET", CONTENT_TYPE_SJS + "?fmt=video", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "webm",
fullMimeType: "video/webm"
});
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(7),
"GET", CONTENT_TYPE_SJS + "?fmt=flash", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "x-shockwave-flash",
fullMimeType: "application/x-shockwave-flash"
});
return Promise.resolve(null);
}
aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
});
}

View File

@ -0,0 +1,181 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test if filtering items in the network table works correctly with new requests.
*/
function test() {
initNetMonitor(FILTERING_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { $, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 8).then(() => {
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
testButtons();
testContents([1, 1, 1, 1, 1, 1, 1, 1])
.then(() => {
info("Testing html filtering.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
testButtons("html");
return testContents([1, 0, 0, 0, 0, 0, 0, 0]);
})
.then(() => {
info("Performing more requests.");
aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
return waitForNetworkEvents(aMonitor, 8);
})
.then(() => {
info("Testing html filtering again.");
testButtons("html");
return testContents([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]);
})
.then(() => {
info("Performing more requests.");
aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
return waitForNetworkEvents(aMonitor, 8);
})
.then(() => {
info("Testing html filtering again.");
testButtons("html");
return testContents([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]);
})
.then(() => {
return teardown(aMonitor);
})
.then(finish);
});
function testButtons(aFilterType) {
let doc = aMonitor.panelWin.document;
let target = doc.querySelector("#requests-menu-filter-" + aFilterType + "-button");
let buttons = doc.querySelectorAll(".requests-menu-footer-button");
for (let button of buttons) {
if (button != target) {
is(button.hasAttribute("checked"), false,
"The " + button.id + " button should not have a 'checked' attribute.");
} else {
is(button.hasAttribute("checked"), true,
"The " + button.id + " button should have a 'checked' attribute.");
}
}
}
function testContents(aVisibility) {
isnot(RequestsMenu.selectedItem, null,
"There should still be a selected item after filtering.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be still selected after filtering.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after filtering.");
is(RequestsMenu.allItems.length, aVisibility.length,
"There should be a specific amount of items in the requests menu.");
is(RequestsMenu.visibleItems.length, aVisibility.filter(e => e).length,
"There should be a specific amount of visbile items in the requests menu.");
for (let i = 0; i < aVisibility.length; i++) {
is(RequestsMenu.getItemAtIndex(i).target.hidden, !aVisibility[i],
"The item at index " + i + " doesn't have the correct hidden state.");
}
for (let i = 0; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "html",
fullMimeType: "text/html; charset=utf-8"
});
}
for (let i = 1; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "css",
fullMimeType: "text/css; charset=utf-8"
});
}
for (let i = 2; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "js",
fullMimeType: "application/javascript; charset=utf-8"
});
}
for (let i = 3; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=font", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "woff",
fullMimeType: "font/woff"
});
}
for (let i = 4; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=image", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "png",
fullMimeType: "image/png"
});
}
for (let i = 5; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=audio", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "ogg",
fullMimeType: "audio/ogg"
});
}
for (let i = 6; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=video", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "webm",
fullMimeType: "video/webm"
});
}
for (let i = 7; i < aVisibility.length; i += 8) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
"GET", CONTENT_TYPE_SJS + "?fmt=flash", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "x-shockwave-flash",
fullMimeType: "application/x-shockwave-flash"
});
}
return Promise.resolve(null);
}
aDebuggee.performRequests('{ "getMedia": true, "getFlash": true }');
});
}

View File

@ -0,0 +1,187 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Test if filtering items in the network table works correctly with new requests
* and while sorting is enabled.
*/
function test() {
initNetMonitor(FILTERING_URL).then(([aTab, aDebuggee, aMonitor]) => {
info("Starting test... ");
let { $, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
waitForNetworkEvents(aMonitor, 7).then(() => {
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
isnot(RequestsMenu.selectedItem, null,
"There should be a selected item in the requests menu.");
is(RequestsMenu.selectedIndex, 0,
"The first item should be selected in the requests menu.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should not be hidden after toggle button was pressed.");
testButtons();
testContents([0, 1, 2, 3, 4, 5, 6], 7, 0)
.then(() => {
info("Sorting by size, ascending.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
testButtons();
return testContents([6, 4, 5, 0, 1, 2, 3], 7, 6);
})
.then(() => {
info("Testing html filtering.");
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
testButtons("html");
return testContents([6, 4, 5, 0, 1, 2, 3], 1, 6);
})
.then(() => {
info("Performing more requests.");
aDebuggee.performRequests('{ "getMedia": true }');
return waitForNetworkEvents(aMonitor, 7);
})
.then(() => {
info("Testing html filtering again.");
resetSorting();
testButtons("html");
return testContents([8, 13, 9, 11, 10, 12, 0, 4, 1, 5, 2, 6, 3, 7], 2, 13);
})
.then(() => {
info("Performing more requests.");
aDebuggee.performRequests('{ "getMedia": true }');
return waitForNetworkEvents(aMonitor, 7);
})
.then(() => {
info("Testing html filtering again.");
resetSorting();
testButtons("html");
return testContents([12, 13, 20, 14, 16, 18, 15, 17, 19, 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11], 3, 20);
})
.then(() => {
return teardown(aMonitor);
})
.then(finish);
});
function resetSorting() {
for (let i = 0; i < 3; i++) {
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-size-button"));
}
}
function testButtons(aFilterType) {
let doc = aMonitor.panelWin.document;
let target = doc.querySelector("#requests-menu-filter-" + aFilterType + "-button");
let buttons = doc.querySelectorAll(".requests-menu-footer-button");
for (let button of buttons) {
if (button != target) {
is(button.hasAttribute("checked"), false,
"The " + button.id + " button should not have a 'checked' attribute.");
} else {
is(button.hasAttribute("checked"), true,
"The " + button.id + " button should have a 'checked' attribute.");
}
}
}
function testContents(aOrder, aVisible, aSelection) {
isnot(RequestsMenu.selectedItem, null,
"There should still be a selected item after filtering.");
is(RequestsMenu.selectedIndex, aSelection,
"The first item should be still selected after filtering.");
is(NetMonitorView.detailsPaneHidden, false,
"The details pane should still be visible after filtering.");
is(RequestsMenu.allItems.length, aOrder.length,
"There should be a specific amount of items in the requests menu.");
is(RequestsMenu.visibleItems.length, aVisible,
"There should be a specific amount of visbile items in the requests menu.");
for (let i = 0; i < aOrder.length; i++) {
is(RequestsMenu.getItemAtIndex(i), RequestsMenu.allItems[i],
"The requests menu items aren't ordered correctly. Misplaced item " + i + ".");
}
for (let i = 0, len = aOrder.length / 7; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i]),
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "html",
fullMimeType: "text/html; charset=utf-8"
});
}
for (let i = 0, len = aOrder.length / 7; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len]),
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "css",
fullMimeType: "text/css; charset=utf-8"
});
}
for (let i = 0, len = aOrder.length / 7; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 2]),
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "js",
fullMimeType: "application/javascript; charset=utf-8"
});
}
for (let i = 0, len = aOrder.length / 7; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 3]),
"GET", CONTENT_TYPE_SJS + "?fmt=font", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "woff",
fullMimeType: "font/woff"
});
}
for (let i = 0, len = aOrder.length / 7; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 4]),
"GET", CONTENT_TYPE_SJS + "?fmt=image", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "png",
fullMimeType: "image/png"
});
}
for (let i = 0, len = aOrder.length / 7; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 5]),
"GET", CONTENT_TYPE_SJS + "?fmt=audio", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "ogg",
fullMimeType: "audio/ogg"
});
}
for (let i = 0, len = aOrder.length / 7; i < len; i++) {
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(aOrder[i + len * 6]),
"GET", CONTENT_TYPE_SJS + "?fmt=video", {
fuzzyUrl: true,
status: 200,
statusText: "OK",
type: "webm",
fullMimeType: "video/webm"
});
}
return Promise.resolve(null);
}
let str = "'<p>'" + new Array(10).join(Math.random(10)) + "'</p>'";
aDebuggee.performRequests('{ "htmlContent": "' + str + '", "getMedia": true }');
});
}

View File

@ -17,7 +17,7 @@ function test() {
is(document.querySelector("#details-pane-toggle")
.hasAttribute("disabled"), true,
"The pane toggle button should be disabled when the frontend is opened.");
is(document.querySelector(".requests-menu-empty-notice")
is(document.querySelector("#requests-menu-empty-notice")
.hasAttribute("hidden"), false,
"An empty notice should be displayed when the frontend is opened.");
is(RequestsMenu.itemCount, 0,
@ -29,7 +29,7 @@ function test() {
is(document.querySelector("#details-pane-toggle")
.hasAttribute("disabled"), false,
"The pane toggle button should be enabled after the first request.");
is(document.querySelector(".requests-menu-empty-notice")
is(document.querySelector("#requests-menu-empty-notice")
.hasAttribute("hidden"), true,
"The empty notice should be hidden after the first request.");
is(RequestsMenu.itemCount, 1,
@ -41,7 +41,7 @@ function test() {
is(document.querySelector("#details-pane-toggle")
.hasAttribute("disabled"), false,
"The pane toggle button should be still be enabled after a reload.");
is(document.querySelector(".requests-menu-empty-notice")
is(document.querySelector("#requests-menu-empty-notice")
.hasAttribute("hidden"), true,
"The empty notice should be still hidden after a reload.");
is(RequestsMenu.itemCount, 1,

View File

@ -142,6 +142,7 @@ function test() {
return testContents([3, 4, 0, 1, 2]);
})
.then(() => {
info("Clearing sort.");
RequestsMenu.sortBy(null);
return testContents([0, 1, 2, 3, 4]);
})
@ -152,8 +153,6 @@ function test() {
});
function testContents([a, b, c, d, e]) {
let deferred = Promise.defer();
is(RequestsMenu.allItems.length, 5,
"There should be a total of 5 items in the requests menu.");
is(RequestsMenu.visibleItems.length, 5,
@ -216,8 +215,7 @@ function test() {
time: true
});
executeSoon(deferred.resolve);
return deferred.promise;
return Promise.resolve(null);
}
aDebuggee.performRequests();

View File

@ -149,8 +149,6 @@ function test() {
}
function testContents([a, b, c, d, e]) {
let deferred = Promise.defer();
isnot(RequestsMenu.selectedItem, null,
"There should still be a selected item after sorting.");
is(RequestsMenu.selectedIndex, a,
@ -225,8 +223,7 @@ function test() {
time: true
});
executeSoon(deferred.resolve);
return deferred.promise;
return Promise.resolve(null);
}
aDebuggee.performRequests();

View File

@ -91,8 +91,6 @@ function test() {
}
function testContents(aOrder, aSelection) {
let deferred = Promise.defer();
isnot(RequestsMenu.selectedItem, null,
"There should still be a selected item after sorting.");
is(RequestsMenu.selectedIndex, aSelection,
@ -171,8 +169,7 @@ function test() {
});
}
executeSoon(deferred.resolve);
return deferred.promise;
return Promise.resolve(null);
}
aDebuggee.performRequests();

View File

@ -14,8 +14,7 @@ function test() {
RequestsMenu.lazyUpdate = false;
is(document.querySelector(".requests-menu-empty-notice")
.hasAttribute("hidden"), false,
ok(document.querySelector("#requests-menu-waterfall-label"),
"An timeline label should be displayed when the frontend is opened.");
ok(document.querySelectorAll(".requests-menu-timings-division").length == 0,
"No tick labels should be displayed when the frontend is opened.");
@ -26,8 +25,7 @@ function test() {
"No 2d context should be created when the frontend is opened.");
waitForNetworkEvents(aMonitor, 1).then(() => {
is(document.querySelector(".requests-menu-empty-notice")
.hasAttribute("hidden"), true,
ok(!document.querySelector("#requests-menu-waterfall-label"),
"The timeline label should be hidden after the first request.");
ok(document.querySelectorAll(".requests-menu-timings-division").length >= 3,
"There should be at least 3 tick labels in the network requests header.");

View File

@ -22,6 +22,7 @@ const POST_DATA_URL = EXAMPLE_URL + "html_post-data-test-page.html";
const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html";
const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";
const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";

View File

@ -0,0 +1,55 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>Network Monitor test page</title>
</head>
<body>
<p>Filtering test</p>
<script type="text/javascript">
function get(aAddress, aCallback) {
var xhr = new XMLHttpRequest();
// Use a random parameter to defeat caching.
xhr.open("GET", aAddress + "&" + Math.random(), true);
xhr.onreadystatechange = function() {
if (this.readyState == this.DONE) {
aCallback();
}
};
xhr.send(null);
}
function performRequests(aOptions) {
var options = JSON.parse(aOptions);
get("sjs_content-type-test-server.sjs?fmt=html&res=" + options.htmlContent, function() {
get("sjs_content-type-test-server.sjs?fmt=css", function() {
get("sjs_content-type-test-server.sjs?fmt=js", function() {
if (!options.getMedia) {
return;
}
get("sjs_content-type-test-server.sjs?fmt=font", function() {
get("sjs_content-type-test-server.sjs?fmt=image", function() {
get("sjs_content-type-test-server.sjs?fmt=audio", function() {
get("sjs_content-type-test-server.sjs?fmt=video", function() {
if (!options.getFlash) {
return;
}
get("sjs_content-type-test-server.sjs?fmt=flash", function() {
// Done.
});
});
});
});
});
});
});
});
}
</script>
</body>
</html>

View File

@ -25,6 +25,14 @@ function handleRequest(request, response) {
response.finish();
break;
}
case "html": {
let content = params.filter((s) => s.contains("res="))[0].split("=")[1];
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
response.write(content || "<p>Hello HTML!</p>");
response.finish();
break;
}
case "css": {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "text/css; charset=utf-8", false);
@ -62,6 +70,36 @@ function handleRequest(request, response) {
response.finish();
break;
}
case "font": {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "font/woff", false);
response.finish();
break;
}
case "image": {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "image/png", false);
response.finish();
break;
}
case "audio": {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "audio/ogg", false);
response.finish();
break;
}
case "video": {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "video/webm", false);
response.finish();
break;
}
case "flash": {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "application/x-shockwave-flash", false);
response.finish();
break;
}
default: {
response.setStatusLine(request.httpVersion, 404, "Not Found");
response.setHeader("Content-Type", "text/html; charset=utf-8", false);

View File

@ -607,7 +607,7 @@ MenuContainer.prototype = {
// Sort the items before adding them to this container, if preferred.
if (aOptions.sorted) {
stagedItems.sort((a, b) => this._sortPredicate(a.item, b.item));
stagedItems.sort((a, b) => this._currentSortPredicate(a.item, b.item));
}
// Append the prepared items to this container.
for (let { item, options } of stagedItems) {
@ -695,12 +695,32 @@ MenuContainer.prototype = {
/**
* Toggles all the items in this container hidden or visible.
*
* This does not change the default filtering predicate, so newly inserted
* items will always be visible. Use MenuContainer.prototype.filterContents
* if you care.
*
* @param boolean aVisibleFlag
* Specifies the intended visibility.
*/
toggleContents: function(aVisibleFlag) {
for (let [, item] of this._itemsByElement) {
item._target.hidden = !aVisibleFlag;
for (let [element, item] of this._itemsByElement) {
element.hidden = !aVisibleFlag;
}
},
/**
* Toggles all items in this container hidden or visible based on a predicate.
*
* @param function aPredicate [optional]
* Items are toggled according to the return value of this function,
* which will become the new default filtering predicate in this container.
* If unspecified, all items will be toggled visible.
*/
filterContents: function(aPredicate = this._currentFilterPredicate) {
this._currentFilterPredicate = aPredicate;
for (let [element, item] of this._itemsByElement) {
element.hidden = !aPredicate(item);
}
},
@ -712,8 +732,8 @@ MenuContainer.prototype = {
* will become the new default sorting predicate in this container.
* If unspecified, all items will be sorted by their label.
*/
sortContents: function(aPredicate = this._sortPredicate) {
let sortedItems = this.allItems.sort(this._sortPredicate = aPredicate);
sortContents: function(aPredicate = this._currentSortPredicate) {
let sortedItems = this.allItems.sort(this._currentSortPredicate = aPredicate);
for (let i = 0, len = sortedItems.length; i < len; i++) {
this.swapItems(this.getItemAtIndex(i), sortedItems[i]);
@ -1123,7 +1143,7 @@ MenuContainer.prototype = {
let itemCount = this.itemCount;
for (let i = 0; i < itemCount; i++) {
if (this._sortPredicate(this.getItemAtIndex(i), aItem) > 0) {
if (this._currentSortPredicate(this.getItemAtIndex(i), aItem) > 0) {
return i;
}
}
@ -1160,6 +1180,9 @@ MenuContainer.prototype = {
aItem.attachment));
// Handle any additional options after entangling the item.
if (!this._currentFilterPredicate(aItem)) {
aItem._target.hidden = true;
}
if (aOptions.attributes) {
aItem.setAttributes(aOptions.attributes, aItem._target);
}
@ -1217,6 +1240,19 @@ MenuContainer.prototype = {
this._itemsByElement.delete(aItem._target);
},
/**
* The predicate used when filtering items. By default, all items in this
* view are visible.
*
* @param MenuItem aItem
* The filtered menu item.
* @return boolean
* True if the menu item should be visible, false otherwise.
*/
_currentFilterPredicate: function(aItem) {
return true;
},
/**
* The predicate used when sorting items. By default, items in this view
* are sorted by their label.
@ -1230,7 +1266,7 @@ MenuContainer.prototype = {
* 0 to leave aFirst and aSecond unchanged with respect to each other
* 1 to sort aSecond to a lower index than aFirst
*/
_sortPredicate: function(aFirst, aSecond) {
_currentSortPredicate: function(aFirst, aSecond) {
return +(aFirst._label.toLowerCase() > aSecond._label.toLowerCase());
},

View File

@ -63,6 +63,42 @@
- in the network details pane identifying the timings tab. -->
<!ENTITY netmonitorUI.tab.timings "Timings">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterAll): This is the label displayed
- in the network details footer for the "All" filtering button. -->
<!ENTITY netmonitorUI.footer.filterAll "All">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterHTML): This is the label displayed
- in the network details footer for the "HTML" filtering button. -->
<!ENTITY netmonitorUI.footer.filterHTML "HTML">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterCSS): This is the label displayed
- in the network details footer for the "CSS" filtering button. -->
<!ENTITY netmonitorUI.footer.filterCSS "CSS">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterJS): This is the label displayed
- in the network details footer for the "JS" filtering button. -->
<!ENTITY netmonitorUI.footer.filterJS "JS">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterXHR): This is the label displayed
- in the network details footer for the "XHR" filtering button. -->
<!ENTITY netmonitorUI.footer.filterXHR "XHR">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterFonts): This is the label displayed
- in the network details footer for the "Fonts" filtering button. -->
<!ENTITY netmonitorUI.footer.filterFonts "Fonts">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterImages): This is the label displayed
- in the network details footer for the "Images" filtering button. -->
<!ENTITY netmonitorUI.footer.filterImages "Images">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterMedia): This is the label displayed
- in the network details footer for the "Media" filtering button. -->
<!ENTITY netmonitorUI.footer.filterMedia "Media">
<!-- LOCALIZATION NOTE (debuggerUI.footer.filterFlash): This is the label displayed
- in the network details footer for the "Flash" filtering button. -->
<!ENTITY netmonitorUI.footer.filterFlash "Flash">
<!-- LOCALIZATION NOTE (debuggerUI.panesButton.tooltip): This is the tooltip for
- the button that toggles the panes visible or hidden in the netmonitor UI. -->
<!ENTITY netmonitorUI.panesButton.tooltip "Toggle network info">

View File

@ -3,13 +3,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Network requests table */
#body {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
}
.requests-menu-empty-notice {
/* Network requests table */
#requests-menu-empty-notice {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
padding: 12px;
font-size: 110%;
@ -17,19 +17,14 @@
}
#requests-menu-toolbar {
padding: 0;
-moz-padding-start: 4px;
height: 32px;
padding: 0;
}
.requests-menu-header:first-child {
-moz-padding-start: 4px;
}
.requests-menu-header {
text-align: center;
}
.requests-menu-subitem {
padding: 4px;
}
@ -43,12 +38,13 @@
.requests-menu-header-button {
-moz-appearance: none;
background: none;
min-width: 12px;
min-height: 31px; /* Remaining 1px comes from border of the button. */
min-width: 20px;
min-height: 31px; /* Remaining 1px comes from border of the toolbar. */
margin: 0;
border: none;
padding: 2px 0px;
padding: 0;
color: inherit;
font-weight: inherit !important;
transition: background-color 0.1s ease-in-out;
}
@ -80,13 +76,12 @@
/* Network requests table: specific column dimensions */
.requests-menu-status-and-method {
width: 6em;
width: 7em;
}
.requests-menu-status {
width: 12px;
height: 12px;
margin: 0px 2px;
width: 10px;
height: 10px;
}
.requests-menu-method {
@ -118,6 +113,8 @@
box.requests-menu-status {
background: #fff;
-moz-margin-start: 5px;
-moz-margin-end: 5px;
border-radius: 20px;
box-shadow:
0 0 0 1px rgba(255,255,255,0.4) inset,
@ -269,12 +266,14 @@ box.requests-menu-status[code^="5"] {
0 0 4px 0 rgba(255,255,255,1.0) inset;
}
/* SideMenuWidget */
.side-menu-widget-item:nth-child(even) {
background: rgba(255,255,255,0.05);
}
.side-menu-widget-item-contents {
padding: 0px 4px;
padding: 0;
}
/* Network request details */
@ -364,6 +363,51 @@ box.requests-menu-status[code^="5"] {
transition: transform 0.2s ease-out;
}
/* Footer */
#requests-menu-footer {
box-shadow: inset 0 1px 16px hsla(210,8%,5%,.3);
}
.requests-menu-footer-button,
.requests-menu-footer-label {
min-width: 1em;
margin: 0;
border: none;
padding: 2px 1.5vw;
color: #fff;
}
.requests-menu-footer-spacer,
.requests-menu-footer-button {
-moz-border-end: 1px solid hsla(210,8%,5%,.25);
box-shadow: 1px 0 0 hsla(210,16%,76%,.1);
}
.requests-menu-footer-button {
-moz-appearance: none;
background: rgba(0,0,0,0.025);
}
.requests-menu-footer-button:hover {
background: rgba(0,0,0,0.20);
}
.requests-menu-footer-button:hover:active {
background: rgba(0,0,0,0.35);
}
.requests-menu-footer-button:not(:active)[checked] {
background-color: rgba(0,0,0,0.25);
background-image: radial-gradient(farthest-side at center top, hsla(200,100%,70%,.7), hsla(200,100%,70%,0.3));
background-size: 100% 1px;
background-repeat: no-repeat;
}
.requests-menu-footer-label {
font-weight: 600;
}
/* Responsive sidebar */
@media (max-width: 700px) {
#requests-menu-toolbar {
@ -371,7 +415,13 @@ box.requests-menu-status[code^="5"] {
}
.requests-menu-header-button {
min-height: 25px; /* Remaining 1px comes from border of the button. */
min-height: 25px; /* Remaining 1px comes from border of the toolbar. */
font-size: 85%;
}
.requests-menu-footer-button,
.requests-menu-footer-label {
padding: 2px 2vw;
}
#details-pane {
@ -381,13 +431,12 @@ box.requests-menu-status[code^="5"] {
}
.requests-menu-status-and-method {
width: 14vw;
width: 16vw;
}
.requests-menu-file,
.requests-menu-domain {
width: 30vw;
min-width: 10em;
}
.requests-menu-type {

View File

@ -3,13 +3,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Network requests table */
#body {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
}
.requests-menu-empty-notice {
/* Network requests table */
#requests-menu-empty-notice {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
padding: 12px;
font-size: 110%;
@ -17,19 +17,14 @@
}
#requests-menu-toolbar {
padding: 0;
-moz-padding-start: 4px;
height: 32px;
padding: 0;
}
.requests-menu-header:first-child {
-moz-padding-start: 4px;
}
.requests-menu-header {
text-align: center;
}
.requests-menu-subitem {
padding: 4px;
}
@ -43,12 +38,13 @@
.requests-menu-header-button {
-moz-appearance: none;
background: none;
min-width: 1em;
min-height: 31px; /* Remaining 1px comes from border of the button. */
min-width: 20px;
min-height: 31px; /* Remaining 1px comes from border of the toolbar. */
margin: 0;
border: none;
padding: 2px 0px;
padding: 0;
color: inherit;
font-weight: inherit !important;
transition: background-color 0.1s ease-in-out;
}
@ -84,9 +80,8 @@
}
.requests-menu-status {
width: 1em;
height: 1em;
margin: 0px 2px;
width: 10px;
height: 10px;
}
.requests-menu-method {
@ -118,6 +113,8 @@
box.requests-menu-status {
background: #fff;
-moz-margin-start: 5px;
-moz-margin-end: 5px;
border-radius: 20px;
box-shadow:
0 0 0 1px rgba(255,255,255,0.4) inset,
@ -269,12 +266,14 @@ box.requests-menu-status[code^="5"] {
0 0 4px 0 rgba(255,255,255,1.0) inset;
}
/* SideMenuWidget */
.side-menu-widget-item:nth-child(even) {
background: rgba(255,255,255,0.05);
}
.side-menu-widget-item-contents {
padding: 0px 4px;
padding: 0;
}
/* Network request details */
@ -364,6 +363,51 @@ box.requests-menu-status[code^="5"] {
transition: transform 0.2s ease-out;
}
/* Footer */
#requests-menu-footer {
box-shadow: inset 0 1px 16px hsla(210,8%,5%,.3);
}
.requests-menu-footer-button,
.requests-menu-footer-label {
min-width: 1em;
margin: 0;
border: none;
padding: 2px 1.5vw;
color: #fff;
}
.requests-menu-footer-spacer,
.requests-menu-footer-button {
-moz-border-end: 1px solid hsla(210,8%,5%,.25);
box-shadow: 1px 0 0 hsla(210,16%,76%,.1);
}
.requests-menu-footer-button {
-moz-appearance: none;
background: rgba(0,0,0,0.025);
}
.requests-menu-footer-button:hover {
background: rgba(0,0,0,0.20);
}
.requests-menu-footer-button:hover:active {
background: rgba(0,0,0,0.35);
}
.requests-menu-footer-button:not(:active)[checked] {
background-color: rgba(0,0,0,0.25);
background-image: radial-gradient(farthest-side at center top, hsla(200,100%,70%,.7), hsla(200,100%,70%,0.3));
background-size: 100% 1px;
background-repeat: no-repeat;
}
.requests-menu-footer-label {
font-weight: 600;
}
/* Responsive sidebar */
@media (max-width: 700px) {
#requests-menu-toolbar {
@ -371,7 +415,12 @@ box.requests-menu-status[code^="5"] {
}
.requests-menu-header-button {
min-height: 23px; /* Remaining 1px comes from border of the button. */
min-height: 23px; /* Remaining 1px comes from border of the toolbar. */
}
.requests-menu-footer-button,
.requests-menu-footer-label {
padding: 2px 2vw;
}
#details-pane {
@ -381,13 +430,12 @@ box.requests-menu-status[code^="5"] {
}
.requests-menu-status-and-method {
width: 14vw;
width: 16vw;
}
.requests-menu-file,
.requests-menu-domain {
width: 30vw;
min-width: 10em;
}
.requests-menu-type {

View File

@ -3,13 +3,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Network requests table */
#body {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
}
.requests-menu-empty-notice {
/* Network requests table */
#requests-menu-empty-notice {
background: url(background-noise-toolbar.png), hsl(208,11%,27%);
padding: 12px;
font-size: 110%;
@ -17,19 +17,14 @@
}
#requests-menu-toolbar {
padding: 0;
-moz-padding-start: 4px;
height: 33px;
padding: 0;
}
.requests-menu-header:first-child {
-moz-padding-start: 4px;
}
.requests-menu-header {
text-align: center;
}
.requests-menu-subitem {
padding: 4px;
}
@ -43,12 +38,13 @@
.requests-menu-header-button {
-moz-appearance: none;
background: none;
min-width: 10px;
min-height: 32px; /* Remaining 1px comes from border of the button. */
min-width: 20px;
min-height: 32px; /* Remaining 1px comes from border of the toolbar. */
margin: 0;
border: none;
padding: 2px 0px;
padding: 0;
color: inherit;
font-weight: inherit !important;
transition: background-color 0.1s ease-in-out;
}
@ -86,7 +82,6 @@
.requests-menu-status {
width: 10px;
height: 10px;
margin: 0px 2px;
}
.requests-menu-method {
@ -118,6 +113,8 @@
box.requests-menu-status {
background: #fff;
-moz-margin-start: 5px;
-moz-margin-end: 5px;
border-radius: 20px;
box-shadow:
0 0 0 1px rgba(255,255,255,0.4) inset,
@ -269,12 +266,14 @@ box.requests-menu-status[code^="5"] {
0 0 4px 0 rgba(255,255,255,1.0) inset;
}
/* SideMenuWidget */
.side-menu-widget-item:nth-child(even) {
background: rgba(255,255,255,0.05);
}
.side-menu-widget-item-contents {
padding: 0px 4px;
padding: 0;
}
/* Network request details */
@ -364,6 +363,51 @@ box.requests-menu-status[code^="5"] {
transition: transform 0.2s ease-out;
}
/* Footer */
#requests-menu-footer {
box-shadow: inset 0 1px 16px hsla(210,8%,5%,.3);
}
.requests-menu-footer-button,
.requests-menu-footer-label {
min-width: 1em;
margin: 0;
border: none;
padding: 0px 1.5vw;
color: #fff;
}
.requests-menu-footer-spacer,
.requests-menu-footer-button {
-moz-border-end: 1px solid hsla(210,8%,5%,.25);
box-shadow: 1px 0 0 hsla(210,16%,76%,.1);
}
.requests-menu-footer-button {
-moz-appearance: none;
background: rgba(0,0,0,0.025);
}
.requests-menu-footer-button:hover {
background: rgba(0,0,0,0.20);
}
.requests-menu-footer-button:hover:active {
background: rgba(0,0,0,0.35);
}
.requests-menu-footer-button:not(:active)[checked] {
background-color: rgba(0,0,0,0.25);
background-image: radial-gradient(farthest-side at center top, hsla(200,100%,70%,.7), hsla(200,100%,70%,0.3));
background-size: 100% 1px;
background-repeat: no-repeat;
}
.requests-menu-footer-label {
font-weight: 600;
}
/* Responsive sidebar */
@media (max-width: 700px) {
#requests-menu-toolbar {
@ -371,7 +415,12 @@ box.requests-menu-status[code^="5"] {
}
.requests-menu-header-button {
min-height: 23px; /* Remaining 1px comes from border of the button. */
min-height: 23px; /* Remaining 1px comes from border of the toolbar. */
}
.requests-menu-footer-button,
.requests-menu-footer-label {
padding: 0px 2vw;
}
#details-pane {
@ -381,13 +430,12 @@ box.requests-menu-status[code^="5"] {
}
.requests-menu-status-and-method {
width: 14vw;
width: 16vw;
}
.requests-menu-file,
.requests-menu-domain {
width: 30vw;
min-width: 10em;
}
.requests-menu-type {

View File

@ -1065,6 +1065,7 @@ function NetworkEventActor(aNetworkEvent, aWebConsoleActor)
this.conn = this.parent.conn;
this._startedDateTime = aNetworkEvent.startedDateTime;
this._isXHR = aNetworkEvent.isXHR;
this._request = {
method: aNetworkEvent.method,
@ -1108,6 +1109,7 @@ NetworkEventActor.prototype =
startedDateTime: this._startedDateTime,
url: this._request.url,
method: this._request.method,
isXHR: this._isXHR
};
},

View File

@ -1936,6 +1936,15 @@ NetworkMonitor.prototype = {
event.method = aChannel.requestMethod;
event.url = aChannel.URI.spec;
// Determine if this is an XHR request.
try {
let callbacks = aChannel.notificationCallbacks;
let xhrRequest = callbacks ? callbacks.getInterface(Ci.nsIXMLHttpRequest) : null;
event.isXHR = !!xhrRequest;
} catch (e) {
event.isXHR = false;
}
// Determine the HTTP version.
aChannel.QueryInterface(Ci.nsIHttpChannelInternal);
aChannel.getRequestVersion(httpVersionMaj, httpVersionMin);