mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 01:35:35 +00:00
Bug 1418927 - requestHeaders and responseHeaders should be loaded lazily r=Honza,ochameau
MozReview-Commit-ID: 5ADM5AYdJNI --HG-- extra : rebase_source : 05c1506489ef5af67cd9fd3911e2b3c3e4659e05
This commit is contained in:
parent
36f8852817
commit
cf0c6d5220
@ -242,6 +242,7 @@ class RequestListContent extends Component {
|
||||
onItemMouseDown,
|
||||
onSecurityIconMouseDown,
|
||||
onWaterfallMouseDown,
|
||||
requestFilterTypes,
|
||||
scale,
|
||||
selectedRequest,
|
||||
} = this.props;
|
||||
@ -272,6 +273,7 @@ class RequestListContent extends Component {
|
||||
onCauseBadgeMouseDown: () => onCauseBadgeMouseDown(item.cause),
|
||||
onSecurityIconMouseDown: () => onSecurityIconMouseDown(item.securityState),
|
||||
onWaterfallMouseDown: () => onWaterfallMouseDown(),
|
||||
requestFilterTypes,
|
||||
}))
|
||||
)
|
||||
)
|
||||
|
@ -7,7 +7,10 @@
|
||||
const { Component, createFactory } = require("devtools/client/shared/vendor/react");
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const { propertiesEqual } = require("../utils/request-utils");
|
||||
const {
|
||||
fetchNetworkUpdatePacket,
|
||||
propertiesEqual,
|
||||
} = require("../utils/request-utils");
|
||||
const { RESPONSE_HEADERS } = require("../constants");
|
||||
|
||||
// Components
|
||||
@ -57,13 +60,16 @@ const UPDATED_REQ_ITEM_PROPS = [
|
||||
"startedMillis",
|
||||
"totalTime",
|
||||
"requestCookies",
|
||||
"requestHeaders",
|
||||
"responseCookies",
|
||||
"responseHeaders",
|
||||
];
|
||||
|
||||
const UPDATED_REQ_PROPS = [
|
||||
"firstRequestStartedMillis",
|
||||
"index",
|
||||
"isSelected",
|
||||
"requestFilterTypes",
|
||||
"waterfallWidth",
|
||||
];
|
||||
|
||||
@ -86,6 +92,7 @@ class RequestListItem extends Component {
|
||||
onMouseDown: PropTypes.func.isRequired,
|
||||
onSecurityIconMouseDown: PropTypes.func.isRequired,
|
||||
onWaterfallMouseDown: PropTypes.func.isRequired,
|
||||
requestFilterTypes: PropTypes.string.isRequired,
|
||||
waterfallWidth: PropTypes.number,
|
||||
};
|
||||
}
|
||||
@ -94,6 +101,26 @@ class RequestListItem extends Component {
|
||||
if (this.props.isSelected) {
|
||||
this.refs.listItem.focus();
|
||||
}
|
||||
|
||||
let { connector, item, requestFilterTypes } = this.props;
|
||||
// Filtering XHR & WS require to lazily fetch requestHeaders & responseHeaders
|
||||
if (requestFilterTypes.get("xhr") || requestFilterTypes.get("ws")) {
|
||||
fetchNetworkUpdatePacket(connector.requestData, item, [
|
||||
"requestHeaders",
|
||||
"responseHeaders",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
let { connector, item, requestFilterTypes } = nextProps;
|
||||
// Filtering XHR & WS require to lazily fetch requestHeaders & responseHeaders
|
||||
if (requestFilterTypes.get("xhr") || requestFilterTypes.get("ws")) {
|
||||
fetchNetworkUpdatePacket(connector.requestData, item, [
|
||||
"requestHeaders",
|
||||
"responseHeaders",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
|
@ -46,8 +46,6 @@ class StackTracePanel extends Component {
|
||||
fetchNetworkUpdatePacket(connector.requestData, request, ["stackTrace"]);
|
||||
}
|
||||
|
||||
// Rendering
|
||||
|
||||
render() {
|
||||
let {
|
||||
connector,
|
||||
|
@ -18,9 +18,9 @@ const {
|
||||
getTypeFilteredRequests,
|
||||
isNetworkDetailsToggleButtonDisabled,
|
||||
} = require("../selectors/index");
|
||||
|
||||
const { autocompleteProvider } = require("../utils/filter-autocomplete-provider");
|
||||
const { L10N } = require("../utils/l10n");
|
||||
const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
|
||||
|
||||
// Components
|
||||
const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));
|
||||
@ -135,9 +135,12 @@ class Toolbar extends Component {
|
||||
onSearchBoxFocus() {
|
||||
let { connector, filteredRequests } = this.props;
|
||||
|
||||
// Fetch responseCookies for building autocomplete list
|
||||
// Fetch responseCookies & responseHeaders for building autocomplete list
|
||||
filteredRequests.forEach((request) => {
|
||||
connector.requestData(request.id, "responseCookies");
|
||||
fetchNetworkUpdatePacket(connector.requestData, request, [
|
||||
"responseCookies",
|
||||
"responseHeaders",
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -71,16 +71,18 @@ class FirefoxConnector {
|
||||
this.removeListeners();
|
||||
|
||||
if (this.tabTarget) {
|
||||
// Unregister `will-navigate` needs to be done before `this.timelineFront.destroy()`
|
||||
// since this.tabTarget might be nullified after timelineFront.destroy().
|
||||
this.tabTarget.off("will-navigate");
|
||||
// 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.off("will-navigate");
|
||||
this.tabTarget = null;
|
||||
}
|
||||
|
||||
this.webConsoleClient = null;
|
||||
this.timelineFront = null;
|
||||
this.dataProvider = null;
|
||||
|
@ -24,8 +24,7 @@ class FirefoxDataProvider {
|
||||
this.actions = actions;
|
||||
|
||||
// Internal properties
|
||||
this.payloadQueue = [];
|
||||
this.rdpRequestMap = new Map();
|
||||
this.payloadQueue = new Map();
|
||||
|
||||
// Map[key string => Promise] used by `requestData` to prevent requesting the same
|
||||
// request data twice.
|
||||
@ -33,7 +32,6 @@ class FirefoxDataProvider {
|
||||
|
||||
// Fetching data from the backend
|
||||
this.getLongString = this.getLongString.bind(this);
|
||||
this.getRequestFromQueue = this.getRequestFromQueue.bind(this);
|
||||
|
||||
// Event handlers
|
||||
this.onNetworkEvent = this.onNetworkEvent.bind(this);
|
||||
@ -72,9 +70,8 @@ class FirefoxDataProvider {
|
||||
stacktrace: cause.stacktrace,
|
||||
|
||||
fromCache,
|
||||
fromServiceWorker},
|
||||
true,
|
||||
);
|
||||
fromServiceWorker,
|
||||
}, true);
|
||||
}
|
||||
|
||||
emit(EVENTS.REQUEST_ADDED, id);
|
||||
@ -88,7 +85,6 @@ class FirefoxDataProvider {
|
||||
*/
|
||||
async updateRequest(id, data) {
|
||||
let {
|
||||
mimeType,
|
||||
responseContent,
|
||||
responseCookies,
|
||||
responseHeaders,
|
||||
@ -106,7 +102,7 @@ class FirefoxDataProvider {
|
||||
requestCookiesObj,
|
||||
responseCookiesObj,
|
||||
] = await Promise.all([
|
||||
this.fetchResponseContent(mimeType, responseContent),
|
||||
this.fetchResponseContent(responseContent),
|
||||
this.fetchRequestHeaders(requestHeaders),
|
||||
this.fetchResponseHeaders(responseHeaders),
|
||||
this.fetchPostData(requestPostData),
|
||||
@ -124,18 +120,24 @@ class FirefoxDataProvider {
|
||||
responseCookiesObj
|
||||
);
|
||||
|
||||
this.pushRequestToQueue(id, payload);
|
||||
if (this.actions.updateRequest) {
|
||||
await this.actions.updateRequest(id, payload, true);
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
async fetchResponseContent(mimeType, responseContent) {
|
||||
async fetchResponseContent(responseContent) {
|
||||
let payload = {};
|
||||
if (mimeType && responseContent && responseContent.content) {
|
||||
if (responseContent && responseContent.content) {
|
||||
let { text } = responseContent.content;
|
||||
let response = await this.getLongString(text);
|
||||
responseContent.content.text = response;
|
||||
payload.responseContent = responseContent;
|
||||
|
||||
// Lock down responseContentAvailable once we fetch data from back-end.
|
||||
// Using this as flag to prevent fetching arrived data again.
|
||||
payload.responseContentAvailable = false;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
@ -147,6 +149,10 @@ class FirefoxDataProvider {
|
||||
if (headers) {
|
||||
payload.requestHeaders = headers;
|
||||
}
|
||||
|
||||
// Lock down requestHeadersAvailable once we fetch data from back-end.
|
||||
// Using this as flag to prevent fetching arrived data again.
|
||||
payload.requestHeadersAvailable = false;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
@ -158,6 +164,10 @@ class FirefoxDataProvider {
|
||||
if (headers) {
|
||||
payload.responseHeaders = headers;
|
||||
}
|
||||
|
||||
// Lock down responseHeadersAvailable once we fetch data from back-end.
|
||||
// Using this as flag to prevent fetching arrived data again.
|
||||
payload.responseHeadersAvailable = false;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
@ -186,32 +196,6 @@ class FirefoxDataProvider {
|
||||
return payload;
|
||||
}
|
||||
|
||||
async fetchResponseCookies(responseCookies) {
|
||||
let payload = {};
|
||||
if (responseCookies) {
|
||||
let resCookies = [];
|
||||
// response store cookies in responseCookies or responseCookies.cookies
|
||||
let cookies = responseCookies.cookies ?
|
||||
responseCookies.cookies : responseCookies;
|
||||
// make sure cookies is iterable
|
||||
if (typeof cookies[Symbol.iterator] === "function") {
|
||||
for (let cookie of cookies) {
|
||||
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) {
|
||||
@ -238,14 +222,30 @@ class FirefoxDataProvider {
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a payload item from payload queue.
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @return {boolean} return a queued payload item from queue.
|
||||
*/
|
||||
getRequestFromQueue(id) {
|
||||
return this.payloadQueue.find((item) => item.id === id);
|
||||
async fetchResponseCookies(responseCookies) {
|
||||
let payload = {};
|
||||
if (responseCookies) {
|
||||
let resCookies = [];
|
||||
// response store cookies in responseCookies or responseCookies.cookies
|
||||
let cookies = responseCookies.cookies ?
|
||||
responseCookies.cookies : responseCookies;
|
||||
// make sure cookies is iterable
|
||||
if (typeof cookies[Symbol.iterator] === "function") {
|
||||
for (let cookie of cookies) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,51 +254,22 @@ class FirefoxDataProvider {
|
||||
* @return {boolean} returns true if the payload queue is empty
|
||||
*/
|
||||
isPayloadQueueEmpty() {
|
||||
return this.payloadQueue.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if payload is ready (all data fetched from the backend)
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @return {boolean} return whether a specific networkEvent has been updated completely.
|
||||
*/
|
||||
isRequestPayloadReady(id) {
|
||||
let record = this.rdpRequestMap.get(id);
|
||||
if (!record) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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.eventTimings &&
|
||||
(record.responseHeaders || payload.securityState === "broken" ||
|
||||
(!payload.status && payload.responseContentAvailable));
|
||||
return this.payloadQueue.size === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge upcoming networkEventUpdate payload into existing one.
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @param {string} id request actor id
|
||||
* @param {object} payload request data payload
|
||||
*/
|
||||
pushRequestToQueue(id, payload) {
|
||||
let request = this.getRequestFromQueue(id);
|
||||
if (!request) {
|
||||
this.payloadQueue.push({ id, payload });
|
||||
} else {
|
||||
// Merge upcoming networkEventUpdate payload into existing one
|
||||
request.payload = Object.assign({}, request.payload, payload);
|
||||
let payloadFromQueue = this.payloadQueue.get(id);
|
||||
if (!payloadFromQueue) {
|
||||
payloadFromQueue = {};
|
||||
this.payloadQueue.set(id, payloadFromQueue);
|
||||
}
|
||||
}
|
||||
|
||||
cleanUpQueue(id) {
|
||||
this.payloadQueue = this.payloadQueue.filter(
|
||||
request => request.id != id);
|
||||
Object.assign(payloadFromQueue, payload);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,7 +303,7 @@ class FirefoxDataProvider {
|
||||
* @param {string} type message type
|
||||
* @param {object} networkInfo network request information
|
||||
*/
|
||||
onNetworkEvent(type, networkInfo) {
|
||||
async onNetworkEvent(type, networkInfo) {
|
||||
let {
|
||||
actor,
|
||||
cause,
|
||||
@ -346,14 +317,7 @@ class FirefoxDataProvider {
|
||||
startedDateTime,
|
||||
} = networkInfo;
|
||||
|
||||
// Create tracking record for this request.
|
||||
this.rdpRequestMap.set(actor, {
|
||||
requestHeaders: false,
|
||||
responseHeaders: false,
|
||||
eventTimings: false,
|
||||
});
|
||||
|
||||
this.addRequest(actor, {
|
||||
await this.addRequest(actor, {
|
||||
cause,
|
||||
fromCache,
|
||||
fromServiceWorker,
|
||||
@ -373,116 +337,72 @@ class FirefoxDataProvider {
|
||||
* @param {object} packet the message received from the server.
|
||||
* @param {object} networkInfo the network request information.
|
||||
*/
|
||||
onNetworkEventUpdate(type, data) {
|
||||
async onNetworkEventUpdate(type, data) {
|
||||
let { packet, networkInfo } = data;
|
||||
let { actor } = networkInfo;
|
||||
let { updateType } = packet;
|
||||
|
||||
// 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 "responseHeaders":
|
||||
this.requestPayloadData(actor, updateType);
|
||||
break;
|
||||
case "requestCookies":
|
||||
case "responseCookies":
|
||||
case "requestPostData":
|
||||
// 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 });
|
||||
this.pushRequestToQueue(actor, { securityState: networkInfo.securityInfo });
|
||||
break;
|
||||
case "responseStart":
|
||||
this.updateRequest(actor, {
|
||||
this.pushRequestToQueue(actor, {
|
||||
httpVersion: networkInfo.response.httpVersion,
|
||||
remoteAddress: networkInfo.response.remoteAddress,
|
||||
remotePort: networkInfo.response.remotePort,
|
||||
status: networkInfo.response.status,
|
||||
statusText: networkInfo.response.statusText,
|
||||
headersSize: networkInfo.response.headersSize
|
||||
}).then(() => {
|
||||
emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
|
||||
});
|
||||
emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
|
||||
break;
|
||||
case "responseContent":
|
||||
this.updateRequest(actor, {
|
||||
this.pushRequestToQueue(actor, {
|
||||
contentSize: networkInfo.response.bodySize,
|
||||
transferredSize: networkInfo.response.transferredSize,
|
||||
mimeType: networkInfo.response.content.mimeType,
|
||||
// This field helps knowing when/if responseContent property is available
|
||||
// and can be requested via `requestData`
|
||||
responseContentAvailable: true,
|
||||
});
|
||||
break;
|
||||
case "eventTimings":
|
||||
this.pushRequestToQueue(actor, { totalTime: networkInfo.totalTime });
|
||||
this.requestPayloadData(actor, updateType);
|
||||
await this._requestData(actor, updateType);
|
||||
break;
|
||||
}
|
||||
|
||||
// This available field helps knowing when/if updateType property is arrived
|
||||
// and can be requested via `requestData`
|
||||
this.pushRequestToQueue(actor, { [`${updateType}Available`]: true });
|
||||
|
||||
this.onPayloadDataReceived(actor);
|
||||
|
||||
emit(EVENTS.NETWORK_EVENT_UPDATED, actor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper method for requesting HTTP details data for the payload.
|
||||
*
|
||||
* It is specific to all requests done from `onNetworkEventUpdate`, for data that are
|
||||
* immediately fetched whenever the data is available.
|
||||
*
|
||||
* All these requests are cached into `rdpRequestMap`. All requests related to a given
|
||||
* actor will be collected in the same record.
|
||||
*
|
||||
* Once bug 1404917 is completed, we should no longer use this method.
|
||||
* All request fields should be loaded only on-demand, via `requestData` method.
|
||||
*
|
||||
* @param {string} actor actor id (used as request id)
|
||||
* @param {string} method identifier of the data we want to fetch
|
||||
* Notify actions when messages from onNetworkEventUpdate are done, networkEventUpdate
|
||||
* messages contain initial network info for each updateType and then we can invoke
|
||||
* requestData to fetch its corresponded data lazily.
|
||||
* Once all updateTypes of networkEventUpdate message are arrived, we flush merged
|
||||
* request payload from pending queue and then update component.
|
||||
*/
|
||||
requestPayloadData(actor, method) {
|
||||
let record = this.rdpRequestMap.get(actor);
|
||||
async onPayloadDataReceived(actor) {
|
||||
let payload = this.payloadQueue.get(actor) || {};
|
||||
|
||||
// If data has been already requested, do nothing.
|
||||
if (record[method]) {
|
||||
if (!payload.requestHeadersAvailable || !payload.requestCookiesAvailable ||
|
||||
!payload.eventTimingsAvailable || !payload.responseContentAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
let promise = this._requestData(actor, method);
|
||||
promise.then(() => {
|
||||
// Once we got the data toggle the Map item to `true` in order to
|
||||
// make isRequestPayloadReady return `true` once all the data is fetched.
|
||||
record[method] = true;
|
||||
this.onPayloadDataReceived(actor, method, !record);
|
||||
});
|
||||
}
|
||||
this.payloadQueue.delete(actor);
|
||||
|
||||
/**
|
||||
* Executed when new data are received from the backend.
|
||||
*/
|
||||
async onPayloadDataReceived(actor, type) {
|
||||
// Notify actions when all the sync request from onNetworkEventUpdate are done,
|
||||
// or, everytime requestData is called for fetching data lazily.
|
||||
if (this.isRequestPayloadReady(actor)) {
|
||||
let payloadFromQueue = this.getRequestFromQueue(actor).payload;
|
||||
|
||||
// Clean up
|
||||
this.cleanUpQueue(actor);
|
||||
this.rdpRequestMap.delete(actor);
|
||||
|
||||
if (this.actions.updateRequest) {
|
||||
await this.actions.updateRequest(actor, payloadFromQueue, true);
|
||||
}
|
||||
|
||||
// This event is fired only once per request, once all the properties are fetched
|
||||
// from `onNetworkEventUpdate`. There should be no more RDP requests after this.
|
||||
emit(EVENTS.PAYLOAD_READY, actor);
|
||||
if (this.actions.updateRequest) {
|
||||
await this.actions.updateRequest(actor, payload, true);
|
||||
}
|
||||
|
||||
// This event is fired only once per request, once all the properties are fetched
|
||||
// from `onNetworkEventUpdate`. There should be no more RDP requests after this.
|
||||
emit(EVENTS.PAYLOAD_READY, actor);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -508,21 +428,20 @@ class FirefoxDataProvider {
|
||||
return promise;
|
||||
}
|
||||
// Fetch the data
|
||||
promise = this._requestData(actor, method);
|
||||
this.lazyRequestData.set(key, promise);
|
||||
promise.then(async () => {
|
||||
promise = this._requestData(actor, method).then(async (payload) => {
|
||||
// Remove the request from the cache, any new call to requestData will fetch the
|
||||
// data again.
|
||||
this.lazyRequestData.delete(key, promise);
|
||||
this.lazyRequestData.delete(key);
|
||||
|
||||
if (this.actions.updateRequest) {
|
||||
await this.actions.updateRequest(
|
||||
actor,
|
||||
this.getRequestFromQueue(actor).payload,
|
||||
true,
|
||||
);
|
||||
await this.actions.updateRequest(actor, payload, true);
|
||||
}
|
||||
|
||||
return payload;
|
||||
});
|
||||
|
||||
this.lazyRequestData.set(key, promise);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
@ -557,7 +476,7 @@ class FirefoxDataProvider {
|
||||
// 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.message);
|
||||
reject(new Error(res.message));
|
||||
}
|
||||
resolve(res);
|
||||
});
|
||||
@ -581,12 +500,25 @@ class FirefoxDataProvider {
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onRequestHeaders(response) {
|
||||
return this.updateRequest(response.from, {
|
||||
async onRequestHeaders(response) {
|
||||
let payload = await this.updateRequest(response.from, {
|
||||
requestHeaders: response
|
||||
}).then(() => {
|
||||
emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
|
||||
});
|
||||
emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
|
||||
return payload.requestHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseHeaders" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
async onResponseHeaders(response) {
|
||||
let payload = await this.updateRequest(response.from, {
|
||||
responseHeaders: response
|
||||
});
|
||||
emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
|
||||
return payload.responseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -620,25 +552,12 @@ class FirefoxDataProvider {
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onSecurityInfo(response) {
|
||||
return this.updateRequest(response.from, {
|
||||
async onSecurityInfo(response) {
|
||||
let payload = await this.updateRequest(response.from, {
|
||||
securityInfo: response.securityInfo
|
||||
}).then(() => {
|
||||
emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseHeaders" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onResponseHeaders(response) {
|
||||
return this.updateRequest(response.from, {
|
||||
responseHeaders: response
|
||||
}).then(() => {
|
||||
emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
|
||||
});
|
||||
emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
|
||||
return payload.securityInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -676,12 +595,12 @@ class FirefoxDataProvider {
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onEventTimings(response) {
|
||||
return this.updateRequest(response.from, {
|
||||
async onEventTimings(response) {
|
||||
let payload = await this.updateRequest(response.from, {
|
||||
eventTimings: response
|
||||
}).then(() => {
|
||||
emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
|
||||
});
|
||||
emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
|
||||
return payload.eventTimings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,8 +73,8 @@ const EVENTS = {
|
||||
RECEIVED_REQUEST_POST_DATA: "NetMonitor:NetworkEventUpdated:RequestPostData",
|
||||
|
||||
// When security information begins and finishes receiving.
|
||||
UPDATING_SECURITY_INFO: "NetMonitor::NetworkEventUpdating:SecurityInfo",
|
||||
RECEIVED_SECURITY_INFO: "NetMonitor::NetworkEventUpdated:SecurityInfo",
|
||||
UPDATING_SECURITY_INFO: "NetMonitor:NetworkEventUpdating:SecurityInfo",
|
||||
RECEIVED_SECURITY_INFO: "NetMonitor:NetworkEventUpdated:SecurityInfo",
|
||||
|
||||
// When response headers begin and finish receiving.
|
||||
UPDATING_RESPONSE_HEADERS: "NetMonitor:NetworkEventUpdating:ResponseHeaders",
|
||||
@ -113,6 +113,7 @@ const UPDATE_PROPS = [
|
||||
"httpVersion",
|
||||
"securityState",
|
||||
"securityInfo",
|
||||
"securityInfoAvailable",
|
||||
"mimeType",
|
||||
"contentSize",
|
||||
"transferredSize",
|
||||
@ -121,12 +122,14 @@ const UPDATE_PROPS = [
|
||||
"headersSize",
|
||||
"customQueryValue",
|
||||
"requestHeaders",
|
||||
"requestHeadersAvailable",
|
||||
"requestHeadersFromUploadStream",
|
||||
"requestCookies",
|
||||
"requestCookiesAvailable",
|
||||
"requestPostData",
|
||||
"requestPostDataAvailable",
|
||||
"responseHeaders",
|
||||
"responseHeadersAvailable",
|
||||
"responseCookies",
|
||||
"responseCookiesAvailable",
|
||||
"responseContent",
|
||||
|
@ -153,18 +153,31 @@ HarBuilder.prototype = {
|
||||
},
|
||||
|
||||
buildRequest: async function (file) {
|
||||
// When using HarAutomation, HarCollector will automatically fetch requestHeaders
|
||||
// and requestCookies, but when we use it from netmonitor, FirefoxDataProvider
|
||||
// should fetch it itself lazily, via requestData.
|
||||
|
||||
let requestHeaders = file.requestHeaders;
|
||||
if (!requestHeaders && this._options.requestData) {
|
||||
requestHeaders = await this._options.requestData(file.id, "requestHeaders");
|
||||
}
|
||||
|
||||
let requestCookies = file.requestCookies;
|
||||
if (!requestCookies && this._options.requestData) {
|
||||
requestCookies = await this._options.requestData(file.id, "requestCookies");
|
||||
}
|
||||
|
||||
let request = {
|
||||
bodySize: 0
|
||||
};
|
||||
|
||||
request.method = file.method;
|
||||
request.url = file.url;
|
||||
request.httpVersion = file.httpVersion || "";
|
||||
request.headers = this.buildHeaders(file.requestHeaders);
|
||||
request.headers = this.buildHeaders(requestHeaders);
|
||||
request.headers = this.appendHeadersPostData(request.headers, file);
|
||||
request.cookies = this.buildCookies(file.requestCookies);
|
||||
request.cookies = this.buildCookies(requestCookies);
|
||||
request.queryString = parseQueryString(getUrlQuery(file.url)) || [];
|
||||
request.headersSize = file.requestHeaders.headersSize;
|
||||
request.headersSize = requestHeaders.headersSize;
|
||||
request.postData = await this.buildPostData(file);
|
||||
|
||||
if (request.postData && request.postData.text) {
|
||||
@ -234,9 +247,9 @@ HarBuilder.prototype = {
|
||||
},
|
||||
|
||||
buildPostData: async function (file) {
|
||||
// When using HarAutomation, HarCollector will automatically fetch requestPostData,
|
||||
// but when we use it from netmonitor, FirefoxDataProvider should fetch it itself
|
||||
// lazily, via requestData.
|
||||
// When using HarAutomation, HarCollector will automatically fetch requestPostData
|
||||
// and requestHeaders, but when we use it from netmonitor, FirefoxDataProvider
|
||||
// should fetch it itself lazily, via requestData.
|
||||
let requestPostData = file.requestPostData;
|
||||
let requestHeaders = file.requestHeaders;
|
||||
let requestHeadersFromUploadStream;
|
||||
@ -251,6 +264,10 @@ HarBuilder.prototype = {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!requestHeaders && this._options.requestData) {
|
||||
requestHeaders = await this._options.requestData(file.id, "requestHeaders");
|
||||
}
|
||||
|
||||
let postData = {
|
||||
mimeType: findValue(requestHeaders.headers, "content-type"),
|
||||
params: [],
|
||||
@ -288,6 +305,20 @@ HarBuilder.prototype = {
|
||||
},
|
||||
|
||||
buildResponse: async function (file) {
|
||||
// When using HarAutomation, HarCollector will automatically fetch responseHeaders
|
||||
// and responseCookies, but when we use it from netmonitor, FirefoxDataProvider
|
||||
// should fetch it itself lazily, via requestData.
|
||||
|
||||
let responseHeaders = file.responseHeaders;
|
||||
if (!responseHeaders && this._options.requestData) {
|
||||
responseHeaders = await this._options.requestData(file.id, "responseHeaders");
|
||||
}
|
||||
|
||||
let responseCookies = file.responseCookies;
|
||||
if (!responseCookies && this._options.requestData) {
|
||||
responseCookies = await this._options.requestData(file.id, "responseCookies");
|
||||
}
|
||||
|
||||
let response = {
|
||||
status: 0
|
||||
};
|
||||
@ -296,14 +327,11 @@ HarBuilder.prototype = {
|
||||
if (file.status) {
|
||||
response.status = parseInt(file.status, 10);
|
||||
}
|
||||
|
||||
let responseHeaders = file.responseHeaders;
|
||||
|
||||
response.statusText = file.statusText || "";
|
||||
response.httpVersion = file.httpVersion || "";
|
||||
|
||||
response.headers = this.buildHeaders(responseHeaders);
|
||||
response.cookies = this.buildCookies(file.responseCookies);
|
||||
response.cookies = this.buildCookies(responseCookies);
|
||||
response.content = await this.buildContent(file);
|
||||
|
||||
let headers = responseHeaders ? responseHeaders.headers : null;
|
||||
|
@ -7,7 +7,6 @@ support-files =
|
||||
html_har_post-data-test-page.html
|
||||
!/devtools/client/netmonitor/test/head.js
|
||||
!/devtools/client/framework/test/shared-head.js
|
||||
!/devtools/client/netmonitor/test/shared-head.js
|
||||
!/devtools/client/netmonitor/test/html_simple-test-page.html
|
||||
|
||||
[browser_net_har_copy_all_as_har.js]
|
||||
|
@ -47,11 +47,13 @@ function* throttleUploadTest(actuallyThrottle) {
|
||||
});
|
||||
|
||||
// Execute one POST request on the page and wait till its done.
|
||||
let onEventTimings = monitor.panelWin.once(EVENTS.RECEIVED_EVENT_TIMINGS);
|
||||
let wait = waitForNetworkEvents(monitor, 1);
|
||||
yield ContentTask.spawn(tab.linkedBrowser, { size }, function* (args) {
|
||||
content.wrappedJSObject.executeTest2(args.size);
|
||||
});
|
||||
yield wait;
|
||||
yield onEventTimings;
|
||||
|
||||
// Copy HAR into the clipboard (asynchronous).
|
||||
let contextMenu = new RequestListContextMenu({ connector });
|
||||
|
@ -56,8 +56,8 @@ function getAutocompleteValuesForFlag(flag, request) {
|
||||
break;
|
||||
case "has-response-header":
|
||||
// Some requests not having responseHeaders..?
|
||||
values = request.responseHeaders &&
|
||||
request.responseHeaders.headers.map(h => h.name);
|
||||
values = request.responseHeaders ?
|
||||
request.responseHeaders.headers.map(h => h.name) : [];
|
||||
break;
|
||||
case "protocol":
|
||||
values.push(request.httpVersion);
|
||||
@ -108,10 +108,11 @@ function getLastTokenFlagValues(lastToken, requests) {
|
||||
values = [...new Set(values)];
|
||||
|
||||
return values
|
||||
.filter(value => value)
|
||||
.filter(value => {
|
||||
if (typedFlagValue) {
|
||||
let lowerTyped = typedFlagValue.toLowerCase(),
|
||||
lowerValue = value.toLowerCase();
|
||||
if (typedFlagValue && value) {
|
||||
let lowerTyped = typedFlagValue.toLowerCase();
|
||||
let lowerValue = value.toLowerCase();
|
||||
return lowerValue.includes(lowerTyped) && lowerValue !== lowerTyped;
|
||||
}
|
||||
return typeof value !== "undefined" && value !== "" && value !== "undefined";
|
||||
|
@ -420,33 +420,40 @@ function getResponseHeader(item, header) {
|
||||
/**
|
||||
* Extracts any urlencoded form data sections from a POST request.
|
||||
*/
|
||||
function updateFormDataSections(props) {
|
||||
async function updateFormDataSections(props) {
|
||||
let {
|
||||
connector,
|
||||
request = {},
|
||||
updateRequest,
|
||||
} = props;
|
||||
let {
|
||||
id,
|
||||
formDataSections,
|
||||
requestHeaders,
|
||||
requestHeadersAvailable,
|
||||
requestHeadersFromUploadStream,
|
||||
requestPostData,
|
||||
requestPostDataAvailable,
|
||||
} = request;
|
||||
|
||||
if (!formDataSections && requestHeaders &&
|
||||
requestHeadersFromUploadStream && requestPostData) {
|
||||
getFormDataSections(
|
||||
if (requestHeadersAvailable && !requestHeaders) {
|
||||
requestHeaders = await connector.requestData(id, "requestHeaders");
|
||||
}
|
||||
|
||||
if (requestPostDataAvailable && !requestPostData) {
|
||||
requestPostData = await connector.requestData(id, "requestPostData");
|
||||
}
|
||||
|
||||
if (!formDataSections && requestHeaders && requestPostData &&
|
||||
requestHeadersFromUploadStream) {
|
||||
formDataSections = await getFormDataSections(
|
||||
requestHeaders,
|
||||
requestHeadersFromUploadStream,
|
||||
requestPostData,
|
||||
connector.getLongString,
|
||||
).then((newFormDataSections) => {
|
||||
updateRequest(
|
||||
request.id,
|
||||
{ formDataSections: newFormDataSections },
|
||||
true,
|
||||
);
|
||||
});
|
||||
);
|
||||
|
||||
updateRequest(request.id, { formDataSections }, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -455,7 +462,7 @@ function updateFormDataSections(props) {
|
||||
* incoming network update packets. It's used by Network and
|
||||
* Console panel reducers.
|
||||
*/
|
||||
function processNetworkUpdates(request) {
|
||||
function processNetworkUpdates(request = {}) {
|
||||
let result = {};
|
||||
for (let [key, value] of Object.entries(request)) {
|
||||
if (UPDATE_PROPS.includes(key)) {
|
||||
|
@ -34,9 +34,11 @@ class RequestListContextMenu {
|
||||
mimeType,
|
||||
httpVersion,
|
||||
requestHeaders,
|
||||
requestHeadersAvailable,
|
||||
requestPostData,
|
||||
requestPostDataAvailable,
|
||||
responseHeaders,
|
||||
responseHeadersAvailable,
|
||||
responseContentAvailable,
|
||||
url,
|
||||
} = selectedRequest;
|
||||
@ -67,6 +69,8 @@ class RequestListContextMenu {
|
||||
id: "request-list-context-copy-post-data",
|
||||
label: L10N.getStr("netmonitor.context.copyPostData"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
|
||||
// Menu item will be visible even if data hasn't arrived, so we need to check
|
||||
// *Available property and then fetch data lazily once user triggers the action.
|
||||
visible: !!(selectedRequest && (requestPostDataAvailable || requestPostData)),
|
||||
click: () => this.copyPostData(id, formDataSections),
|
||||
});
|
||||
@ -75,7 +79,9 @@ class RequestListContextMenu {
|
||||
id: "request-list-context-copy-as-curl",
|
||||
label: L10N.getStr("netmonitor.context.copyAsCurl"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
|
||||
visible: !!selectedRequest,
|
||||
// Menu item will be visible even if data hasn't arrived, so we need to check
|
||||
// *Available property and then fetch data lazily once user triggers the action.
|
||||
visible: !!(selectedRequest && (requestHeadersAvailable || requestHeaders)),
|
||||
click: () => this.copyAsCurl(id, url, method, requestHeaders, httpVersion),
|
||||
});
|
||||
|
||||
@ -88,22 +94,28 @@ class RequestListContextMenu {
|
||||
id: "request-list-context-copy-request-headers",
|
||||
label: L10N.getStr("netmonitor.context.copyRequestHeaders"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"),
|
||||
visible: !!(selectedRequest && requestHeaders && requestHeaders.rawHeaders),
|
||||
click: () => this.copyRequestHeaders(requestHeaders.rawHeaders.trim()),
|
||||
// Menu item will be visible even if data hasn't arrived, so we need to check
|
||||
// *Available property and then fetch data lazily once user triggers the action.
|
||||
visible: !!(selectedRequest && (requestHeadersAvailable || requestHeaders)),
|
||||
click: () => this.copyRequestHeaders(id, requestHeaders),
|
||||
});
|
||||
|
||||
copySubmenu.push({
|
||||
id: "response-list-context-copy-response-headers",
|
||||
label: L10N.getStr("netmonitor.context.copyResponseHeaders"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"),
|
||||
visible: !!(selectedRequest && responseHeaders && responseHeaders.rawHeaders),
|
||||
click: () => this.copyResponseHeaders(responseHeaders.rawHeaders.trim()),
|
||||
// Menu item will be visible even if data hasn't arrived, so we need to check
|
||||
// *Available property and then fetch data lazily once user triggers the action.
|
||||
visible: !!(selectedRequest && (responseHeadersAvailable || responseHeaders)),
|
||||
click: () => this.copyResponseHeaders(id, responseHeaders),
|
||||
});
|
||||
|
||||
copySubmenu.push({
|
||||
id: "request-list-context-copy-response",
|
||||
label: L10N.getStr("netmonitor.context.copyResponse"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
|
||||
// Menu item will be visible even if data hasn't arrived, so we need to check
|
||||
// *Available property and then fetch data lazily once user triggers the action.
|
||||
visible: !!(selectedRequest && responseContentAvailable),
|
||||
click: () => this.copyResponse(id),
|
||||
});
|
||||
@ -280,6 +292,9 @@ class RequestListContextMenu {
|
||||
* Copy a cURL command from the currently selected item.
|
||||
*/
|
||||
async copyAsCurl(id, url, method, requestHeaders, httpVersion) {
|
||||
if (!requestHeaders) {
|
||||
requestHeaders = await this.props.connector.requestData(id, "requestHeaders");
|
||||
}
|
||||
let { requestPostData } = await this.props.connector
|
||||
.requestData(id, "requestPostData");
|
||||
// Create a sanitized object for the Curl command generator.
|
||||
@ -296,7 +311,12 @@ class RequestListContextMenu {
|
||||
/**
|
||||
* Copy the raw request headers from the currently selected item.
|
||||
*/
|
||||
copyRequestHeaders(rawHeaders) {
|
||||
async copyRequestHeaders(id, requestHeaders) {
|
||||
if (!requestHeaders) {
|
||||
requestHeaders = await this.props.connector.requestData(id, "requestHeaders");
|
||||
}
|
||||
let rawHeaders = requestHeaders.rawHeaders.trim();
|
||||
|
||||
if (Services.appinfo.OS !== "WINNT") {
|
||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||
}
|
||||
@ -306,7 +326,12 @@ class RequestListContextMenu {
|
||||
/**
|
||||
* Copy the raw response headers from the currently selected item.
|
||||
*/
|
||||
copyResponseHeaders(rawHeaders) {
|
||||
async copyResponseHeaders(id, responseHeaders) {
|
||||
if (!responseHeaders) {
|
||||
responseHeaders = await this.props.connector.requestData(id, "responseHeaders");
|
||||
}
|
||||
let rawHeaders = responseHeaders.rawHeaders.trim();
|
||||
|
||||
if (Services.appinfo.OS !== "WINNT") {
|
||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ subsuite = devtools
|
||||
support-files =
|
||||
dropmarker.svg
|
||||
head.js
|
||||
shared-head.js
|
||||
html_cause-test-page.html
|
||||
html_content-type-without-cache-test-page.html
|
||||
html_brotli-test-page.html
|
||||
|
@ -33,9 +33,11 @@ add_task(function* () {
|
||||
|
||||
// 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")));
|
||||
let requests = getSortedRequests(store.getState())
|
||||
.filter((req) => !req.stacktrace)
|
||||
.map((req) => connector.requestData(req.id, "stackTrace"));
|
||||
|
||||
yield Promise.all(requests);
|
||||
|
||||
EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
|
||||
let item = getSortedRequests(store.getState()).get(i);
|
||||
|
@ -32,8 +32,6 @@ add_task(function* () {
|
||||
|
||||
let selectedRequest = getSelectedRequest(store.getState());
|
||||
is(selectedRequest, requestItem, "Proper request is selected");
|
||||
ok(selectedRequest.requestHeaders, "Selected request should have request headers");
|
||||
ok(selectedRequest.responseHeaders, "Selected request should have response headers");
|
||||
|
||||
const EXPECTED_REQUEST_HEADERS = [
|
||||
`${method} ${SIMPLE_URL} ${httpVersion}`,
|
||||
|
@ -235,7 +235,7 @@ function testEscapeStringWin() {
|
||||
}
|
||||
|
||||
function* createCurlData(selected, getLongString, requestData) {
|
||||
let { url, method, httpVersion } = selected;
|
||||
let { id, url, method, httpVersion } = selected;
|
||||
|
||||
// Create a sanitized object for the Curl command generator.
|
||||
let data = {
|
||||
@ -246,13 +246,14 @@ function* createCurlData(selected, getLongString, requestData) {
|
||||
postDataText: null
|
||||
};
|
||||
|
||||
let requestHeaders = yield requestData(id, "requestHeaders");
|
||||
// Fetch header values.
|
||||
for (let { name, value } of selected.requestHeaders.headers) {
|
||||
for (let { name, value } of requestHeaders.headers) {
|
||||
let text = yield getLongString(value);
|
||||
data.headers.push({ name: name, value: text });
|
||||
}
|
||||
|
||||
let { requestPostData } = yield requestData(selected.id, "requestPostData");
|
||||
let { requestPostData } = yield requestData(id, "requestPostData");
|
||||
// Fetch the request payload.
|
||||
if (requestPostData) {
|
||||
let postData = requestPostData.postData.text;
|
||||
|
@ -317,8 +317,15 @@ add_task(function* () {
|
||||
is(getSelectedIndex(store.getState()), 0,
|
||||
"The first item should be still selected after filtering.");
|
||||
|
||||
const items = getSortedRequests(store.getState());
|
||||
const visibleItems = getDisplayedRequests(store.getState());
|
||||
let items = getSortedRequests(store.getState());
|
||||
let visibleItems;
|
||||
|
||||
// Filter results will be updated asynchronously, so we should wait until
|
||||
// displayed requests reach final state.
|
||||
yield waitUntil(() => {
|
||||
visibleItems = getDisplayedRequests(store.getState());
|
||||
return visibleItems.size === visibility.filter(e => e).length;
|
||||
});
|
||||
|
||||
is(items.size, visibility.length,
|
||||
"There should be a specific amount of items in the requests menu.");
|
||||
|
@ -145,8 +145,20 @@ add_task(function* () {
|
||||
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
function type(string) {
|
||||
for (let ch of string) {
|
||||
EventUtils.synthesizeKey(ch, {}, monitor.panelWin);
|
||||
}
|
||||
}
|
||||
|
||||
// Filtering network request will start fetching data lazily
|
||||
// (fetching requestHeaders & responseHeaders for filtering WS & XHR)
|
||||
// Lazy fetching will be executed when user focuses on filter box.
|
||||
function setFreetextFilter(value) {
|
||||
store.dispatch(Actions.setRequestFilterText(value));
|
||||
let filterBox = document.querySelector(".devtools-filterinput");
|
||||
filterBox.focus();
|
||||
filterBox.value = "";
|
||||
type(value);
|
||||
}
|
||||
|
||||
info("Starting test... ");
|
||||
@ -350,8 +362,15 @@ add_task(function* () {
|
||||
yield waitUntil(() => requestsListStatus.title);
|
||||
}
|
||||
|
||||
const items = getSortedRequests(store.getState());
|
||||
const visibleItems = getDisplayedRequests(store.getState());
|
||||
let items = getSortedRequests(store.getState());
|
||||
let visibleItems = getDisplayedRequests(store.getState());
|
||||
|
||||
// Filter results will be updated asynchronously, so we should wait until
|
||||
// displayed requests reach final state.
|
||||
yield waitUntil(() => {
|
||||
visibleItems = getDisplayedRequests(store.getState());
|
||||
return visibleItems.size === visibility.filter(e => e).length;
|
||||
});
|
||||
|
||||
is(items.size, visibility.length,
|
||||
"There should be a specific amount of items in the requests menu.");
|
||||
@ -362,6 +381,15 @@ add_task(function* () {
|
||||
let itemId = items.get(i).id;
|
||||
let shouldBeVisible = !!visibility[i];
|
||||
let isThere = visibleItems.some(r => r.id == itemId);
|
||||
|
||||
// Filter results will be updated asynchronously, so we should wait until
|
||||
// displayed requests reach final state.
|
||||
yield waitUntil(() => {
|
||||
visibleItems = getDisplayedRequests(store.getState());
|
||||
isThere = visibleItems.some(r => r.id == itemId);
|
||||
return isThere === shouldBeVisible;
|
||||
});
|
||||
|
||||
is(isThere, shouldBeVisible,
|
||||
`The item at index ${i} has visibility=${shouldBeVisible}`);
|
||||
|
||||
|
@ -12,12 +12,14 @@ 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));
|
||||
|
||||
tab.linkedBrowser.reload();
|
||||
|
||||
let wait = waitForNetworkEvents(monitor, 1);
|
||||
tab.linkedBrowser.reload();
|
||||
yield wait;
|
||||
|
||||
wait = waitForDOM(document, ".headers-overview");
|
||||
@ -25,6 +27,11 @@ add_task(function* () {
|
||||
document.querySelectorAll(".request-list-item")[0]);
|
||||
yield wait;
|
||||
|
||||
yield waitUntil(() => {
|
||||
let request = getSortedRequests(store.getState()).get(0);
|
||||
return request.requestHeaders && request.responseHeaders;
|
||||
});
|
||||
|
||||
info("Check if Request-Headers and Response-Headers are sorted");
|
||||
let expectedResponseHeaders = ["cache-control", "connection", "content-length",
|
||||
"content-type", "date", "expires", "foo-bar",
|
||||
|
@ -12,17 +12,26 @@ add_task(function* () {
|
||||
let { tab, monitor } = yield initNetMonitor(SINGLE_GET_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document } = monitor.panelWin;
|
||||
let { document, store, windowRequire } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
Services.prefs.setBoolPref("devtools.netmonitor.persistlog", false);
|
||||
|
||||
yield reloadAndWait();
|
||||
|
||||
// Using waitUntil in the test is necessary to ensure all requests are added correctly.
|
||||
// Because reloadAndWait call may catch early uncaught requests from initNetMonitor, so
|
||||
// the actual number of requests after reloadAndWait could be wrong since all requests
|
||||
// haven't finished.
|
||||
yield waitUntil(() => document.querySelectorAll(".request-list-item").length === 2);
|
||||
is(document.querySelectorAll(".request-list-item").length, 2,
|
||||
"The request list should have two items at this point.");
|
||||
|
||||
yield reloadAndWait();
|
||||
|
||||
yield waitUntil(() => document.querySelectorAll(".request-list-item").length === 2);
|
||||
// Since the reload clears the log, we still expect two requests in the log
|
||||
is(document.querySelectorAll(".request-list-item").length, 2,
|
||||
"The request list should still have two items at this point.");
|
||||
@ -32,6 +41,7 @@ add_task(function* () {
|
||||
|
||||
yield reloadAndWait();
|
||||
|
||||
yield waitUntil(() => document.querySelectorAll(".request-list-item").length === 4);
|
||||
// Since we togged the persistence logs, we expect four items after the reload
|
||||
is(document.querySelectorAll(".request-list-item").length, 4,
|
||||
"The request list should now have four items at this point.");
|
||||
|
@ -25,7 +25,7 @@ add_task(function* () {
|
||||
});
|
||||
yield wait;
|
||||
|
||||
wait = waitForDOM(document, ".headers-overview");
|
||||
wait = waitForDOM(document, "#headers-panel .tree-section", 2);
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll(".request-list-item")[0]);
|
||||
yield wait;
|
||||
|
@ -17,7 +17,6 @@ add_task(function* () {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, store, windowRequire, connector } = monitor.panelWin;
|
||||
let { requestData } = connector;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let {
|
||||
getSelectedRequest,
|
||||
@ -56,7 +55,15 @@ add_task(function* () {
|
||||
store.dispatch(Actions.sendCustomRequest(connector));
|
||||
yield wait;
|
||||
|
||||
let sentItem = getSelectedRequest(store.getState());
|
||||
let sentItem;
|
||||
// Testing sent request will require updated requestHeaders and requestPostData,
|
||||
// we must wait for both properties get updated before starting test.
|
||||
yield waitUntil(() => {
|
||||
sentItem = getSelectedRequest(store.getState());
|
||||
origItem = getSortedRequests(store.getState()).get(0);
|
||||
return sentItem.requestHeaders && sentItem.requestPostData &&
|
||||
origItem.requestHeaders && origItem.requestPostData;
|
||||
});
|
||||
|
||||
yield testSentRequest(sentItem, origItem);
|
||||
|
||||
@ -164,13 +171,8 @@ add_task(function* () {
|
||||
let hasUAHeader = headers.some(h => `${h.name}: ${h.value}` == ADD_UA_HEADER);
|
||||
ok(hasUAHeader, "User-Agent header added to sent request");
|
||||
|
||||
let { requestPostData: clonedRequestPostData } = yield requestData(data.id,
|
||||
"requestPostData");
|
||||
let { requestPostData: origRequestPostData } = yield requestData(origData.id,
|
||||
"requestPostData");
|
||||
|
||||
is(clonedRequestPostData.postData.text,
|
||||
origRequestPostData.postData.text + ADD_POSTDATA,
|
||||
is(data.requestPostData.postData.text,
|
||||
origData.requestPostData.postData.text + ADD_POSTDATA,
|
||||
"post data added to sent request");
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ add_task(function* () {
|
||||
let { store, windowRequire, connector } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let {
|
||||
getRequestById,
|
||||
getSortedRequests,
|
||||
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
|
||||
|
||||
@ -41,16 +42,27 @@ add_task(function* () {
|
||||
// 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);
|
||||
ITEMS.forEach((item) => {
|
||||
for (let item of ITEMS) {
|
||||
info(`Selecting the ${item.method} request`);
|
||||
store.dispatch(Actions.selectRequest(item.id));
|
||||
|
||||
// Wait for requestHeaders and responseHeaders are required when fetching data
|
||||
// from back-end.
|
||||
yield waitUntil(() => {
|
||||
item = getRequestById(store.getState(), item.id);
|
||||
return item.requestHeaders && item.responseHeaders;
|
||||
});
|
||||
|
||||
let { size } = getSortedRequests(store.getState());
|
||||
|
||||
info("Cloning the selected request into a custom clone");
|
||||
store.dispatch(Actions.cloneSelectedRequest());
|
||||
|
||||
info("Sending the cloned request (without change)");
|
||||
store.dispatch(Actions.sendCustomRequest(connector));
|
||||
});
|
||||
|
||||
yield waitUntil(() => getSortedRequests(store.getState()).size === size + 1);
|
||||
}
|
||||
|
||||
info("Waiting for both resent requests");
|
||||
yield onRequests;
|
||||
@ -63,14 +75,26 @@ add_task(function* () {
|
||||
is(item.status, 200, `The ${item.method} response has the right status`);
|
||||
|
||||
if (item.method === "POST") {
|
||||
// Force fetching lazy load data
|
||||
let responseContent = yield connector.requestData(item.id, "responseContent");
|
||||
let { requestPostData } = yield connector.requestData(item.id, "requestPostData");
|
||||
is(item.method, "POST", `The ${item.method} request has the right method`);
|
||||
|
||||
is(requestPostData.postData.text, "post-data",
|
||||
// Trigger responseContent update requires to wait until
|
||||
// responseContentAvailable set true
|
||||
yield waitUntil(() => {
|
||||
item = getRequestById(store.getState(), item.id);
|
||||
return item.responseContentAvailable;
|
||||
});
|
||||
yield connector.requestData(item.id, "responseContent");
|
||||
|
||||
// Wait for both requestPostData & responseContent payloads arrived.
|
||||
yield waitUntil(() => {
|
||||
item = getRequestById(store.getState(), item.id);
|
||||
return item.responseContent && item.requestPostData;
|
||||
});
|
||||
|
||||
is(item.requestPostData.postData.text, "post-data",
|
||||
"The POST request has the right POST data");
|
||||
// eslint-disable-next-line mozilla/no-cpows-in-tests
|
||||
is(responseContent.content.text, "Access-Control-Allow-Origin: *",
|
||||
is(item.responseContent.content.text, "Access-Control-Allow-Origin: *",
|
||||
"The POST response has the right content");
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ add_task(function* () {
|
||||
|
||||
let { store, windowRequire, connector } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let { sendHTTPRequest } = connector;
|
||||
let { requestData, sendHTTPRequest } = connector;
|
||||
let {
|
||||
getSortedRequests,
|
||||
} = windowRequire("devtools/client/netmonitor/src/selectors/index");
|
||||
@ -40,6 +40,19 @@ add_task(function* () {
|
||||
yield wait;
|
||||
|
||||
let item = getSortedRequests(store.getState()).get(0);
|
||||
|
||||
ok(item.requestHeadersAvailable, "headers are available for lazily fetching");
|
||||
|
||||
if (item.requestHeadersAvailable && !item.requestHeaders) {
|
||||
requestData(item.id, "requestHeaders");
|
||||
}
|
||||
|
||||
// Wait until requestHeaders packet gets updated.
|
||||
yield waitUntil(() => {
|
||||
item = getSortedRequests(store.getState()).get(0);
|
||||
return item.requestHeaders;
|
||||
});
|
||||
|
||||
is(item.method, "POST", "The request has the right method");
|
||||
is(item.url, requestUrl, "The request has the right URL");
|
||||
|
||||
|
@ -11,13 +11,12 @@ add_task(function* () {
|
||||
let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
|
||||
let { document, store, windowRequire } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
|
||||
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
info("Requesting a resource that has a certificate problem.");
|
||||
|
||||
let requestsDone = waitForSecurityBrokenNetworkEvent();
|
||||
let requestsDone = waitForNetworkEvents(monitor, 1);
|
||||
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
|
||||
content.wrappedJSObject.performRequests(1, "https://nocert.example.com");
|
||||
});
|
||||
@ -36,25 +35,4 @@ add_task(function* () {
|
||||
isnot(errormsg.textContent, "", "Error message is not empty.");
|
||||
|
||||
return teardown(monitor);
|
||||
|
||||
/**
|
||||
* Returns a promise that's resolved once a request with security issues is
|
||||
* completed.
|
||||
*/
|
||||
function waitForSecurityBrokenNetworkEvent() {
|
||||
let awaitedEvents = [
|
||||
"UPDATING_REQUEST_HEADERS",
|
||||
"RECEIVED_REQUEST_HEADERS",
|
||||
"UPDATING_REQUEST_COOKIES",
|
||||
"RECEIVED_REQUEST_COOKIES",
|
||||
"UPDATING_EVENT_TIMINGS",
|
||||
"RECEIVED_EVENT_TIMINGS",
|
||||
];
|
||||
|
||||
let promises = awaitedEvents.map((event) => {
|
||||
return monitor.panelWin.once(EVENTS[event]);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
});
|
||||
|
@ -56,10 +56,7 @@ add_task(function* () {
|
||||
});
|
||||
}
|
||||
|
||||
// waitForNetworkEvents does not work for requests with security errors as
|
||||
// those only emit 9/13 events of a successful request.
|
||||
let done = waitForSecurityBrokenNetworkEvent();
|
||||
|
||||
let done = waitForNetworkEvents(monitor, 1);
|
||||
info("Requesting a resource that has a certificate problem.");
|
||||
yield executeRequests(1, "https://nocert.example.com");
|
||||
|
||||
@ -80,7 +77,7 @@ add_task(function* () {
|
||||
yield executeRequests(1, "https://example.com" + CORS_SJS_PATH);
|
||||
yield done;
|
||||
|
||||
done = waitForSecurityBrokenNetworkEvent();
|
||||
done = waitForNetworkEvents(monitor, 1);
|
||||
info("Requesting a resource over HTTP to localhost.");
|
||||
yield executeRequests(1, "http://localhost" + CORS_SJS_PATH);
|
||||
yield done;
|
||||
@ -90,25 +87,4 @@ add_task(function* () {
|
||||
expectedCount,
|
||||
expectedCount + " events logged.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that's resolved once a request with security issues is
|
||||
* completed.
|
||||
*/
|
||||
function waitForSecurityBrokenNetworkEvent() {
|
||||
let awaitedEvents = [
|
||||
"UPDATING_REQUEST_HEADERS",
|
||||
"RECEIVED_REQUEST_HEADERS",
|
||||
"UPDATING_REQUEST_COOKIES",
|
||||
"RECEIVED_REQUEST_COOKIES",
|
||||
"UPDATING_EVENT_TIMINGS",
|
||||
"RECEIVED_EVENT_TIMINGS",
|
||||
];
|
||||
|
||||
let promises = awaitedEvents.map((event) => {
|
||||
return monitor.panelWin.once(EVENTS[event]);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
});
|
||||
|
@ -101,10 +101,6 @@ add_task(function* () {
|
||||
*/
|
||||
function waitForSecurityBrokenNetworkEvent() {
|
||||
let awaitedEvents = [
|
||||
"UPDATING_REQUEST_HEADERS",
|
||||
"RECEIVED_REQUEST_HEADERS",
|
||||
"UPDATING_REQUEST_COOKIES",
|
||||
"RECEIVED_REQUEST_COOKIES",
|
||||
"UPDATING_EVENT_TIMINGS",
|
||||
"RECEIVED_EVENT_TIMINGS",
|
||||
];
|
||||
|
@ -27,7 +27,7 @@ function test() {
|
||||
initNetMonitor(SIMPLE_SJS).then(async ({ tab, monitor }) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, store, windowRequire } = monitor.panelWin;
|
||||
let { document, store, windowRequire, connector } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
|
||||
let {
|
||||
@ -347,7 +347,18 @@ function test() {
|
||||
);
|
||||
});
|
||||
|
||||
let wait = waitForNetworkEvents(monitor, 1);
|
||||
tab.linkedBrowser.reload();
|
||||
await wait;
|
||||
|
||||
let requestItem = getSortedRequests(store.getState()).get(0);
|
||||
|
||||
if (!requestItem.requestHeaders) {
|
||||
connector.requestData(requestItem.id, "requestHeaders");
|
||||
}
|
||||
if (!requestItem.responseHeaders) {
|
||||
connector.requestData(requestItem.id, "responseHeaders");
|
||||
}
|
||||
|
||||
await Promise.all(promiseList);
|
||||
await teardown(monitor);
|
||||
|
@ -172,6 +172,9 @@ add_task(function* () {
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll(".request-list-item")[index]);
|
||||
|
||||
yield waitUntil(() => document.querySelector(
|
||||
"#headers-panel .tabpanel-summary-value.textbox-input"));
|
||||
|
||||
let panel = document.querySelector("#headers-panel");
|
||||
let summaryValues = panel.querySelectorAll(".tabpanel-summary-value.textbox-input");
|
||||
let { method, correctUri, details: { status, statusText } } = data;
|
||||
|
@ -13,10 +13,6 @@ 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, 2);
|
||||
@ -44,14 +40,6 @@ add_task(function* () {
|
||||
is(store.getState().requests.requests.size, 2,
|
||||
"There should be only two requests made.");
|
||||
|
||||
let firstRequest = getSortedRequests(store.getState()).get(0);
|
||||
let lastRequest = getSortedRequests(store.getState()).get(1);
|
||||
|
||||
info("First request happened at: " +
|
||||
firstRequest.responseHeaders.headers.find(e => e.name == "date").value);
|
||||
info("Last request happened at: " +
|
||||
lastRequest.responseHeaders.headers.find(e => e.name == "date").value);
|
||||
|
||||
ok(secDivs.length,
|
||||
"There should be at least one division on the seconds time scale.");
|
||||
ok(secDivs[0].textContent.match(/\d+\.\d{2}\s\w+/),
|
||||
|
@ -11,13 +11,17 @@ add_task(function* () {
|
||||
let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
|
||||
let { document } = monitor.panelWin;
|
||||
|
||||
yield performRequestsAndWait();
|
||||
|
||||
let wait = waitForDOM(document, "#timings-panel");
|
||||
let timing = document.querySelectorAll(".requests-list-timings")[0];
|
||||
let onAllEvents = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
|
||||
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
|
||||
content.wrappedJSObject.performRequests();
|
||||
});
|
||||
yield onAllEvents;
|
||||
|
||||
info("Clicking waterfall and waiting for panel update.");
|
||||
EventUtils.synthesizeMouseAtCenter(timing, {}, monitor.panelWin);
|
||||
let wait = waitForDOM(document, "#timings-panel");
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll(".requests-list-timings")[0]);
|
||||
|
||||
yield wait;
|
||||
|
||||
@ -25,12 +29,4 @@ add_task(function* () {
|
||||
"Timings tab is selected.");
|
||||
|
||||
return teardown(monitor);
|
||||
|
||||
function* performRequestsAndWait() {
|
||||
let onAllEvents = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
|
||||
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
|
||||
content.wrappedJSObject.performRequests();
|
||||
});
|
||||
yield onAllEvents;
|
||||
}
|
||||
});
|
||||
|
@ -2,7 +2,6 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from ../../framework/test/shared-head.js */
|
||||
/* import-globals-from shared-head.js */
|
||||
/* exported Toolbox, restartNetMonitor, teardown, waitForExplicitFinish,
|
||||
verifyRequestItemTarget, waitFor, testFilterButtons, loadCommonFrameScript,
|
||||
performRequestsInContent, waitForNetworkEvents, selectIndexAndWaitForSourceEditor */
|
||||
@ -14,10 +13,6 @@ Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
|
||||
this);
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js",
|
||||
this);
|
||||
|
||||
const {
|
||||
getFormattedIPAndPort,
|
||||
getFormattedTime,
|
||||
@ -30,6 +25,7 @@ const {
|
||||
getUrlQuery,
|
||||
getUrlScheme,
|
||||
} = require("devtools/client/netmonitor/src/utils/request-utils");
|
||||
const { EVENTS } = require("devtools/client/netmonitor/src/constants");
|
||||
|
||||
/* eslint-disable no-unused-vars, max-len */
|
||||
const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
|
||||
@ -225,23 +221,35 @@ let finishedQueue = {};
|
||||
let updatingTypes = [
|
||||
"NetMonitor:NetworkEventUpdating:RequestCookies",
|
||||
"NetMonitor:NetworkEventUpdating:ResponseCookies",
|
||||
"NetMonitor:NetworkEventUpdating:RequestHeaders",
|
||||
"NetMonitor:NetworkEventUpdating:ResponseHeaders",
|
||||
"NetMonitor:NetworkEventUpdating:RequestPostData",
|
||||
"NetMonitor:NetworkEventUpdating:ResponseContent",
|
||||
"NetMonitor:NetworkEventUpdating:SecurityInfo",
|
||||
"NetMonitor:NetworkEventUpdating:EventTimings",
|
||||
];
|
||||
let updatedTypes = [
|
||||
"NetMonitor:NetworkEventUpdated:RequestCookies",
|
||||
"NetMonitor:NetworkEventUpdated:ResponseCookies",
|
||||
"NetMonitor:NetworkEventUpdated:RequestHeaders",
|
||||
"NetMonitor:NetworkEventUpdated:ResponseHeaders",
|
||||
"NetMonitor:NetworkEventUpdated:RequestPostData",
|
||||
"NetMonitor:NetworkEventUpdated:ResponseContent",
|
||||
"NetMonitor:NetworkEventUpdated:SecurityInfo",
|
||||
"NetMonitor:NetworkEventUpdated:EventTimings",
|
||||
];
|
||||
|
||||
// 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:", "");
|
||||
let key = actor + "-" + updatedTypes[updatingTypes.indexOf(event)];
|
||||
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]--;
|
||||
let key = actor + "-" + event;
|
||||
finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] - 1 : -1;
|
||||
}));
|
||||
}
|
||||
|
||||
@ -329,10 +337,6 @@ 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.");
|
||||
|
||||
@ -346,44 +350,17 @@ function waitForNetworkEvents(monitor, getRequests) {
|
||||
return new Promise((resolve) => {
|
||||
let panel = monitor.panelWin;
|
||||
let { getNetworkRequest } = panel.connector;
|
||||
let progress = {};
|
||||
let genericEvents = 0;
|
||||
let networkEvent = 0;
|
||||
let payloadReady = 0;
|
||||
let awaitedEventsToListeners = [
|
||||
["UPDATING_REQUEST_HEADERS", onGenericEvent],
|
||||
["RECEIVED_REQUEST_HEADERS", onGenericEvent],
|
||||
["UPDATING_RESPONSE_HEADERS", onGenericEvent],
|
||||
["RECEIVED_RESPONSE_HEADERS", onGenericEvent],
|
||||
["UPDATING_EVENT_TIMINGS", onGenericEvent],
|
||||
["RECEIVED_EVENT_TIMINGS", onGenericEvent],
|
||||
["PAYLOAD_READY", onPayloadReady]
|
||||
];
|
||||
let expectedGenericEvents = awaitedEventsToListeners
|
||||
.filter(([, listener]) => listener == onGenericEvent).length;
|
||||
|
||||
function initProgressForURL(url) {
|
||||
if (progress[url]) {
|
||||
return;
|
||||
}
|
||||
progress[url] = {};
|
||||
awaitedEventsToListeners.forEach(function ([e]) {
|
||||
progress[url][e] = 0;
|
||||
});
|
||||
}
|
||||
|
||||
function updateProgressForURL(url, event) {
|
||||
initProgressForURL(url);
|
||||
progress[url][Object.keys(EVENTS).find(e => EVENTS[e] == event)] = 1;
|
||||
}
|
||||
|
||||
function onGenericEvent(event, actor) {
|
||||
function onNetworkEvent(event, actor) {
|
||||
let networkInfo = getNetworkRequest(actor);
|
||||
if (!networkInfo) {
|
||||
// Must have been related to reloading document to disable cache.
|
||||
// Ignore the event.
|
||||
return;
|
||||
}
|
||||
genericEvents++;
|
||||
networkEvent++;
|
||||
maybeResolve(event, actor, networkInfo);
|
||||
}
|
||||
|
||||
@ -394,38 +371,30 @@ function waitForNetworkEvents(monitor, getRequests) {
|
||||
// Ignore the event.
|
||||
return;
|
||||
}
|
||||
|
||||
payloadReady++;
|
||||
maybeResolve(event, actor, networkInfo);
|
||||
}
|
||||
|
||||
function maybeResolve(event, actor, networkInfo) {
|
||||
info("> Network event progress: " +
|
||||
"Payload: " + payloadReady + "/" + getRequests + ", " +
|
||||
"Generic: " + genericEvents + "/" + (getRequests * expectedGenericEvents) + ", " +
|
||||
"NetworkEvent: " + networkEvent + "/" + getRequests + ", " +
|
||||
"PayloadReady: " + payloadReady + "/" + getRequests + ", " +
|
||||
"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 &&
|
||||
genericEvents >= getRequests * expectedGenericEvents) {
|
||||
awaitedEventsToListeners.forEach(([e, l]) => panel.off(EVENTS[e], l));
|
||||
// Wait until networkEvent & payloadReady finish for each request.
|
||||
if (networkEvent >= getRequests && payloadReady >= getRequests) {
|
||||
panel.off(EVENTS.NETWORK_EVENT, onNetworkEvent);
|
||||
panel.off(EVENTS.PAYLOAD_READY, onPayloadReady);
|
||||
executeSoon(resolve);
|
||||
}
|
||||
}
|
||||
|
||||
awaitedEventsToListeners.forEach(([e, l]) => panel.on(EVENTS[e], l));
|
||||
panel.on(EVENTS.NETWORK_EVENT, onNetworkEvent);
|
||||
panel.on(EVENTS.PAYLOAD_READY, onPayloadReady);
|
||||
});
|
||||
}
|
||||
|
||||
function verifyRequestItemTarget(document, requestList, requestItem, method,
|
||||
function* verifyRequestItemTarget(document, requestList, requestItem, method,
|
||||
url, data = {}) {
|
||||
info("> Verifying: " + method + " " + url + " " + data.toSource());
|
||||
|
||||
|
@ -1,40 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* exported EVENTS, waitForExistingRequests */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { EVENTS } = require("devtools/client/netmonitor/src/constants");
|
||||
|
||||
async function waitForExistingRequests(monitor) {
|
||||
let { store } = monitor.panelWin;
|
||||
function getRequests() {
|
||||
return store.getState().requests.requests;
|
||||
}
|
||||
function areAllRequestsFullyLoaded() {
|
||||
let requests = getRequests().valueSeq();
|
||||
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.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;
|
||||
}
|
||||
while (!areAllRequestsFullyLoaded()) {
|
||||
await monitor.panelWin.once(EVENTS.PAYLOAD_READY);
|
||||
}
|
||||
}
|
@ -61,7 +61,6 @@ support-files =
|
||||
!/devtools/client/inspector/shared/test/head.js
|
||||
!/devtools/client/inspector/test/head.js
|
||||
!/devtools/client/inspector/test/shared-head.js
|
||||
!/devtools/client/netmonitor/test/shared-head.js
|
||||
!/devtools/client/responsive.html/test/browser/devices.json
|
||||
!/devtools/client/shared/test/test-actor-registry.js
|
||||
!/devtools/client/shared/test/test-actor.js
|
||||
|
@ -3,14 +3,9 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from ../../netmonitor/test/shared-head.js */
|
||||
|
||||
// A test to ensure Style Editor doesn't bybass cache when loading style sheet
|
||||
// contents (bug 978688).
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
|
||||
|
||||
const TEST_URL = TEST_BASE_HTTP + "doc_uncached.html";
|
||||
|
||||
add_task(function* () {
|
||||
@ -39,8 +34,6 @@ add_task(function* () {
|
||||
info("Waiting for the source to be loaded.");
|
||||
yield styleeditor.UI.editors[0].getSourceEditor();
|
||||
|
||||
yield waitForExistingRequests(monitor);
|
||||
|
||||
info("Checking Netmonitor contents.");
|
||||
let items = [];
|
||||
for (let item of getSortedRequests(store.getState())) {
|
||||
|
@ -165,7 +165,6 @@ support-files =
|
||||
!/devtools/client/netmonitor/test/sjs_cors-test-server.sjs
|
||||
!/image/test/mochitest/blue.png
|
||||
!/devtools/client/framework/test/shared-head.js
|
||||
!/devtools/client/netmonitor/test/shared-head.js
|
||||
[browser_console.js]
|
||||
skip-if = true # Bug 1406060
|
||||
[browser_console_addonsdk_loader_exception.js]
|
||||
|
@ -64,6 +64,4 @@ async function testNetmonitor(toolbox) {
|
||||
let item = getSortedRequests(store.getState()).get(0);
|
||||
is(item.method, "GET", "The attached method is correct.");
|
||||
is(item.url, TEST_PATH, "The attached url is correct.");
|
||||
|
||||
await waitForExistingRequests(monitor);
|
||||
}
|
||||
|
@ -47,10 +47,8 @@ add_task(async function task() {
|
||||
await consoleReady;
|
||||
|
||||
info("network-request-payload-ready received");
|
||||
|
||||
await testNetworkMessage(messageNode);
|
||||
|
||||
await waitForExistingRequests(monitor);
|
||||
await waitForLazyRequests(toolbox);
|
||||
});
|
||||
|
||||
async function testNetworkMessage(messageNode) {
|
||||
@ -59,7 +57,24 @@ async function testNetworkMessage(messageNode) {
|
||||
ok(headersTab, "Headers tab is available");
|
||||
|
||||
// Headers tab should be selected by default, so just check its content.
|
||||
let headersContent = messageNode.querySelector(
|
||||
"#headers-panel .headers-overview");
|
||||
let headersContent;
|
||||
await waitUntil(() => {
|
||||
headersContent = messageNode.querySelector(
|
||||
"#headers-panel .headers-overview");
|
||||
return headersContent;
|
||||
});
|
||||
|
||||
ok(headersContent, "Headers content is available");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until all lazily fetch requests in netmonitor get finsished.
|
||||
* Otherwise test will be shutdown too early and cause failure.
|
||||
*/
|
||||
async function waitForLazyRequests(toolbox) {
|
||||
let { ui } = toolbox.getCurrentPanel().hud;
|
||||
let proxy = ui.jsterm.hud.proxy;
|
||||
return waitUntil(() => {
|
||||
return !proxy.networkDataProvider.lazyRequestData.size;
|
||||
});
|
||||
}
|
||||
|
@ -247,6 +247,10 @@ async function testTimings(messageNode) {
|
||||
|
||||
// Select Timings tab and check the content.
|
||||
timingsTab.click();
|
||||
await waitUntil(() => {
|
||||
return !!messageNode.querySelector(
|
||||
"#timings-panel .timings-container .timings-label");
|
||||
});
|
||||
let timingsContent = messageNode.querySelector(
|
||||
"#timings-panel .timings-container .timings-label");
|
||||
ok(timingsContent, "Timings content is available");
|
||||
@ -299,6 +303,10 @@ async function waitForRequestUpdates(toolbox) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until all lazily fetch requests in netmonitor get finsished.
|
||||
* Otherwise test will be shutdown too early and cause failure.
|
||||
*/
|
||||
async function waitForLazyRequests(toolbox) {
|
||||
let {ui} = toolbox.getCurrentPanel().hud;
|
||||
let proxy = ui.jsterm.hud.proxy;
|
||||
|
@ -75,7 +75,4 @@ async function testNetmonitorLink(toolbox, hud, url) {
|
||||
});
|
||||
|
||||
ok(true, "The attached url is correct.");
|
||||
|
||||
let monitor = toolbox.getCurrentPanel();
|
||||
await waitForExistingRequests(monitor);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* import-globals-from ../../../../framework/test/shared-head.js */
|
||||
/* import-globals-from ../../../../netmonitor/test/shared-head.js */
|
||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||
|
||||
"use strict";
|
||||
@ -14,9 +13,6 @@ Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
|
||||
this);
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
|
||||
|
||||
var {HUDService} = require("devtools/client/webconsole/hudservice");
|
||||
var WCUL10n = require("devtools/client/webconsole/webconsole-l10n");
|
||||
const DOCS_GA_PARAMS = "?utm_source=mozilla" +
|
||||
|
@ -5,9 +5,6 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf8,Test that the netmonitor " +
|
||||
"displays requests that have been recorded in the " +
|
||||
"web console, even if the netmonitor hadn't opened yet.";
|
||||
@ -68,17 +65,17 @@ function* testNetmonitor(toolbox) {
|
||||
|
||||
let { store, windowRequire } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let { getSortedRequests } = windowRequire("devtools/client/netmonitor/src/selectors/index");
|
||||
let { getSortedRequests } =
|
||||
windowRequire("devtools/client/netmonitor/src/selectors/index");
|
||||
|
||||
store.dispatch(Actions.batchEnable(false));
|
||||
|
||||
yield waitUntil(() => store.getState().requests.requests.size > 0);
|
||||
|
||||
is(store.getState().requests.requests.size, 1, "Network request appears in the network panel");
|
||||
is(store.getState().requests.requests.size, 1,
|
||||
"Network request appears in the network panel");
|
||||
|
||||
let item = getSortedRequests(store.getState()).get(0);
|
||||
is(item.method, "GET", "The attached method is correct.");
|
||||
is(item.url, TEST_PATH, "The attached url is correct.");
|
||||
|
||||
yield waitForExistingRequests(monitor);
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from ../../netmonitor/test/shared-head.js */
|
||||
|
||||
// Tests that network log messages bring up the network panel.
|
||||
|
||||
"use strict";
|
||||
@ -12,9 +10,6 @@ const TEST_NETWORK_REQUEST_URI =
|
||||
"http://example.com/browser/devtools/client/webconsole/test/" +
|
||||
"test-network-request.html";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
|
||||
|
||||
add_task(function* () {
|
||||
let finishedRequest = waitForFinishedRequest(({ request }) => {
|
||||
return request.url.endsWith("test-network-request.html");
|
||||
@ -29,13 +24,12 @@ add_task(function* () {
|
||||
let monitor = toolbox.getCurrentPanel();
|
||||
|
||||
let { store, windowRequire } = monitor.panelWin;
|
||||
let { getSelectedRequest } = windowRequire("devtools/client/netmonitor/src/selectors/index");
|
||||
let { getSelectedRequest } =
|
||||
windowRequire("devtools/client/netmonitor/src/selectors/index");
|
||||
|
||||
let selected = getSelectedRequest(store.getState());
|
||||
is(selected.method, request.request.method,
|
||||
"The correct request is selected");
|
||||
is(selected.url, request.request.url,
|
||||
"The correct request is definitely selected");
|
||||
|
||||
yield waitForExistingRequests(monitor);
|
||||
});
|
||||
|
@ -3,8 +3,6 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from ../../netmonitor/test/shared-head.js */
|
||||
|
||||
// Tests that network log messages bring up the network panel and select the
|
||||
// right request even if it was previously filtered off.
|
||||
|
||||
@ -15,9 +13,6 @@ const TEST_FILE_URI =
|
||||
"test-network.html";
|
||||
const TEST_URI = "data:text/html;charset=utf8,<p>test file URI";
|
||||
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/netmonitor/test/shared-head.js", this);
|
||||
|
||||
var hud;
|
||||
|
||||
add_task(function* () {
|
||||
@ -69,8 +64,6 @@ add_task(function* () {
|
||||
// All tests are done. Shutdown.
|
||||
HUDService.lastFinishedRequest.callback = null;
|
||||
htmlRequest = browser = requests = hud = null;
|
||||
|
||||
yield waitForExistingRequests(monitor);
|
||||
});
|
||||
|
||||
function testMessages() {
|
||||
|
Loading…
Reference in New Issue
Block a user