mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 731311 - Network monitor should allow to replay and edit requests; r=vporof
This commit is contained in:
parent
0c801e1d35
commit
e512549c46
@ -250,6 +250,7 @@ TargetEventsHandler.prototype = {
|
||||
case "will-navigate": {
|
||||
// Reset UI.
|
||||
NetMonitorView.RequestsMenu.reset();
|
||||
NetMonitorView.Sidebar.reset();
|
||||
NetMonitorView.NetworkDetails.reset();
|
||||
|
||||
// Reset global helpers cache.
|
||||
|
@ -165,7 +165,7 @@ let NetMonitorView = {
|
||||
}
|
||||
|
||||
if (aTabIndex !== undefined) {
|
||||
$("#details-pane").selectedIndex = aTabIndex;
|
||||
$("#event-details-pane").selectedIndex = aTabIndex;
|
||||
}
|
||||
},
|
||||
|
||||
@ -351,8 +351,63 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
|
||||
this.refreshSummary();
|
||||
this.refreshZebra();
|
||||
|
||||
if (aId == this._preferredItemId) {
|
||||
this.selectedItem = requestItem;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new custom request form populated with the data from
|
||||
* the currently selected request.
|
||||
*/
|
||||
cloneSelectedRequest: function() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
|
||||
// Create the element node for the network request item.
|
||||
let menuView = this._createMenuView(selected.method, selected.url);
|
||||
|
||||
let newItem = this.push([menuView], {
|
||||
attachment: Object.create(selected, {
|
||||
isCustom: { value: true }
|
||||
})
|
||||
});
|
||||
|
||||
// Immediately switch to new request pane.
|
||||
this.selectedItem = newItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a new HTTP request using the data in the custom request form.
|
||||
*/
|
||||
sendCustomRequest: function() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
|
||||
let data = Object.create(selected, {
|
||||
headers: { value: selected.requestHeaders.headers }
|
||||
});
|
||||
|
||||
if (selected.requestPostData) {
|
||||
data.body = selected.requestPostData.postData.text;
|
||||
}
|
||||
|
||||
NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
|
||||
let id = response.eventActor.actor;
|
||||
this._preferredItemId = id;
|
||||
});
|
||||
|
||||
this.closeCustomRequest();
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the currently selected custom request.
|
||||
*/
|
||||
closeCustomRequest: function() {
|
||||
this.remove(this.selectedItem);
|
||||
|
||||
NetMonitorView.Sidebar.toggle(false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Filters all network requests in this container by a specified type.
|
||||
*
|
||||
@ -690,11 +745,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
break;
|
||||
case "status":
|
||||
requestItem.attachment.status = value;
|
||||
this._updateMenuView(requestItem, key, value);
|
||||
this.updateMenuView(requestItem, key, value);
|
||||
break;
|
||||
case "statusText":
|
||||
requestItem.attachment.statusText = value;
|
||||
this._updateMenuView(requestItem, key,
|
||||
this.updateMenuView(requestItem, key,
|
||||
requestItem.attachment.status + " " +
|
||||
requestItem.attachment.statusText);
|
||||
break;
|
||||
@ -703,11 +758,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
break;
|
||||
case "contentSize":
|
||||
requestItem.attachment.contentSize = value;
|
||||
this._updateMenuView(requestItem, key, value);
|
||||
this.updateMenuView(requestItem, key, value);
|
||||
break;
|
||||
case "mimeType":
|
||||
requestItem.attachment.mimeType = value;
|
||||
this._updateMenuView(requestItem, key, value);
|
||||
this.updateMenuView(requestItem, key, value);
|
||||
break;
|
||||
case "responseContent":
|
||||
requestItem.attachment.responseContent = value;
|
||||
@ -715,7 +770,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
case "totalTime":
|
||||
requestItem.attachment.totalTime = value;
|
||||
requestItem.attachment.endedMillis = requestItem.attachment.startedMillis + value;
|
||||
this._updateMenuView(requestItem, key, value);
|
||||
this.updateMenuView(requestItem, key, value);
|
||||
this._registerLastRequestEnd(requestItem.attachment.endedMillis);
|
||||
break;
|
||||
case "eventTimings":
|
||||
@ -757,23 +812,11 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The network request view.
|
||||
*/
|
||||
_createMenuView: function(aMethod, aUrl) {
|
||||
let uri = nsIURL(aUrl);
|
||||
let nameWithQuery = this._getUriNameWithQuery(uri);
|
||||
let hostPort = this._getUriHostPort(uri);
|
||||
|
||||
let template = $("#requests-menu-item-template");
|
||||
let fragment = document.createDocumentFragment();
|
||||
|
||||
let method = $(".requests-menu-method", template);
|
||||
method.setAttribute("value", aMethod);
|
||||
|
||||
let file = $(".requests-menu-file", template);
|
||||
file.setAttribute("value", nameWithQuery);
|
||||
file.setAttribute("tooltiptext", nameWithQuery);
|
||||
|
||||
let domain = $(".requests-menu-domain", template);
|
||||
domain.setAttribute("value", hostPort);
|
||||
domain.setAttribute("tooltiptext", hostPort);
|
||||
this.updateMenuView(template, 'method', aMethod);
|
||||
this.updateMenuView(template, 'url', aUrl);
|
||||
|
||||
let waterfall = $(".requests-menu-waterfall", template);
|
||||
waterfall.style.backgroundImage = this._cachedWaterfallBackground;
|
||||
@ -796,22 +839,48 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* @param any aValue
|
||||
* The new value to be shown.
|
||||
*/
|
||||
_updateMenuView: function(aItem, aKey, aValue) {
|
||||
updateMenuView: function(aItem, aKey, aValue) {
|
||||
let target = aItem.target || aItem;
|
||||
|
||||
switch (aKey) {
|
||||
case "method": {
|
||||
let node = $(".requests-menu-method", target);
|
||||
node.setAttribute("value", aValue);
|
||||
break;
|
||||
}
|
||||
case "url": {
|
||||
let uri;
|
||||
try {
|
||||
uri = nsIURL(aValue);
|
||||
} catch(e) {
|
||||
break; // User input may not make a well-formed url yet.
|
||||
}
|
||||
let nameWithQuery = this._getUriNameWithQuery(uri);
|
||||
let hostPort = this._getUriHostPort(uri);
|
||||
|
||||
let node = $(".requests-menu-file", target);
|
||||
node.setAttribute("value", nameWithQuery);
|
||||
node.setAttribute("tooltiptext", nameWithQuery);
|
||||
|
||||
let domain = $(".requests-menu-domain", target);
|
||||
domain.setAttribute("value", hostPort);
|
||||
domain.setAttribute("tooltiptext", hostPort);
|
||||
break;
|
||||
}
|
||||
case "status": {
|
||||
let node = $(".requests-menu-status", aItem.target);
|
||||
let node = $(".requests-menu-status", target);
|
||||
node.setAttribute("code", aValue);
|
||||
break;
|
||||
}
|
||||
case "statusText": {
|
||||
let node = $(".requests-menu-status-and-method", aItem.target);
|
||||
let node = $(".requests-menu-status-and-method", target);
|
||||
node.setAttribute("tooltiptext", aValue);
|
||||
break;
|
||||
}
|
||||
case "contentSize": {
|
||||
let kb = aValue / 1024;
|
||||
let size = L10N.numberWithDecimals(kb, CONTENT_SIZE_DECIMALS);
|
||||
let node = $(".requests-menu-size", aItem.target);
|
||||
let node = $(".requests-menu-size", target);
|
||||
let text = L10N.getFormatStr("networkMenu.sizeKB", size);
|
||||
node.setAttribute("value", text);
|
||||
node.setAttribute("tooltiptext", text);
|
||||
@ -819,14 +888,14 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
}
|
||||
case "mimeType": {
|
||||
let type = this._getAbbreviatedMimeType(aValue);
|
||||
let node = $(".requests-menu-type", aItem.target);
|
||||
let node = $(".requests-menu-type", target);
|
||||
let text = CONTENT_MIME_TYPE_ABBREVIATIONS[type] || type;
|
||||
node.setAttribute("value", text);
|
||||
node.setAttribute("tooltiptext", aValue);
|
||||
break;
|
||||
}
|
||||
case "totalTime": {
|
||||
let node = $(".requests-menu-timings-total", aItem.target);
|
||||
let node = $(".requests-menu-timings-total", target);
|
||||
let text = L10N.getFormatStr("networkMenu.totalMS", aValue); // integer
|
||||
node.setAttribute("value", text);
|
||||
node.setAttribute("tooltiptext", text);
|
||||
@ -1089,10 +1158,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
*/
|
||||
_onSelect: function({ detail: item }) {
|
||||
if (item) {
|
||||
NetMonitorView.NetworkDetails.populate(item.attachment);
|
||||
NetMonitorView.NetworkDetails.toggle(true);
|
||||
NetMonitorView.Sidebar.populate(item.attachment);
|
||||
NetMonitorView.Sidebar.toggle(true);
|
||||
} else {
|
||||
NetMonitorView.NetworkDetails.toggle(false);
|
||||
NetMonitorView.Sidebar.toggle(false);
|
||||
}
|
||||
},
|
||||
|
||||
@ -1104,6 +1173,14 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
drain("resize-events", RESIZE_REFRESH_RATE, () => this._flushWaterfallViews(true));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the context menu opening. Hide items if no request is selected.
|
||||
*/
|
||||
_onContextShowing: function() {
|
||||
let element = $("#request-menu-context-resend");
|
||||
element.hidden = !this.selectedItem || this.selectedItem.attachment.isCustom;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the specified unix time is the first one to be known of,
|
||||
* and saves it if so.
|
||||
@ -1239,6 +1316,162 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
||||
_resizeTimeout: null
|
||||
});
|
||||
|
||||
/**
|
||||
* Functions handling the sidebar details view.
|
||||
*/
|
||||
function SidebarView() {
|
||||
dumpn("SidebarView was instantiated");
|
||||
}
|
||||
|
||||
SidebarView.prototype = {
|
||||
/**
|
||||
* Sets this view hidden or visible. It's visible by default.
|
||||
*
|
||||
* @param boolean aVisibleFlag
|
||||
* Specifies the intended visibility.
|
||||
*/
|
||||
toggle: function(aVisibleFlag) {
|
||||
NetMonitorView.toggleDetailsPane({ visible: aVisibleFlag });
|
||||
NetMonitorView.RequestsMenu._flushWaterfallViews(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Populates this view with the specified data.
|
||||
*
|
||||
* @param object aData
|
||||
* The data source (this should be the attachment of a request item).
|
||||
*/
|
||||
populate: function(aData) {
|
||||
if (aData.isCustom) {
|
||||
NetMonitorView.CustomRequest.populate(aData);
|
||||
$("#details-pane").selectedIndex = 0;
|
||||
} else {
|
||||
NetMonitorView.NetworkDetails.populate(aData);
|
||||
$("#details-pane").selectedIndex = 1;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides this container.
|
||||
*/
|
||||
reset: function() {
|
||||
this.toggle(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions handling the custom request view.
|
||||
*/
|
||||
function CustomRequestView() {
|
||||
dumpn("CustomRequestView was instantiated");
|
||||
}
|
||||
|
||||
CustomRequestView.prototype = {
|
||||
/**
|
||||
* Populates this view with the specified data.
|
||||
*
|
||||
* @param object aData
|
||||
* The data source (this should be the attachment of a request item).
|
||||
*/
|
||||
populate: function(aData) {
|
||||
$("#custom-url-value").value = aData.url;
|
||||
$("#custom-method-value").value = aData.method;
|
||||
$("#custom-headers-value").value =
|
||||
writeHeaderText(aData.requestHeaders.headers);
|
||||
|
||||
if (aData.requestPostData) {
|
||||
let body = aData.requestPostData.postData.text;
|
||||
|
||||
gNetwork.getString(body).then((aString) => {
|
||||
$("#custom-postdata-value").value = aString;
|
||||
});
|
||||
}
|
||||
|
||||
this.updateCustomQuery(aData.url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle user input in the custom request form.
|
||||
*
|
||||
* @param object aField
|
||||
* the field that the user updated.
|
||||
*/
|
||||
onUpdate: function(aField) {
|
||||
let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
|
||||
let field = aField;
|
||||
let value;
|
||||
|
||||
switch(aField) {
|
||||
case 'method':
|
||||
value = $("#custom-method-value").value.trim();
|
||||
selectedItem.attachment.method = value;
|
||||
break;
|
||||
case 'url':
|
||||
value = $("#custom-url-value").value;
|
||||
this.updateCustomQuery(value);
|
||||
selectedItem.attachment.url = value;
|
||||
break;
|
||||
case 'query':
|
||||
let query = $("#custom-query-value").value;
|
||||
this.updateCustomUrl(query);
|
||||
field = 'url';
|
||||
value = $("#custom-url-value").value
|
||||
selectedItem.attachment.url = value;
|
||||
break;
|
||||
case 'body':
|
||||
value = $("#custom-postdata-value").value;
|
||||
selectedItem.attachment.requestPostData = {
|
||||
postData: {
|
||||
text: value
|
||||
}
|
||||
};
|
||||
break;
|
||||
case 'headers':
|
||||
let headersText = $("#custom-headers-value").value;
|
||||
value = parseHeaderText(headersText);
|
||||
selectedItem.attachment.requestHeaders = {
|
||||
headers: value
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
NetMonitorView.RequestsMenu.updateMenuView(selectedItem, field, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the query string field based on the url.
|
||||
*
|
||||
* @param object aUrl
|
||||
* url to extract query string from.
|
||||
*/
|
||||
updateCustomQuery: function(aUrl) {
|
||||
let paramsArray = parseQueryString(nsIURL(aUrl).query);
|
||||
if (!paramsArray) {
|
||||
$("#custom-query").hidden = true;
|
||||
return;
|
||||
}
|
||||
$("#custom-query").hidden = false;
|
||||
$("#custom-query-value").value = writeQueryText(paramsArray);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the url based on the query string field.
|
||||
*
|
||||
* @param object aQueryText
|
||||
* contents of the query string field.
|
||||
*/
|
||||
updateCustomUrl: function(aQueryText) {
|
||||
let params = parseQueryText(aQueryText);
|
||||
let queryString = writeQueryString(params);
|
||||
|
||||
let url = $("#custom-url-value").value;
|
||||
let oldQuery = nsIURL(url).query;
|
||||
let path = url.replace(oldQuery, queryString);
|
||||
|
||||
$("#custom-url-value").value = path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions handling the requests details view.
|
||||
*/
|
||||
@ -1253,9 +1486,9 @@ NetworkDetailsView.prototype = {
|
||||
* Initialization function, called when the network monitor is started.
|
||||
*/
|
||||
initialize: function() {
|
||||
dumpn("Initializing the RequestsMenuView");
|
||||
dumpn("Initializing the NetworkDetailsView");
|
||||
|
||||
this.widget = $("#details-pane");
|
||||
this.widget = $("#event-details-pane");
|
||||
|
||||
this._headers = new VariablesView($("#all-headers"),
|
||||
Heritage.extend(GENERIC_VARIABLES_VIEW_SETTINGS, {
|
||||
@ -1292,25 +1525,13 @@ NetworkDetailsView.prototype = {
|
||||
* Destruction function, called when the network monitor is closed.
|
||||
*/
|
||||
destroy: function() {
|
||||
dumpn("Destroying the SourcesView");
|
||||
dumpn("Destroying the NetworkDetailsView");
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets this view hidden or visible. It's visible by default.
|
||||
*
|
||||
* @param boolean aVisibleFlag
|
||||
* Specifies the intended visibility.
|
||||
*/
|
||||
toggle: function(aVisibleFlag) {
|
||||
NetMonitorView.toggleDetailsPane({ visible: aVisibleFlag });
|
||||
NetMonitorView.RequestsMenu._flushWaterfallViews(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides and resets this container (removes all the networking information).
|
||||
* Resets this container (removes all the networking information).
|
||||
*/
|
||||
reset: function() {
|
||||
this.toggle(false);
|
||||
this._dataSrc = null;
|
||||
},
|
||||
|
||||
@ -1581,21 +1802,14 @@ NetworkDetailsView.prototype = {
|
||||
*
|
||||
* @param string aName
|
||||
* The type of params to populate (get or post).
|
||||
* @param string aParams
|
||||
* @param string aQueryString
|
||||
* A query string of params (e.g. "?foo=bar&baz=42").
|
||||
*/
|
||||
_addParams: function(aName, aParams) {
|
||||
// Make sure there's at least one param available.
|
||||
if (!aParams || !aParams.contains("=")) {
|
||||
_addParams: function(aName, aQueryString) {
|
||||
let paramsArray = parseQueryString(aQueryString);
|
||||
if (!paramsArray) {
|
||||
return;
|
||||
}
|
||||
// Turn the params string into an array containing { name: value } tuples.
|
||||
let paramsArray = aParams.replace(/^[?&]/, "").split("&").map((e) =>
|
||||
let (param = e.split("=")) {
|
||||
name: NetworkHelper.convertToUnicode(unescape(param[0])),
|
||||
value: NetworkHelper.convertToUnicode(unescape(param[1]))
|
||||
});
|
||||
|
||||
let paramsScope = this._params.addScope(aName);
|
||||
paramsScope.expanded = true;
|
||||
|
||||
@ -1807,6 +2021,110 @@ function nsIURL(aUrl, aStore = nsIURL.store) {
|
||||
}
|
||||
nsIURL.store = new Map();
|
||||
|
||||
/**
|
||||
* Parse a url's query string into its components
|
||||
*
|
||||
* @param string aQueryString
|
||||
* The query part of a url
|
||||
* @return array
|
||||
* Array of query params {name, value}
|
||||
*/
|
||||
function parseQueryString(aQueryString) {
|
||||
// Make sure there's at least one param available.
|
||||
if (!aQueryString || !aQueryString.contains("=")) {
|
||||
return;
|
||||
}
|
||||
// Turn the params string into an array containing { name: value } tuples.
|
||||
let paramsArray = aQueryString.replace(/^[?&]/, "").split("&").map((e) =>
|
||||
let (param = e.split("=")) {
|
||||
name: NetworkHelper.convertToUnicode(unescape(param[0])),
|
||||
value: NetworkHelper.convertToUnicode(unescape(param[1]))
|
||||
});
|
||||
return paramsArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse text representation of HTTP headers.
|
||||
*
|
||||
* @param string aText
|
||||
* Text of headers
|
||||
* @return array
|
||||
* Array of headers info {name, value}
|
||||
*/
|
||||
function parseHeaderText(aText) {
|
||||
return parseRequestText(aText, ":");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse readable text list of a query string.
|
||||
*
|
||||
* @param string aText
|
||||
* Text of query string represetation
|
||||
* @return array
|
||||
* Array of query params {name, value}
|
||||
*/
|
||||
function parseQueryText(aText) {
|
||||
return parseRequestText(aText, "=");
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a text representation of a name:value list with
|
||||
* the given name:value divider character.
|
||||
*
|
||||
* @param string aText
|
||||
* Text of list
|
||||
* @return array
|
||||
* Array of headers info {name, value}
|
||||
*/
|
||||
function parseRequestText(aText, aDivider) {
|
||||
let regex = new RegExp("(.+?)\\" + aDivider + "\\s*(.+)");
|
||||
let pairs = [];
|
||||
for (let line of aText.split("\n")) {
|
||||
let matches;
|
||||
if (matches = regex.exec(line)) {
|
||||
let [, name, value] = matches;
|
||||
pairs.push({name: name, value: value});
|
||||
}
|
||||
}
|
||||
return pairs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out a list of headers into a chunk of text
|
||||
*
|
||||
* @param array aHeaders
|
||||
* Array of headers info {name, value}
|
||||
* @return string aText
|
||||
* List of headers in text format
|
||||
*/
|
||||
function writeHeaderText(aHeaders) {
|
||||
return [(name + ": " + value) for ({name, value} of aHeaders)].join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out a list of query params into a chunk of text
|
||||
*
|
||||
* @param array aParams
|
||||
* Array of query params {name, value}
|
||||
* @return string
|
||||
* List of query params in text format
|
||||
*/
|
||||
function writeQueryText(aParams) {
|
||||
return [(name + "=" + value) for ({name, value} of aParams)].join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out a list of query params into a query string
|
||||
*
|
||||
* @param array aParams
|
||||
* Array of query params {name, value}
|
||||
* @return string
|
||||
* Query string that can be appended to a url.
|
||||
*/
|
||||
function writeQueryString(aParams) {
|
||||
return [(name + "=" + value) for ({name, value} of aParams)].join("&");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for draining a rapid succession of events and invoking a callback
|
||||
* once everything settles down.
|
||||
@ -1822,4 +2140,6 @@ drain.store = new Map();
|
||||
*/
|
||||
NetMonitorView.Toolbar = new ToolbarView();
|
||||
NetMonitorView.RequestsMenu = new RequestsMenuView();
|
||||
NetMonitorView.Sidebar = new SidebarView();
|
||||
NetMonitorView.CustomRequest = new CustomRequestView();
|
||||
NetMonitorView.NetworkDetails = new NetworkDetailsView();
|
||||
|
@ -15,6 +15,10 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#custom-pane {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#timings-summary-blocked {
|
||||
display: none; /* This doesn't work yet. */
|
||||
}
|
||||
|
@ -17,6 +17,16 @@
|
||||
<script type="text/javascript" src="netmonitor-controller.js"/>
|
||||
<script type="text/javascript" src="netmonitor-view.js"/>
|
||||
|
||||
<popupset id="networkPopupSet">
|
||||
<menupopup id="network-request-popup"
|
||||
onpopupshowing="NetMonitorView.RequestsMenu._onContextShowing(event);">
|
||||
<menuitem id="request-menu-context-resend"
|
||||
label="&netmonitorUI.summary.resend;"
|
||||
accesskey="&netmonitorUI.summary.resend.accesskey;"
|
||||
oncommand="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
|
||||
<box id="body"
|
||||
class="devtools-responsive-container"
|
||||
flex="1">
|
||||
@ -103,7 +113,7 @@
|
||||
</toolbar>
|
||||
<label id="requests-menu-empty-notice"
|
||||
value="&netmonitorUI.emptyNotice2;"/>
|
||||
<vbox id="requests-menu-contents" flex="1">
|
||||
<vbox id="requests-menu-contents" flex="1" context="network-request-popup">
|
||||
<hbox id="requests-menu-item-template" hidden="true">
|
||||
<hbox class="requests-menu-subitem requests-menu-status-and-method"
|
||||
align="center">
|
||||
@ -194,184 +204,251 @@
|
||||
|
||||
<splitter class="devtools-side-splitter"/>
|
||||
|
||||
<tabbox id="details-pane"
|
||||
class="devtools-sidebar-tabs"
|
||||
hidden="true">
|
||||
<tabs>
|
||||
<tab label="&netmonitorUI.tab.headers;"/>
|
||||
<tab label="&netmonitorUI.tab.cookies;"/>
|
||||
<tab label="&netmonitorUI.tab.params;"/>
|
||||
<tab label="&netmonitorUI.tab.response;"/>
|
||||
<tab label="&netmonitorUI.tab.timings;"/>
|
||||
</tabs>
|
||||
<tabpanels flex="1">
|
||||
<tabpanel id="headers-tabppanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<hbox id="headers-summary-url"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.summary.url;"/>
|
||||
<label id="headers-summary-url-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="headers-summary-method"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.summary.method;"/>
|
||||
<label id="headers-summary-method-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="headers-summary-status"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.summary.status;"/>
|
||||
<box id="headers-summary-status-circle"
|
||||
class="requests-menu-status"/>
|
||||
<label id="headers-summary-status-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="headers-summary-version"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.summary.version;"/>
|
||||
<label id="headers-summary-version-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<vbox id="all-headers" flex="1"/>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="cookies-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<vbox id="all-cookies" flex="1"/>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="params-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<vbox id="request-params-box" flex="1" hidden="true">
|
||||
<vbox id="request-params" flex="1"/>
|
||||
</vbox>
|
||||
<vbox id="request-post-data-textarea-box" flex="1" hidden="true">
|
||||
<vbox id="request-post-data-textarea" flex="1"/>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="response-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<label id="response-content-info-header"/>
|
||||
<vbox id="response-content-json-box" flex="1" hidden="true">
|
||||
<vbox id="response-content-json" flex="1"/>
|
||||
</vbox>
|
||||
<vbox id="response-content-textarea-box" flex="1" hidden="true">
|
||||
<vbox id="response-content-textarea" flex="1"/>
|
||||
</vbox>
|
||||
<vbox id="response-content-image-box" flex="1" hidden="true">
|
||||
<image id="response-content-image"/>
|
||||
<hbox>
|
||||
<deck id="details-pane"
|
||||
hidden="true">
|
||||
<vbox id="custom-pane"
|
||||
class="tabpanel-content">
|
||||
<hbox align="baseline">
|
||||
<label value="&netmonitorUI.custom.newRequest;"
|
||||
class="plain tabpanel-summary-label
|
||||
custom-header"/>
|
||||
<hbox flex="1" pack="end">
|
||||
<button class="devtools-toolbarbutton"
|
||||
label="&netmonitorUI.custom.send;"
|
||||
onclick="NetMonitorView.RequestsMenu.sendCustomRequest();"/>
|
||||
<button class="devtools-toolbarbutton"
|
||||
label="&netmonitorUI.custom.cancel;"
|
||||
onclick="NetMonitorView.RequestsMenu.closeCustomRequest();"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
<hbox id="custom-method-and-url"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<textbox id="custom-method-value"
|
||||
oninput="NetMonitorView.CustomRequest.onUpdate('method');"
|
||||
multiline="true"
|
||||
cols="6"
|
||||
rows="1"/>
|
||||
<textbox id="custom-url-value"
|
||||
flex="1"
|
||||
oninput="NetMonitorView.CustomRequest.onUpdate('url');"/>
|
||||
</hbox>
|
||||
<vbox id="custom-query"
|
||||
class="tabpanel-summary-container custom-section">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.custom.query;"/>
|
||||
<textbox id="custom-query-value"
|
||||
class="tabpanel-summary-input"
|
||||
multiline="true"
|
||||
rows="4"
|
||||
wrap="off"
|
||||
oninput="NetMonitorView.CustomRequest.onUpdate('query');"/>
|
||||
</vbox>
|
||||
<vbox id="custom-headers"
|
||||
class="tabpanel-summary-container custom-section">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.custom.headers;"/>
|
||||
<textbox id="custom-headers-value"
|
||||
class="tabpanel-summary-input"
|
||||
multiline="true"
|
||||
rows="6"
|
||||
wrap="off"
|
||||
oninput="NetMonitorView.CustomRequest.onUpdate('headers');"/>
|
||||
</vbox>
|
||||
<vbox id="custom-postdata"
|
||||
class="tabpanel-summary-container custom-section">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.custom.postData;"/>
|
||||
<textbox id="custom-postdata-value"
|
||||
class="tabpanel-summary-input"
|
||||
multiline="true"
|
||||
rows="6"
|
||||
wrap="off"
|
||||
oninput="NetMonitorView.CustomRequest.onUpdate('body');"/>
|
||||
</vbox>
|
||||
</vbox>
|
||||
<tabbox id="event-details-pane"
|
||||
class="devtools-sidebar-tabs">
|
||||
<tabs>
|
||||
<tab label="&netmonitorUI.tab.headers;"/>
|
||||
<tab label="&netmonitorUI.tab.cookies;"/>
|
||||
<tab label="&netmonitorUI.tab.params;"/>
|
||||
<tab label="&netmonitorUI.tab.response;"/>
|
||||
<tab label="&netmonitorUI.tab.timings;"/>
|
||||
</tabs>
|
||||
<tabpanels flex="1">
|
||||
<tabpanel id="headers-tabppanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<hbox id="headers-summary-url"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.name;"/>
|
||||
<label id="response-content-image-name-value"
|
||||
value="&netmonitorUI.summary.url;"/>
|
||||
<label id="headers-summary-url-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<hbox id="headers-summary-method"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.dimensions;"/>
|
||||
<label id="response-content-image-dimensions-value"
|
||||
value="&netmonitorUI.summary.method;"/>
|
||||
<label id="headers-summary-method-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<hbox id="headers-summary-status"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.mime;"/>
|
||||
<label id="response-content-image-mime-value"
|
||||
value="&netmonitorUI.summary.status;"/>
|
||||
<box id="headers-summary-status-circle"
|
||||
class="requests-menu-status"/>
|
||||
<label id="headers-summary-status-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
<button id="headers-summary-resend"
|
||||
label="&netmonitorUI.summary.resend;"
|
||||
class="devtools-toolbarbutton"
|
||||
onclick="NetMonitorView.RequestsMenu.cloneSelectedRequest();"/>
|
||||
</hbox>
|
||||
<hbox id="headers-summary-version"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.summary.version;"/>
|
||||
<label id="headers-summary-version-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<vbox id="all-headers" flex="1"/>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="cookies-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<vbox id="all-cookies" flex="1"/>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="params-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<vbox id="request-params-box" flex="1" hidden="true">
|
||||
<vbox id="request-params" flex="1"/>
|
||||
</vbox>
|
||||
<vbox id="request-post-data-textarea-box" flex="1" hidden="true">
|
||||
<vbox id="request-post-data-textarea" flex="1"/>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="response-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<label id="response-content-info-header"/>
|
||||
<vbox id="response-content-json-box" flex="1" hidden="true">
|
||||
<vbox id="response-content-json" flex="1"/>
|
||||
</vbox>
|
||||
<vbox id="response-content-textarea-box" flex="1" hidden="true">
|
||||
<vbox id="response-content-textarea" flex="1"/>
|
||||
</vbox>
|
||||
<vbox id="response-content-image-box" flex="1" hidden="true">
|
||||
<image id="response-content-image"/>
|
||||
<hbox>
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.name;"/>
|
||||
<label id="response-content-image-name-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.dimensions;"/>
|
||||
<label id="response-content-image-dimensions-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.mime;"/>
|
||||
<label id="response-content-image-mime-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.encoding;"/>
|
||||
<label id="response-content-image-encoding-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="timings-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<hbox id="timings-summary-blocked"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.response.encoding;"/>
|
||||
<label id="response-content-image-encoding-value"
|
||||
class="plain tabpanel-summary-value"
|
||||
crop="end"
|
||||
flex="1"/>
|
||||
value="&netmonitorUI.timings.blocked;"/>
|
||||
<hbox class="requests-menu-timings-box blocked"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-dns"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.dns;"/>
|
||||
<hbox class="requests-menu-timings-box dns"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-connect"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.connect;"/>
|
||||
<hbox class="requests-menu-timings-box connect"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-send"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.send;"/>
|
||||
<hbox class="requests-menu-timings-box send"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-wait"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.wait;"/>
|
||||
<hbox class="requests-menu-timings-box wait"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-receive"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.receive;"/>
|
||||
<hbox class="requests-menu-timings-box receive"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
<tabpanel id="timings-tabpanel"
|
||||
class="tabpanel-content">
|
||||
<vbox flex="1">
|
||||
<hbox id="timings-summary-blocked"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.blocked;"/>
|
||||
<hbox class="requests-menu-timings-box blocked"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-dns"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.dns;"/>
|
||||
<hbox class="requests-menu-timings-box dns"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-connect"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.connect;"/>
|
||||
<hbox class="requests-menu-timings-box connect"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-send"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.send;"/>
|
||||
<hbox class="requests-menu-timings-box send"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-wait"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.wait;"/>
|
||||
<hbox class="requests-menu-timings-box wait"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
<hbox id="timings-summary-receive"
|
||||
class="tabpanel-summary-container"
|
||||
align="center">
|
||||
<label class="plain tabpanel-summary-label"
|
||||
value="&netmonitorUI.timings.receive;"/>
|
||||
<hbox class="requests-menu-timings-box receive"/>
|
||||
<label class="plain requests-menu-timings-total"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
</deck>
|
||||
</box>
|
||||
|
||||
</window>
|
||||
|
@ -42,6 +42,7 @@ MOCHITEST_BROWSER_TESTS = \
|
||||
browser_net_accessibility-01.js \
|
||||
browser_net_accessibility-02.js \
|
||||
browser_net_footer-summary.js \
|
||||
browser_net_resend.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -20,8 +20,8 @@ function test() {
|
||||
NetMonitorView.toggleDetailsPane({ visible: true }, 2)
|
||||
RequestsMenu.selectedIndex = 0;
|
||||
|
||||
let tab = document.querySelectorAll("#details-pane tab")[2];
|
||||
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[2];
|
||||
let tab = document.querySelectorAll("#event-details-pane tab")[2];
|
||||
let tabpanel = document.querySelectorAll("#event-details-pane tabpanel")[2];
|
||||
|
||||
is(tab.getAttribute("selected"), "true",
|
||||
"The params tab in the network details pane should be selected.");
|
||||
|
167
browser/devtools/netmonitor/test/browser_net_resend.js
Normal file
167
browser/devtools/netmonitor/test/browser_net_resend.js
Normal file
@ -0,0 +1,167 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let gPanelWin;
|
||||
let gPanelDoc;
|
||||
|
||||
const ADD_QUERY = "t1=t2";
|
||||
const ADD_HEADER = "Test-header: true";
|
||||
const ADD_POSTDATA = "t3=t4";
|
||||
|
||||
/**
|
||||
* Tests if resending a request works.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(POST_DATA_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
gPanelWin = aMonitor.panelWin;
|
||||
gPanelDoc = gPanelWin.document;
|
||||
|
||||
let { NetMonitorView } = gPanelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
waitForNetworkEvents(aMonitor, 0, 2).then(() => {
|
||||
let origItem = RequestsMenu.getItemAtIndex(0);
|
||||
RequestsMenu.selectedItem = origItem;
|
||||
|
||||
// add a new custom request cloned from selected request
|
||||
RequestsMenu.cloneSelectedRequest();
|
||||
testCustomForm(origItem.attachment);
|
||||
|
||||
let customItem = RequestsMenu.selectedItem;
|
||||
testCustomItem(customItem, origItem);
|
||||
|
||||
// edit the custom request
|
||||
editCustomForm(() => {
|
||||
testCustomItemChanged(customItem, origItem);
|
||||
|
||||
waitForNetworkEvents(aMonitor, 0, 1).then(() => {
|
||||
let sentItem = RequestsMenu.selectedItem;
|
||||
testSentRequest(sentItem.attachment, origItem.attachment);
|
||||
finishUp(aMonitor);
|
||||
});
|
||||
// send the new request
|
||||
RequestsMenu.sendCustomRequest();
|
||||
});
|
||||
});
|
||||
|
||||
aDebuggee.performRequests();
|
||||
});
|
||||
}
|
||||
|
||||
function testCustomItem(aItem, aOrigItem) {
|
||||
let method = aItem.target.querySelector(".requests-menu-method").value;
|
||||
let origMethod = aOrigItem.target.querySelector(".requests-menu-method").value;
|
||||
is(method, origMethod, "menu item is showing the same method as original request");
|
||||
|
||||
let file = aItem.target.querySelector(".requests-menu-file").value;
|
||||
let origFile = aOrigItem.target.querySelector(".requests-menu-file").value;
|
||||
is(file, origFile, "menu item is showing the same file name as original request");
|
||||
|
||||
let domain = aItem.target.querySelector(".requests-menu-domain").value;
|
||||
let origDomain = aOrigItem.target.querySelector(".requests-menu-domain").value;
|
||||
is(domain, origDomain, "menu item is showing the same domain as original request");
|
||||
}
|
||||
|
||||
function testCustomItemChanged(aItem, aOrigItem) {
|
||||
let file = aItem.target.querySelector(".requests-menu-file").value;
|
||||
let expectedFile = aOrigItem.target.querySelector(".requests-menu-file").value + "&" + ADD_QUERY;
|
||||
|
||||
is(file, expectedFile, "menu item is updated to reflect url entered in form");
|
||||
}
|
||||
|
||||
/*
|
||||
* Test that the New Request form was populated correctly
|
||||
*/
|
||||
function testCustomForm(aData) {
|
||||
is(gPanelDoc.getElementById("custom-method-value").value, aData.method,
|
||||
"new request form showing correct method");
|
||||
|
||||
is(gPanelDoc.getElementById("custom-url-value").value, aData.url,
|
||||
"new request form showing correct url");
|
||||
|
||||
let query = gPanelDoc.getElementById("custom-query-value");
|
||||
is(query.value, "foo=bar\nbaz=42\ntype=urlencoded",
|
||||
"new request form showing correct query string");
|
||||
|
||||
let headers = gPanelDoc.getElementById("custom-headers-value").value.split("\n");
|
||||
for (let {name, value} of aData.requestHeaders.headers) {
|
||||
ok(headers.indexOf(name + ": " + value) >= 0, "form contains header from request");
|
||||
}
|
||||
|
||||
let postData = gPanelDoc.getElementById("custom-postdata-value");
|
||||
is(postData.value, aData.requestPostData.postData.text,
|
||||
"new request form showing correct post data");
|
||||
}
|
||||
|
||||
/*
|
||||
* Add some params and headers to the request form
|
||||
*/
|
||||
function editCustomForm(callback) {
|
||||
gPanelWin.focus();
|
||||
|
||||
let query = gPanelDoc.getElementById("custom-query-value");
|
||||
query.addEventListener("focus", function onFocus() {
|
||||
query.removeEventListener("focus", onFocus, false);
|
||||
|
||||
// add params to url query string field
|
||||
type(["VK_RETURN"]);
|
||||
type(ADD_QUERY);
|
||||
|
||||
let headers = gPanelDoc.getElementById("custom-headers-value");
|
||||
headers.addEventListener("focus", function onFocus() {
|
||||
headers.removeEventListener("focus", onFocus, false);
|
||||
|
||||
// add a header
|
||||
type(["VK_RETURN"]);
|
||||
type(ADD_HEADER);
|
||||
|
||||
let postData = gPanelDoc.getElementById("custom-postdata-value");
|
||||
postData.addEventListener("focus", function onFocus() {
|
||||
postData.removeEventListener("focus", onFocus, false);
|
||||
|
||||
// add to POST data
|
||||
type(ADD_POSTDATA);
|
||||
callback();
|
||||
}, false);
|
||||
postData.focus();
|
||||
}, false);
|
||||
headers.focus();
|
||||
}, false);
|
||||
query.focus();
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure newly created event matches expected request
|
||||
*/
|
||||
function testSentRequest(aData, aOrigData) {
|
||||
is(aData.method, aOrigData.method, "correct method in sent request");
|
||||
is(aData.url, aOrigData.url + "&" + ADD_QUERY, "correct url in sent request");
|
||||
|
||||
let hasHeader = aData.requestHeaders.headers.some((header) => {
|
||||
return (header.name + ": " + header.value) == ADD_HEADER;
|
||||
})
|
||||
ok(hasHeader, "new header added to sent request");
|
||||
|
||||
is(aData.requestPostData.postData.text,
|
||||
aOrigData.requestPostData.postData.text + ADD_POSTDATA,
|
||||
"post data added to sent request");
|
||||
}
|
||||
|
||||
|
||||
function type(aString) {
|
||||
for (let ch of aString) {
|
||||
EventUtils.synthesizeKey(ch, {}, gPanelWin);
|
||||
}
|
||||
}
|
||||
|
||||
function finishUp(aMonitor) {
|
||||
gPanelWin = null;
|
||||
gPanelDoc = null;
|
||||
|
||||
teardown(aMonitor).then(finish);
|
||||
}
|
@ -168,3 +168,36 @@
|
||||
- in the network details timings tab identifying the amount of time spent
|
||||
- in a "receive" state. -->
|
||||
<!ENTITY netmonitorUI.timings.receive "Receiving:">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||
- on the button in the headers tab that opens a form to resend the currently
|
||||
displayed request -->
|
||||
<!ENTITY netmonitorUI.summary.resend "Resend">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the access key
|
||||
- for the Resend menu item displayed in the context menu for a request -->
|
||||
<!ENTITY netmonitorUI.summary.resend.accesskey "R">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.newRequest): This is the label displayed
|
||||
- as the title of the new custom request form -->
|
||||
<!ENTITY netmonitorUI.custom.newRequest "New Request">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.query): This is the label displayed
|
||||
- above the query string entry in the custom request form -->
|
||||
<!ENTITY netmonitorUI.custom.query "Query String:">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||
- above the request headers entry in the custom request form -->
|
||||
<!ENTITY netmonitorUI.custom.headers "Request Headers:">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||
- above the request body entry in the custom request form -->
|
||||
<!ENTITY netmonitorUI.custom.postData "Request Body:">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||
- on the button which sends the custom request -->
|
||||
<!ENTITY netmonitorUI.custom.send "Send">
|
||||
|
||||
<!-- LOCALIZATION NOTE (debuggerUI.custom.headers): This is the label displayed
|
||||
- on the button which cancels and closes the custom request form -->
|
||||
<!ENTITY netmonitorUI.custom.cancel "Cancel">
|
||||
|
@ -383,6 +383,11 @@ box.requests-menu-status[code^="5"] {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
#headers-summary-resend {
|
||||
margin: 0 6px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
/* Response tabpanel */
|
||||
|
||||
#response-content-info-header {
|
||||
@ -422,6 +427,20 @@ box.requests-menu-status[code^="5"] {
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
/* Custom request form */
|
||||
|
||||
#custom-pane {
|
||||
padding: 0.6em 0.5em;
|
||||
}
|
||||
|
||||
.custom-header {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.custom-section {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
#requests-menu-footer {
|
||||
|
@ -383,6 +383,11 @@ box.requests-menu-status[code^="5"] {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
#headers-summary-resend {
|
||||
margin: 0 6px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
/* Response tabpanel */
|
||||
|
||||
#response-content-info-header {
|
||||
@ -422,6 +427,20 @@ box.requests-menu-status[code^="5"] {
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
/* Custom request form */
|
||||
|
||||
#custom-pane {
|
||||
padding: 0.6em 0.5em;
|
||||
}
|
||||
|
||||
.custom-header {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.custom-section {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
#requests-menu-footer {
|
||||
|
@ -383,6 +383,11 @@ box.requests-menu-status[code^="5"] {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
#headers-summary-resend {
|
||||
margin: 0 6px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
/* Response tabpanel */
|
||||
|
||||
#response-content-info-header {
|
||||
@ -422,6 +427,20 @@ box.requests-menu-status[code^="5"] {
|
||||
transition: transform 0.2s ease-out;
|
||||
}
|
||||
|
||||
/* Custom request form */
|
||||
|
||||
#custom-pane {
|
||||
padding: 0.6em 0.5em;
|
||||
}
|
||||
|
||||
.custom-header {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.custom-section {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
|
||||
#requests-menu-footer {
|
||||
|
@ -89,6 +89,7 @@ function WebConsoleActor(aConnection, aParentActor)
|
||||
|
||||
this._protoChains = new Map();
|
||||
this._dbgGlobals = new Map();
|
||||
this._netEvents = new Map();
|
||||
this._getDebuggerGlobal(this.window);
|
||||
|
||||
this._onObserverNotification = this._onObserverNotification.bind(this);
|
||||
@ -147,6 +148,15 @@ WebConsoleActor.prototype =
|
||||
*/
|
||||
_dbgGlobals: null,
|
||||
|
||||
/**
|
||||
* Holds a map between nsIChannel objects and NetworkEventActors for requests
|
||||
* created with sendHTTPRequest.
|
||||
*
|
||||
* @private
|
||||
* @type Map
|
||||
*/
|
||||
_netEvents: null,
|
||||
|
||||
/**
|
||||
* Object that holds the JSTerm API, the helper functions, for the default
|
||||
* window object.
|
||||
@ -252,6 +262,8 @@ WebConsoleActor.prototype =
|
||||
"last-pb-context-exited");
|
||||
}
|
||||
this._actorPool = null;
|
||||
|
||||
this._netEvents.clear();
|
||||
this._protoChains.clear();
|
||||
this._dbgGlobals.clear();
|
||||
this._jstermHelpers = null;
|
||||
@ -1000,10 +1012,10 @@ WebConsoleActor.prototype =
|
||||
* A new NetworkEventActor is returned. This is used for tracking the
|
||||
* network request and response.
|
||||
*/
|
||||
onNetworkEvent: function WCA_onNetworkEvent(aEvent)
|
||||
onNetworkEvent: function WCA_onNetworkEvent(aEvent, aChannel)
|
||||
{
|
||||
let actor = new NetworkEventActor(aEvent, this);
|
||||
this._actorPool.addActor(actor);
|
||||
let actor = this.getNetworkEventActor(aChannel);
|
||||
actor.init(aEvent);
|
||||
|
||||
let packet = {
|
||||
from: this.actorID,
|
||||
@ -1016,6 +1028,57 @@ WebConsoleActor.prototype =
|
||||
return actor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the NetworkEventActor for a nsIChannel, if it exists,
|
||||
* otherwise create a new one.
|
||||
*
|
||||
* @param object aChannel
|
||||
* The channel for the network event.
|
||||
*/
|
||||
getNetworkEventActor: function WCA_getNetworkEventActor(aChannel) {
|
||||
let actor = this._netEvents.get(aChannel);
|
||||
if (actor) {
|
||||
// delete from map as we should only need to do this check once
|
||||
this._netEvents.delete(aChannel);
|
||||
actor.channel = null;
|
||||
return actor;
|
||||
}
|
||||
|
||||
actor = new NetworkEventActor(aChannel, this);
|
||||
this._actorPool.addActor(actor);
|
||||
return actor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a new HTTP request from the target's window.
|
||||
*
|
||||
* @param object aMessage
|
||||
* Object with 'request' - the HTTP request details.
|
||||
*/
|
||||
onSendHTTPRequest: function WCA_onSendHTTPRequest(aMessage)
|
||||
{
|
||||
let details = aMessage.request;
|
||||
|
||||
// send request from target's window
|
||||
let request = new this._window.XMLHttpRequest();
|
||||
request.open(details.method, details.url, true);
|
||||
|
||||
for (let {name, value} of details.headers) {
|
||||
request.setRequestHeader(name, value);
|
||||
}
|
||||
request.send(details.body);
|
||||
|
||||
let actor = this.getNetworkEventActor(request.channel);
|
||||
|
||||
// map channel to actor so we can associate future events with it
|
||||
this._netEvents.set(request.channel, actor);
|
||||
|
||||
return {
|
||||
from: this.actorID,
|
||||
eventActor: actor.grip()
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for file activity. This method sends the file request information
|
||||
* to the remote Web Console client.
|
||||
@ -1113,7 +1176,7 @@ WebConsoleActor.prototype =
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
WebConsoleActor.prototype.requestTypes =
|
||||
@ -1125,32 +1188,31 @@ WebConsoleActor.prototype.requestTypes =
|
||||
autocomplete: WebConsoleActor.prototype.onAutocomplete,
|
||||
clearMessagesCache: WebConsoleActor.prototype.onClearMessagesCache,
|
||||
setPreferences: WebConsoleActor.prototype.onSetPreferences,
|
||||
sendHTTPRequest: WebConsoleActor.prototype.onSendHTTPRequest
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an actor for a network event.
|
||||
*
|
||||
* @constructor
|
||||
* @param object aNetworkEvent
|
||||
* The network event you want to use the actor for.
|
||||
* @param object aChannel
|
||||
* The nsIChannel associated with this event.
|
||||
* @param object aWebConsoleActor
|
||||
* The parent WebConsoleActor instance for this object.
|
||||
*/
|
||||
function NetworkEventActor(aNetworkEvent, aWebConsoleActor)
|
||||
function NetworkEventActor(aChannel, aWebConsoleActor)
|
||||
{
|
||||
this.parent = aWebConsoleActor;
|
||||
this.conn = this.parent.conn;
|
||||
|
||||
this._startedDateTime = aNetworkEvent.startedDateTime;
|
||||
this._isXHR = aNetworkEvent.isXHR;
|
||||
this.channel = aChannel;
|
||||
|
||||
this._request = {
|
||||
method: aNetworkEvent.method,
|
||||
url: aNetworkEvent.url,
|
||||
httpVersion: aNetworkEvent.httpVersion,
|
||||
method: null,
|
||||
url: null,
|
||||
httpVersion: null,
|
||||
headers: [],
|
||||
cookies: [],
|
||||
headersSize: aNetworkEvent.headersSize,
|
||||
headersSize: null,
|
||||
postData: {},
|
||||
};
|
||||
|
||||
@ -1164,10 +1226,6 @@ function NetworkEventActor(aNetworkEvent, aWebConsoleActor)
|
||||
|
||||
// Keep track of LongStringActors owned by this NetworkEventActor.
|
||||
this._longStringActors = new Set();
|
||||
|
||||
this._discardRequestBody = aNetworkEvent.discardRequestBody;
|
||||
this._discardResponseBody = aNetworkEvent.discardResponseBody;
|
||||
this._private = aNetworkEvent.private;
|
||||
}
|
||||
|
||||
NetworkEventActor.prototype =
|
||||
@ -1206,6 +1264,10 @@ NetworkEventActor.prototype =
|
||||
}
|
||||
}
|
||||
this._longStringActors = new Set();
|
||||
|
||||
if (this.channel) {
|
||||
this.parent._netEvents.delete(this.channel);
|
||||
}
|
||||
this.parent.releaseActor(this);
|
||||
},
|
||||
|
||||
@ -1218,6 +1280,27 @@ NetworkEventActor.prototype =
|
||||
return {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the properties of this actor based on it's corresponding
|
||||
* network event.
|
||||
*
|
||||
* @param object aNetworkEvent
|
||||
* The network event associated with this actor.
|
||||
*/
|
||||
init: function NEA_init(aNetworkEvent)
|
||||
{
|
||||
this._startedDateTime = aNetworkEvent.startedDateTime;
|
||||
this._isXHR = aNetworkEvent.isXHR;
|
||||
|
||||
for (let prop of ['method', 'url', 'httpVersion', 'headersSize']) {
|
||||
this._request[prop] = aNetworkEvent[prop];
|
||||
}
|
||||
|
||||
this._discardRequestBody = aNetworkEvent.discardRequestBody;
|
||||
this._discardResponseBody = aNetworkEvent.discardResponseBody;
|
||||
this._private = aNetworkEvent.private;
|
||||
},
|
||||
|
||||
/**
|
||||
* The "getRequestHeaders" packet type handler.
|
||||
*
|
||||
@ -1545,4 +1628,3 @@ NetworkEventActor.prototype.requestTypes =
|
||||
DebuggerServer.addTabActor(WebConsoleActor, "consoleActor");
|
||||
DebuggerServer.addGlobalActor(WebConsoleActor, "consoleActor");
|
||||
|
||||
|
||||
|
@ -284,6 +284,23 @@ WebConsoleClient.prototype = {
|
||||
this._client.request(packet, aOnResponse);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a HTTP request with the given data.
|
||||
*
|
||||
* @param string aData
|
||||
* The details of the HTTP request.
|
||||
* @param function aOnResponse
|
||||
* The function invoked when the response is received.
|
||||
*/
|
||||
sendHTTPRequest: function WCC_sendHTTPRequest(aData, aOnResponse) {
|
||||
let packet = {
|
||||
to: this._actor,
|
||||
type: "sendHTTPRequest",
|
||||
request: aData
|
||||
};
|
||||
this._client.request(packet, aOnResponse);
|
||||
},
|
||||
|
||||
/**
|
||||
* Start the given Web Console listeners.
|
||||
*
|
||||
|
@ -1748,10 +1748,10 @@ NetworkResponseListener.prototype = {
|
||||
* window is given, all browser network requests are logged.
|
||||
* @param object aOwner
|
||||
* The network monitor owner. This object needs to hold:
|
||||
* - onNetworkEvent(aRequestInfo). This method is invoked once for every
|
||||
* new network request and it is given one arguments: the initial network
|
||||
* request information. onNetworkEvent() must return an object which
|
||||
* holds several add*() methods which are used to add further network
|
||||
* - onNetworkEvent(aRequestInfo, aChannel). This method is invoked once for
|
||||
* every new network request and it is given two arguments: the initial network
|
||||
* request information, and the channel. onNetworkEvent() must return an object
|
||||
* which holds several add*() methods which are used to add further network
|
||||
* request/response information.
|
||||
* - saveRequestAndResponseBodies property which tells if you want to log
|
||||
* request and response bodies.
|
||||
@ -2052,7 +2052,7 @@ NetworkMonitor.prototype = {
|
||||
cookies = NetworkHelper.parseCookieHeader(cookieHeader);
|
||||
}
|
||||
|
||||
httpActivity.owner = this.owner.onNetworkEvent(event);
|
||||
httpActivity.owner = this.owner.onNetworkEvent(event, aChannel);
|
||||
|
||||
this._setupResponseListener(httpActivity);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user