Bug 1418928 - requestCookies and responseCookies should be loaded lazily r=Honza

MozReview-Commit-ID: bywCQWGqWI

--HG--
extra : rebase_source : edfec280965ff6e822b006adb1b49379e14751e8
This commit is contained in:
Ricky Chien 2017-11-24 14:57:10 +08:00
Родитель b084a22343
Коммит d572b0ccc3
47 изменённых файлов: 312 добавлений и 171 удалений

Просмотреть файл

@ -4,7 +4,7 @@
"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");
@ -28,74 +28,98 @@ const SECTION_NAMES = [
* 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;
componentDidMount() {
this.maybeFetchCookies(this.props);
}
if (!requestCookies.length && !responseCookies.length) {
return div({ className: "empty-notice" },
COOKIES_EMPTY_TEXT
componentWillReceiveProps(nextProps) {
this.maybeFetchCookies(nextProps);
}
/**
* 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");
}
}
/**
* 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;
}, {});
}
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
);
}
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,
})
)
);
}
let object = {};
if (responseCookies.length) {
object[RESPONSE_COOKIES] = sortObjectKeys(getProperties(responseCookies));
}
if (requestCookies.length) {
object[REQUEST_COOKIES] = sortObjectKeys(getProperties(requestCookies));
}
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}
*/
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;
}
return map;
}, {});
}
module.exports = CookiesPanel;

Просмотреть файл

@ -95,7 +95,7 @@ class MonitorPanel extends Component {
return (
div({ className: "monitor-panel" },
Toolbar(),
Toolbar({ connector }),
SplitBox({
className: "devtools-responsive-container",
initialWidth: `${initialWidth}px`,

Просмотреть файл

@ -13,10 +13,19 @@ 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;
@ -25,6 +34,15 @@ class RequestListColumnCookies extends Component {
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;

Просмотреть файл

@ -13,10 +13,19 @@ 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;
@ -25,6 +34,15 @@ class RequestListColumnSetCookies extends Component {
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;

Просмотреть файл

@ -211,6 +211,7 @@ class RequestListContent extends Component {
render() {
const {
connector,
columns,
displayedRequests,
firstRequestStartedMillis,
@ -236,6 +237,7 @@ class RequestListContent extends Component {
displayedRequests.map((item, index) => RequestListItem({
firstRequestStartedMillis,
fromCache: item.status === "304" || item.fromCache,
connector,
columns,
item,
index,

Просмотреть файл

@ -57,6 +57,8 @@ const UPDATED_REQ_ITEM_PROPS = [
"transferredSize",
"startedMillis",
"totalTime",
"requestCookies",
"responseCookies",
];
const UPDATED_REQ_PROPS = [
@ -72,6 +74,7 @@ const UPDATED_REQ_PROPS = [
class RequestListItem extends Component {
static get propTypes() {
return {
connector: PropTypes.object.isRequired,
columns: PropTypes.object.isRequired,
item: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
@ -111,6 +114,7 @@ class RequestListItem extends Component {
render() {
let {
connector,
columns,
item,
index,
@ -147,8 +151,8 @@ class RequestListItem extends Component {
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") &&

Просмотреть файл

@ -68,7 +68,11 @@ function TabboxPanel({
id: PANELS.COOKIES,
title: COOKIES_TITLE,
},
CookiesPanel({ request, openLink }),
CookiesPanel({
connector,
openLink,
request,
}),
),
TabPanel({
id: PANELS.PARAMS,

Просмотреть файл

@ -56,6 +56,7 @@ const DISABLE_CACHE_LABEL = L10N.getStr("netmonitor.toolbar.disableCache.label")
class Toolbar extends Component {
static get propTypes() {
return {
connector: PropTypes.object.isRequired,
toggleRecording: PropTypes.func.isRequired,
recording: PropTypes.bool.isRequired,
clearRequests: PropTypes.func.isRequired,
@ -78,6 +79,7 @@ class Toolbar extends Component {
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);
@ -130,6 +132,15 @@ class Toolbar extends Component {
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,
@ -238,6 +249,7 @@ class Toolbar extends Component {
type: "filter",
ref: "searchbox",
onChange: setRequestFilterText,
onFocus: this.onSearchBoxFocus,
autocompleteProvider: this.autocompleteProvider,
}),
button({

Просмотреть файл

@ -68,18 +68,19 @@ class FirefoxConnector {
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;
@ -103,9 +104,13 @@ class FirefoxConnector {
}
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() {
@ -316,6 +321,12 @@ class FirefoxConnector {
}
}
/**
* 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);
}

Просмотреть файл

@ -180,6 +180,10 @@ class FirefoxDataProvider {
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;
}
@ -202,6 +206,10 @@ class FirefoxDataProvider {
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;
}
@ -224,6 +232,10 @@ class FirefoxDataProvider {
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;
}
@ -265,12 +277,9 @@ class FirefoxDataProvider {
// 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));
}
/**
@ -342,9 +351,7 @@ class FirefoxDataProvider {
// Create tracking record for this request.
this.rdpRequestMap.set(actor, {
requestHeaders: false,
requestCookies: false,
responseHeaders: false,
responseCookies: false,
eventTimings: false,
});
@ -381,17 +388,15 @@ class FirefoxDataProvider {
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 });
@ -591,12 +596,12 @@ class FirefoxDataProvider {
*
* @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;
}
/**
@ -643,12 +648,12 @@ class FirefoxDataProvider {
*
* @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;
}
/**

Просмотреть файл

@ -123,10 +123,12 @@ const UPDATE_PROPS = [
"requestHeaders",
"requestHeadersFromUploadStream",
"requestCookies",
"requestCookiesAvailable",
"requestPostData",
"requestPostDataAvailable",
"responseHeaders",
"responseCookies",
"responseCookiesAvailable",
"responseContent",
"responseContentAvailable",
"formDataSections",

Просмотреть файл

@ -268,7 +268,6 @@ HarBuilder.prototype = {
postDataText: postData.text,
})) {
postData.mimeType = "application/x-www-form-urlencoded";
// Extract form parameters and produce nice HAR array.
let formDataSections = await getFormDataSections(
requestHeaders,

Просмотреть файл

@ -22,7 +22,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -47,7 +47,7 @@ function* throttleUploadTest(actuallyThrottle) {
});
// 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);
});

Просмотреть файл

@ -43,6 +43,7 @@ class RequestListContextMenu {
mimeType,
httpVersion,
requestHeaders,
requestPostData,
requestPostDataAvailable,
responseHeaders,
responseContentAvailable,
@ -73,7 +74,7 @@ class RequestListContextMenu {
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),
});

Просмотреть файл

@ -6,7 +6,7 @@
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
@ -70,7 +70,7 @@ function getAutocompleteValuesForFlag(flag, request) {
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"]

Просмотреть файл

@ -75,8 +75,6 @@ add_task(function* () {
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;

Просмотреть файл

@ -102,8 +102,6 @@ add_task(function* () {
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")));
@ -111,7 +109,7 @@ add_task(function* () {
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);
@ -127,6 +125,8 @@ add_task(function* () {
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,

Просмотреть файл

@ -31,17 +31,17 @@ add_task(function* () {
yield performRequests(2, HSTS_SJS);
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")));
EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
EXPECTED_REQUESTS.forEach(async ({status, hasStack}, i) => {
let item = getSortedRequests(store.getState()).get(i);
is(item.status, status, `Request #${i} has the expected status`);
await waitUntil(() => !!item.stacktrace);
let { stacktrace } = item;
let stackLen = stacktrace ? stacktrace.length : 0;

Просмотреть файл

@ -8,12 +8,17 @@
*/
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,

Просмотреть файл

@ -11,12 +11,16 @@
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,

Просмотреть файл

@ -10,12 +10,16 @@
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: [{

Просмотреть файл

@ -11,12 +11,16 @@
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,

Просмотреть файл

@ -10,12 +10,16 @@
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: [{

Просмотреть файл

@ -10,12 +10,16 @@
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,

Просмотреть файл

@ -10,12 +10,16 @@
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: {

Просмотреть файл

@ -18,7 +18,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -16,7 +16,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -19,7 +19,7 @@ add_task(function* () {
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;

Просмотреть файл

@ -25,7 +25,7 @@ add_task(function* () {
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);
});

Просмотреть файл

@ -20,7 +20,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -58,8 +58,6 @@ add_task(function* () {
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;

Просмотреть файл

@ -16,7 +16,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -25,7 +25,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -19,7 +19,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -19,7 +19,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -19,7 +19,7 @@ add_task(function* () {
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();
});

Просмотреть файл

@ -19,7 +19,7 @@ add_task(function* () {
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();
});
@ -30,11 +30,9 @@ add_task(function* () {
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);

Просмотреть файл

@ -26,7 +26,7 @@ add_task(function* () {
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();
});
@ -52,7 +52,7 @@ add_task(function* () {
testCustomItemChanged(customItem, origItem);
// send the new request
wait = waitForNetworkEvents(monitor, 0, 1);
wait = waitForNetworkEvents(monitor, 1);
store.dispatch(Actions.sendCustomRequest(connector));
yield wait;

Просмотреть файл

@ -23,7 +23,7 @@ add_task(function* () {
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");
});
@ -40,7 +40,7 @@ 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, 0);
let onRequests = waitForNetworkEvents(monitor, 1);
ITEMS.forEach((item) => {
info(`Selecting the ${item.method} request`);
store.dispatch(Actions.selectRequest(item.id));

Просмотреть файл

@ -30,7 +30,7 @@ add_task(function* () {
{ name: "Accept-Language", value: "cs-CZ" }
];
let wait = waitForNetworkEvents(monitor, 0, 1);
let wait = waitForNetworkEvents(monitor, 1);
sendHTTPRequest({
url: requestUrl,
method: "POST",

Просмотреть файл

@ -16,7 +16,6 @@ function* throttleTest(actuallyThrottle) {
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,
@ -46,9 +45,9 @@ function* throttleTest(actuallyThrottle) {
});
});
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);

Просмотреть файл

@ -8,12 +8,6 @@
*/
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... ");

Просмотреть файл

@ -17,7 +17,7 @@ add_task(async function () {
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;

Просмотреть файл

@ -221,6 +221,8 @@ function waitForAllRequestsFinished(monitor) {
});
}
let lazyLoadEvents = [];
function initNetMonitor(url, enableCache) {
info("Initializing a network monitor pane.");
@ -238,6 +240,15 @@ function initNetMonitor(url, enableCache) {
let monitor = toolbox.getCurrentPanel();
// Start collecting all lazy fetching events when panel is opened.
// removeTab() should be called until all corresponded RECEIVED_* events finished.
monitor.panelWin.on(EVENTS.UPDATING_REQUEST_COOKIES, () => {
lazyLoadEvents.push(monitor.panelWin.once(EVENTS.RECEIVED_REQUEST_COOKIES));
});
monitor.panelWin.on(EVENTS.UPDATING_RESPONSE_COOKIES, () => {
lazyLoadEvents.push(monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_COOKIES));
});
if (!enableCache) {
let panel = monitor.panelWin;
let { store, windowRequire } = panel;
@ -271,6 +282,9 @@ function restartNetMonitor(monitor, newUrl) {
let tab = monitor.toolbox.target.tab;
let url = newUrl || tab.linkedBrowser.currentURI.spec;
info("Wait for completion of all lazily fetched RDP requests...");
yield Promise.all(lazyLoadEvents);
let onDestroyed = monitor.once("destroyed");
yield removeTab(tab);
yield onDestroyed;
@ -289,6 +303,9 @@ function teardown(monitor) {
// done from FirefoxDataProvider.
info("Wait for completion of all pending RDP requests...");
yield waitForExistingRequests(monitor);
info("Wait for completion of all lazily fetched RDP requests...");
yield Promise.all(lazyLoadEvents);
info("All pending requests finished.");
let onDestroyed = monitor.once("destroyed");
@ -297,7 +314,7 @@ function teardown(monitor) {
});
}
function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
function waitForNetworkEvents(monitor, getRequests) {
return new Promise((resolve) => {
let panel = monitor.panelWin;
let { getNetworkRequest } = panel.connector;
@ -307,12 +324,8 @@ function waitForNetworkEvents(monitor, getRequests, postRequests = 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]
@ -359,10 +372,9 @@ function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
}
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;
@ -374,8 +386,8 @@ function waitForNetworkEvents(monitor, getRequests, postRequests = 0) {
// 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);
}

Просмотреть файл

@ -22,11 +22,9 @@ async function waitForExistingRequests(monitor) {
}
// 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;
}
}

Просмотреть файл

@ -18,6 +18,9 @@ class SearchBox extends Component {
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,
@ -96,14 +99,26 @@ class SearchBox extends Component {
}
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;