--- a/devtools/client/netmonitor/src/components/CookiesPanel.js
+++ b/devtools/client/netmonitor/src/components/CookiesPanel.js
@@ -1,15 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
-const { createFactory } = require("devtools/client/shared/vendor/react");
+const { Component, createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const { L10N } = require("../utils/l10n");
const { sortObjectKeys } = require("../utils/sort-utils");
// Component
const PropertiesView = createFactory(require("./PropertiesView"));
@@ -23,79 +23,103 @@ const SECTION_NAMES = [
RESPONSE_COOKIES,
REQUEST_COOKIES,
];
/*
* Cookies panel component
* This tab lists full details of any cookies sent with the request or response
*/
-function CookiesPanel({
- request,
- openLink,
-}) {
- let {
- requestCookies = { cookies: [] },
- responseCookies = { cookies: [] },
- } = request;
+class CookiesPanel extends Component {
+ static get propTypes() {
+ return {
+ connector: PropTypes.object.isRequired,
+ openLink: PropTypes.func,
+ request: PropTypes.object.isRequired,
+ };
+ }
- requestCookies = requestCookies.cookies || requestCookies;
- responseCookies = responseCookies.cookies || responseCookies;
-
- if (!requestCookies.length && !responseCookies.length) {
- return div({ className: "empty-notice" },
- COOKIES_EMPTY_TEXT
- );
+ componentDidMount() {
+ this.maybeFetchCookies(this.props);
}
- let object = {};
-
- if (responseCookies.length) {
- object[RESPONSE_COOKIES] = sortObjectKeys(getProperties(responseCookies));
+ componentWillReceiveProps(nextProps) {
+ this.maybeFetchCookies(nextProps);
}
- if (requestCookies.length) {
- object[REQUEST_COOKIES] = sortObjectKeys(getProperties(requestCookies));
+ /**
+ * When switching to another request, lazily fetch request cookies
+ * from the backend. The panel will first be empty and then display the content.
+ */
+ maybeFetchCookies(props) {
+ if (props.request.requestCookiesAvailable && !props.request.requestCookies) {
+ props.connector.requestData(props.request.id, "requestCookies");
+ }
+ if (props.request.responseCookiesAvailable && !props.request.responseCookies) {
+ props.connector.requestData(props.request.id, "responseCookies");
+ }
}
- return (
- div({ className: "panel-container" },
- PropertiesView({
- object,
- filterPlaceHolder: COOKIES_FILTER_TEXT,
- sectionNames: SECTION_NAMES,
- openLink,
- })
- )
- );
-}
-
-CookiesPanel.displayName = "CookiesPanel";
-
-CookiesPanel.propTypes = {
- request: PropTypes.object.isRequired,
- openLink: PropTypes.func,
-};
+ /**
+ * Mapping array to dict for TreeView usage.
+ * Since TreeView only support Object(dict) format.
+ *
+ * @param {Object[]} arr - key-value pair array like cookies or params
+ * @returns {Object}
+ */
+ getProperties(arr) {
+ return arr.reduce((map, obj) => {
+ // Generally cookies object contains only name and value properties and can
+ // be rendered as name: value pair.
+ // When there are more properties in cookies object such as extra or path,
+ // We will pass the object to display these extra information
+ if (Object.keys(obj).length > 2) {
+ map[obj.name] = Object.assign({}, obj);
+ delete map[obj.name].name;
+ } else {
+ map[obj.name] = obj.value;
+ }
+ return map;
+ }, {});
+ }
-/**
- * Mapping array to dict for TreeView usage.
- * Since TreeView only support Object(dict) format.
- *
- * @param {Object[]} arr - key-value pair array like cookies or params
- * @returns {Object}
- */
-function getProperties(arr) {
- return arr.reduce((map, obj) => {
- // Generally cookies object contains only name and value properties and can
- // be rendered as name: value pair.
- // When there are more properties in cookies object such as extra or path,
- // We will pass the object to display these extra information
- if (Object.keys(obj).length > 2) {
- map[obj.name] = Object.assign({}, obj);
- delete map[obj.name].name;
- } else {
- map[obj.name] = obj.value;
+ render() {
+ let {
+ request: {
+ requestCookies = { cookies: [] },
+ responseCookies = { cookies: [] },
+ },
+ openLink,
+ } = this.props;
+
+ requestCookies = requestCookies.cookies || requestCookies;
+ responseCookies = responseCookies.cookies || responseCookies;
+
+ if (!requestCookies.length && !responseCookies.length) {
+ return div({ className: "empty-notice" },
+ COOKIES_EMPTY_TEXT
+ );
}
- return map;
- }, {});
+
+ let object = {};
+
+ if (responseCookies.length) {
+ object[RESPONSE_COOKIES] = sortObjectKeys(this.getProperties(responseCookies));
+ }
+
+ if (requestCookies.length) {
+ object[REQUEST_COOKIES] = sortObjectKeys(this.getProperties(requestCookies));
+ }
+
+ return (
+ div({ className: "panel-container" },
+ PropertiesView({
+ object,
+ filterPlaceHolder: COOKIES_FILTER_TEXT,
+ sectionNames: SECTION_NAMES,
+ openLink,
+ })
+ )
+ );
+ }
}
module.exports = CookiesPanel;
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -90,17 +90,17 @@ class MonitorPanel extends Component {
let initialWidth = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-width");
let initialHeight = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-height");
return (
div({ className: "monitor-panel" },
- Toolbar(),
+ Toolbar({ connector }),
SplitBox({
className: "devtools-responsive-container",
initialWidth: `${initialWidth}px`,
initialHeight: `${initialHeight}px`,
minSize: "50px",
maxSize: "80%",
splitterSize: "1px",
startPanel: RequestList({ isEmpty, connector }),
--- a/devtools/client/netmonitor/src/components/RequestListColumnCookies.js
+++ b/devtools/client/netmonitor/src/components/RequestListColumnCookies.js
@@ -8,28 +8,46 @@ const { Component } = require("devtools/
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { div } = dom;
class RequestListColumnCookies extends Component {
static get propTypes() {
return {
+ connector: PropTypes.object.isRequired,
item: PropTypes.object.isRequired,
};
}
+ componentDidMount() {
+ this.maybeFetchRequestCookies(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.maybeFetchRequestCookies(nextProps);
+ }
+
shouldComponentUpdate(nextProps) {
let { requestCookies: currRequestCookies = { cookies: [] } } = this.props.item;
let { requestCookies: nextRequestCookies = { cookies: [] } } = nextProps.item;
currRequestCookies = currRequestCookies.cookies || currRequestCookies;
nextRequestCookies = nextRequestCookies.cookies || nextRequestCookies;
return currRequestCookies !== nextRequestCookies;
}
+ /**
+ * Lazily fetch request cookies from the backend.
+ */
+ maybeFetchRequestCookies(props) {
+ if (props.item.requestCookiesAvailable && !props.requestCookies) {
+ props.connector.requestData(props.item.id, "requestCookies");
+ }
+ }
+
render() {
let { requestCookies = { cookies: [] } } = this.props.item;
requestCookies = requestCookies.cookies || requestCookies;
let requestCookiesLength = requestCookies.length > 0 ? requestCookies.length : "";
return (
div({
className: "requests-list-column requests-list-cookies",
title: requestCookiesLength
--- a/devtools/client/netmonitor/src/components/RequestListColumnSetCookies.js
+++ b/devtools/client/netmonitor/src/components/RequestListColumnSetCookies.js
@@ -8,28 +8,46 @@ const { Component } = require("devtools/
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { div } = dom;
class RequestListColumnSetCookies extends Component {
static get propTypes() {
return {
+ connector: PropTypes.object.isRequired,
item: PropTypes.object.isRequired,
};
}
+ componentDidMount() {
+ this.maybeFetchResponseCookies(this.props);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ this.maybeFetchResponseCookies(nextProps);
+ }
+
shouldComponentUpdate(nextProps) {
let { responseCookies: currResponseCookies = { cookies: [] } } = this.props.item;
let { responseCookies: nextResponseCookies = { cookies: [] } } = nextProps.item;
currResponseCookies = currResponseCookies.cookies || currResponseCookies;
nextResponseCookies = nextResponseCookies.cookies || nextResponseCookies;
return currResponseCookies !== nextResponseCookies;
}
+ /**
+ * Lazily fetch response cookies from the backend.
+ */
+ maybeFetchResponseCookies(props) {
+ if (props.item.responseCookiesAvailable && !props.responseCookies) {
+ props.connector.requestData(props.item.id, "responseCookies");
+ }
+ }
+
render() {
let { responseCookies = { cookies: [] } } = this.props.item;
responseCookies = responseCookies.cookies || responseCookies;
let responseCookiesLength = responseCookies.length > 0 ? responseCookies.length : "";
return (
div({
className: "requests-list-column requests-list-set-cookies",
title: responseCookiesLength
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -206,16 +206,17 @@ class RequestListContent extends Compone
* scrolled to bottom, but allow scrolling up with the selection.
*/
onFocusedNodeChange() {
this.shouldScrollBottom = false;
}
render() {
const {
+ connector,
columns,
displayedRequests,
firstRequestStartedMillis,
onCauseBadgeMouseDown,
onItemMouseDown,
onSecurityIconMouseDown,
onWaterfallMouseDown,
scale,
@@ -231,16 +232,17 @@ class RequestListContent extends Compone
tabIndex: 0,
onKeyDown: this.onKeyDown,
style: { "--timings-scale": scale, "--timings-rev-scale": 1 / scale }
},
RequestListHeader(),
displayedRequests.map((item, index) => RequestListItem({
firstRequestStartedMillis,
fromCache: item.status === "304" || item.fromCache,
+ connector,
columns,
item,
index,
isSelected: item.id === (selectedRequest && selectedRequest.id),
key: item.id,
onContextMenu: this.onContextMenu,
onFocusedNodeChange: this.onFocusedNodeChange,
onMouseDown: () => onItemMouseDown(item.id),
--- a/devtools/client/netmonitor/src/components/RequestListItem.js
+++ b/devtools/client/netmonitor/src/components/RequestListItem.js
@@ -52,31 +52,34 @@ const UPDATED_REQ_ITEM_PROPS = [
"method",
"url",
"remoteAddress",
"cause",
"contentSize",
"transferredSize",
"startedMillis",
"totalTime",
+ "requestCookies",
+ "responseCookies",
];
const UPDATED_REQ_PROPS = [
"firstRequestStartedMillis",
"index",
"isSelected",
"waterfallWidth",
];
/**
* Render one row in the request list.
*/
class RequestListItem extends Component {
static get propTypes() {
return {
+ connector: PropTypes.object.isRequired,
columns: PropTypes.object.isRequired,
item: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
isSelected: PropTypes.bool.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
fromCache: PropTypes.bool,
onCauseBadgeMouseDown: PropTypes.func.isRequired,
onContextMenu: PropTypes.func.isRequired,
@@ -106,16 +109,17 @@ class RequestListItem extends Component
if (this.props.onFocusedNodeChange) {
this.props.onFocusedNodeChange();
}
}
}
render() {
let {
+ connector,
columns,
item,
index,
isSelected,
firstRequestStartedMillis,
fromCache,
onContextMenu,
onMouseDown,
@@ -142,18 +146,18 @@ class RequestListItem extends Component
columns.get("file") && RequestListColumnFile({ item }),
columns.get("protocol") && RequestListColumnProtocol({ item }),
columns.get("scheme") && RequestListColumnScheme({ item }),
columns.get("domain") && RequestListColumnDomain({ item,
onSecurityIconMouseDown }),
columns.get("remoteip") && RequestListColumnRemoteIP({ item }),
columns.get("cause") && RequestListColumnCause({ item, onCauseBadgeMouseDown }),
columns.get("type") && RequestListColumnType({ item }),
- columns.get("cookies") && RequestListColumnCookies({ item }),
- columns.get("setCookies") && RequestListColumnSetCookies({ item }),
+ columns.get("cookies") && RequestListColumnCookies({ connector, item }),
+ columns.get("setCookies") && RequestListColumnSetCookies({ connector, item }),
columns.get("transferred") && RequestListColumnTransferredSize({ item }),
columns.get("contentSize") && RequestListColumnContentSize({ item }),
columns.get("startTime") &&
RequestListColumnStartTime({ item, firstRequestStartedMillis }),
columns.get("endTime") &&
RequestListColumnEndTime({ item, firstRequestStartedMillis }),
columns.get("responseTime") &&
RequestListColumnResponseTime({ item, firstRequestStartedMillis }),
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -63,17 +63,21 @@ function TabboxPanel({
openLink,
request,
}),
),
TabPanel({
id: PANELS.COOKIES,
title: COOKIES_TITLE,
},
- CookiesPanel({ request, openLink }),
+ CookiesPanel({
+ connector,
+ openLink,
+ request,
+ }),
),
TabPanel({
id: PANELS.PARAMS,
title: PARAMS_TITLE,
},
ParamsPanel({ connector, openLink, request }),
),
TabPanel({
--- a/devtools/client/netmonitor/src/components/Toolbar.js
+++ b/devtools/client/netmonitor/src/components/Toolbar.js
@@ -51,16 +51,17 @@ const DISABLE_CACHE_LABEL = L10N.getStr(
* Network monitor toolbar component.
*
* Toolbar contains a set of useful tools to control network requests
* as well as set of filters for filtering the content.
*/
class Toolbar extends Component {
static get propTypes() {
return {
+ connector: PropTypes.object.isRequired,
toggleRecording: PropTypes.func.isRequired,
recording: PropTypes.bool.isRequired,
clearRequests: PropTypes.func.isRequired,
requestFilterTypes: PropTypes.object.isRequired,
setRequestFilterText: PropTypes.func.isRequired,
networkDetailsToggleDisabled: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
toggleNetworkDetails: PropTypes.func.isRequired,
@@ -73,16 +74,17 @@ class Toolbar extends Component {
toggleRequestFilterType: PropTypes.func.isRequired,
filteredRequests: PropTypes.array.isRequired,
};
}
constructor(props) {
super(props);
this.autocompleteProvider = this.autocompleteProvider.bind(this);
+ this.onSearchBoxFocus = this.onSearchBoxFocus.bind(this);
this.toggleRequestFilterType = this.toggleRequestFilterType.bind(this);
this.updatePersistentLogsEnabled = this.updatePersistentLogsEnabled.bind(this);
this.updateBrowserCacheDisabled = this.updateBrowserCacheDisabled.bind(this);
}
componentDidMount() {
Services.prefs.addObserver(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
this.updatePersistentLogsEnabled);
@@ -125,16 +127,25 @@ class Toolbar extends Component {
this.props.disableBrowserCache(
Services.prefs.getBoolPref(DEVTOOLS_DISABLE_CACHE_PREF));
}
autocompleteProvider(filter) {
return autocompleteProvider(filter, this.props.filteredRequests);
}
+ onSearchBoxFocus() {
+ let { connector, filteredRequests } = this.props;
+
+ // Fetch responseCookies for building autocomplete list
+ filteredRequests.forEach((request) => {
+ connector.requestData(request.id, "responseCookies");
+ });
+ }
+
render() {
let {
toggleRecording,
clearRequests,
requestFilterTypes,
setRequestFilterText,
networkDetailsToggleDisabled,
networkDetailsOpen,
@@ -233,16 +244,17 @@ class Toolbar extends Component {
span({ className: "devtools-toolbar-group" },
SearchBox({
delay: FILTER_SEARCH_DELAY,
keyShortcut: SEARCH_KEY_SHORTCUT,
placeholder: SEARCH_PLACE_HOLDER,
type: "filter",
ref: "searchbox",
onChange: setRequestFilterText,
+ onFocus: this.onSearchBoxFocus,
autocompleteProvider: this.autocompleteProvider,
}),
button({
className: toggleDetailButtonClass,
title: toggleDetailButtonTitle,
disabled: networkDetailsToggleDisabled,
tabIndex: "0",
onClick: toggleNetworkDetails,
--- a/devtools/client/netmonitor/src/connector/firefox-connector.js
+++ b/devtools/client/netmonitor/src/connector/firefox-connector.js
@@ -63,28 +63,29 @@ class FirefoxConnector {
}
this.displayCachedEvents();
}
async disconnect() {
this.actions.batchReset();
- // The timeline front wasn't initialized and started if the server wasn't
- // recent enough to emit the markers we were interested in.
- if (this.tabTarget.getTrait("documentLoadingMarkers") && this.timelineFront) {
- this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
- await this.timelineFront.destroy();
- }
-
this.removeListeners();
- this.tabTarget.off("will-navigate");
+ if (this.tabTarget) {
+ // The timeline front wasn't initialized and started if the server wasn't
+ // recent enough to emit the markers we were interested in.
+ if (this.tabTarget.getTrait("documentLoadingMarkers") && this.timelineFront) {
+ this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
+ await this.timelineFront.destroy();
+ }
- this.tabTarget = null;
+ this.tabTarget.off("will-navigate");
+ this.tabTarget = null;
+ }
this.webConsoleClient = null;
this.timelineFront = null;
this.dataProvider = null;
this.panel = null;
}
pause() {
this.removeListeners();
@@ -98,19 +99,23 @@ class FirefoxConnector {
this.tabTarget.on("close", this.disconnect);
this.webConsoleClient.on("networkEvent",
this.dataProvider.onNetworkEvent);
this.webConsoleClient.on("networkEventUpdate",
this.dataProvider.onNetworkEventUpdate);
}
removeListeners() {
- this.tabTarget.off("close");
- this.webConsoleClient.off("networkEvent");
- this.webConsoleClient.off("networkEventUpdate");
+ if (this.tabTarget) {
+ this.tabTarget.off("close");
+ }
+ if (this.webConsoleClient) {
+ this.webConsoleClient.off("networkEvent");
+ this.webConsoleClient.off("networkEventUpdate");
+ }
}
willNavigate() {
if (!Services.prefs.getBoolPref("devtools.netmonitor.persistlog")) {
this.actions.batchReset();
this.actions.clearRequests();
} else {
// If the log is persistent, just clear all accumulated timing markers.
@@ -311,14 +316,20 @@ class FirefoxConnector {
* @param {number} sourceLine source line number
*/
viewSourceInDebugger(sourceURL, sourceLine) {
if (this.toolbox) {
this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
}
}
+ /**
+ * Fetch networkEventUpdate websocket message from back-end when
+ * data provider is connected.
+ * @param {object} request network request instance
+ * @param {string} type NetworkEventUpdate type
+ */
requestData(request, type) {
return this.dataProvider.requestData(request, type);
}
}
module.exports = new FirefoxConnector();
--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -2,19 +2,17 @@
* 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/. */
/* eslint-disable block-scoped-var */
"use strict";
const { EVENTS } = require("../constants");
const { CurlUtils } = require("devtools/client/shared/curl");
-const {
- fetchHeaders,
-} = require("../utils/request-utils");
+const { fetchHeaders } = require("../utils/request-utils");
/**
* This object is responsible for fetching additional HTTP
* data from the backend over RDP protocol.
*
* The object also keeps track of RDP requests in-progress,
* so it's possible to determine whether all has been fetched
* or not.
@@ -175,16 +173,20 @@ class FirefoxDataProvider {
// two new-line characters at the end.
const headersSize = headers.reduce((acc, { name, value }) => {
return acc + name.length + value.length + 2;
}, 0);
requestPostData.postData.text = postData;
payload.requestPostData = Object.assign({}, requestPostData);
payload.requestHeadersFromUploadStream = { headers, headersSize };
+
+ // Lock down requestPostDataAvailable once we fetch data from back-end.
+ // Using this as flag to prevent fetching arrived data again.
+ payload.requestPostDataAvailable = false;
}
return payload;
}
async fetchResponseCookies(responseCookies) {
let payload = {};
if (responseCookies) {
let resCookies = [];
@@ -197,16 +199,20 @@ class FirefoxDataProvider {
resCookies.push(Object.assign({}, cookie, {
value: await this.getLongString(cookie.value),
}));
}
if (resCookies.length) {
payload.responseCookies = resCookies;
}
}
+
+ // Lock down responseCookiesAvailable once we fetch data from back-end.
+ // Using this as flag to prevent fetching arrived data again.
+ payload.responseCookiesAvailable = false;
}
return payload;
}
async fetchRequestCookies(requestCookies) {
let payload = {};
if (requestCookies) {
let reqCookies = [];
@@ -219,16 +225,20 @@ class FirefoxDataProvider {
reqCookies.push(Object.assign({}, cookie, {
value: await this.getLongString(cookie.value),
}));
}
if (reqCookies.length) {
payload.requestCookies = reqCookies;
}
}
+
+ // Lock down requestCookiesAvailable once we fetch data from back-end.
+ // Using this as flag to prevent fetching arrived data again.
+ payload.requestCookiesAvailable = false;
}
return payload;
}
/**
* Access a payload item from payload queue.
*
* @param {string} id request id
@@ -260,22 +270,19 @@ class FirefoxDataProvider {
}
let { payload } = this.getRequestFromQueue(id);
// The payload is ready when all values in the record are true.
// Note that we never fetch response header/cookies for request with security issues.
// Bug 1404917 should simplify this heuristic by making all these field be lazily
// fetched, only on-demand.
- return record.requestHeaders && record.requestCookies && record.eventTimings &&
- (
- (record.responseHeaders && record.responseCookies) ||
- payload.securityState === "broken" ||
- (!payload.status && payload.responseContentAvailable)
- );
+ return record.requestHeaders && record.eventTimings &&
+ (record.responseHeaders || payload.securityState === "broken" ||
+ (!payload.status && payload.responseContentAvailable));
}
/**
* Merge upcoming networkEventUpdate payload into existing one.
*
* @param {string} id request id
* @param {object} payload request data payload
*/
@@ -337,19 +344,17 @@ class FirefoxDataProvider {
url,
},
startedDateTime,
} = networkInfo;
// Create tracking record for this request.
this.rdpRequestMap.set(actor, {
requestHeaders: false,
- requestCookies: false,
responseHeaders: false,
- responseCookies: false,
eventTimings: false,
});
this.addRequest(actor, {
cause,
fromCache,
fromServiceWorker,
isXHR,
@@ -376,27 +381,25 @@ class FirefoxDataProvider {
// When we pause and resume, we may receive `networkEventUpdate` for a request
// that started during the pause and we missed its `networkEvent`.
if (!this.rdpRequestMap.has(actor)) {
return;
}
switch (updateType) {
case "requestHeaders":
- case "requestCookies":
case "responseHeaders":
- case "responseCookies":
this.requestPayloadData(actor, updateType);
break;
+ case "requestCookies":
+ case "responseCookies":
case "requestPostData":
- this.updateRequest(actor, {
- // This field helps knowing when/if requestPostData property is available
- // and can be requested via `requestData`
- requestPostDataAvailable: true
- });
+ // This field helps knowing when/if updateType property is available
+ // and can be requested via `requestData`
+ this.updateRequest(actor, { [`${updateType}Available`]: true });
break;
case "securityInfo":
this.updateRequest(actor, { securityState: networkInfo.securityInfo });
break;
case "responseStart":
this.updateRequest(actor, {
httpVersion: networkInfo.response.httpVersion,
remoteAddress: networkInfo.response.remoteAddress,
@@ -549,17 +552,17 @@ class FirefoxDataProvider {
let response = await new Promise((resolve, reject) => {
// Do a RDP request to fetch data from the actor.
if (typeof this.webConsoleClient[clientMethodName] === "function") {
// Make sure we fetch the real actor data instead of cloned actor
// e.g. CustomRequestPanel will clone a request with additional '-clone' actor id
this.webConsoleClient[clientMethodName](actor.replace("-clone", ""), (res) => {
if (res.error) {
- console.error(res.error);
+ console.error(res.message);
}
resolve(res);
});
} else {
reject(new Error(`Error: No such client method '${clientMethodName}'!`));
}
});
@@ -586,22 +589,22 @@ class FirefoxDataProvider {
});
}
/**
* Handles additional information received for a "requestCookies" packet.
*
* @param {object} response the message received from the server.
*/
- onRequestCookies(response) {
- return this.updateRequest(response.from, {
+ async onRequestCookies(response) {
+ let payload = await this.updateRequest(response.from, {
requestCookies: response
- }).then(() => {
- emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
});
+ emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
+ return payload.requestCookies;
}
/**
* Handles additional information received for a "requestPostData" packet.
*
* @param {object} response the message received from the server.
*/
async onRequestPostData(response) {
@@ -638,22 +641,22 @@ class FirefoxDataProvider {
});
}
/**
* Handles additional information received for a "responseCookies" packet.
*
* @param {object} response the message received from the server.
*/
- onResponseCookies(response) {
- return this.updateRequest(response.from, {
+ async onResponseCookies(response) {
+ let payload = await this.updateRequest(response.from, {
responseCookies: response
- }).then(() => {
- emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
});
+ emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
+ return payload.responseCookies;
}
/**
* Handles additional information received via "getResponseContent" request.
*
* @param {object} response the message received from the server.
*/
async onResponseContent(response) {
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -118,20 +118,22 @@ const UPDATE_PROPS = [
"transferredSize",
"totalTime",
"eventTimings",
"headersSize",
"customQueryValue",
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
+ "requestCookiesAvailable",
"requestPostData",
"requestPostDataAvailable",
"responseHeaders",
"responseCookies",
+ "responseCookiesAvailable",
"responseContent",
"responseContentAvailable",
"formDataSections",
"stacktrace",
];
const PANELS = {
COOKIES: "cookies",
--- a/devtools/client/netmonitor/src/har/har-builder.js
+++ b/devtools/client/netmonitor/src/har/har-builder.js
@@ -263,17 +263,16 @@ HarBuilder.prototype = {
}
// If we are dealing with URL encoded body, parse parameters.
if (CurlUtils.isUrlEncodedRequest({
headers: requestHeaders.headers,
postDataText: postData.text,
})) {
postData.mimeType = "application/x-www-form-urlencoded";
-
// Extract form parameters and produce nice HAR array.
let formDataSections = await getFormDataSections(
requestHeaders,
requestHeadersFromUploadStream,
requestPostData,
this._options.getString,
);
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
@@ -17,17 +17,17 @@ add_task(function* () {
let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu");
let { getSortedRequests } = windowRequire(
"devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
// Execute one POST request on the page and wait till its done.
- let wait = waitForNetworkEvents(monitor, 0, 1);
+ let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.executeTest();
});
yield wait;
// Copy HAR into the clipboard (asynchronous).
let contextMenu = new RequestListContextMenu({ connector });
let jsonString = yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
@@ -42,17 +42,17 @@ function* throttleUploadTest(actuallyThr
info("sending throttle request");
yield new Promise((resolve) => {
connector.setPreferences(request, (response) => {
resolve(response);
});
});
// Execute one POST request on the page and wait till its done.
- let wait = waitForNetworkEvents(monitor, 0, 1);
+ let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, { size }, function* (args) {
content.wrappedJSObject.executeTest2(args.size);
});
yield wait;
// Copy HAR into the clipboard (asynchronous).
let contextMenu = new RequestListContextMenu({ connector });
let jsonString = yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
--- a/devtools/client/netmonitor/src/request-list-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-context-menu.js
@@ -38,16 +38,17 @@ class RequestListContextMenu {
let copySubmenu = [];
let {
id,
isCustom,
method,
mimeType,
httpVersion,
requestHeaders,
+ requestPostData,
requestPostDataAvailable,
responseHeaders,
responseContentAvailable,
url,
} = selectedRequest || {};
let {
cloneSelectedRequest,
openStatistics,
@@ -68,17 +69,17 @@ class RequestListContextMenu {
visible: !!(selectedRequest && getUrlQuery(url)),
click: () => this.copyUrlParams(url),
});
copySubmenu.push({
id: "request-list-context-copy-post-data",
label: L10N.getStr("netmonitor.context.copyPostData"),
accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
- visible: !!(selectedRequest && requestPostDataAvailable),
+ visible: !!(selectedRequest && (requestPostDataAvailable || requestPostData)),
click: () => this.copyPostData(id),
});
copySubmenu.push({
id: "request-list-context-copy-as-curl",
label: L10N.getStr("netmonitor.context.copyAsCurl"),
accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
visible: !!selectedRequest,
--- a/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
+++ b/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
@@ -1,17 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
"use strict";
const { FILTER_FLAGS } = require("../constants");
-/*
+/**
* Generates a value for the given filter
* ie. if flag = status-code, will generate "200" from the given request item.
* For flags related to cookies, it might generate an array based on the request
* ie. ["cookie-name-1", "cookie-name-2", ...]
*
* @param {string} flag - flag specified in filter, ie. "status-code"
* @param {object} request - Network request item
* @return {string|Array} - The output is a string or an array based on the request
@@ -65,17 +65,17 @@ function getAutocompleteValuesForFlag(fl
case "method":
default:
values.push(request[flag]);
}
return values;
}
-/*
+/**
* For a given lastToken passed ie. "is:", returns an array of populated flag
* values for consumption in autocompleteProvider
* ie. ["is:cached", "is:running", "is:from-cache"]
*
* @param {string} lastToken - lastToken parsed from filter input, ie "is:"
* @param {object} requests - List of requests from which values are generated
* @return {Array} - array of autocomplete values
*/
--- a/devtools/client/netmonitor/test/browser_net_autoscroll.js
+++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js
@@ -70,18 +70,16 @@ add_task(function* () {
return waitUntil(() => !!document.querySelector(".requests-list-contents"));
}
function* waitForRequestsToOverflowContainer() {
info("Waiting for enough requests to overflow the container");
while (true) {
info("Waiting for one network request");
yield waitForNetworkEvents(monitor, 1);
- console.log(requestsContainer.scrollHeight);
- console.log(requestsContainer.clientHeight);
if (requestsContainer.scrollHeight > requestsContainer.clientHeight) {
info("The list is long enough, returning");
return;
}
}
}
function scrolledToBottom(element) {
--- a/devtools/client/netmonitor/test/browser_net_cause.js
+++ b/devtools/client/netmonitor/test/browser_net_cause.js
@@ -97,41 +97,41 @@ add_task(function* () {
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
tab.linkedBrowser.loadURI(CAUSE_URL);
yield wait;
- // Fetch stack-trace data from the backend and wait till
- // all packets are received.
let requests = getSortedRequests(store.getState());
yield Promise.all(requests.map(requestItem =>
connector.requestData(requestItem.id, "stackTrace")));
is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
"All the page events should be recorded.");
- EXPECTED_REQUESTS.forEach((spec, i) => {
+ EXPECTED_REQUESTS.forEach(async (spec, i) => {
let { method, url, causeType, causeUri, stack } = spec;
let requestItem = getSortedRequests(store.getState()).get(i);
verifyRequestItemTarget(
document,
getDisplayedRequests(store.getState()),
requestItem,
method,
url,
{ cause: { type: causeType, loadingDocumentUri: causeUri } }
);
let stacktrace = requestItem.stacktrace;
let stackLen = stacktrace ? stacktrace.length : 0;
+ await waitUntil(() => !!requestItem.stacktrace);
+
if (stack) {
ok(stacktrace, `Request #${i} has a stacktrace`);
ok(stackLen > 0,
`Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
// if "stack" is array, check the details about the top stack frames
if (Array.isArray(stack)) {
stack.forEach((frame, j) => {
--- a/devtools/client/netmonitor/test/browser_net_charts-01.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-01.js
@@ -3,22 +3,27 @@
"use strict";
/**
* Makes sure Pie Charts have the right internal structure.
*/
add_task(function* () {
- let { monitor } = yield initNetMonitor(SIMPLE_URL);
+ let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
+
info("Starting test... ");
let { document, windowRequire } = monitor.panelWin;
let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
+ let wait = waitForNetworkEvents(monitor, 1);
+ tab.linkedBrowser.loadURI(SIMPLE_URL);
+ yield wait;
+
let pie = Chart.Pie(document, {
width: 100,
height: 100,
data: [{
size: 1,
label: "foo"
}, {
size: 2,
--- a/devtools/client/netmonitor/test/browser_net_charts-02.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-02.js
@@ -6,22 +6,26 @@
/**
* Makes sure Pie Charts have the right internal structure when
* initialized with empty data.
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
- let { monitor } = yield initNetMonitor(SIMPLE_URL);
+ let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, windowRequire } = monitor.panelWin;
let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
+ let wait = waitForNetworkEvents(monitor, 1);
+ tab.linkedBrowser.loadURI(SIMPLE_URL);
+ yield wait;
+
let pie = Chart.Pie(document, {
data: null,
width: 100,
height: 100
});
let node = pie.node;
let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob");
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -5,22 +5,26 @@
/**
* Makes sure Table Charts have the right internal structure.
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
- let { monitor } = yield initNetMonitor(SIMPLE_URL);
+ let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, windowRequire } = monitor.panelWin;
let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
+ let wait = waitForNetworkEvents(monitor, 1);
+ tab.linkedBrowser.loadURI(SIMPLE_URL);
+ yield wait;
+
let table = Chart.Table(document, {
title: "Table title",
data: [{
label1: 1,
label2: 11.1
}, {
label1: 2,
label2: 12.2
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -6,22 +6,26 @@
/**
* Makes sure Pie Charts have the right internal structure when
* initialized with empty data.
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
- let { monitor } = yield initNetMonitor(SIMPLE_URL);
+ let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, windowRequire } = monitor.panelWin;
let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
+ let wait = waitForNetworkEvents(monitor, 1);
+ tab.linkedBrowser.loadURI(SIMPLE_URL);
+ yield wait;
+
let table = Chart.Table(document, {
title: "Table title",
data: null,
totals: {
label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
label2: value => "World " + L10N.numberWithDecimals(value, 2)
},
header: {
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -5,22 +5,26 @@
/**
* Makes sure Pie+Table Charts have the right internal structure.
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
- let { monitor } = yield initNetMonitor(SIMPLE_URL);
+ let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, windowRequire } = monitor.panelWin;
let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
+ let wait = waitForNetworkEvents(monitor, 1);
+ tab.linkedBrowser.loadURI(SIMPLE_URL);
+ yield wait;
+
let chart = Chart.PieTable(document, {
title: "Table title",
data: [{
size: 1,
label: 11.1
}, {
size: 2,
label: 12.2
--- a/devtools/client/netmonitor/test/browser_net_charts-06.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-06.js
@@ -5,22 +5,26 @@
/**
* Makes sure Pie Charts correctly handle empty source data.
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
- let { monitor } = yield initNetMonitor(SIMPLE_URL);
+ let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, windowRequire } = monitor.panelWin;
let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
+ let wait = waitForNetworkEvents(monitor, 1);
+ tab.linkedBrowser.loadURI(SIMPLE_URL);
+ yield wait;
+
let pie = Chart.Pie(document, {
data: [],
width: 100,
height: 100
});
let node = pie.node;
let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob");
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -5,22 +5,26 @@
/**
* Makes sure Table Charts correctly handle empty source data.
*/
add_task(function* () {
let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
- let { monitor } = yield initNetMonitor(SIMPLE_URL);
+ let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
info("Starting test... ");
let { document, windowRequire } = monitor.panelWin;
let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
+ let wait = waitForNetworkEvents(monitor, 1);
+ tab.linkedBrowser.loadURI(SIMPLE_URL);
+ yield wait;
+
let table = Chart.Table(document, {
data: [],
totals: {
label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
label2: value => "World " + L10N.numberWithDecimals(value, 2)
},
header: {
label1: "",
--- a/devtools/client/netmonitor/test/browser_net_complex-params.js
+++ b/devtools/client/netmonitor/test/browser_net_complex-params.js
@@ -13,17 +13,17 @@ add_task(function* () {
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 1, 6);
+ let wait = waitForNetworkEvents(monitor, 7);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
wait = waitForDOM(document, "#params-panel .tree-section", 2);
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[0]);
--- a/devtools/client/netmonitor/test/browser_net_copy_params.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_params.js
@@ -11,17 +11,17 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(PARAMS_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 1, 6);
+ let wait = waitForNetworkEvents(monitor, 7);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
yield testCopyUrlParamsHidden(0, false);
yield testCopyUrlParams(0, "a");
yield testCopyPostDataHidden(0, false);
--- a/devtools/client/netmonitor/test/browser_net_cors_requests.js
+++ b/devtools/client/netmonitor/test/browser_net_cors_requests.js
@@ -14,17 +14,17 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getDisplayedRequests,
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 1, 1);
+ let wait = waitForNetworkEvents(monitor, 2);
info("Performing a CORS request");
let requestUrl = "http://test1.example.com" + CORS_SJS_PATH;
yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) {
content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data");
});
info("Waiting until the requests appear in netmonitor");
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -20,17 +20,17 @@ add_task(function* () {
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
let {
getLongString,
requestData,
} = connector;
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 1, 3);
+ let wait = waitForNetworkEvents(monitor, 4);
yield ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, function* (url) {
content.wrappedJSObject.performRequests(url);
});
yield wait;
let requests = {
get: getSortedRequests(store.getState()).get(0),
post: getSortedRequests(store.getState()).get(1),
--- a/devtools/client/netmonitor/test/browser_net_header-docs.js
+++ b/devtools/client/netmonitor/test/browser_net_header-docs.js
@@ -15,17 +15,17 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
let { getHeadersURL } = require("devtools/client/netmonitor/src/utils/mdn-utils");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 2);
+ let wait = waitForNetworkEvents(monitor, 2);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
EventUtils.sendMouseEvent({ type: "click" },
document.querySelectorAll(".request-list-item")[0]);
--- a/devtools/client/netmonitor/test/browser_net_headers-alignment.js
+++ b/devtools/client/netmonitor/test/browser_net_headers-alignment.js
@@ -53,17 +53,15 @@ add_task(function* () {
return waitUntil(() => !!document.querySelector(".requests-list-contents"));
}
function* waitForRequestsToOverflowContainer() {
info("Waiting for enough requests to overflow the container");
while (true) {
info("Waiting for one network request");
yield waitForNetworkEvents(monitor, 1);
- console.log(requestsContainer.scrollHeight);
- console.log(requestsContainer.clientHeight);
if (requestsContainer.scrollHeight > requestsContainer.clientHeight) {
info("The list is long enough, returning");
return;
}
}
}
});
--- a/devtools/client/netmonitor/test/browser_net_params_sorted.js
+++ b/devtools/client/netmonitor/test/browser_net_params_sorted.js
@@ -11,17 +11,17 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 2);
+ let wait = waitForNetworkEvents(monitor, 2);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
wait = waitForDOM(document, ".headers-overview");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[0]);
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js
@@ -20,17 +20,17 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getDisplayedRequests,
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 2);
+ let wait = waitForNetworkEvents(monitor, 2);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
let requestItems = document.querySelectorAll(".request-list-item");
for (let requestItem of requestItems) {
requestItem.scrollIntoView();
--- a/devtools/client/netmonitor/test/browser_net_post-data-02.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-02.js
@@ -14,17 +14,17 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(POST_RAW_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 1);
+ let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
// Wait for all tree view updated by react
wait = waitForDOM(document, "#params-panel .tree-section");
EventUtils.sendMouseEvent({ type: "mousedown" },
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -14,17 +14,17 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(POST_RAW_WITH_HEADERS_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 1);
+ let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
// Wait for all tree view updated by react
wait = waitForDOM(document, "#headers-panel .tree-section .treeLabel", 3);
EventUtils.sendMouseEvent({ type: "click" },
--- a/devtools/client/netmonitor/test/browser_net_post-data-04.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -14,17 +14,17 @@ add_task(function* () {
let { tab, monitor } = yield initNetMonitor(POST_JSON_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 1);
+ let wait = waitForNetworkEvents(monitor, 1);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
// Wait for all tree view updated by react
wait = waitForDOM(document, "#params-panel .tree-section");
EventUtils.sendMouseEvent({ type: "click" },
--- a/devtools/client/netmonitor/test/browser_net_raw_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_raw_headers.js
@@ -14,32 +14,30 @@ add_task(function* () {
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 2);
+ let wait = waitForNetworkEvents(monitor, 2);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
wait = waitForDOM(document, ".headers-overview");
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll(".request-list-item")[0]);
yield wait;
- let onRequestPostData = monitor.panelWin.once(EVENTS.RECEIVED_REQUEST_POST_DATA);
wait = waitForDOM(document, ".raw-headers-container textarea", 2);
EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersButton());
yield wait;
- yield onRequestPostData;
testRawHeaderButtonStyle(true);
testShowRawHeaders(getSortedRequests(store.getState()).get(0));
EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersButton());
testRawHeaderButtonStyle(false);
--- a/devtools/client/netmonitor/test/browser_net_resend.js
+++ b/devtools/client/netmonitor/test/browser_net_resend.js
@@ -21,17 +21,17 @@ add_task(function* () {
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getSelectedRequest,
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
- let wait = waitForNetworkEvents(monitor, 0, 2);
+ let wait = waitForNetworkEvents(monitor, 2);
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.wrappedJSObject.performRequests();
});
yield wait;
let origItem = getSortedRequests(store.getState()).get(0);
store.dispatch(Actions.selectRequest(origItem.id));
@@ -47,17 +47,17 @@ add_task(function* () {
// edit the custom request
yield editCustomForm();
// FIXME: reread the customItem, it's been replaced by a new object (immutable!)
customItem = getSelectedRequest(store.getState());
testCustomItemChanged(customItem, origItem);
// send the new request
- wait = waitForNetworkEvents(monitor, 0, 1);
+ wait = waitForNetworkEvents(monitor, 1);
store.dispatch(Actions.sendCustomRequest(connector));
yield wait;
let sentItem = getSelectedRequest(store.getState());
yield testSentRequest(sentItem, origItem);
// Ensure the UI shows the new request, selected, and that the detail panel was closed.
--- a/devtools/client/netmonitor/test/browser_net_resend_cors.js
+++ b/devtools/client/netmonitor/test/browser_net_resend_cors.js
@@ -18,34 +18,34 @@ add_task(function* () {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
store.dispatch(Actions.batchEnable(false));
let requestUrl = "http://test1.example.com" + CORS_SJS_PATH;
info("Waiting for OPTIONS, then POST");
- let wait = waitForNetworkEvents(monitor, 1, 1);
+ let wait = waitForNetworkEvents(monitor, 2);
yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) {
content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data");
});
yield wait;
const METHODS = ["OPTIONS", "POST"];
const ITEMS = METHODS.map((val, i) => getSortedRequests(store.getState()).get(i));
// Check the requests that were sent
ITEMS.forEach((item, i) => {
is(item.method, METHODS[i], `The ${item.method} request has the right method`);
is(item.url, requestUrl, `The ${item.method} request has the right URL`);
});
// Resend both requests without modification. Wait for resent OPTIONS, then POST.
// POST is supposed to have no preflight OPTIONS request this time (CORS is disabled)
- let onRequests = waitForNetworkEvents(monitor, 1, 0);
+ let onRequests = waitForNetworkEvents(monitor, 1);
ITEMS.forEach((item) => {
info(`Selecting the ${item.method} request`);
store.dispatch(Actions.selectRequest(item.id));
info("Cloning the selected request into a custom clone");
store.dispatch(Actions.cloneSelectedRequest());
info("Sending the cloned request (without change)");
--- a/devtools/client/netmonitor/test/browser_net_resend_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_resend_headers.js
@@ -25,17 +25,17 @@ add_task(function* () {
{ name: "Host", value: "fakehost.example.com" },
{ name: "User-Agent", value: "Testzilla" },
{ name: "Referer", value: "http://example.com/referrer" },
{ name: "Accept", value: "application/jarda"},
{ name: "Accept-Encoding", value: "compress, identity, funcoding" },
{ name: "Accept-Language", value: "cs-CZ" }
];
- let wait = waitForNetworkEvents(monitor, 0, 1);
+ let wait = waitForNetworkEvents(monitor, 1);
sendHTTPRequest({
url: requestUrl,
method: "POST",
headers: requestHeaders,
body: "Hello"
});
yield wait;
--- a/devtools/client/netmonitor/test/browser_net_throttle.js
+++ b/devtools/client/netmonitor/test/browser_net_throttle.js
@@ -11,17 +11,16 @@ add_task(function* () {
});
function* throttleTest(actuallyThrottle) {
requestLongerTimeout(2);
let { monitor } = yield initNetMonitor(SIMPLE_URL);
let { store, windowRequire, connector } = monitor.panelWin;
let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
- let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
let { setPreferences, triggerActivity } = connector;
let {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
// When throttling, must be smaller than the length of the content
@@ -41,19 +40,19 @@ function* throttleTest(actuallyThrottle)
info("sending throttle request");
yield new Promise((resolve) => {
setPreferences(request, response => {
resolve(response);
});
});
- let eventPromise = monitor.panelWin.once(EVENTS.RECEIVED_EVENT_TIMINGS);
+ let wait = waitForNetworkEvents(monitor, 1);
yield triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED);
- yield eventPromise;
+ yield wait;
yield waitUntil(() => {
let requestItem = getSortedRequests(store.getState()).get(0);
return requestItem && requestItem.eventTimings;
});
let requestItem = getSortedRequests(store.getState()).get(0);
const reportedOneSecond = requestItem.eventTimings.timings.receive > 1000;
--- a/devtools/client/netmonitor/test/browser_net_timing-division.js
+++ b/devtools/client/netmonitor/test/browser_net_timing-division.js
@@ -3,22 +3,16 @@
"use strict";
/**
* Tests if timing intervals are divided againts seconds when appropriate.
*/
add_task(function* () {
- // Make sure timing division can render properly
- Services.prefs.setCharPref(
- "devtools.netmonitor.visibleColumns",
- "[\"waterfall\"]"
- );
-
let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let {
getSortedRequests,
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
--- a/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
+++ b/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
@@ -12,17 +12,17 @@ add_task(async function () {
let { tab, monitor, toolbox } = await initNetMonitor(POST_DATA_URL);
info("Starting test... ");
let { document, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
store.dispatch(Actions.batchEnable(false));
- let waitForContentRequests = waitForNetworkEvents(monitor, 0, 2);
+ let waitForContentRequests = waitForNetworkEvents(monitor, 2);
await ContentTask.spawn(tab.linkedBrowser, {},
() => content.wrappedJSObject.performRequests());
await waitForContentRequests;
info("Clicking stack-trace tab and waiting for stack-trace panel to open");
let wait = waitForDOM(document, "#stack-trace-panel .frame-link", 4);
// Click on the first request
EventUtils.sendMouseEvent({ type: "mousedown" },
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -216,16 +216,55 @@ function waitForAllRequestsFinished(moni
resolve();
}
window.on(EVENTS.NETWORK_EVENT, onRequest);
window.on(EVENTS.PAYLOAD_READY, onTimings);
});
}
+let finishedQueue = {};
+let updatingTypes = [
+ "NetMonitor:NetworkEventUpdating:RequestCookies",
+ "NetMonitor:NetworkEventUpdating:ResponseCookies",
+];
+let updatedTypes = [
+ "NetMonitor:NetworkEventUpdated:RequestCookies",
+ "NetMonitor:NetworkEventUpdated:ResponseCookies",
+];
+
+// Start collecting all networkEventUpdate event when panel is opened.
+// removeTab() should be called once all corresponded RECEIVED_* events finished.
+function startNetworkEventUpdateObserver(panelWin) {
+ updatingTypes.forEach((type) => panelWin.on(type, (event, actor) => {
+ let key = actor + "-" + event.replace("NetMonitor:NetworkEventUpdating:", "");
+ finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] + 1 : 1;
+ }));
+
+ updatedTypes.forEach((type) => panelWin.on(type, (event, actor) => {
+ let key = actor + "-" + event.replace("NetMonitor:NetworkEventUpdated:", "");
+ finishedQueue[key]--;
+ }));
+}
+
+function* waitForAllNetworkUpdateEvents() {
+ function checkNetworkEventUpdateState() {
+ for (let key in finishedQueue) {
+ if (finishedQueue[key] > 0) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ info("Wait for completion of all NetworkUpdateEvents packets...");
+ yield waitUntil(() => checkNetworkEventUpdateState());
+ finishedQueue = {};
+}
+
function initNetMonitor(url, enableCache) {
info("Initializing a network monitor pane.");
return Task.spawn(function* () {
let tab = yield addTab(url);
info("Net tab added successfully: " + url);
let target = TargetFactory.forTab(tab);
@@ -233,16 +272,18 @@ function initNetMonitor(url, enableCache
yield target.makeRemote();
info("Target remoted.");
let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
info("Network monitor pane shown successfully.");
let monitor = toolbox.getCurrentPanel();
+ startNetworkEventUpdateObserver(monitor.panelWin);
+
if (!enableCache) {
let panel = monitor.panelWin;
let { store, windowRequire } = panel;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
info("Disabling cache and reloading page.");
let requestsDone = waitForAllRequestsFinished(monitor);
let markersDone = waitForTimelineMarkers(monitor);
@@ -266,16 +307,19 @@ function initNetMonitor(url, enableCache
function restartNetMonitor(monitor, newUrl) {
info("Restarting the specified network monitor.");
return Task.spawn(function* () {
let tab = monitor.toolbox.target.tab;
let url = newUrl || tab.linkedBrowser.currentURI.spec;
+ yield waitForAllNetworkUpdateEvents();
+ info("All pending requests finished.");
+
let onDestroyed = monitor.once("destroyed");
yield removeTab(tab);
yield onDestroyed;
return initNetMonitor(url);
});
}
@@ -284,40 +328,37 @@ function teardown(monitor) {
return Task.spawn(function* () {
let tab = monitor.toolbox.target.tab;
// Ensure that there is no pending RDP requests related to payload request
// done from FirefoxDataProvider.
info("Wait for completion of all pending RDP requests...");
yield waitForExistingRequests(monitor);
+ yield waitForAllNetworkUpdateEvents();
info("All pending requests finished.");
let onDestroyed = monitor.once("destroyed");
yield removeTab(tab);
yield onDestroyed;
});
}
-function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
+function waitForNetworkEvents(monitor, getRequests) {
return new Promise((resolve) => {
let panel = monitor.panelWin;
let { getNetworkRequest } = panel.connector;
let progress = {};
let genericEvents = 0;
let payloadReady = 0;
let awaitedEventsToListeners = [
["UPDATING_REQUEST_HEADERS", onGenericEvent],
["RECEIVED_REQUEST_HEADERS", onGenericEvent],
- ["UPDATING_REQUEST_COOKIES", onGenericEvent],
- ["RECEIVED_REQUEST_COOKIES", onGenericEvent],
["UPDATING_RESPONSE_HEADERS", onGenericEvent],
["RECEIVED_RESPONSE_HEADERS", onGenericEvent],
- ["UPDATING_RESPONSE_COOKIES", onGenericEvent],
- ["RECEIVED_RESPONSE_COOKIES", onGenericEvent],
["UPDATING_EVENT_TIMINGS", onGenericEvent],
["RECEIVED_EVENT_TIMINGS", onGenericEvent],
["PAYLOAD_READY", onPayloadReady]
];
let expectedGenericEvents = awaitedEventsToListeners
.filter(([, listener]) => listener == onGenericEvent).length;
function initProgressForURL(url) {
@@ -354,33 +395,32 @@ function waitForNetworkEvents(monitor, g
return;
}
payloadReady++;
maybeResolve(event, actor, networkInfo);
}
function maybeResolve(event, actor, networkInfo) {
- info("> Network events progress: " +
- "Payload: " + payloadReady + "/" + (getRequests + postRequests) + ", " +
- "Generic: " + genericEvents + "/" +
- ((getRequests + postRequests) * expectedGenericEvents) + ", " +
+ info("> Network event progress: " +
+ "Payload: " + payloadReady + "/" + getRequests + ", " +
+ "Generic: " + genericEvents + "/" + (getRequests * expectedGenericEvents) + ", " +
"got " + event + " for " + actor);
let url = networkInfo.request.url;
updateProgressForURL(url, event);
// Uncomment this to get a detailed progress logging (when debugging a test)
// info("> Current state: " + JSON.stringify(progress, null, 2));
// There are `expectedGenericEvents` updates which need to be fired for a request
// to be considered finished. The "requestPostData" packet isn't fired for non-POST
// requests.
- if (payloadReady >= (getRequests + postRequests) &&
- genericEvents >= (getRequests + postRequests) * expectedGenericEvents) {
+ if (payloadReady >= getRequests &&
+ genericEvents >= getRequests * expectedGenericEvents) {
awaitedEventsToListeners.forEach(([e, l]) => panel.off(EVENTS[e], l));
executeSoon(resolve);
}
}
awaitedEventsToListeners.forEach(([e, l]) => panel.on(EVENTS[e], l));
});
}
--- a/devtools/client/netmonitor/test/shared-head.js
+++ b/devtools/client/netmonitor/test/shared-head.js
@@ -17,21 +17,19 @@ async function waitForExistingRequests(m
for (let request of requests) {
// Ignore cloned request as we don't lazily fetch data for them
// and have arbitrary number of field set.
if (request.id.includes("-clone")) {
continue;
}
// Do same check than FirefoxDataProvider.isRequestPayloadReady,
// in order to ensure there is no more pending payload requests to be done.
- if (!request.requestHeaders || !request.requestCookies ||
- !request.eventTimings ||
- ((!request.responseHeaders || !request.responseCookies) &&
- request.securityState != "broken" &&
- (!request.responseContentAvailable || request.status))) {
+ if (!request.requestHeaders || !request.eventTimings ||
+ (!request.responseHeaders && request.securityState !== "broken" &&
+ (!request.responseContentAvailable || request.status))) {
return false;
}
}
return true;
}
// If there is no request, we are good to go.
if (getRequests().size == 0) {
return;
--- a/devtools/client/shared/components/SearchBox.js
+++ b/devtools/client/shared/components/SearchBox.js
@@ -13,16 +13,19 @@ const KeyShortcuts = require("devtools/c
const AutocompletePopup = createFactory(require("devtools/client/shared/components/AutoCompletePopup"));
class SearchBox extends Component {
static get propTypes() {
return {
delay: PropTypes.number,
keyShortcut: PropTypes.string,
onChange: PropTypes.func,
+ onFocus: PropTypes.func,
+ onBlur: PropTypes.func,
+ onKeyDown: PropTypes.func,
placeholder: PropTypes.string,
type: PropTypes.string,
autocompleteProvider: PropTypes.func,
};
}
constructor(props) {
super(props);
@@ -91,24 +94,36 @@ class SearchBox extends Component {
}
onClearButtonClick() {
this.refs.input.value = "";
this.onChange();
}
onFocus() {
+ if (this.props.onFocus) {
+ this.props.onFocus();
+ }
+
this.setState({ focused: true });
}
onBlur() {
+ if (this.props.onBlur) {
+ this.props.onBlur();
+ }
+
this.setState({ focused: false });
}
onKeyDown(e) {
+ if (this.props.onKeyDown) {
+ this.props.onKeyDown();
+ }
+
let { autocomplete } = this.refs;
if (!autocomplete || autocomplete.state.list.length <= 0) {
return;
}
switch (e.key) {
case "ArrowDown":
autocomplete.jumpBy(1);