forked from mirrors/gecko-dev
Bug 1309866 - Migrate RequestsMenuView to a React component with Redux store r=Honza
MozReview-Commit-ID: IMu1sJLxQYy --HG-- extra : rebase_source : aa5a6f1a67ad0d4d83bcb0308d3dd6fe0de0ee0f
This commit is contained in:
parent
ebeb98b7b8
commit
bcce5eb099
120 changed files with 3352 additions and 2677 deletions
42
devtools/client/netmonitor/actions/batching.js
Normal file
42
devtools/client/netmonitor/actions/batching.js
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {
|
||||||
|
BATCH_ACTIONS,
|
||||||
|
BATCH_ENABLE,
|
||||||
|
BATCH_RESET,
|
||||||
|
} = require("../constants");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process multiple actions at once as part of one dispatch, and produce only one
|
||||||
|
* state update at the end. This action is not processed by any reducer, but by a
|
||||||
|
* special store enhancer.
|
||||||
|
*/
|
||||||
|
function batchActions(actions) {
|
||||||
|
return {
|
||||||
|
type: BATCH_ACTIONS,
|
||||||
|
actions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function batchEnable(enabled) {
|
||||||
|
return {
|
||||||
|
type: BATCH_ENABLE,
|
||||||
|
enabled
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function batchReset() {
|
||||||
|
return {
|
||||||
|
type: BATCH_RESET,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
batchActions,
|
||||||
|
batchEnable,
|
||||||
|
batchReset,
|
||||||
|
};
|
||||||
|
|
@ -42,12 +42,12 @@ function enableFilterTypeOnly(filter) {
|
||||||
/**
|
/**
|
||||||
* Set filter text.
|
* Set filter text.
|
||||||
*
|
*
|
||||||
* @param {string} url - A filter text is going to be set
|
* @param {string} text - A filter text is going to be set
|
||||||
*/
|
*/
|
||||||
function setFilterText(url) {
|
function setFilterText(text) {
|
||||||
return {
|
return {
|
||||||
type: SET_FILTER_TEXT,
|
type: SET_FILTER_TEXT,
|
||||||
url,
|
text,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,20 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const batching = require("./batching");
|
||||||
const filters = require("./filters");
|
const filters = require("./filters");
|
||||||
const requests = require("./requests");
|
const requests = require("./requests");
|
||||||
|
const selection = require("./selection");
|
||||||
|
const sort = require("./sort");
|
||||||
|
const timingMarkers = require("./timing-markers");
|
||||||
const ui = require("./ui");
|
const ui = require("./ui");
|
||||||
|
|
||||||
module.exports = Object.assign({}, filters, requests, ui);
|
Object.assign(exports,
|
||||||
|
batching,
|
||||||
|
filters,
|
||||||
|
requests,
|
||||||
|
selection,
|
||||||
|
sort,
|
||||||
|
timingMarkers,
|
||||||
|
ui
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,12 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
|
'batching.js',
|
||||||
'filters.js',
|
'filters.js',
|
||||||
'index.js',
|
'index.js',
|
||||||
'requests.js',
|
'requests.js',
|
||||||
|
'selection.js',
|
||||||
|
'sort.js',
|
||||||
|
'timing-markers.js',
|
||||||
'ui.js',
|
'ui.js',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,61 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
UPDATE_REQUESTS,
|
ADD_REQUEST,
|
||||||
|
UPDATE_REQUEST,
|
||||||
|
CLONE_SELECTED_REQUEST,
|
||||||
|
REMOVE_SELECTED_CUSTOM_REQUEST,
|
||||||
|
CLEAR_REQUESTS,
|
||||||
} = require("../constants");
|
} = require("../constants");
|
||||||
|
|
||||||
/**
|
function addRequest(id, data, batch) {
|
||||||
* Update request items
|
|
||||||
*
|
|
||||||
* @param {array} requests - visible request items
|
|
||||||
*/
|
|
||||||
function updateRequests(items) {
|
|
||||||
return {
|
return {
|
||||||
type: UPDATE_REQUESTS,
|
type: ADD_REQUEST,
|
||||||
items,
|
id,
|
||||||
|
data,
|
||||||
|
meta: { batch },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRequest(id, data, batch) {
|
||||||
|
return {
|
||||||
|
type: UPDATE_REQUEST,
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
meta: { batch },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone the currently selected request, set the "isCustom" attribute.
|
||||||
|
* Used by the "Edit and Resend" feature.
|
||||||
|
*/
|
||||||
|
function cloneSelectedRequest() {
|
||||||
|
return {
|
||||||
|
type: CLONE_SELECTED_REQUEST
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a request from the list. Supports removing only cloned requests with a
|
||||||
|
* "isCustom" attribute. Other requests never need to be removed.
|
||||||
|
*/
|
||||||
|
function removeSelectedCustomRequest() {
|
||||||
|
return {
|
||||||
|
type: REMOVE_SELECTED_CUSTOM_REQUEST
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearRequests() {
|
||||||
|
return {
|
||||||
|
type: CLEAR_REQUESTS
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateRequests,
|
addRequest,
|
||||||
|
updateRequest,
|
||||||
|
cloneSelectedRequest,
|
||||||
|
removeSelectedCustomRequest,
|
||||||
|
clearRequests,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
67
devtools/client/netmonitor/actions/selection.js
Normal file
67
devtools/client/netmonitor/actions/selection.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { getDisplayedRequests } = require("../selectors/index");
|
||||||
|
const { SELECT_REQUEST, PRESELECT_REQUEST } = require("../constants");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a new request with a given id is added in future, select it immediately.
|
||||||
|
* Used by the "Edit and Resend" feature, where we know in advance the ID of the
|
||||||
|
* request, at a time when it wasn't sent yet.
|
||||||
|
*/
|
||||||
|
function preselectRequest(id) {
|
||||||
|
return {
|
||||||
|
type: PRESELECT_REQUEST,
|
||||||
|
id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select request with a given id.
|
||||||
|
*/
|
||||||
|
function selectRequest(id) {
|
||||||
|
return {
|
||||||
|
type: SELECT_REQUEST,
|
||||||
|
id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move the selection up to down according to the "delta" parameter. Possible values:
|
||||||
|
* - Number: positive or negative, move up or down by specified distance
|
||||||
|
* - "PAGE_UP" | "PAGE_DOWN" (String): page up or page down
|
||||||
|
* - +Infinity | -Infinity: move to the start or end of the list
|
||||||
|
*/
|
||||||
|
function selectDelta(delta) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const requests = getDisplayedRequests(state);
|
||||||
|
|
||||||
|
if (requests.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selIndex = requests.findIndex(r => r.id === state.requests.selectedId);
|
||||||
|
|
||||||
|
if (delta === "PAGE_DOWN") {
|
||||||
|
delta = Math.ceil(requests.size / PAGE_SIZE_ITEM_COUNT_RATIO);
|
||||||
|
} else if (delta === "PAGE_UP") {
|
||||||
|
delta = -Math.ceil(requests.size / PAGE_SIZE_ITEM_COUNT_RATIO);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newIndex = Math.min(Math.max(0, selIndex + delta), requests.size - 1);
|
||||||
|
const newItem = requests.get(newIndex);
|
||||||
|
dispatch(selectRequest(newItem.id));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
preselectRequest,
|
||||||
|
selectRequest,
|
||||||
|
selectDelta,
|
||||||
|
};
|
||||||
18
devtools/client/netmonitor/actions/sort.js
Normal file
18
devtools/client/netmonitor/actions/sort.js
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { SORT_BY } = require("../constants");
|
||||||
|
|
||||||
|
function sortBy(sortType) {
|
||||||
|
return {
|
||||||
|
type: SORT_BY,
|
||||||
|
sortType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sortBy
|
||||||
|
};
|
||||||
19
devtools/client/netmonitor/actions/timing-markers.js
Normal file
19
devtools/client/netmonitor/actions/timing-markers.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { ADD_TIMING_MARKER, CLEAR_TIMING_MARKERS } = require("../constants");
|
||||||
|
|
||||||
|
exports.addTimingMarker = (marker) => {
|
||||||
|
return {
|
||||||
|
type: ADD_TIMING_MARKER,
|
||||||
|
marker
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.clearTimingMarkers = () => {
|
||||||
|
return {
|
||||||
|
type: CLEAR_TIMING_MARKERS
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
const {
|
const {
|
||||||
OPEN_SIDEBAR,
|
OPEN_SIDEBAR,
|
||||||
TOGGLE_SIDEBAR,
|
WATERFALL_RESIZE,
|
||||||
} = require("../constants");
|
} = require("../constants");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,12 +25,21 @@ function openSidebar(open) {
|
||||||
* Toggle sidebar open state.
|
* Toggle sidebar open state.
|
||||||
*/
|
*/
|
||||||
function toggleSidebar() {
|
function toggleSidebar() {
|
||||||
|
return (dispatch, getState) => dispatch(openSidebar(!getState().ui.sidebarOpen));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waterfall width has changed (likely on window resize). Update the UI.
|
||||||
|
*/
|
||||||
|
function resizeWaterfall(width) {
|
||||||
return {
|
return {
|
||||||
type: TOGGLE_SIDEBAR,
|
type: WATERFALL_RESIZE,
|
||||||
|
width
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
openSidebar,
|
openSidebar,
|
||||||
toggleSidebar,
|
toggleSidebar,
|
||||||
|
resizeWaterfall,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/* globals NetMonitorView */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { DOM } = require("devtools/client/shared/vendor/react");
|
const { DOM } = require("devtools/client/shared/vendor/react");
|
||||||
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
const { L10N } = require("../l10n");
|
const { L10N } = require("../l10n");
|
||||||
|
const Actions = require("../actions/index");
|
||||||
|
|
||||||
const { button } = DOM;
|
const { button } = DOM;
|
||||||
|
|
||||||
|
|
@ -15,15 +15,18 @@ const { button } = DOM;
|
||||||
* Clear button component
|
* Clear button component
|
||||||
* A type of tool button is responsible for cleaning network requests.
|
* A type of tool button is responsible for cleaning network requests.
|
||||||
*/
|
*/
|
||||||
function ClearButton() {
|
function ClearButton({ onClick }) {
|
||||||
return button({
|
return button({
|
||||||
id: "requests-menu-clear-button",
|
id: "requests-menu-clear-button",
|
||||||
className: "devtools-button devtools-clear-icon",
|
className: "devtools-button devtools-clear-icon",
|
||||||
title: L10N.getStr("netmonitor.toolbar.clear"),
|
title: L10N.getStr("netmonitor.toolbar.clear"),
|
||||||
onClick: () => {
|
onClick,
|
||||||
NetMonitorView.RequestsMenu.clear();
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ClearButton;
|
module.exports = connect(
|
||||||
|
undefined,
|
||||||
|
dispatch => ({
|
||||||
|
onClick: () => dispatch(Actions.clearRequests())
|
||||||
|
})
|
||||||
|
)(ClearButton);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,12 @@
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
'clear-button.js',
|
'clear-button.js',
|
||||||
'filter-buttons.js',
|
'filter-buttons.js',
|
||||||
|
'request-list-content.js',
|
||||||
|
'request-list-empty.js',
|
||||||
|
'request-list-header.js',
|
||||||
|
'request-list-item.js',
|
||||||
|
'request-list-tooltip.js',
|
||||||
|
'request-list.js',
|
||||||
'search-box.js',
|
'search-box.js',
|
||||||
'summary-button.js',
|
'summary-button.js',
|
||||||
'toggle-button.js',
|
'toggle-button.js',
|
||||||
|
|
|
||||||
255
devtools/client/netmonitor/components/request-list-content.js
Normal file
255
devtools/client/netmonitor/components/request-list-content.js
Normal file
|
|
@ -0,0 +1,255 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
/* globals NetMonitorView */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { Task } = require("devtools/shared/task");
|
||||||
|
const { createClass, createFactory, DOM } = require("devtools/client/shared/vendor/react");
|
||||||
|
const { div } = DOM;
|
||||||
|
const Actions = require("../actions/index");
|
||||||
|
const RequestListItem = createFactory(require("./request-list-item"));
|
||||||
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
|
const { setTooltipImageContent,
|
||||||
|
setTooltipStackTraceContent } = require("./request-list-tooltip");
|
||||||
|
const { getDisplayedRequests,
|
||||||
|
getWaterfallScale } = require("../selectors/index");
|
||||||
|
const { KeyCodes } = require("devtools/client/shared/keycodes");
|
||||||
|
|
||||||
|
// tooltip show/hide delay in ms
|
||||||
|
const REQUESTS_TOOLTIP_TOGGLE_DELAY = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the actual contents of the request list.
|
||||||
|
*/
|
||||||
|
const RequestListContent = createClass({
|
||||||
|
displayName: "RequestListContent",
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
// Set the CSS variables for waterfall scaling
|
||||||
|
this.setScalingStyles();
|
||||||
|
|
||||||
|
// Install event handler for displaying a tooltip
|
||||||
|
this.props.tooltip.startTogglingOnHover(this.refs.contentEl, this.onHover, {
|
||||||
|
toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY,
|
||||||
|
interactive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Install event handler to hide the tooltip on scroll
|
||||||
|
this.refs.contentEl.addEventListener("scroll", this.onScroll, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUpdate() {
|
||||||
|
// Check if the list is scrolled to bottom, before UI update
|
||||||
|
this.shouldScrollBottom = this.isScrolledToBottom();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
// Update the CSS variables for waterfall scaling after props change
|
||||||
|
this.setScalingStyles();
|
||||||
|
|
||||||
|
// Keep the list scrolled to bottom if a new row was added
|
||||||
|
if (this.shouldScrollBottom) {
|
||||||
|
let node = this.refs.contentEl;
|
||||||
|
node.scrollTop = node.scrollHeight;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.refs.contentEl.removeEventListener("scroll", this.onScroll, true);
|
||||||
|
|
||||||
|
// Uninstall the tooltip event handler
|
||||||
|
this.props.tooltip.stopTogglingOnHover();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the CSS variables for waterfall scaling. If React supported setting CSS
|
||||||
|
* variables as part of the "style" property of a DOM element, we would use that.
|
||||||
|
*
|
||||||
|
* However, React doesn't support this, so we need to use a hack and update the
|
||||||
|
* DOM element directly: https://github.com/facebook/react/issues/6411
|
||||||
|
*/
|
||||||
|
setScalingStyles(prevProps) {
|
||||||
|
const { scale } = this.props;
|
||||||
|
if (scale == this.currentScale) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentScale = scale;
|
||||||
|
|
||||||
|
const { style } = this.refs.contentEl;
|
||||||
|
style.removeProperty("--timings-scale");
|
||||||
|
style.removeProperty("--timings-rev-scale");
|
||||||
|
style.setProperty("--timings-scale", scale);
|
||||||
|
style.setProperty("--timings-rev-scale", 1 / scale);
|
||||||
|
},
|
||||||
|
|
||||||
|
isScrolledToBottom() {
|
||||||
|
const { contentEl } = this.refs;
|
||||||
|
const lastChildEl = contentEl.lastElementChild;
|
||||||
|
|
||||||
|
if (!lastChildEl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastChildRect = lastChildEl.getBoundingClientRect();
|
||||||
|
let contentRect = contentEl.getBoundingClientRect();
|
||||||
|
|
||||||
|
return (lastChildRect.height + lastChildRect.top) <= contentRect.bottom;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The predicate used when deciding whether a popup should be shown
|
||||||
|
* over a request item or not.
|
||||||
|
*
|
||||||
|
* @param nsIDOMNode target
|
||||||
|
* The element node currently being hovered.
|
||||||
|
* @param object tooltip
|
||||||
|
* The current tooltip instance.
|
||||||
|
* @return {Promise}
|
||||||
|
*/
|
||||||
|
onHover: Task.async(function* (target, tooltip) {
|
||||||
|
let itemEl = target.closest(".request-list-item");
|
||||||
|
if (!itemEl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let itemId = itemEl.dataset.id;
|
||||||
|
if (!itemId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let requestItem = this.props.displayedRequests.find(r => r.id == itemId);
|
||||||
|
if (!requestItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestItem.responseContent && target.closest(".requests-menu-icon-and-file")) {
|
||||||
|
return setTooltipImageContent(tooltip, itemEl, requestItem);
|
||||||
|
} else if (requestItem.cause && target.closest(".requests-menu-cause-stack")) {
|
||||||
|
return setTooltipStackTraceContent(tooltip, requestItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll listener for the requests menu view.
|
||||||
|
*/
|
||||||
|
onScroll() {
|
||||||
|
this.props.tooltip.hide();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for keyboard events. For arrow up/down, page up/down, home/end,
|
||||||
|
* move the selection up or down.
|
||||||
|
*/
|
||||||
|
onKeyDown(e) {
|
||||||
|
let delta;
|
||||||
|
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case KeyCodes.DOM_VK_UP:
|
||||||
|
case KeyCodes.DOM_VK_LEFT:
|
||||||
|
delta = -1;
|
||||||
|
break;
|
||||||
|
case KeyCodes.DOM_VK_DOWN:
|
||||||
|
case KeyCodes.DOM_VK_RIGHT:
|
||||||
|
delta = +1;
|
||||||
|
break;
|
||||||
|
case KeyCodes.DOM_VK_PAGE_UP:
|
||||||
|
delta = "PAGE_UP";
|
||||||
|
break;
|
||||||
|
case KeyCodes.DOM_VK_PAGE_DOWN:
|
||||||
|
delta = "PAGE_DOWN";
|
||||||
|
break;
|
||||||
|
case KeyCodes.DOM_VK_HOME:
|
||||||
|
delta = -Infinity;
|
||||||
|
break;
|
||||||
|
case KeyCodes.DOM_VK_END:
|
||||||
|
delta = +Infinity;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta) {
|
||||||
|
// Prevent scrolling when pressing navigation keys.
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.props.onSelectDelta(delta);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If selection has just changed (by keyboard navigation), don't keep the list
|
||||||
|
* scrolled to bottom, but allow scrolling up with the selection.
|
||||||
|
*/
|
||||||
|
onFocusedNodeChange() {
|
||||||
|
this.shouldScrollBottom = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a focused item was unmounted, transfer the focus to the container element.
|
||||||
|
*/
|
||||||
|
onFocusedNodeUnmount() {
|
||||||
|
if (this.refs.contentEl) {
|
||||||
|
this.refs.contentEl.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { selectedRequestId,
|
||||||
|
displayedRequests,
|
||||||
|
firstRequestStartedMillis,
|
||||||
|
onItemMouseDown,
|
||||||
|
onItemContextMenu,
|
||||||
|
onSecurityIconClick } = this.props;
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{
|
||||||
|
ref: "contentEl",
|
||||||
|
className: "requests-menu-contents",
|
||||||
|
tabIndex: 0,
|
||||||
|
onKeyDown: this.onKeyDown,
|
||||||
|
},
|
||||||
|
displayedRequests.map((item, index) => RequestListItem({
|
||||||
|
key: item.id,
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
isSelected: item.id === selectedRequestId,
|
||||||
|
firstRequestStartedMillis,
|
||||||
|
onMouseDown: e => onItemMouseDown(e, item.id),
|
||||||
|
onContextMenu: e => onItemContextMenu(e, item.id),
|
||||||
|
onSecurityIconClick: e => onSecurityIconClick(e, item),
|
||||||
|
onFocusedNodeChange: this.onFocusedNodeChange,
|
||||||
|
onFocusedNodeUnmount: this.onFocusedNodeUnmount,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
state => ({
|
||||||
|
displayedRequests: getDisplayedRequests(state),
|
||||||
|
selectedRequestId: state.requests.selectedId,
|
||||||
|
scale: getWaterfallScale(state),
|
||||||
|
firstRequestStartedMillis: state.requests.firstStartedMillis,
|
||||||
|
tooltip: NetMonitorView.RequestsMenu.tooltip,
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
onItemMouseDown: (e, item) => dispatch(Actions.selectRequest(item)),
|
||||||
|
onItemContextMenu: (e, item) => {
|
||||||
|
e.preventDefault();
|
||||||
|
NetMonitorView.RequestsMenu.contextMenu.open(e);
|
||||||
|
},
|
||||||
|
onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
|
||||||
|
/**
|
||||||
|
* A handler that opens the security tab in the details view if secure or
|
||||||
|
* broken security indicator is clicked.
|
||||||
|
*/
|
||||||
|
onSecurityIconClick: (e, item) => {
|
||||||
|
const { securityState } = item;
|
||||||
|
if (securityState && securityState !== "insecure") {
|
||||||
|
// Choose the security tab.
|
||||||
|
NetMonitorView.NetworkDetails.widget.selectedIndex = 5;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)(RequestListContent);
|
||||||
65
devtools/client/netmonitor/components/request-list-empty.js
Normal file
65
devtools/client/netmonitor/components/request-list-empty.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
/* globals NetMonitorView */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
|
||||||
|
const { L10N } = require("../l10n");
|
||||||
|
const { div, span, button } = DOM;
|
||||||
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI displayed when the request list is empty. Contains instructions on reloading
|
||||||
|
* the page and on triggering performance analysis of the page.
|
||||||
|
*/
|
||||||
|
const RequestListEmptyNotice = createClass({
|
||||||
|
displayName: "RequestListEmptyNotice",
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
onReloadClick: PropTypes.func.isRequired,
|
||||||
|
onPerfClick: PropTypes.func.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return div(
|
||||||
|
{
|
||||||
|
id: "requests-menu-empty-notice",
|
||||||
|
className: "request-list-empty-notice",
|
||||||
|
},
|
||||||
|
div({ id: "notice-reload-message" },
|
||||||
|
span(null, L10N.getStr("netmonitor.reloadNotice1")),
|
||||||
|
button(
|
||||||
|
{
|
||||||
|
id: "requests-menu-reload-notice-button",
|
||||||
|
className: "devtools-toolbarbutton",
|
||||||
|
"data-standalone": true,
|
||||||
|
onClick: this.props.onReloadClick,
|
||||||
|
},
|
||||||
|
L10N.getStr("netmonitor.reloadNotice2")
|
||||||
|
),
|
||||||
|
span(null, L10N.getStr("netmonitor.reloadNotice3"))
|
||||||
|
),
|
||||||
|
div({ id: "notice-perf-message" },
|
||||||
|
span(null, L10N.getStr("netmonitor.perfNotice1")),
|
||||||
|
button({
|
||||||
|
id: "requests-menu-perf-notice-button",
|
||||||
|
title: L10N.getStr("netmonitor.perfNotice3"),
|
||||||
|
className: "devtools-button",
|
||||||
|
"data-standalone": true,
|
||||||
|
onClick: this.props.onPerfClick,
|
||||||
|
}),
|
||||||
|
span(null, L10N.getStr("netmonitor.perfNotice2"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
undefined,
|
||||||
|
dispatch => ({
|
||||||
|
onPerfClick: e => NetMonitorView.toggleFrontendMode(),
|
||||||
|
onReloadClick: e => NetMonitorView.reloadPage(),
|
||||||
|
})
|
||||||
|
)(RequestListEmptyNotice);
|
||||||
197
devtools/client/netmonitor/components/request-list-header.js
Normal file
197
devtools/client/netmonitor/components/request-list-header.js
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/* globals document */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
|
||||||
|
const { div, button } = DOM;
|
||||||
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
|
const { L10N } = require("../l10n");
|
||||||
|
const { getWaterfallScale } = require("../selectors/index");
|
||||||
|
const Actions = require("../actions/index");
|
||||||
|
const WaterfallBackground = require("../waterfall-background");
|
||||||
|
|
||||||
|
// ms
|
||||||
|
const REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE = 5;
|
||||||
|
// px
|
||||||
|
const REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN = 60;
|
||||||
|
|
||||||
|
const REQUEST_TIME_DECIMALS = 2;
|
||||||
|
|
||||||
|
const HEADERS = [
|
||||||
|
{ name: "status", label: "status3" },
|
||||||
|
{ name: "method" },
|
||||||
|
{ name: "file", boxName: "icon-and-file" },
|
||||||
|
{ name: "domain", boxName: "security-and-domain" },
|
||||||
|
{ name: "cause" },
|
||||||
|
{ name: "type" },
|
||||||
|
{ name: "transferred" },
|
||||||
|
{ name: "size" },
|
||||||
|
{ name: "waterfall" }
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the request list header with sorting arrows for columns.
|
||||||
|
* Displays tick marks in the waterfall column header.
|
||||||
|
* Also draws the waterfall background canvas and updates it when needed.
|
||||||
|
*/
|
||||||
|
const RequestListHeader = createClass({
|
||||||
|
displayName: "RequestListHeader",
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
sort: PropTypes.object,
|
||||||
|
scale: PropTypes.number,
|
||||||
|
waterfallWidth: PropTypes.number,
|
||||||
|
onHeaderClick: PropTypes.func.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.background = new WaterfallBackground(document);
|
||||||
|
this.background.draw(this.props);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.background.draw(this.props);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.background.destroy();
|
||||||
|
this.background = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { sort, scale, waterfallWidth, onHeaderClick } = this.props;
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{ id: "requests-menu-toolbar", className: "devtools-toolbar" },
|
||||||
|
div({ id: "toolbar-labels" },
|
||||||
|
HEADERS.map(header => {
|
||||||
|
const name = header.name;
|
||||||
|
const boxName = header.boxName || name;
|
||||||
|
const label = L10N.getStr(`netmonitor.toolbar.${header.label || name}`);
|
||||||
|
|
||||||
|
let sorted, sortedTitle;
|
||||||
|
const active = sort.type == name ? true : undefined;
|
||||||
|
if (active) {
|
||||||
|
sorted = sort.ascending ? "ascending" : "descending";
|
||||||
|
sortedTitle = L10N.getStr(sort.ascending
|
||||||
|
? "networkMenu.sortedAsc"
|
||||||
|
: "networkMenu.sortedDesc");
|
||||||
|
}
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{
|
||||||
|
id: `requests-menu-${boxName}-header-box`,
|
||||||
|
key: name,
|
||||||
|
className: `requests-menu-header requests-menu-${boxName}`,
|
||||||
|
// Used to style the next column.
|
||||||
|
"data-active": active,
|
||||||
|
},
|
||||||
|
button(
|
||||||
|
{
|
||||||
|
id: `requests-menu-${name}-button`,
|
||||||
|
className: `requests-menu-header-button requests-menu-${name}`,
|
||||||
|
"data-sorted": sorted,
|
||||||
|
title: sortedTitle,
|
||||||
|
onClick: () => onHeaderClick(name),
|
||||||
|
},
|
||||||
|
name == "waterfall" ? WaterfallLabel(waterfallWidth, scale, label)
|
||||||
|
: div({ className: "button-text" }, label),
|
||||||
|
div({ className: "button-icon" })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the waterfall header - timing tick marks with the right spacing
|
||||||
|
*/
|
||||||
|
function waterfallDivisionLabels(waterfallWidth, scale) {
|
||||||
|
let labels = [];
|
||||||
|
|
||||||
|
// Build new millisecond tick labels...
|
||||||
|
let timingStep = REQUESTS_WATERFALL_HEADER_TICKS_MULTIPLE;
|
||||||
|
let scaledStep = scale * timingStep;
|
||||||
|
|
||||||
|
// Ignore any divisions that would end up being too close to each other.
|
||||||
|
while (scaledStep < REQUESTS_WATERFALL_HEADER_TICKS_SPACING_MIN) {
|
||||||
|
scaledStep *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert one label for each division on the current scale.
|
||||||
|
for (let x = 0; x < waterfallWidth; x += scaledStep) {
|
||||||
|
let millisecondTime = x / scale;
|
||||||
|
|
||||||
|
let normalizedTime = millisecondTime;
|
||||||
|
let divisionScale = "millisecond";
|
||||||
|
|
||||||
|
// If the division is greater than 1 minute.
|
||||||
|
if (normalizedTime > 60000) {
|
||||||
|
normalizedTime /= 60000;
|
||||||
|
divisionScale = "minute";
|
||||||
|
} else if (normalizedTime > 1000) {
|
||||||
|
// If the division is greater than 1 second.
|
||||||
|
normalizedTime /= 1000;
|
||||||
|
divisionScale = "second";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Showing too many decimals is bad UX.
|
||||||
|
if (divisionScale == "millisecond") {
|
||||||
|
normalizedTime |= 0;
|
||||||
|
} else {
|
||||||
|
normalizedTime = L10N.numberWithDecimals(normalizedTime, REQUEST_TIME_DECIMALS);
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = (x + scaledStep | 0) - (x | 0);
|
||||||
|
// Adjust the first marker for the borders
|
||||||
|
if (x == 0) {
|
||||||
|
width -= 2;
|
||||||
|
}
|
||||||
|
// Last marker doesn't need a width specified at all
|
||||||
|
if (x + scaledStep >= waterfallWidth) {
|
||||||
|
width = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
labels.push(div(
|
||||||
|
{
|
||||||
|
key: labels.length,
|
||||||
|
className: "requests-menu-timings-division",
|
||||||
|
"data-division-scale": divisionScale,
|
||||||
|
style: { width }
|
||||||
|
},
|
||||||
|
L10N.getFormatStr("networkMenu." + divisionScale, normalizedTime)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WaterfallLabel(waterfallWidth, scale, label) {
|
||||||
|
let className = "button-text requests-menu-waterfall-label-wrapper";
|
||||||
|
|
||||||
|
if (scale != null) {
|
||||||
|
label = waterfallDivisionLabels(waterfallWidth, scale);
|
||||||
|
className += " requests-menu-waterfall-visible";
|
||||||
|
}
|
||||||
|
|
||||||
|
return div({ className }, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
state => ({
|
||||||
|
sort: state.sort,
|
||||||
|
scale: getWaterfallScale(state),
|
||||||
|
waterfallWidth: state.ui.waterfallWidth,
|
||||||
|
firstRequestStartedMillis: state.requests.firstStartedMillis,
|
||||||
|
timingMarkers: state.timingMarkers,
|
||||||
|
}),
|
||||||
|
dispatch => ({
|
||||||
|
onHeaderClick: type => dispatch(Actions.sortBy(type)),
|
||||||
|
})
|
||||||
|
)(RequestListHeader);
|
||||||
346
devtools/client/netmonitor/components/request-list-item.js
Normal file
346
devtools/client/netmonitor/components/request-list-item.js
Normal file
|
|
@ -0,0 +1,346 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
|
||||||
|
const { div, span, img } = DOM;
|
||||||
|
const { L10N } = require("../l10n");
|
||||||
|
const { getFormattedSize } = require("../utils/format-utils");
|
||||||
|
const { getAbbreviatedMimeType } = require("../request-utils");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render one row in the request list.
|
||||||
|
*/
|
||||||
|
const RequestListItem = createClass({
|
||||||
|
displayName: "RequestListItem",
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
item: PropTypes.object.isRequired,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
isSelected: PropTypes.bool.isRequired,
|
||||||
|
firstRequestStartedMillis: PropTypes.number.isRequired,
|
||||||
|
onContextMenu: PropTypes.func.isRequired,
|
||||||
|
onMouseDown: PropTypes.func.isRequired,
|
||||||
|
onSecurityIconClick: PropTypes.func.isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
if (this.props.isSelected) {
|
||||||
|
this.refs.el.focus();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate(nextProps) {
|
||||||
|
return !relevantPropsEqual(this.props.item, nextProps.item)
|
||||||
|
|| this.props.index !== nextProps.index
|
||||||
|
|| this.props.isSelected !== nextProps.isSelected
|
||||||
|
|| this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis;
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
if (!prevProps.isSelected && this.props.isSelected) {
|
||||||
|
this.refs.el.focus();
|
||||||
|
if (this.props.onFocusedNodeChange) {
|
||||||
|
this.props.onFocusedNodeChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
// If this node is being destroyed and has focus, transfer the focus manually
|
||||||
|
// to the parent tree component. Otherwise, the focus will get lost and keyboard
|
||||||
|
// navigation in the tree will stop working. This is a workaround for a XUL bug.
|
||||||
|
// See bugs 1259228 and 1152441 for details.
|
||||||
|
// DE-XUL: Remove this hack once all usages are only in HTML documents.
|
||||||
|
if (this.props.isSelected) {
|
||||||
|
this.refs.el.blur();
|
||||||
|
if (this.props.onFocusedNodeUnmount) {
|
||||||
|
this.props.onFocusedNodeUnmount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
isSelected,
|
||||||
|
firstRequestStartedMillis,
|
||||||
|
onContextMenu,
|
||||||
|
onMouseDown,
|
||||||
|
onSecurityIconClick
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let classList = [ "request-list-item" ];
|
||||||
|
if (isSelected) {
|
||||||
|
classList.push("selected");
|
||||||
|
}
|
||||||
|
classList.push(index % 2 ? "odd" : "even");
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{
|
||||||
|
ref: "el",
|
||||||
|
className: classList.join(" "),
|
||||||
|
"data-id": item.id,
|
||||||
|
tabIndex: 0,
|
||||||
|
onContextMenu,
|
||||||
|
onMouseDown,
|
||||||
|
},
|
||||||
|
StatusColumn(item),
|
||||||
|
MethodColumn(item),
|
||||||
|
FileColumn(item),
|
||||||
|
DomainColumn(item, onSecurityIconClick),
|
||||||
|
CauseColumn(item),
|
||||||
|
TypeColumn(item),
|
||||||
|
TransferredSizeColumn(item),
|
||||||
|
ContentSizeColumn(item),
|
||||||
|
WaterfallColumn(item, firstRequestStartedMillis)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by shouldComponentUpdate: compare two items, and compare only properties
|
||||||
|
* relevant for rendering the RequestListItem. Other properties (like request and
|
||||||
|
* response headers, cookies, bodies) are ignored. These are very useful for the
|
||||||
|
* sidebar details, but not here.
|
||||||
|
*/
|
||||||
|
const RELEVANT_ITEM_PROPS = [
|
||||||
|
"status",
|
||||||
|
"statusText",
|
||||||
|
"fromCache",
|
||||||
|
"fromServiceWorker",
|
||||||
|
"method",
|
||||||
|
"url",
|
||||||
|
"responseContentDataUri",
|
||||||
|
"remoteAddress",
|
||||||
|
"securityState",
|
||||||
|
"cause",
|
||||||
|
"mimeType",
|
||||||
|
"contentSize",
|
||||||
|
"transferredSize",
|
||||||
|
"startedMillis",
|
||||||
|
"totalTime",
|
||||||
|
"eventTimings",
|
||||||
|
];
|
||||||
|
|
||||||
|
function relevantPropsEqual(item1, item2) {
|
||||||
|
return item1 === item2 || RELEVANT_ITEM_PROPS.every(p => item1[p] === item2[p]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusColumn(item) {
|
||||||
|
const { status, statusText, fromCache, fromServiceWorker } = item;
|
||||||
|
|
||||||
|
let code, title;
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
if (fromCache) {
|
||||||
|
code = "cached";
|
||||||
|
} else if (fromServiceWorker) {
|
||||||
|
code = "service worker";
|
||||||
|
} else {
|
||||||
|
code = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusText) {
|
||||||
|
title = `${status} ${statusText}`;
|
||||||
|
if (fromCache) {
|
||||||
|
title += " (cached)";
|
||||||
|
}
|
||||||
|
if (fromServiceWorker) {
|
||||||
|
title += " (service worker)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return div({ className: "requests-menu-subitem requests-menu-status", title },
|
||||||
|
div({ className: "requests-menu-status-icon", "data-code": code }),
|
||||||
|
span({ className: "subitem-label requests-menu-status-code" }, status)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MethodColumn(item) {
|
||||||
|
const { method } = item;
|
||||||
|
return div({ className: "requests-menu-subitem requests-menu-method-box" },
|
||||||
|
span({ className: "subitem-label requests-menu-method" }, method)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FileColumn(item) {
|
||||||
|
const { urlDetails, responseContentDataUri } = item;
|
||||||
|
|
||||||
|
return div({ className: "requests-menu-subitem requests-menu-icon-and-file" },
|
||||||
|
img({
|
||||||
|
className: "requests-menu-icon",
|
||||||
|
src: responseContentDataUri,
|
||||||
|
hidden: !responseContentDataUri,
|
||||||
|
"data-type": responseContentDataUri ? "thumbnail" : undefined
|
||||||
|
}),
|
||||||
|
div(
|
||||||
|
{
|
||||||
|
className: "subitem-label requests-menu-file",
|
||||||
|
title: urlDetails.unicodeUrl
|
||||||
|
},
|
||||||
|
urlDetails.baseNameWithQuery
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DomainColumn(item, onSecurityIconClick) {
|
||||||
|
const { urlDetails, remoteAddress, securityState } = item;
|
||||||
|
|
||||||
|
let iconClassList = [ "requests-security-state-icon" ];
|
||||||
|
let iconTitle;
|
||||||
|
if (urlDetails.isLocal) {
|
||||||
|
iconClassList.push("security-state-local");
|
||||||
|
iconTitle = L10N.getStr("netmonitor.security.state.secure");
|
||||||
|
} else if (securityState) {
|
||||||
|
iconClassList.push(`security-state-${securityState}`);
|
||||||
|
iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = urlDetails.host + (remoteAddress ? ` (${remoteAddress})` : "");
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{ className: "requests-menu-subitem requests-menu-security-and-domain" },
|
||||||
|
div({
|
||||||
|
className: iconClassList.join(" "),
|
||||||
|
title: iconTitle,
|
||||||
|
onClick: onSecurityIconClick,
|
||||||
|
}),
|
||||||
|
span({ className: "subitem-label requests-menu-domain", title }, urlDetails.host)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CauseColumn(item) {
|
||||||
|
const { cause } = item;
|
||||||
|
|
||||||
|
let causeType = "";
|
||||||
|
let causeUri = undefined;
|
||||||
|
let causeHasStack = false;
|
||||||
|
|
||||||
|
if (cause) {
|
||||||
|
causeType = cause.type;
|
||||||
|
causeUri = cause.loadingDocumentUri;
|
||||||
|
causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{ className: "requests-menu-subitem requests-menu-cause", title: causeUri },
|
||||||
|
span({ className: "requests-menu-cause-stack", hidden: !causeHasStack }, "JS"),
|
||||||
|
span({ className: "subitem-label" }, causeType)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTENT_MIME_TYPE_ABBREVIATIONS = {
|
||||||
|
"ecmascript": "js",
|
||||||
|
"javascript": "js",
|
||||||
|
"x-javascript": "js"
|
||||||
|
};
|
||||||
|
|
||||||
|
function TypeColumn(item) {
|
||||||
|
const { mimeType } = item;
|
||||||
|
let abbrevType;
|
||||||
|
if (mimeType) {
|
||||||
|
abbrevType = getAbbreviatedMimeType(mimeType);
|
||||||
|
abbrevType = CONTENT_MIME_TYPE_ABBREVIATIONS[abbrevType] || abbrevType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{ className: "requests-menu-subitem requests-menu-type", title: mimeType },
|
||||||
|
span({ className: "subitem-label" }, abbrevType)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransferredSizeColumn(item) {
|
||||||
|
const { transferredSize, fromCache, fromServiceWorker } = item;
|
||||||
|
|
||||||
|
let text;
|
||||||
|
let className = "subitem-label";
|
||||||
|
if (fromCache) {
|
||||||
|
text = L10N.getStr("networkMenu.sizeCached");
|
||||||
|
className += " theme-comment";
|
||||||
|
} else if (fromServiceWorker) {
|
||||||
|
text = L10N.getStr("networkMenu.sizeServiceWorker");
|
||||||
|
className += " theme-comment";
|
||||||
|
} else if (typeof transferredSize == "number") {
|
||||||
|
text = getFormattedSize(transferredSize);
|
||||||
|
} else if (transferredSize === null) {
|
||||||
|
text = L10N.getStr("networkMenu.sizeUnavailable");
|
||||||
|
}
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{ className: "requests-menu-subitem requests-menu-transferred", title: text },
|
||||||
|
span({ className }, text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ContentSizeColumn(item) {
|
||||||
|
const { contentSize } = item;
|
||||||
|
|
||||||
|
let text;
|
||||||
|
if (typeof contentSize == "number") {
|
||||||
|
text = getFormattedSize(contentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return div(
|
||||||
|
{ className: "requests-menu-subitem subitem-label requests-menu-size", title: text },
|
||||||
|
span({ className: "subitem-label" }, text)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List of properties of the timing info we want to create boxes for
|
||||||
|
const TIMING_KEYS = ["blocked", "dns", "connect", "send", "wait", "receive"];
|
||||||
|
|
||||||
|
function timingBoxes(item) {
|
||||||
|
const { eventTimings, totalTime, fromCache, fromServiceWorker } = item;
|
||||||
|
let boxes = [];
|
||||||
|
|
||||||
|
if (fromCache || fromServiceWorker) {
|
||||||
|
return boxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventTimings) {
|
||||||
|
// Add a set of boxes representing timing information.
|
||||||
|
for (let key of TIMING_KEYS) {
|
||||||
|
let width = eventTimings.timings[key];
|
||||||
|
|
||||||
|
// Don't render anything if it surely won't be visible.
|
||||||
|
// One millisecond == one unscaled pixel.
|
||||||
|
if (width > 0) {
|
||||||
|
boxes.push(div({
|
||||||
|
key,
|
||||||
|
className: "requests-menu-timings-box " + key,
|
||||||
|
style: { width }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof totalTime == "number") {
|
||||||
|
let text = L10N.getFormatStr("networkMenu.totalMS", totalTime);
|
||||||
|
boxes.push(div({
|
||||||
|
key: "total",
|
||||||
|
className: "requests-menu-timings-total",
|
||||||
|
title: text
|
||||||
|
}, text));
|
||||||
|
}
|
||||||
|
|
||||||
|
return boxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function WaterfallColumn(item, firstRequestStartedMillis) {
|
||||||
|
const startedDeltaMillis = item.startedMillis - firstRequestStartedMillis;
|
||||||
|
const paddingInlineStart = `${startedDeltaMillis}px`;
|
||||||
|
|
||||||
|
return div({ className: "requests-menu-subitem requests-menu-waterfall" },
|
||||||
|
div(
|
||||||
|
{ className: "requests-menu-timings", style: { paddingInlineStart } },
|
||||||
|
timingBoxes(item)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = RequestListItem;
|
||||||
107
devtools/client/netmonitor/components/request-list-tooltip.js
Normal file
107
devtools/client/netmonitor/components/request-list-tooltip.js
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/* globals gNetwork, NetMonitorController */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { Task } = require("devtools/shared/task");
|
||||||
|
const { formDataURI } = require("../request-utils");
|
||||||
|
const { WEBCONSOLE_L10N } = require("../l10n");
|
||||||
|
const { setImageTooltip,
|
||||||
|
getImageDimensions } = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
|
||||||
|
|
||||||
|
// px
|
||||||
|
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400;
|
||||||
|
// px
|
||||||
|
const REQUESTS_TOOLTIP_STACK_TRACE_WIDTH = 600;
|
||||||
|
|
||||||
|
const HTML_NS = "http://www.w3.org/1999/xhtml";
|
||||||
|
|
||||||
|
const setTooltipImageContent = Task.async(function* (tooltip, itemEl, requestItem) {
|
||||||
|
let { mimeType, text, encoding } = requestItem.responseContent.content;
|
||||||
|
|
||||||
|
if (!mimeType || !mimeType.includes("image/")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let string = yield gNetwork.getString(text);
|
||||||
|
let src = formDataURI(mimeType, encoding, string);
|
||||||
|
let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
|
||||||
|
let { naturalWidth, naturalHeight } = yield getImageDimensions(tooltip.doc, src);
|
||||||
|
let options = { maxDim, naturalWidth, naturalHeight };
|
||||||
|
setImageTooltip(tooltip, tooltip.doc, src, options);
|
||||||
|
|
||||||
|
return itemEl.querySelector(".requests-menu-icon");
|
||||||
|
});
|
||||||
|
|
||||||
|
const setTooltipStackTraceContent = Task.async(function* (tooltip, requestItem) {
|
||||||
|
let {stacktrace} = requestItem.cause;
|
||||||
|
|
||||||
|
if (!stacktrace || stacktrace.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let doc = tooltip.doc;
|
||||||
|
let el = doc.createElementNS(HTML_NS, "div");
|
||||||
|
el.className = "stack-trace-tooltip devtools-monospace";
|
||||||
|
|
||||||
|
for (let f of stacktrace) {
|
||||||
|
let { functionName, filename, lineNumber, columnNumber, asyncCause } = f;
|
||||||
|
|
||||||
|
if (asyncCause) {
|
||||||
|
// if there is asyncCause, append a "divider" row into the trace
|
||||||
|
let asyncFrameEl = doc.createElementNS(HTML_NS, "div");
|
||||||
|
asyncFrameEl.className = "stack-frame stack-frame-async";
|
||||||
|
asyncFrameEl.textContent =
|
||||||
|
WEBCONSOLE_L10N.getFormatStr("stacktrace.asyncStack", asyncCause);
|
||||||
|
el.appendChild(asyncFrameEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a source name in format "url -> url"
|
||||||
|
let sourceUrl = filename.split(" -> ").pop();
|
||||||
|
|
||||||
|
let frameEl = doc.createElementNS(HTML_NS, "div");
|
||||||
|
frameEl.className = "stack-frame stack-frame-call";
|
||||||
|
|
||||||
|
let funcEl = doc.createElementNS(HTML_NS, "span");
|
||||||
|
funcEl.className = "stack-frame-function-name";
|
||||||
|
funcEl.textContent =
|
||||||
|
functionName || WEBCONSOLE_L10N.getStr("stacktrace.anonymousFunction");
|
||||||
|
frameEl.appendChild(funcEl);
|
||||||
|
|
||||||
|
let sourceEl = doc.createElementNS(HTML_NS, "span");
|
||||||
|
sourceEl.className = "stack-frame-source-name";
|
||||||
|
frameEl.appendChild(sourceEl);
|
||||||
|
|
||||||
|
let sourceInnerEl = doc.createElementNS(HTML_NS, "span");
|
||||||
|
sourceInnerEl.className = "stack-frame-source-name-inner";
|
||||||
|
sourceEl.appendChild(sourceInnerEl);
|
||||||
|
|
||||||
|
sourceInnerEl.textContent = sourceUrl;
|
||||||
|
sourceInnerEl.title = sourceUrl;
|
||||||
|
|
||||||
|
let lineEl = doc.createElementNS(HTML_NS, "span");
|
||||||
|
lineEl.className = "stack-frame-line";
|
||||||
|
lineEl.textContent = `:${lineNumber}:${columnNumber}`;
|
||||||
|
sourceInnerEl.appendChild(lineEl);
|
||||||
|
|
||||||
|
frameEl.addEventListener("click", () => {
|
||||||
|
// hide the tooltip immediately, not after delay
|
||||||
|
tooltip.hide();
|
||||||
|
NetMonitorController.viewSourceInDebugger(filename, lineNumber);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
el.appendChild(frameEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip.setContent(el, {width: REQUESTS_TOOLTIP_STACK_TRACE_WIDTH});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
setTooltipImageContent,
|
||||||
|
setTooltipStackTraceContent,
|
||||||
|
};
|
||||||
34
devtools/client/netmonitor/components/request-list.js
Normal file
34
devtools/client/netmonitor/components/request-list.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { createFactory, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
|
||||||
|
const { div } = DOM;
|
||||||
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
|
const RequestListHeader = createFactory(require("./request-list-header"));
|
||||||
|
const RequestListEmptyNotice = createFactory(require("./request-list-empty"));
|
||||||
|
const RequestListContent = createFactory(require("./request-list-content"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the request list - header, empty text, the actual content with rows
|
||||||
|
*/
|
||||||
|
const RequestList = function ({ isEmpty }) {
|
||||||
|
return div({ className: "request-list-container" },
|
||||||
|
RequestListHeader(),
|
||||||
|
isEmpty ? RequestListEmptyNotice() : RequestListContent()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
RequestList.displayName = "RequestList";
|
||||||
|
|
||||||
|
RequestList.propTypes = {
|
||||||
|
isEmpty: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = connect(
|
||||||
|
state => ({
|
||||||
|
isEmpty: state.requests.requests.isEmpty()
|
||||||
|
})
|
||||||
|
)(RequestList);
|
||||||
|
|
@ -14,7 +14,7 @@ const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
const { PluralForm } = require("devtools/shared/plural-form");
|
const { PluralForm } = require("devtools/shared/plural-form");
|
||||||
const { L10N } = require("../l10n");
|
const { L10N } = require("../l10n");
|
||||||
const { getSummary } = require("../selectors/index");
|
const { getDisplayedRequestsSummary } = require("../selectors/index");
|
||||||
|
|
||||||
const { button, span } = DOM;
|
const { button, span } = DOM;
|
||||||
|
|
||||||
|
|
@ -22,14 +22,12 @@ function SummaryButton({
|
||||||
summary,
|
summary,
|
||||||
triggerSummary,
|
triggerSummary,
|
||||||
}) {
|
}) {
|
||||||
let { count, totalBytes, totalMillis } = summary;
|
let { count, bytes, millis } = summary;
|
||||||
const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
|
const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
|
||||||
PluralForm.get(count, L10N.getStr("networkMenu.summary"))
|
PluralForm.get(count, L10N.getStr("networkMenu.summary"))
|
||||||
.replace("#1", count)
|
.replace("#1", count)
|
||||||
.replace("#2", L10N.numberWithDecimals(totalBytes / 1024,
|
.replace("#2", L10N.numberWithDecimals(bytes / 1024, CONTENT_SIZE_DECIMALS))
|
||||||
CONTENT_SIZE_DECIMALS))
|
.replace("#3", L10N.numberWithDecimals(millis / 1000, REQUEST_TIME_DECIMALS));
|
||||||
.replace("#3", L10N.numberWithDecimals(totalMillis / 1000,
|
|
||||||
REQUEST_TIME_DECIMALS));
|
|
||||||
|
|
||||||
return button({
|
return button({
|
||||||
id: "requests-menu-network-summary-button",
|
id: "requests-menu-network-summary-button",
|
||||||
|
|
@ -47,7 +45,7 @@ SummaryButton.propTypes = {
|
||||||
|
|
||||||
module.exports = connect(
|
module.exports = connect(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
summary: getSummary(state),
|
summary: getDisplayedRequestsSummary(state),
|
||||||
}),
|
}),
|
||||||
(dispatch) => ({
|
(dispatch) => ({
|
||||||
triggerSummary: () => {
|
triggerSummary: () => {
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,20 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/* globals NetMonitorView */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
|
const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||||
const { L10N } = require("../l10n");
|
const { L10N } = require("../l10n");
|
||||||
const Actions = require("../actions/index");
|
const Actions = require("../actions/index");
|
||||||
|
const { isSidebarToggleButtonDisabled } = require("../selectors/index");
|
||||||
|
|
||||||
const { button } = DOM;
|
const { button } = DOM;
|
||||||
|
|
||||||
function ToggleButton({
|
function ToggleButton({
|
||||||
disabled,
|
disabled,
|
||||||
open,
|
open,
|
||||||
triggerSidebar,
|
onToggle,
|
||||||
}) {
|
}) {
|
||||||
let className = ["devtools-button"];
|
let className = ["devtools-button"];
|
||||||
if (!open) {
|
if (!open) {
|
||||||
|
|
@ -32,34 +31,21 @@ function ToggleButton({
|
||||||
title,
|
title,
|
||||||
disabled,
|
disabled,
|
||||||
tabIndex: "0",
|
tabIndex: "0",
|
||||||
onMouseDown: triggerSidebar,
|
onMouseDown: onToggle,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ToggleButton.propTypes = {
|
ToggleButton.propTypes = {
|
||||||
disabled: PropTypes.bool.isRequired,
|
disabled: PropTypes.bool.isRequired,
|
||||||
triggerSidebar: PropTypes.func.isRequired,
|
onToggle: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = connect(
|
module.exports = connect(
|
||||||
(state) => ({
|
(state) => ({
|
||||||
disabled: state.requests.items.length === 0,
|
disabled: isSidebarToggleButtonDisabled(state),
|
||||||
open: state.ui.sidebar.open,
|
open: state.ui.sidebarOpen,
|
||||||
}),
|
}),
|
||||||
(dispatch) => ({
|
(dispatch) => ({
|
||||||
triggerSidebar: () => {
|
onToggle: () => dispatch(Actions.toggleSidebar())
|
||||||
dispatch(Actions.toggleSidebar());
|
|
||||||
|
|
||||||
let requestsMenu = NetMonitorView.RequestsMenu;
|
|
||||||
let selectedIndex = requestsMenu.selectedIndex;
|
|
||||||
|
|
||||||
// Make sure there's a selection if the button is pressed, to avoid
|
|
||||||
// showing an empty network details pane.
|
|
||||||
if (selectedIndex == -1 && requestsMenu.itemCount) {
|
|
||||||
requestsMenu.selectedIndex = 0;
|
|
||||||
} else {
|
|
||||||
requestsMenu.selectedIndex = -1;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
)(ToggleButton);
|
)(ToggleButton);
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,23 @@ const general = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionTypes = {
|
const actionTypes = {
|
||||||
|
BATCH_ACTIONS: "BATCH_ACTIONS",
|
||||||
|
BATCH_ENABLE: "BATCH_ENABLE",
|
||||||
|
ADD_TIMING_MARKER: "ADD_TIMING_MARKER",
|
||||||
|
CLEAR_TIMING_MARKERS: "CLEAR_TIMING_MARKERS",
|
||||||
|
ADD_REQUEST: "ADD_REQUEST",
|
||||||
|
UPDATE_REQUEST: "UPDATE_REQUEST",
|
||||||
|
CLEAR_REQUESTS: "CLEAR_REQUESTS",
|
||||||
|
CLONE_SELECTED_REQUEST: "CLONE_SELECTED_REQUEST",
|
||||||
|
REMOVE_SELECTED_CUSTOM_REQUEST: "REMOVE_SELECTED_CUSTOM_REQUEST",
|
||||||
|
SELECT_REQUEST: "SELECT_REQUEST",
|
||||||
|
PRESELECT_REQUEST: "PRESELECT_REQUEST",
|
||||||
|
SORT_BY: "SORT_BY",
|
||||||
TOGGLE_FILTER_TYPE: "TOGGLE_FILTER_TYPE",
|
TOGGLE_FILTER_TYPE: "TOGGLE_FILTER_TYPE",
|
||||||
ENABLE_FILTER_TYPE_ONLY: "ENABLE_FILTER_TYPE_ONLY",
|
ENABLE_FILTER_TYPE_ONLY: "ENABLE_FILTER_TYPE_ONLY",
|
||||||
SET_FILTER_TEXT: "SET_FILTER_TEXT",
|
SET_FILTER_TEXT: "SET_FILTER_TEXT",
|
||||||
OPEN_SIDEBAR: "OPEN_SIDEBAR",
|
OPEN_SIDEBAR: "OPEN_SIDEBAR",
|
||||||
TOGGLE_SIDEBAR: "TOGGLE_SIDEBAR",
|
WATERFALL_RESIZE: "WATERFALL_RESIZE",
|
||||||
UPDATE_REQUESTS: "UPDATE_REQUESTS",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Object.assign({}, general, actionTypes);
|
module.exports = Object.assign({}, general, actionTypes);
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,11 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { Task } = require("devtools/shared/task");
|
const { Task } = require("devtools/shared/task");
|
||||||
const {
|
const { writeHeaderText,
|
||||||
writeHeaderText,
|
getKeyWithEvent,
|
||||||
getKeyWithEvent,
|
getUrlQuery,
|
||||||
getUrlQuery,
|
parseQueryString } = require("./request-utils");
|
||||||
parseQueryString,
|
const Actions = require("./actions/index");
|
||||||
} = require("./request-utils");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functions handling the custom request view.
|
* Functions handling the custom request view.
|
||||||
|
|
@ -76,37 +75,41 @@ CustomRequestView.prototype = {
|
||||||
*/
|
*/
|
||||||
onUpdate: function (field) {
|
onUpdate: function (field) {
|
||||||
let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
|
let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
|
||||||
|
let store = NetMonitorView.RequestsMenu.store;
|
||||||
let value;
|
let value;
|
||||||
|
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case "method":
|
case "method":
|
||||||
value = $("#custom-method-value").value.trim();
|
value = $("#custom-method-value").value.trim();
|
||||||
selectedItem.attachment.method = value;
|
store.dispatch(Actions.updateRequest(selectedItem.id, { method: value }));
|
||||||
break;
|
break;
|
||||||
case "url":
|
case "url":
|
||||||
value = $("#custom-url-value").value;
|
value = $("#custom-url-value").value;
|
||||||
this.updateCustomQuery(value);
|
this.updateCustomQuery(value);
|
||||||
selectedItem.attachment.url = value;
|
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
|
||||||
break;
|
break;
|
||||||
case "query":
|
case "query":
|
||||||
let query = $("#custom-query-value").value;
|
let query = $("#custom-query-value").value;
|
||||||
this.updateCustomUrl(query);
|
this.updateCustomUrl(query);
|
||||||
field = "url";
|
|
||||||
value = $("#custom-url-value").value;
|
value = $("#custom-url-value").value;
|
||||||
selectedItem.attachment.url = value;
|
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
|
||||||
break;
|
break;
|
||||||
case "body":
|
case "body":
|
||||||
value = $("#custom-postdata-value").value;
|
value = $("#custom-postdata-value").value;
|
||||||
selectedItem.attachment.requestPostData = { postData: { text: value } };
|
store.dispatch(Actions.updateRequest(selectedItem.id, {
|
||||||
|
requestPostData: {
|
||||||
|
postData: { text: value }
|
||||||
|
}
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
case "headers":
|
case "headers":
|
||||||
let headersText = $("#custom-headers-value").value;
|
let headersText = $("#custom-headers-value").value;
|
||||||
value = parseHeadersText(headersText);
|
value = parseHeadersText(headersText);
|
||||||
selectedItem.attachment.requestHeaders = { headers: value };
|
store.dispatch(Actions.updateRequest(selectedItem.id, {
|
||||||
|
requestHeaders: { headers: value }
|
||||||
|
}));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetMonitorView.RequestsMenu.updateMenuView(selectedItem, field, value);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -161,7 +164,7 @@ function parseHeadersText(text) {
|
||||||
* Parse readable text list of a query string.
|
* Parse readable text list of a query string.
|
||||||
*
|
*
|
||||||
* @param string text
|
* @param string text
|
||||||
* Text of query string represetation
|
* Text of query string representation
|
||||||
* @return array
|
* @return array
|
||||||
* Array of query params {name, value}
|
* Array of query params {name, value}
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -267,10 +267,6 @@ DetailsView.prototype = {
|
||||||
// Tab is selected but not dirty. We're done here.
|
// Tab is selected but not dirty. We're done here.
|
||||||
populated[tab] = true;
|
populated[tab] = true;
|
||||||
window.emit(EVENTS.TAB_UPDATED);
|
window.emit(EVENTS.TAB_UPDATED);
|
||||||
|
|
||||||
if (NetMonitorController.isConnected()) {
|
|
||||||
NetMonitorView.RequestsMenu.ensureSelectedItemIsVisible();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (viewState.dirty[tab]) {
|
} else if (viewState.dirty[tab]) {
|
||||||
// Tab is dirty but no longer selected. Don't refresh it now, it'll be
|
// Tab is dirty but no longer selected. Don't refresh it now, it'll be
|
||||||
|
|
@ -328,7 +324,7 @@ DetailsView.prototype = {
|
||||||
} else {
|
} else {
|
||||||
code = data.status;
|
code = data.status;
|
||||||
}
|
}
|
||||||
$("#headers-summary-status-circle").setAttribute("code", code);
|
$("#headers-summary-status-circle").setAttribute("data-code", code);
|
||||||
$("#headers-summary-status-value").setAttribute("value",
|
$("#headers-summary-status-value").setAttribute("value",
|
||||||
data.status + " " + data.statusText);
|
data.status + " " + data.statusText);
|
||||||
$("#headers-summary-status").removeAttribute("hidden");
|
$("#headers-summary-status").removeAttribute("hidden");
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,7 @@ HarBuilder.prototype = {
|
||||||
let log = this.buildLog();
|
let log = this.buildLog();
|
||||||
|
|
||||||
// Build entries.
|
// Build entries.
|
||||||
let items = this._options.items;
|
for (let file of this._options.items) {
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
let file = items[i].attachment;
|
|
||||||
log.entries.push(this.buildEntry(log, file));
|
log.entries.push(this.buildEntry(log, file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,9 +201,7 @@ HarCollector.prototype = {
|
||||||
this.files.set(actor, file);
|
this.files.set(actor, file);
|
||||||
|
|
||||||
// Mimic the Net panel data structure
|
// Mimic the Net panel data structure
|
||||||
this.items.push({
|
this.items.push(file);
|
||||||
attachment: file
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onNetworkEventUpdate: function (type, packet) {
|
onNetworkEventUpdate: function (type, packet) {
|
||||||
|
|
|
||||||
132
devtools/client/netmonitor/middleware/batching.js
Normal file
132
devtools/client/netmonitor/middleware/batching.js
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { BATCH_ACTIONS, BATCH_ENABLE, BATCH_RESET } = require("../constants");
|
||||||
|
|
||||||
|
// ms
|
||||||
|
const REQUESTS_REFRESH_RATE = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware that watches for actions with a "batch = true" value in their meta field.
|
||||||
|
* These actions are queued and dispatched as one batch after a timeout.
|
||||||
|
* Special actions that are handled by this middleware:
|
||||||
|
* - BATCH_ENABLE can be used to enable and disable the batching.
|
||||||
|
* - BATCH_RESET discards the actions that are currently in the queue.
|
||||||
|
*/
|
||||||
|
function batchingMiddleware(store) {
|
||||||
|
return next => {
|
||||||
|
let queuedActions = [];
|
||||||
|
let enabled = true;
|
||||||
|
let flushTask = null;
|
||||||
|
|
||||||
|
return action => {
|
||||||
|
if (action.type === BATCH_ENABLE) {
|
||||||
|
return setEnabled(action.enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === BATCH_RESET) {
|
||||||
|
return resetQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.meta && action.meta.batch) {
|
||||||
|
if (!enabled) {
|
||||||
|
next(action);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedActions.push(action);
|
||||||
|
|
||||||
|
if (!flushTask) {
|
||||||
|
flushTask = new DelayedTask(flushActions, REQUESTS_REFRESH_RATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return flushTask.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
};
|
||||||
|
|
||||||
|
function setEnabled(value) {
|
||||||
|
enabled = value;
|
||||||
|
|
||||||
|
// If disabling the batching, flush the actions that have been queued so far
|
||||||
|
if (!enabled && flushTask) {
|
||||||
|
flushTask.runNow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQueue() {
|
||||||
|
queuedActions = [];
|
||||||
|
|
||||||
|
if (flushTask) {
|
||||||
|
flushTask.cancel();
|
||||||
|
flushTask = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushActions() {
|
||||||
|
const actions = queuedActions;
|
||||||
|
queuedActions = [];
|
||||||
|
|
||||||
|
next({
|
||||||
|
type: BATCH_ACTIONS,
|
||||||
|
actions,
|
||||||
|
});
|
||||||
|
|
||||||
|
flushTask = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a delayed task that calls the specified task function after a delay.
|
||||||
|
*/
|
||||||
|
function DelayedTask(taskFn, delay) {
|
||||||
|
this._promise = new Promise((resolve, reject) => {
|
||||||
|
this.runTask = (cancel) => {
|
||||||
|
if (cancel) {
|
||||||
|
reject("Task cancelled");
|
||||||
|
} else {
|
||||||
|
taskFn();
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
this.runTask = null;
|
||||||
|
};
|
||||||
|
this.timeout = setTimeout(this.runTask, delay);
|
||||||
|
}).catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayedTask.prototype = {
|
||||||
|
/**
|
||||||
|
* Return a promise that is resolved after the task is performed or canceled.
|
||||||
|
*/
|
||||||
|
get promise() {
|
||||||
|
return this._promise;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the execution of the task.
|
||||||
|
*/
|
||||||
|
cancel() {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
if (this.runTask) {
|
||||||
|
this.runTask(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the scheduled task immediately, without waiting for the timeout.
|
||||||
|
* Resolves the promise correctly.
|
||||||
|
*/
|
||||||
|
runNow() {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
if (this.runTask) {
|
||||||
|
this.runTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = batchingMiddleware;
|
||||||
7
devtools/client/netmonitor/middleware/moz.build
Normal file
7
devtools/client/netmonitor/middleware/moz.build
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
DevToolsModules(
|
||||||
|
'batching.js',
|
||||||
|
)
|
||||||
|
|
@ -6,8 +6,10 @@ DIRS += [
|
||||||
'actions',
|
'actions',
|
||||||
'components',
|
'components',
|
||||||
'har',
|
'har',
|
||||||
|
'middleware',
|
||||||
'reducers',
|
'reducers',
|
||||||
'selectors'
|
'selectors',
|
||||||
|
'utils',
|
||||||
]
|
]
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
|
|
@ -27,6 +29,7 @@ DevToolsModules(
|
||||||
'sort-predicates.js',
|
'sort-predicates.js',
|
||||||
'store.js',
|
'store.js',
|
||||||
'toolbar-view.js',
|
'toolbar-view.js',
|
||||||
|
'waterfall-background.js',
|
||||||
)
|
)
|
||||||
|
|
||||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/* eslint-disable mozilla/reject-some-requires */
|
/* eslint-disable mozilla/reject-some-requires */
|
||||||
/* globals window, document, NetMonitorView, gStore, Actions */
|
/* globals window, NetMonitorView, gStore, Actions */
|
||||||
/* exported loader */
|
/* exported loader */
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
@ -41,21 +41,17 @@ const EventEmitter = require("devtools/shared/event-emitter");
|
||||||
const Editor = require("devtools/client/sourceeditor/editor");
|
const Editor = require("devtools/client/sourceeditor/editor");
|
||||||
const {TimelineFront} = require("devtools/shared/fronts/timeline");
|
const {TimelineFront} = require("devtools/shared/fronts/timeline");
|
||||||
const {Task} = require("devtools/shared/task");
|
const {Task} = require("devtools/shared/task");
|
||||||
const {Prefs} = require("./prefs");
|
|
||||||
const {EVENTS} = require("./events");
|
const {EVENTS} = require("./events");
|
||||||
const Actions = require("./actions/index");
|
const Actions = require("./actions/index");
|
||||||
|
const { getDisplayedRequestById } = require("./selectors/index");
|
||||||
|
|
||||||
XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
|
XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
|
||||||
XPCOMUtils.defineConstant(this, "ACTIVITY_TYPE", ACTIVITY_TYPE);
|
XPCOMUtils.defineConstant(this, "ACTIVITY_TYPE", ACTIVITY_TYPE);
|
||||||
XPCOMUtils.defineConstant(this, "Editor", Editor);
|
XPCOMUtils.defineConstant(this, "Editor", Editor);
|
||||||
XPCOMUtils.defineConstant(this, "Prefs", Prefs);
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Chart",
|
XPCOMUtils.defineLazyModuleGetter(this, "Chart",
|
||||||
"resource://devtools/client/shared/widgets/Chart.jsm");
|
"resource://devtools/client/shared/widgets/Chart.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
|
|
||||||
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object defining the network monitor controller components.
|
* Object defining the network monitor controller components.
|
||||||
*/
|
*/
|
||||||
|
|
@ -91,6 +87,7 @@ var NetMonitorController = {
|
||||||
}
|
}
|
||||||
this._shutdown = promise.defer();
|
this._shutdown = promise.defer();
|
||||||
{
|
{
|
||||||
|
gStore.dispatch(Actions.batchReset());
|
||||||
NetMonitorView.destroy();
|
NetMonitorView.destroy();
|
||||||
this.TargetEventsHandler.disconnect();
|
this.TargetEventsHandler.disconnect();
|
||||||
this.NetworkEventsHandler.disconnect();
|
this.NetworkEventsHandler.disconnect();
|
||||||
|
|
@ -287,19 +284,18 @@ var NetMonitorController = {
|
||||||
let deferred = promise.defer();
|
let deferred = promise.defer();
|
||||||
let request = null;
|
let request = null;
|
||||||
let inspector = function () {
|
let inspector = function () {
|
||||||
let predicate = i => i.value === requestId;
|
request = getDisplayedRequestById(gStore.getState(), requestId);
|
||||||
request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
|
|
||||||
if (!request) {
|
if (!request) {
|
||||||
// Reset filters so that the request is visible.
|
// Reset filters so that the request is visible.
|
||||||
gStore.dispatch(Actions.toggleFilterType("all"));
|
gStore.dispatch(Actions.toggleFilterType("all"));
|
||||||
request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
|
request = getDisplayedRequestById(gStore.getState(), requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the request was found, select it. Otherwise this function will be
|
// If the request was found, select it. Otherwise this function will be
|
||||||
// called again once new requests arrive.
|
// called again once new requests arrive.
|
||||||
if (request) {
|
if (request) {
|
||||||
window.off(EVENTS.REQUEST_ADDED, inspector);
|
window.off(EVENTS.REQUEST_ADDED, inspector);
|
||||||
NetMonitorView.RequestsMenu.selectedItem = request;
|
gStore.dispatch(Actions.selectRequest(request.id));
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -398,14 +394,14 @@ TargetEventsHandler.prototype = {
|
||||||
// Reset UI.
|
// Reset UI.
|
||||||
if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
|
if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
|
||||||
NetMonitorView.RequestsMenu.reset();
|
NetMonitorView.RequestsMenu.reset();
|
||||||
NetMonitorView.Sidebar.toggle(false);
|
} else {
|
||||||
|
// If the log is persistent, just clear all accumulated timing markers.
|
||||||
|
gStore.dispatch(Actions.clearTimingMarkers());
|
||||||
}
|
}
|
||||||
// Switch to the default network traffic inspector view.
|
// Switch to the default network traffic inspector view.
|
||||||
if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) {
|
if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) {
|
||||||
NetMonitorView.showNetworkInspectorView();
|
NetMonitorView.showNetworkInspectorView();
|
||||||
}
|
}
|
||||||
// Clear any accumulated markers.
|
|
||||||
NetMonitorController.NetworkEventsHandler.clearMarkers();
|
|
||||||
|
|
||||||
window.emit(EVENTS.TARGET_WILL_NAVIGATE);
|
window.emit(EVENTS.TARGET_WILL_NAVIGATE);
|
||||||
break;
|
break;
|
||||||
|
|
@ -429,8 +425,6 @@ TargetEventsHandler.prototype = {
|
||||||
* Functions handling target network events.
|
* Functions handling target network events.
|
||||||
*/
|
*/
|
||||||
function NetworkEventsHandler() {
|
function NetworkEventsHandler() {
|
||||||
this._markers = [];
|
|
||||||
|
|
||||||
this._onNetworkEvent = this._onNetworkEvent.bind(this);
|
this._onNetworkEvent = this._onNetworkEvent.bind(this);
|
||||||
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
|
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
|
||||||
this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
|
this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
|
||||||
|
|
@ -456,19 +450,6 @@ NetworkEventsHandler.prototype = {
|
||||||
return NetMonitorController.timelineFront;
|
return NetMonitorController.timelineFront;
|
||||||
},
|
},
|
||||||
|
|
||||||
get firstDocumentDOMContentLoadedTimestamp() {
|
|
||||||
let marker = this._markers.filter(e => {
|
|
||||||
return e.name == "document::DOMContentLoaded";
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
return marker ? marker.unixTime / 1000 : -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
get firstDocumentLoadTimestamp() {
|
|
||||||
let marker = this._markers.filter(e => e.name == "document::Load")[0];
|
|
||||||
return marker ? marker.unixTime / 1000 : -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to the current target client.
|
* Connect to the current target client.
|
||||||
*/
|
*/
|
||||||
|
|
@ -525,7 +506,7 @@ NetworkEventsHandler.prototype = {
|
||||||
*/
|
*/
|
||||||
_onDocLoadingMarker: function (marker) {
|
_onDocLoadingMarker: function (marker) {
|
||||||
window.emit(EVENTS.TIMELINE_EVENT, marker);
|
window.emit(EVENTS.TIMELINE_EVENT, marker);
|
||||||
this._markers.push(marker);
|
gStore.dispatch(Actions.addTimingMarker(marker));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -547,8 +528,7 @@ NetworkEventsHandler.prototype = {
|
||||||
} = networkInfo;
|
} = networkInfo;
|
||||||
|
|
||||||
NetMonitorView.RequestsMenu.addRequest(
|
NetMonitorView.RequestsMenu.addRequest(
|
||||||
actor, startedDateTime, method, url, isXHR, cause, fromCache,
|
actor, {startedDateTime, method, url, isXHR, cause, fromCache, fromServiceWorker}
|
||||||
fromServiceWorker
|
|
||||||
);
|
);
|
||||||
window.emit(EVENTS.NETWORK_EVENT, actor);
|
window.emit(EVENTS.NETWORK_EVENT, actor);
|
||||||
},
|
},
|
||||||
|
|
@ -637,7 +617,7 @@ NetworkEventsHandler.prototype = {
|
||||||
_onRequestHeaders: function (response) {
|
_onRequestHeaders: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
requestHeaders: response
|
requestHeaders: response
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
|
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -651,7 +631,7 @@ NetworkEventsHandler.prototype = {
|
||||||
_onRequestCookies: function (response) {
|
_onRequestCookies: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
requestCookies: response
|
requestCookies: response
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
|
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -665,7 +645,7 @@ NetworkEventsHandler.prototype = {
|
||||||
_onRequestPostData: function (response) {
|
_onRequestPostData: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
requestPostData: response
|
requestPostData: response
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
|
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -679,7 +659,7 @@ NetworkEventsHandler.prototype = {
|
||||||
_onSecurityInfo: function (response) {
|
_onSecurityInfo: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
securityInfo: response.securityInfo
|
securityInfo: response.securityInfo
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
|
window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -693,7 +673,7 @@ NetworkEventsHandler.prototype = {
|
||||||
_onResponseHeaders: function (response) {
|
_onResponseHeaders: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
responseHeaders: response
|
responseHeaders: response
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
|
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -707,7 +687,7 @@ NetworkEventsHandler.prototype = {
|
||||||
_onResponseCookies: function (response) {
|
_onResponseCookies: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
responseCookies: response
|
responseCookies: response
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
|
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -721,7 +701,7 @@ NetworkEventsHandler.prototype = {
|
||||||
_onResponseContent: function (response) {
|
_onResponseContent: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
responseContent: response
|
responseContent: response
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
|
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -735,18 +715,11 @@ NetworkEventsHandler.prototype = {
|
||||||
_onEventTimings: function (response) {
|
_onEventTimings: function (response) {
|
||||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||||
eventTimings: response
|
eventTimings: response
|
||||||
}, () => {
|
}).then(() => {
|
||||||
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
|
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all accumulated markers.
|
|
||||||
*/
|
|
||||||
clearMarkers: function () {
|
|
||||||
this._markers.length = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the full text of a LongString.
|
* Fetches the full text of a LongString.
|
||||||
*
|
*
|
||||||
|
|
@ -763,15 +736,6 @@ NetworkEventsHandler.prototype = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if this is document is in RTL mode.
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
XPCOMUtils.defineLazyGetter(window, "isRTL", function () {
|
|
||||||
return window.getComputedStyle(document.documentElement, null)
|
|
||||||
.direction == "rtl";
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenient way of emitting events from the panel window.
|
* Convenient way of emitting events from the panel window.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const { ToolbarView } = require("./toolbar-view");
|
||||||
const { SidebarView } = require("./sidebar-view");
|
const { SidebarView } = require("./sidebar-view");
|
||||||
const { DetailsView } = require("./details-view");
|
const { DetailsView } = require("./details-view");
|
||||||
const { PerformanceStatisticsView } = require("./performance-statistics-view");
|
const { PerformanceStatisticsView } = require("./performance-statistics-view");
|
||||||
|
var {Prefs} = require("./prefs");
|
||||||
|
|
||||||
// Initialize the global redux variables
|
// Initialize the global redux variables
|
||||||
var gStore = configureStore();
|
var gStore = configureStore();
|
||||||
|
|
@ -80,12 +81,6 @@ var NetMonitorView = {
|
||||||
this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
|
this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
|
||||||
this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
|
this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
|
||||||
this.toggleDetailsPane({ visible: false });
|
this.toggleDetailsPane({ visible: false });
|
||||||
|
|
||||||
// Disable the performance statistics mode.
|
|
||||||
if (!Prefs.statistics) {
|
|
||||||
$("#request-menu-context-perf").hidden = true;
|
|
||||||
$("#notice-perf-message").hidden = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -169,7 +164,6 @@ var NetMonitorView = {
|
||||||
*/
|
*/
|
||||||
showNetworkInspectorView: function () {
|
showNetworkInspectorView: function () {
|
||||||
this._body.selectedPanel = $("#network-inspector-view");
|
this._body.selectedPanel = $("#network-inspector-view");
|
||||||
this.RequestsMenu._flushWaterfallViews(true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -192,7 +186,7 @@ var NetMonitorView = {
|
||||||
// • The response content size and request total time are necessary for
|
// • The response content size and request total time are necessary for
|
||||||
// populating the statistics view.
|
// populating the statistics view.
|
||||||
// • The response mime type is used for categorization.
|
// • The response mime type is used for categorization.
|
||||||
yield whenDataAvailable(requestsView, [
|
yield whenDataAvailable(requestsView.store, [
|
||||||
"responseHeaders", "status", "contentSize", "mimeType", "totalTime"
|
"responseHeaders", "status", "contentSize", "mimeType", "totalTime"
|
||||||
]);
|
]);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
|
@ -200,8 +194,9 @@ var NetMonitorView = {
|
||||||
console.error(ex);
|
console.error(ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
statisticsView.createPrimedCacheChart(requestsView.items);
|
const requests = requestsView.store.getState().requests.requests;
|
||||||
statisticsView.createEmptyCacheChart(requestsView.items);
|
statisticsView.createPrimedCacheChart(requests);
|
||||||
|
statisticsView.createEmptyCacheChart(requests);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -251,34 +246,36 @@ var $all = (selector, target = document) => target.querySelectorAll(selector);
|
||||||
/**
|
/**
|
||||||
* Makes sure certain properties are available on all objects in a data store.
|
* Makes sure certain properties are available on all objects in a data store.
|
||||||
*
|
*
|
||||||
* @param array dataStore
|
* @param Store dataStore
|
||||||
* The request view object from which to fetch the item list.
|
* A Redux store for which to check the availability of properties.
|
||||||
* @param array mandatoryFields
|
* @param array mandatoryFields
|
||||||
* A list of strings representing properties of objects in dataStore.
|
* A list of strings representing properties of objects in dataStore.
|
||||||
* @return object
|
* @return object
|
||||||
* A promise resolved when all objects in dataStore contain the
|
* A promise resolved when all objects in dataStore contain the
|
||||||
* properties defined in mandatoryFields.
|
* properties defined in mandatoryFields.
|
||||||
*/
|
*/
|
||||||
function whenDataAvailable(requestsView, mandatoryFields) {
|
function whenDataAvailable(dataStore, mandatoryFields) {
|
||||||
let deferred = promise.defer();
|
return new Promise((resolve, reject) => {
|
||||||
|
let interval = setInterval(() => {
|
||||||
|
const { requests } = dataStore.getState().requests;
|
||||||
|
const allFieldsPresent = !requests.isEmpty() && requests.every(
|
||||||
|
item => mandatoryFields.every(
|
||||||
|
field => item.get(field) !== undefined
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
let interval = setInterval(() => {
|
if (allFieldsPresent) {
|
||||||
const { attachments } = requestsView;
|
clearInterval(interval);
|
||||||
if (attachments.length > 0 && attachments.every(item => {
|
clearTimeout(timer);
|
||||||
return mandatoryFields.every(field => field in item);
|
resolve();
|
||||||
})) {
|
}
|
||||||
|
}, WDA_DEFAULT_VERIFY_INTERVAL);
|
||||||
|
|
||||||
|
let timer = setTimeout(() => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
clearTimeout(timer);
|
reject(new Error("Timed out while waiting for data"));
|
||||||
deferred.resolve();
|
}, WDA_DEFAULT_GIVE_UP_TIMEOUT);
|
||||||
}
|
});
|
||||||
}, WDA_DEFAULT_VERIFY_INTERVAL);
|
|
||||||
|
|
||||||
let timer = setTimeout(() => {
|
|
||||||
clearInterval(interval);
|
|
||||||
deferred.reject(new Error("Timed out while waiting for data"));
|
|
||||||
}, WDA_DEFAULT_GIVE_UP_TIMEOUT);
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -26,188 +26,10 @@
|
||||||
<hbox id="network-table-and-sidebar"
|
<hbox id="network-table-and-sidebar"
|
||||||
class="devtools-responsive-container"
|
class="devtools-responsive-container"
|
||||||
flex="1">
|
flex="1">
|
||||||
<vbox id="network-table" flex="1" class="devtools-main-content">
|
<html:div xmlns="http://www.w3.org/1999/xhtml"
|
||||||
<toolbar id="requests-menu-toolbar"
|
id="network-table"
|
||||||
class="devtools-toolbar"
|
class="devtools-main-content">
|
||||||
align="center">
|
</html:div>
|
||||||
<hbox id="toolbar-labels" flex="1">
|
|
||||||
<hbox id="requests-menu-status-header-box"
|
|
||||||
class="requests-menu-header requests-menu-status"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-status-button"
|
|
||||||
class="requests-menu-header-button requests-menu-status"
|
|
||||||
data-key="status"
|
|
||||||
data-localization="label=netmonitor.toolbar.status3"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-method-header-box"
|
|
||||||
class="requests-menu-header requests-menu-method"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-method-button"
|
|
||||||
class="requests-menu-header-button requests-menu-method"
|
|
||||||
data-key="method"
|
|
||||||
data-localization="label=netmonitor.toolbar.method"
|
|
||||||
crop="end"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-icon-and-file-header-box"
|
|
||||||
class="requests-menu-header requests-menu-icon-and-file"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-file-button"
|
|
||||||
class="requests-menu-header-button requests-menu-file"
|
|
||||||
data-key="file"
|
|
||||||
data-localization="label=netmonitor.toolbar.file"
|
|
||||||
crop="end"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-domain-header-box"
|
|
||||||
class="requests-menu-header requests-menu-security-and-domain"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-domain-button"
|
|
||||||
class="requests-menu-header-button requests-menu-security-and-domain"
|
|
||||||
data-key="domain"
|
|
||||||
data-localization="label=netmonitor.toolbar.domain"
|
|
||||||
crop="end"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-cause-header-box"
|
|
||||||
class="requests-menu-header requests-menu-cause"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-cause-button"
|
|
||||||
class="requests-menu-header-button requests-menu-cause"
|
|
||||||
data-key="cause"
|
|
||||||
data-localization="label=netmonitor.toolbar.cause"
|
|
||||||
crop="end"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-type-header-box"
|
|
||||||
class="requests-menu-header requests-menu-type"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-type-button"
|
|
||||||
class="requests-menu-header-button requests-menu-type"
|
|
||||||
data-key="type"
|
|
||||||
data-localization="label=netmonitor.toolbar.type"
|
|
||||||
crop="end"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-transferred-header-box"
|
|
||||||
class="requests-menu-header requests-menu-transferred"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-transferred-button"
|
|
||||||
class="requests-menu-header-button requests-menu-transferred"
|
|
||||||
data-key="transferred"
|
|
||||||
data-localization="label=netmonitor.toolbar.transferred"
|
|
||||||
crop="end"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-size-header-box"
|
|
||||||
class="requests-menu-header requests-menu-size"
|
|
||||||
align="center">
|
|
||||||
<button id="requests-menu-size-button"
|
|
||||||
class="requests-menu-header-button requests-menu-size"
|
|
||||||
data-key="size"
|
|
||||||
data-localization="label=netmonitor.toolbar.size"
|
|
||||||
crop="end"
|
|
||||||
flex="1">
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="requests-menu-waterfall-header-box"
|
|
||||||
class="requests-menu-header requests-menu-waterfall"
|
|
||||||
align="center"
|
|
||||||
flex="1">
|
|
||||||
<button id="requests-menu-waterfall-button"
|
|
||||||
class="requests-menu-header-button requests-menu-waterfall"
|
|
||||||
data-key="waterfall"
|
|
||||||
pack="start"
|
|
||||||
data-localization="label=netmonitor.toolbar.waterfall"
|
|
||||||
flex="1">
|
|
||||||
<image id="requests-menu-waterfall-image"/>
|
|
||||||
<box id="requests-menu-waterfall-label-wrapper">
|
|
||||||
<label id="requests-menu-waterfall-label"
|
|
||||||
class="plain requests-menu-waterfall"
|
|
||||||
data-localization="value=netmonitor.toolbar.waterfall"/>
|
|
||||||
</box>
|
|
||||||
</button>
|
|
||||||
</hbox>
|
|
||||||
</hbox>
|
|
||||||
</toolbar>
|
|
||||||
|
|
||||||
<vbox id="requests-menu-empty-notice"
|
|
||||||
class="side-menu-widget-empty-text">
|
|
||||||
<hbox id="notice-reload-message" align="center">
|
|
||||||
<label data-localization="content=netmonitor.reloadNotice1"/>
|
|
||||||
<button id="requests-menu-reload-notice-button"
|
|
||||||
class="devtools-toolbarbutton"
|
|
||||||
standalone="true"
|
|
||||||
data-localization="label=netmonitor.reloadNotice2"/>
|
|
||||||
<label data-localization="content=netmonitor.reloadNotice3"/>
|
|
||||||
</hbox>
|
|
||||||
<hbox id="notice-perf-message" align="center">
|
|
||||||
<label data-localization="content=netmonitor.perfNotice1"/>
|
|
||||||
<button id="requests-menu-perf-notice-button"
|
|
||||||
class="devtools-toolbarbutton"
|
|
||||||
standalone="true"
|
|
||||||
data-localization="tooltiptext=netmonitor.perfNotice3"/>
|
|
||||||
<label data-localization="content=netmonitor.perfNotice2"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
|
|
||||||
<vbox id="requests-menu-contents" flex="1">
|
|
||||||
<hbox id="requests-menu-item-template" hidden="true">
|
|
||||||
<hbox class="requests-menu-subitem requests-menu-status"
|
|
||||||
align="center">
|
|
||||||
<box class="requests-menu-status-icon"/>
|
|
||||||
<label class="plain requests-menu-status-code"
|
|
||||||
crop="end"/>
|
|
||||||
</hbox>
|
|
||||||
<hbox class="requests-menu-subitem requests-menu-method-box"
|
|
||||||
align="center">
|
|
||||||
<label class="plain requests-menu-method"
|
|
||||||
crop="end"
|
|
||||||
flex="1"/>
|
|
||||||
</hbox>
|
|
||||||
<hbox class="requests-menu-subitem requests-menu-icon-and-file"
|
|
||||||
align="center">
|
|
||||||
<image class="requests-menu-icon" hidden="true"/>
|
|
||||||
<label class="plain requests-menu-file"
|
|
||||||
crop="end"
|
|
||||||
flex="1"/>
|
|
||||||
</hbox>
|
|
||||||
<hbox class="requests-menu-subitem requests-menu-security-and-domain"
|
|
||||||
align="center">
|
|
||||||
<image class="requests-security-state-icon" />
|
|
||||||
<label class="plain requests-menu-domain"
|
|
||||||
crop="end"
|
|
||||||
flex="1"/>
|
|
||||||
</hbox>
|
|
||||||
<hbox class="requests-menu-subitem requests-menu-cause" align="center">
|
|
||||||
<label class="requests-menu-cause-stack" value="JS" hidden="true"/>
|
|
||||||
<label class="plain requests-menu-cause-label" flex="1" crop="end"/>
|
|
||||||
</hbox>
|
|
||||||
<label class="plain requests-menu-subitem requests-menu-type"
|
|
||||||
crop="end"/>
|
|
||||||
<label class="plain requests-menu-subitem requests-menu-transferred"
|
|
||||||
crop="end"/>
|
|
||||||
<label class="plain requests-menu-subitem requests-menu-size"
|
|
||||||
crop="end"/>
|
|
||||||
<hbox class="requests-menu-subitem requests-menu-waterfall"
|
|
||||||
align="center"
|
|
||||||
flex="1">
|
|
||||||
<hbox class="requests-menu-timings"
|
|
||||||
align="center">
|
|
||||||
<label class="plain requests-menu-timings-total"/>
|
|
||||||
</hbox>
|
|
||||||
</hbox>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
|
||||||
</vbox>
|
|
||||||
|
|
||||||
<splitter id="network-inspector-view-splitter"
|
<splitter id="network-inspector-view-splitter"
|
||||||
class="devtools-side-splitter"/>
|
class="devtools-side-splitter"/>
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ PerformanceStatisticsView.prototype = {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
for (let requestItem of items) {
|
for (let requestItem of items) {
|
||||||
let details = requestItem.attachment;
|
let details = requestItem;
|
||||||
let type;
|
let type;
|
||||||
|
|
||||||
if (Filters.html(details)) {
|
if (Filters.html(details)) {
|
||||||
|
|
@ -237,11 +237,8 @@ function responseIsFresh({ responseHeaders, status }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let list = responseHeaders.headers;
|
let list = responseHeaders.headers;
|
||||||
let cacheControl = list.filter(e => {
|
let cacheControl = list.find(e => e.name.toLowerCase() == "cache-control");
|
||||||
return e.name.toLowerCase() == "cache-control";
|
let expires = list.find(e => e.name.toLowerCase() == "expires");
|
||||||
})[0];
|
|
||||||
|
|
||||||
let expires = list.filter(e => e.name.toLowerCase() == "expires")[0];
|
|
||||||
|
|
||||||
// Check the "Cache-Control" header for a maximum age value.
|
// Check the "Cache-Control" header for a maximum age value.
|
||||||
if (cacheControl) {
|
if (cacheControl) {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,5 @@ const {PrefsHelper} = require("devtools/client/shared/prefs");
|
||||||
exports.Prefs = new PrefsHelper("devtools.netmonitor", {
|
exports.Prefs = new PrefsHelper("devtools.netmonitor", {
|
||||||
networkDetailsWidth: ["Int", "panes-network-details-width"],
|
networkDetailsWidth: ["Int", "panes-network-details-width"],
|
||||||
networkDetailsHeight: ["Int", "panes-network-details-height"],
|
networkDetailsHeight: ["Int", "panes-network-details-height"],
|
||||||
statistics: ["Bool", "statistics"],
|
|
||||||
filters: ["Json", "filters"]
|
filters: ["Json", "filters"]
|
||||||
});
|
});
|
||||||
|
|
|
||||||
25
devtools/client/netmonitor/reducers/batching.js
Normal file
25
devtools/client/netmonitor/reducers/batching.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { BATCH_ACTIONS } = require("../constants");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reducer to handle batched actions. For each action in the BATCH_ACTIONS array,
|
||||||
|
* the reducer is called successively on the array of batched actions, resulting in
|
||||||
|
* only one state update.
|
||||||
|
*/
|
||||||
|
function batchingReducer(nextReducer) {
|
||||||
|
return function reducer(state, action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case BATCH_ACTIONS:
|
||||||
|
return action.actions.reduce(reducer, state);
|
||||||
|
default:
|
||||||
|
return nextReducer(state, action);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = batchingReducer;
|
||||||
|
|
@ -27,7 +27,7 @@ const FilterTypes = I.Record({
|
||||||
|
|
||||||
const Filters = I.Record({
|
const Filters = I.Record({
|
||||||
types: new FilterTypes({ all: true }),
|
types: new FilterTypes({ all: true }),
|
||||||
url: "",
|
text: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
function toggleFilterType(state, action) {
|
function toggleFilterType(state, action) {
|
||||||
|
|
@ -72,7 +72,7 @@ function filters(state = new Filters(), action) {
|
||||||
case ENABLE_FILTER_TYPE_ONLY:
|
case ENABLE_FILTER_TYPE_ONLY:
|
||||||
return state.set("types", enableFilterTypeOnly(state.types, action));
|
return state.set("types", enableFilterTypeOnly(state.types, action));
|
||||||
case SET_FILTER_TEXT:
|
case SET_FILTER_TEXT:
|
||||||
return state.set("url", action.url);
|
return state.set("text", action.text);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,19 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { combineReducers } = require("devtools/client/shared/vendor/redux");
|
const { combineReducers } = require("devtools/client/shared/vendor/redux");
|
||||||
const filters = require("./filters");
|
const batchingReducer = require("./batching");
|
||||||
const requests = require("./requests");
|
const requests = require("./requests");
|
||||||
|
const sort = require("./sort");
|
||||||
|
const filters = require("./filters");
|
||||||
|
const timingMarkers = require("./timing-markers");
|
||||||
const ui = require("./ui");
|
const ui = require("./ui");
|
||||||
|
|
||||||
module.exports = combineReducers({
|
module.exports = batchingReducer(
|
||||||
filters,
|
combineReducers({
|
||||||
requests,
|
requests,
|
||||||
ui,
|
sort,
|
||||||
});
|
filters,
|
||||||
|
timingMarkers,
|
||||||
|
ui,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
|
'batching.js',
|
||||||
'filters.js',
|
'filters.js',
|
||||||
'index.js',
|
'index.js',
|
||||||
'requests.js',
|
'requests.js',
|
||||||
|
'sort.js',
|
||||||
|
'timing-markers.js',
|
||||||
'ui.js',
|
'ui.js',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,242 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const I = require("devtools/client/shared/vendor/immutable");
|
const I = require("devtools/client/shared/vendor/immutable");
|
||||||
|
const { getUrlDetails } = require("../request-utils");
|
||||||
const {
|
const {
|
||||||
UPDATE_REQUESTS,
|
ADD_REQUEST,
|
||||||
|
UPDATE_REQUEST,
|
||||||
|
CLEAR_REQUESTS,
|
||||||
|
SELECT_REQUEST,
|
||||||
|
PRESELECT_REQUEST,
|
||||||
|
CLONE_SELECTED_REQUEST,
|
||||||
|
REMOVE_SELECTED_CUSTOM_REQUEST,
|
||||||
|
OPEN_SIDEBAR
|
||||||
} = require("../constants");
|
} = require("../constants");
|
||||||
|
|
||||||
const Requests = I.Record({
|
const Request = I.Record({
|
||||||
items: [],
|
id: null,
|
||||||
|
// Set to true in case of a request that's being edited as part of "edit and resend"
|
||||||
|
isCustom: false,
|
||||||
|
// Request properties - at the beginning, they are unknown and are gradually filled in
|
||||||
|
startedMillis: undefined,
|
||||||
|
method: undefined,
|
||||||
|
url: undefined,
|
||||||
|
urlDetails: undefined,
|
||||||
|
remotePort: undefined,
|
||||||
|
remoteAddress: undefined,
|
||||||
|
isXHR: undefined,
|
||||||
|
cause: undefined,
|
||||||
|
fromCache: undefined,
|
||||||
|
fromServiceWorker: undefined,
|
||||||
|
status: undefined,
|
||||||
|
statusText: undefined,
|
||||||
|
httpVersion: undefined,
|
||||||
|
securityState: undefined,
|
||||||
|
securityInfo: undefined,
|
||||||
|
mimeType: undefined,
|
||||||
|
contentSize: undefined,
|
||||||
|
transferredSize: undefined,
|
||||||
|
totalTime: undefined,
|
||||||
|
eventTimings: undefined,
|
||||||
|
headersSize: undefined,
|
||||||
|
requestHeaders: undefined,
|
||||||
|
requestHeadersFromUploadStream: undefined,
|
||||||
|
requestCookies: undefined,
|
||||||
|
requestPostData: undefined,
|
||||||
|
responseHeaders: undefined,
|
||||||
|
responseCookies: undefined,
|
||||||
|
responseContent: undefined,
|
||||||
|
responseContentDataUri: undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateRequests(state, action) {
|
const Requests = I.Record({
|
||||||
return state.set("items", action.items || state.items);
|
// The request list
|
||||||
}
|
requests: I.List(),
|
||||||
|
// Selection state
|
||||||
|
selectedId: null,
|
||||||
|
preselectedId: null,
|
||||||
|
// Auxiliary fields to hold requests stats
|
||||||
|
firstStartedMillis: +Infinity,
|
||||||
|
lastEndedMillis: -Infinity,
|
||||||
|
});
|
||||||
|
|
||||||
function requests(state = new Requests(), action) {
|
const UPDATE_PROPS = [
|
||||||
|
"method",
|
||||||
|
"url",
|
||||||
|
"remotePort",
|
||||||
|
"remoteAddress",
|
||||||
|
"status",
|
||||||
|
"statusText",
|
||||||
|
"httpVersion",
|
||||||
|
"securityState",
|
||||||
|
"securityInfo",
|
||||||
|
"mimeType",
|
||||||
|
"contentSize",
|
||||||
|
"transferredSize",
|
||||||
|
"totalTime",
|
||||||
|
"eventTimings",
|
||||||
|
"headersSize",
|
||||||
|
"requestHeaders",
|
||||||
|
"requestHeadersFromUploadStream",
|
||||||
|
"requestCookies",
|
||||||
|
"requestPostData",
|
||||||
|
"responseHeaders",
|
||||||
|
"responseCookies",
|
||||||
|
"responseContent",
|
||||||
|
"responseContentDataUri"
|
||||||
|
];
|
||||||
|
|
||||||
|
function requestsReducer(state = new Requests(), action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case UPDATE_REQUESTS:
|
case ADD_REQUEST: {
|
||||||
return updateRequests(state, action);
|
return state.withMutations(st => {
|
||||||
|
let newRequest = new Request(Object.assign(
|
||||||
|
{ id: action.id },
|
||||||
|
action.data,
|
||||||
|
{ urlDetails: getUrlDetails(action.data.url) }
|
||||||
|
));
|
||||||
|
st.requests = st.requests.push(newRequest);
|
||||||
|
|
||||||
|
// Update the started/ended timestamps
|
||||||
|
let { startedMillis } = action.data;
|
||||||
|
if (startedMillis < st.firstStartedMillis) {
|
||||||
|
st.firstStartedMillis = startedMillis;
|
||||||
|
}
|
||||||
|
if (startedMillis > st.lastEndedMillis) {
|
||||||
|
st.lastEndedMillis = startedMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the request if it was preselected and there is no other selection
|
||||||
|
if (st.preselectedId && st.preselectedId === action.id) {
|
||||||
|
st.selectedId = st.selectedId || st.preselectedId;
|
||||||
|
st.preselectedId = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_REQUEST: {
|
||||||
|
let { requests, lastEndedMillis } = state;
|
||||||
|
|
||||||
|
let updateIdx = requests.findIndex(r => r.id === action.id);
|
||||||
|
if (updateIdx === -1) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
requests = requests.update(updateIdx, r => r.withMutations(request => {
|
||||||
|
for (let [key, value] of Object.entries(action.data)) {
|
||||||
|
if (!UPDATE_PROPS.includes(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
request[key] = value;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "url":
|
||||||
|
// Compute the additional URL details
|
||||||
|
request.urlDetails = getUrlDetails(value);
|
||||||
|
break;
|
||||||
|
case "responseContent":
|
||||||
|
// If there's no mime type available when the response content
|
||||||
|
// is received, assume text/plain as a fallback.
|
||||||
|
if (!request.mimeType) {
|
||||||
|
request.mimeType = "text/plain";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "totalTime":
|
||||||
|
const endedMillis = request.startedMillis + value;
|
||||||
|
lastEndedMillis = Math.max(lastEndedMillis, endedMillis);
|
||||||
|
break;
|
||||||
|
case "requestPostData":
|
||||||
|
request.requestHeadersFromUploadStream = {
|
||||||
|
headers: [],
|
||||||
|
headersSize: 0,
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return state.withMutations(st => {
|
||||||
|
st.requests = requests;
|
||||||
|
st.lastEndedMillis = lastEndedMillis;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case CLEAR_REQUESTS: {
|
||||||
|
return new Requests();
|
||||||
|
}
|
||||||
|
case SELECT_REQUEST: {
|
||||||
|
return state.set("selectedId", action.id);
|
||||||
|
}
|
||||||
|
case PRESELECT_REQUEST: {
|
||||||
|
return state.set("preselectedId", action.id);
|
||||||
|
}
|
||||||
|
case CLONE_SELECTED_REQUEST: {
|
||||||
|
let { requests, selectedId } = state;
|
||||||
|
|
||||||
|
if (!selectedId) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clonedIdx = requests.findIndex(r => r.id === selectedId);
|
||||||
|
if (clonedIdx === -1) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clonedRequest = requests.get(clonedIdx);
|
||||||
|
let newRequest = new Request({
|
||||||
|
id: clonedRequest.id + "-clone",
|
||||||
|
method: clonedRequest.method,
|
||||||
|
url: clonedRequest.url,
|
||||||
|
urlDetails: clonedRequest.urlDetails,
|
||||||
|
requestHeaders: clonedRequest.requestHeaders,
|
||||||
|
requestPostData: clonedRequest.requestPostData,
|
||||||
|
isCustom: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Insert the clone right after the original. This ensures that the requests
|
||||||
|
// are always sorted next to each other, even when multiple requests are
|
||||||
|
// equal according to the sorting criteria.
|
||||||
|
requests = requests.insert(clonedIdx + 1, newRequest);
|
||||||
|
|
||||||
|
return state.withMutations(st => {
|
||||||
|
st.requests = requests;
|
||||||
|
st.selectedId = newRequest.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case REMOVE_SELECTED_CUSTOM_REQUEST: {
|
||||||
|
let { requests, selectedId } = state;
|
||||||
|
|
||||||
|
if (!selectedId) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
let removedRequest = requests.find(r => r.id === selectedId);
|
||||||
|
|
||||||
|
// Only custom requests can be removed
|
||||||
|
if (!removedRequest || !removedRequest.isCustom) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.withMutations(st => {
|
||||||
|
st.requests = requests.filter(r => r !== removedRequest);
|
||||||
|
st.selectedId = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
case OPEN_SIDEBAR: {
|
||||||
|
if (!action.open) {
|
||||||
|
return state.set("selectedId", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state.selectedId && !state.requests.isEmpty()) {
|
||||||
|
return state.set("selectedId", state.requests.get(0).id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = requests;
|
module.exports = requestsReducer;
|
||||||
|
|
|
||||||
33
devtools/client/netmonitor/reducers/sort.js
Normal file
33
devtools/client/netmonitor/reducers/sort.js
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const I = require("devtools/client/shared/vendor/immutable");
|
||||||
|
const { SORT_BY } = require("../constants");
|
||||||
|
|
||||||
|
const Sort = I.Record({
|
||||||
|
// null means: sort by "waterfall", but don't highlight the table header
|
||||||
|
type: null,
|
||||||
|
ascending: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
function sortReducer(state = new Sort(), action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case SORT_BY: {
|
||||||
|
return state.withMutations(st => {
|
||||||
|
if (action.sortType == st.type) {
|
||||||
|
st.ascending = !st.ascending;
|
||||||
|
} else {
|
||||||
|
st.type = action.sortType;
|
||||||
|
st.ascending = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = sortReducer;
|
||||||
54
devtools/client/netmonitor/reducers/timing-markers.js
Normal file
54
devtools/client/netmonitor/reducers/timing-markers.js
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const I = require("devtools/client/shared/vendor/immutable");
|
||||||
|
const { ADD_TIMING_MARKER,
|
||||||
|
CLEAR_TIMING_MARKERS,
|
||||||
|
CLEAR_REQUESTS } = require("../constants");
|
||||||
|
|
||||||
|
const TimingMarkers = I.Record({
|
||||||
|
firstDocumentDOMContentLoadedTimestamp: -1,
|
||||||
|
firstDocumentLoadTimestamp: -1,
|
||||||
|
});
|
||||||
|
|
||||||
|
function addTimingMarker(state, action) {
|
||||||
|
if (action.marker.name == "document::DOMContentLoaded" &&
|
||||||
|
state.firstDocumentDOMContentLoadedTimestamp == -1) {
|
||||||
|
return state.set("firstDocumentDOMContentLoadedTimestamp",
|
||||||
|
action.marker.unixTime / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.marker.name == "document::Load" &&
|
||||||
|
state.firstDocumentLoadTimestamp == -1) {
|
||||||
|
return state.set("firstDocumentLoadTimestamp",
|
||||||
|
action.marker.unixTime / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearTimingMarkers(state) {
|
||||||
|
return state.withMutations(st => {
|
||||||
|
st.remove("firstDocumentDOMContentLoadedTimestamp");
|
||||||
|
st.remove("firstDocumentLoadTimestamp");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function timingMarkers(state = new TimingMarkers(), action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case ADD_TIMING_MARKER:
|
||||||
|
return addTimingMarker(state, action);
|
||||||
|
|
||||||
|
case CLEAR_REQUESTS:
|
||||||
|
case CLEAR_TIMING_MARKERS:
|
||||||
|
return clearTimingMarkers(state);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = timingMarkers;
|
||||||
|
|
@ -7,31 +7,31 @@
|
||||||
const I = require("devtools/client/shared/vendor/immutable");
|
const I = require("devtools/client/shared/vendor/immutable");
|
||||||
const {
|
const {
|
||||||
OPEN_SIDEBAR,
|
OPEN_SIDEBAR,
|
||||||
TOGGLE_SIDEBAR,
|
WATERFALL_RESIZE,
|
||||||
} = require("../constants");
|
} = require("../constants");
|
||||||
|
|
||||||
const Sidebar = I.Record({
|
|
||||||
open: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const UI = I.Record({
|
const UI = I.Record({
|
||||||
sidebar: new Sidebar(),
|
sidebarOpen: false,
|
||||||
|
waterfallWidth: 300,
|
||||||
});
|
});
|
||||||
|
|
||||||
function openSidebar(state, action) {
|
function openSidebar(state, action) {
|
||||||
return state.setIn(["sidebar", "open"], action.open);
|
return state.set("sidebarOpen", action.open);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSidebar(state, action) {
|
// Safe bounds for waterfall width (px)
|
||||||
return state.setIn(["sidebar", "open"], !state.sidebar.open);
|
const REQUESTS_WATERFALL_SAFE_BOUNDS = 90;
|
||||||
|
|
||||||
|
function resizeWaterfall(state, action) {
|
||||||
|
return state.set("waterfallWidth", action.width - REQUESTS_WATERFALL_SAFE_BOUNDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ui(state = new UI(), action) {
|
function ui(state = new UI(), action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case OPEN_SIDEBAR:
|
case OPEN_SIDEBAR:
|
||||||
return openSidebar(state, action);
|
return openSidebar(state, action);
|
||||||
case TOGGLE_SIDEBAR:
|
case WATERFALL_RESIZE:
|
||||||
return toggleSidebar(state, action);
|
return resizeWaterfall(state, action);
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ RequestListContextMenu.prototype = {
|
||||||
id: "request-menu-context-copy-url-params",
|
id: "request-menu-context-copy-url-params",
|
||||||
label: L10N.getStr("netmonitor.context.copyUrlParams"),
|
label: L10N.getStr("netmonitor.context.copyUrlParams"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
|
||||||
visible: !!(selectedItem && getUrlQuery(selectedItem.attachment.url)),
|
visible: !!(selectedItem && getUrlQuery(selectedItem.url)),
|
||||||
click: () => this.copyUrlParams(),
|
click: () => this.copyUrlParams(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -66,7 +66,7 @@ RequestListContextMenu.prototype = {
|
||||||
id: "request-menu-context-copy-post-data",
|
id: "request-menu-context-copy-post-data",
|
||||||
label: L10N.getStr("netmonitor.context.copyPostData"),
|
label: L10N.getStr("netmonitor.context.copyPostData"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
|
||||||
visible: !!(selectedItem && selectedItem.attachment.requestPostData),
|
visible: !!(selectedItem && selectedItem.requestPostData),
|
||||||
click: () => this.copyPostData(),
|
click: () => this.copyPostData(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ RequestListContextMenu.prototype = {
|
||||||
id: "request-menu-context-copy-as-curl",
|
id: "request-menu-context-copy-as-curl",
|
||||||
label: L10N.getStr("netmonitor.context.copyAsCurl"),
|
label: L10N.getStr("netmonitor.context.copyAsCurl"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
|
||||||
visible: !!(selectedItem && selectedItem.attachment),
|
visible: !!selectedItem,
|
||||||
click: () => this.copyAsCurl(),
|
click: () => this.copyAsCurl(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ RequestListContextMenu.prototype = {
|
||||||
id: "request-menu-context-copy-request-headers",
|
id: "request-menu-context-copy-request-headers",
|
||||||
label: L10N.getStr("netmonitor.context.copyRequestHeaders"),
|
label: L10N.getStr("netmonitor.context.copyRequestHeaders"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"),
|
||||||
visible: !!(selectedItem && selectedItem.attachment.requestHeaders),
|
visible: !!(selectedItem && selectedItem.requestHeaders),
|
||||||
click: () => this.copyRequestHeaders(),
|
click: () => this.copyRequestHeaders(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -95,7 +95,7 @@ RequestListContextMenu.prototype = {
|
||||||
id: "response-menu-context-copy-response-headers",
|
id: "response-menu-context-copy-response-headers",
|
||||||
label: L10N.getStr("netmonitor.context.copyResponseHeaders"),
|
label: L10N.getStr("netmonitor.context.copyResponseHeaders"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"),
|
||||||
visible: !!(selectedItem && selectedItem.attachment.responseHeaders),
|
visible: !!(selectedItem && selectedItem.responseHeaders),
|
||||||
click: () => this.copyResponseHeaders(),
|
click: () => this.copyResponseHeaders(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -104,9 +104,9 @@ RequestListContextMenu.prototype = {
|
||||||
label: L10N.getStr("netmonitor.context.copyResponse"),
|
label: L10N.getStr("netmonitor.context.copyResponse"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
|
||||||
visible: !!(selectedItem &&
|
visible: !!(selectedItem &&
|
||||||
selectedItem.attachment.responseContent &&
|
selectedItem.responseContent &&
|
||||||
selectedItem.attachment.responseContent.content.text &&
|
selectedItem.responseContent.content.text &&
|
||||||
selectedItem.attachment.responseContent.content.text.length !== 0),
|
selectedItem.responseContent.content.text.length !== 0),
|
||||||
click: () => this.copyResponse(),
|
click: () => this.copyResponse(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -115,9 +115,8 @@ RequestListContextMenu.prototype = {
|
||||||
label: L10N.getStr("netmonitor.context.copyImageAsDataUri"),
|
label: L10N.getStr("netmonitor.context.copyImageAsDataUri"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"),
|
||||||
visible: !!(selectedItem &&
|
visible: !!(selectedItem &&
|
||||||
selectedItem.attachment.responseContent &&
|
selectedItem.responseContent &&
|
||||||
selectedItem.attachment.responseContent.content
|
selectedItem.responseContent.content.mimeType.includes("image/")),
|
||||||
.mimeType.includes("image/")),
|
|
||||||
click: () => this.copyImageAsDataUri(),
|
click: () => this.copyImageAsDataUri(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -130,7 +129,7 @@ RequestListContextMenu.prototype = {
|
||||||
id: "request-menu-context-copy-all-as-har",
|
id: "request-menu-context-copy-all-as-har",
|
||||||
label: L10N.getStr("netmonitor.context.copyAllAsHar"),
|
label: L10N.getStr("netmonitor.context.copyAllAsHar"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
|
||||||
visible: !!this.items.length,
|
visible: this.items.size > 0,
|
||||||
click: () => this.copyAllAsHar(),
|
click: () => this.copyAllAsHar(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -138,7 +137,7 @@ RequestListContextMenu.prototype = {
|
||||||
id: "request-menu-context-save-all-as-har",
|
id: "request-menu-context-save-all-as-har",
|
||||||
label: L10N.getStr("netmonitor.context.saveAllAsHar"),
|
label: L10N.getStr("netmonitor.context.saveAllAsHar"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
|
||||||
visible: !!this.items.length,
|
visible: this.items.size > 0,
|
||||||
click: () => this.saveAllAsHar(),
|
click: () => this.saveAllAsHar(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -152,8 +151,7 @@ RequestListContextMenu.prototype = {
|
||||||
label: L10N.getStr("netmonitor.context.editAndResend"),
|
label: L10N.getStr("netmonitor.context.editAndResend"),
|
||||||
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
|
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
|
||||||
visible: !!(NetMonitorController.supportsCustomRequest &&
|
visible: !!(NetMonitorController.supportsCustomRequest &&
|
||||||
selectedItem &&
|
selectedItem && !selectedItem.isCustom),
|
||||||
!selectedItem.attachment.isCustom),
|
|
||||||
click: () => NetMonitorView.RequestsMenu.cloneSelectedRequest(),
|
click: () => NetMonitorView.RequestsMenu.cloneSelectedRequest(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
@ -187,15 +185,14 @@ RequestListContextMenu.prototype = {
|
||||||
*/
|
*/
|
||||||
openRequestInTab() {
|
openRequestInTab() {
|
||||||
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
||||||
let { url } = this.selectedItem.attachment;
|
win.openUILinkIn(this.selectedItem.url, "tab", { relatedToCurrent: true });
|
||||||
win.openUILinkIn(url, "tab", { relatedToCurrent: true });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy the request url from the currently selected item.
|
* Copy the request url from the currently selected item.
|
||||||
*/
|
*/
|
||||||
copyUrl() {
|
copyUrl() {
|
||||||
clipboardHelper.copyString(this.selectedItem.attachment.url);
|
clipboardHelper.copyString(this.selectedItem.url);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -203,7 +200,7 @@ RequestListContextMenu.prototype = {
|
||||||
* selected item.
|
* selected item.
|
||||||
*/
|
*/
|
||||||
copyUrlParams() {
|
copyUrlParams() {
|
||||||
let { url } = this.selectedItem.attachment;
|
let { url } = this.selectedItem;
|
||||||
let params = getUrlQuery(url).split("&");
|
let params = getUrlQuery(url).split("&");
|
||||||
let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
|
let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
|
||||||
clipboardHelper.copyString(string);
|
clipboardHelper.copyString(string);
|
||||||
|
|
@ -214,7 +211,7 @@ RequestListContextMenu.prototype = {
|
||||||
* the currently selected item.
|
* the currently selected item.
|
||||||
*/
|
*/
|
||||||
copyPostData: Task.async(function* () {
|
copyPostData: Task.async(function* () {
|
||||||
let selected = this.selectedItem.attachment;
|
let selected = this.selectedItem;
|
||||||
|
|
||||||
// Try to extract any form data parameters.
|
// Try to extract any form data parameters.
|
||||||
let formDataSections = yield getFormDataSections(
|
let formDataSections = yield getFormDataSections(
|
||||||
|
|
@ -251,7 +248,7 @@ RequestListContextMenu.prototype = {
|
||||||
* Copy a cURL command from the currently selected item.
|
* Copy a cURL command from the currently selected item.
|
||||||
*/
|
*/
|
||||||
copyAsCurl: Task.async(function* () {
|
copyAsCurl: Task.async(function* () {
|
||||||
let selected = this.selectedItem.attachment;
|
let selected = this.selectedItem;
|
||||||
|
|
||||||
// Create a sanitized object for the Curl command generator.
|
// Create a sanitized object for the Curl command generator.
|
||||||
let data = {
|
let data = {
|
||||||
|
|
@ -281,8 +278,7 @@ RequestListContextMenu.prototype = {
|
||||||
* Copy the raw request headers from the currently selected item.
|
* Copy the raw request headers from the currently selected item.
|
||||||
*/
|
*/
|
||||||
copyRequestHeaders() {
|
copyRequestHeaders() {
|
||||||
let selected = this.selectedItem.attachment;
|
let rawHeaders = this.selectedItem.requestHeaders.rawHeaders.trim();
|
||||||
let rawHeaders = selected.requestHeaders.rawHeaders.trim();
|
|
||||||
if (Services.appinfo.OS !== "WINNT") {
|
if (Services.appinfo.OS !== "WINNT") {
|
||||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||||
}
|
}
|
||||||
|
|
@ -293,8 +289,7 @@ RequestListContextMenu.prototype = {
|
||||||
* Copy the raw response headers from the currently selected item.
|
* Copy the raw response headers from the currently selected item.
|
||||||
*/
|
*/
|
||||||
copyResponseHeaders() {
|
copyResponseHeaders() {
|
||||||
let selected = this.selectedItem.attachment;
|
let rawHeaders = this.selectedItem.responseHeaders.rawHeaders.trim();
|
||||||
let rawHeaders = selected.responseHeaders.rawHeaders.trim();
|
|
||||||
if (Services.appinfo.OS !== "WINNT") {
|
if (Services.appinfo.OS !== "WINNT") {
|
||||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||||
}
|
}
|
||||||
|
|
@ -305,8 +300,7 @@ RequestListContextMenu.prototype = {
|
||||||
* Copy image as data uri.
|
* Copy image as data uri.
|
||||||
*/
|
*/
|
||||||
copyImageAsDataUri() {
|
copyImageAsDataUri() {
|
||||||
let selected = this.selectedItem.attachment;
|
const { mimeType, text, encoding } = this.selectedItem.responseContent.content;
|
||||||
let { mimeType, text, encoding } = selected.responseContent.content;
|
|
||||||
|
|
||||||
gNetwork.getString(text).then(string => {
|
gNetwork.getString(text).then(string => {
|
||||||
let data = formDataURI(mimeType, encoding, string);
|
let data = formDataURI(mimeType, encoding, string);
|
||||||
|
|
@ -318,8 +312,7 @@ RequestListContextMenu.prototype = {
|
||||||
* Copy response data as a string.
|
* Copy response data as a string.
|
||||||
*/
|
*/
|
||||||
copyResponse() {
|
copyResponse() {
|
||||||
let selected = this.selectedItem.attachment;
|
const { text } = this.selectedItem.responseContent.content;
|
||||||
let text = selected.responseContent.content.text;
|
|
||||||
|
|
||||||
gNetwork.getString(text).then(string => {
|
gNetwork.getString(text).then(string => {
|
||||||
clipboardHelper.copyString(string);
|
clipboardHelper.copyString(string);
|
||||||
|
|
@ -348,8 +341,7 @@ RequestListContextMenu.prototype = {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getString: gNetwork.getString.bind(gNetwork),
|
getString: gNetwork.getString.bind(gNetwork),
|
||||||
view: NetMonitorView.RequestsMenu,
|
items: this.items,
|
||||||
items: NetMonitorView.RequestsMenu.items,
|
|
||||||
title: title
|
title: title
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ const getFormDataSections = Task.async(function* (headers, uploadHeaders, postDa
|
||||||
getString) {
|
getString) {
|
||||||
let formDataSections = [];
|
let formDataSections = [];
|
||||||
|
|
||||||
let { headers: requestHeaders } = headers;
|
let requestHeaders = headers.headers;
|
||||||
let { headers: payloadHeaders } = uploadHeaders;
|
let payloadHeaders = uploadHeaders ? uploadHeaders.headers : [];
|
||||||
let allHeaders = [...payloadHeaders, ...requestHeaders];
|
let allHeaders = [...payloadHeaders, ...requestHeaders];
|
||||||
|
|
||||||
let contentTypeHeader = allHeaders.find(e => {
|
let contentTypeHeader = allHeaders.find(e => {
|
||||||
|
|
@ -188,6 +188,37 @@ function getUrlHost(url) {
|
||||||
return decodeUnicodeUrl((new URL(url)).host);
|
return decodeUnicodeUrl((new URL(url)).host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract several details fields from a URL at once.
|
||||||
|
*/
|
||||||
|
function getUrlDetails(url) {
|
||||||
|
let baseNameWithQuery = getUrlBaseNameWithQuery(url);
|
||||||
|
let host = getUrlHost(url);
|
||||||
|
let hostname = getUrlHostName(url);
|
||||||
|
let unicodeUrl = decodeUnicodeUrl(url);
|
||||||
|
|
||||||
|
// Mark local hosts specially, where "local" is as defined in the W3C
|
||||||
|
// spec for secure contexts.
|
||||||
|
// http://www.w3.org/TR/powerful-features/
|
||||||
|
//
|
||||||
|
// * If the name falls under 'localhost'
|
||||||
|
// * If the name is an IPv4 address within 127.0.0.0/8
|
||||||
|
// * If the name is an IPv6 address within ::1/128
|
||||||
|
//
|
||||||
|
// IPv6 parsing is a little sloppy; it assumes that the address has
|
||||||
|
// been validated before it gets here.
|
||||||
|
let isLocal = hostname.match(/(.+\.)?localhost$/) ||
|
||||||
|
hostname.match(/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}/) ||
|
||||||
|
hostname.match(/\[[0:]+1\]/);
|
||||||
|
|
||||||
|
return {
|
||||||
|
baseNameWithQuery,
|
||||||
|
host,
|
||||||
|
unicodeUrl,
|
||||||
|
isLocal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a url's query string into its components
|
* Parse a url's query string into its components
|
||||||
*
|
*
|
||||||
|
|
@ -253,6 +284,7 @@ module.exports = {
|
||||||
getUrlBaseNameWithQuery,
|
getUrlBaseNameWithQuery,
|
||||||
getUrlHostName,
|
getUrlHostName,
|
||||||
getUrlHost,
|
getUrlHost,
|
||||||
|
getUrlDetails,
|
||||||
parseQueryString,
|
parseQueryString,
|
||||||
loadCauseString,
|
loadCauseString,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
13
devtools/client/netmonitor/selectors/filters.js
Normal file
13
devtools/client/netmonitor/selectors/filters.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function getActiveFilters(state) {
|
||||||
|
return state.filters.types.toSeq().filter(checked => checked).keySeq().toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getActiveFilters
|
||||||
|
};
|
||||||
|
|
@ -4,60 +4,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { createSelector } = require("devtools/client/shared/vendor/reselect");
|
const filters = require("./filters");
|
||||||
|
const requests = require("./requests");
|
||||||
|
const ui = require("./ui");
|
||||||
|
|
||||||
/**
|
Object.assign(exports,
|
||||||
* Gets the total number of bytes representing the cumulated content size of
|
filters,
|
||||||
* a set of requests. Returns 0 for an empty set.
|
requests,
|
||||||
*
|
ui
|
||||||
* @param {array} items - an array of request items
|
|
||||||
* @return {number} total bytes of requests
|
|
||||||
*/
|
|
||||||
function getTotalBytesOfRequests(items) {
|
|
||||||
if (!items.length) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = 0;
|
|
||||||
items.forEach((item) => {
|
|
||||||
let size = item.attachment.contentSize;
|
|
||||||
result += (typeof size == "number") ? size : 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the total milliseconds for all requests. Returns null for an
|
|
||||||
* empty set.
|
|
||||||
*
|
|
||||||
* @param {array} items - an array of request items
|
|
||||||
* @return {object} total milliseconds for all requests
|
|
||||||
*/
|
|
||||||
function getTotalMillisOfRequests(items) {
|
|
||||||
if (!items.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldest = items.reduce((prev, curr) =>
|
|
||||||
prev.attachment.startedMillis < curr.attachment.startedMillis ?
|
|
||||||
prev : curr);
|
|
||||||
const newest = items.reduce((prev, curr) =>
|
|
||||||
prev.attachment.startedMillis > curr.attachment.startedMillis ?
|
|
||||||
prev : curr);
|
|
||||||
|
|
||||||
return newest.attachment.endedMillis - oldest.attachment.startedMillis;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSummary = createSelector(
|
|
||||||
(state) => state.requests.items,
|
|
||||||
(requests) => ({
|
|
||||||
count: requests.length,
|
|
||||||
totalBytes: getTotalBytesOfRequests(requests),
|
|
||||||
totalMillis: getTotalMillisOfRequests(requests),
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
getSummary,
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -3,5 +3,8 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
'index.js'
|
'filters.js',
|
||||||
|
'index.js',
|
||||||
|
'requests.js',
|
||||||
|
'ui.js',
|
||||||
)
|
)
|
||||||
|
|
|
||||||
119
devtools/client/netmonitor/selectors/requests.js
Normal file
119
devtools/client/netmonitor/selectors/requests.js
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { createSelector } = require("devtools/client/shared/vendor/reselect");
|
||||||
|
const { Filters, isFreetextMatch } = require("../filter-predicates");
|
||||||
|
const { Sorters } = require("../sort-predicates");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given requests is a clone, find and return the original request if it is.
|
||||||
|
* Cloned requests are sorted by comparing the original ones.
|
||||||
|
*/
|
||||||
|
function getOrigRequest(requests, req) {
|
||||||
|
if (!req.id.endsWith("-clone")) {
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
const origId = req.id.replace(/-clone$/, "");
|
||||||
|
return requests.find(r => r.id === origId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFilterFn = createSelector(
|
||||||
|
state => state.filters,
|
||||||
|
filters => r => {
|
||||||
|
const matchesType = filters.types.some((enabled, filter) => {
|
||||||
|
return enabled && Filters[filter] && Filters[filter](r);
|
||||||
|
});
|
||||||
|
return matchesType && isFreetextMatch(r, filters.text);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getSortFn = createSelector(
|
||||||
|
state => state.requests.requests,
|
||||||
|
state => state.sort,
|
||||||
|
(requests, sort) => {
|
||||||
|
let dataSorter = Sorters[sort.type || "waterfall"];
|
||||||
|
|
||||||
|
function sortWithClones(a, b) {
|
||||||
|
// If one request is a clone of the other, sort them next to each other
|
||||||
|
if (a.id == b.id + "-clone") {
|
||||||
|
return +1;
|
||||||
|
} else if (a.id + "-clone" == b.id) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, get the original requests and compare them
|
||||||
|
return dataSorter(
|
||||||
|
getOrigRequest(requests, a),
|
||||||
|
getOrigRequest(requests, b)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ascending = sort.ascending ? +1 : -1;
|
||||||
|
return (a, b) => ascending * sortWithClones(a, b, dataSorter);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const getSortedRequests = createSelector(
|
||||||
|
state => state.requests.requests,
|
||||||
|
getSortFn,
|
||||||
|
(requests, sortFn) => requests.sort(sortFn)
|
||||||
|
);
|
||||||
|
|
||||||
|
const getDisplayedRequests = createSelector(
|
||||||
|
state => state.requests.requests,
|
||||||
|
getFilterFn,
|
||||||
|
getSortFn,
|
||||||
|
(requests, filterFn, sortFn) => requests.filter(filterFn).sort(sortFn)
|
||||||
|
);
|
||||||
|
|
||||||
|
const getDisplayedRequestsSummary = createSelector(
|
||||||
|
getDisplayedRequests,
|
||||||
|
state => state.requests.lastEndedMillis - state.requests.firstStartedMillis,
|
||||||
|
(requests, totalMillis) => {
|
||||||
|
if (requests.size == 0) {
|
||||||
|
return { count: 0, bytes: 0, millis: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalBytes = requests.reduce((total, item) => {
|
||||||
|
if (typeof item.contentSize == "number") {
|
||||||
|
total += item.contentSize;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
count: requests.size,
|
||||||
|
bytes: totalBytes,
|
||||||
|
millis: totalMillis,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function getRequestById(state, id) {
|
||||||
|
return state.requests.requests.find(r => r.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDisplayedRequestById(state, id) {
|
||||||
|
return getDisplayedRequests(state).find(r => r.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelectedRequest(state) {
|
||||||
|
if (!state.requests.selectedId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getRequestById(state, state.requests.selectedId);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getSortedRequests,
|
||||||
|
getDisplayedRequests,
|
||||||
|
getDisplayedRequestsSummary,
|
||||||
|
getRequestById,
|
||||||
|
getDisplayedRequestById,
|
||||||
|
getSelectedRequest,
|
||||||
|
};
|
||||||
32
devtools/client/netmonitor/selectors/ui.js
Normal file
32
devtools/client/netmonitor/selectors/ui.js
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { getDisplayedRequests } = require("./requests");
|
||||||
|
|
||||||
|
function isSidebarToggleButtonDisabled(state) {
|
||||||
|
return getDisplayedRequests(state).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
const EPSILON = 0.001;
|
||||||
|
|
||||||
|
function getWaterfallScale(state) {
|
||||||
|
const { requests, timingMarkers, ui } = state;
|
||||||
|
|
||||||
|
if (requests.firstStartedMillis == +Infinity) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastEventMillis = Math.max(requests.lastEndedMillis,
|
||||||
|
timingMarkers.firstDocumentDOMContentLoadedTimestamp,
|
||||||
|
timingMarkers.firstDocumentLoadTimestamp);
|
||||||
|
const longestWidth = lastEventMillis - requests.firstStartedMillis;
|
||||||
|
return Math.min(Math.max(ui.waterfallWidth / longestWidth, EPSILON), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
isSidebarToggleButtonDisabled,
|
||||||
|
getWaterfallScale,
|
||||||
|
};
|
||||||
|
|
@ -26,7 +26,6 @@ SidebarView.prototype = {
|
||||||
*/
|
*/
|
||||||
toggle: function (visibleFlag) {
|
toggle: function (visibleFlag) {
|
||||||
NetMonitorView.toggleDetailsPane({ visible: visibleFlag });
|
NetMonitorView.toggleDetailsPane({ visible: visibleFlag });
|
||||||
NetMonitorView.RequestsMenu._flushWaterfallViews(true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ const {
|
||||||
getAbbreviatedMimeType,
|
getAbbreviatedMimeType,
|
||||||
getUrlBaseNameWithQuery,
|
getUrlBaseNameWithQuery,
|
||||||
getUrlHost,
|
getUrlHost,
|
||||||
loadCauseString,
|
|
||||||
} = require("./request-utils");
|
} = require("./request-utils");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -60,8 +59,8 @@ function domain(first, second) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cause(first, second) {
|
function cause(first, second) {
|
||||||
let firstCause = loadCauseString(first.cause.type);
|
let firstCause = first.cause.type;
|
||||||
let secondCause = loadCauseString(second.cause.type);
|
let secondCause = second.cause.type;
|
||||||
if (firstCause == secondCause) {
|
if (firstCause == secondCause) {
|
||||||
return first.startedMillis - second.startedMillis;
|
return first.startedMillis - second.startedMillis;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,19 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const createStore = require("devtools/client/shared/redux/create-store");
|
const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
|
||||||
const reducers = require("./reducers/index");
|
const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
|
||||||
|
const batching = require("./middleware/batching");
|
||||||
|
const rootReducer = require("./reducers/index");
|
||||||
|
|
||||||
function configureStore() {
|
function configureStore() {
|
||||||
return createStore()(reducers);
|
return createStore(
|
||||||
|
rootReducer,
|
||||||
|
applyMiddleware(
|
||||||
|
thunk,
|
||||||
|
batching
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.configureStore = configureStore;
|
exports.configureStore = configureStore;
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ support-files =
|
||||||
skip-if = (toolkit == "cocoa" && e10s) # bug 1252254
|
skip-if = (toolkit == "cocoa" && e10s) # bug 1252254
|
||||||
[browser_net_api-calls.js]
|
[browser_net_api-calls.js]
|
||||||
[browser_net_autoscroll.js]
|
[browser_net_autoscroll.js]
|
||||||
skip-if = true # Bug 1309191 - replace with rewritten version in React
|
|
||||||
[browser_net_cached-status.js]
|
[browser_net_cached-status.js]
|
||||||
[browser_net_cause.js]
|
[browser_net_cause.js]
|
||||||
[browser_net_cause_redirect.js]
|
[browser_net_cause_redirect.js]
|
||||||
|
|
@ -89,9 +88,11 @@ subsuite = clipboard
|
||||||
[browser_net_cyrillic-01.js]
|
[browser_net_cyrillic-01.js]
|
||||||
[browser_net_cyrillic-02.js]
|
[browser_net_cyrillic-02.js]
|
||||||
[browser_net_details-no-duplicated-content.js]
|
[browser_net_details-no-duplicated-content.js]
|
||||||
skip-if = (os == 'linux' && e10s && debug) # Bug 1242204
|
skip-if = true # Test broken in React version, is too low-level
|
||||||
[browser_net_frame.js]
|
[browser_net_frame.js]
|
||||||
|
skip-if = (os == 'linux' && debug && bits == 32) # Bug 1321434
|
||||||
[browser_net_filter-01.js]
|
[browser_net_filter-01.js]
|
||||||
|
skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
|
||||||
[browser_net_filter-02.js]
|
[browser_net_filter-02.js]
|
||||||
[browser_net_filter-03.js]
|
[browser_net_filter-03.js]
|
||||||
[browser_net_filter-04.js]
|
[browser_net_filter-04.js]
|
||||||
|
|
@ -140,6 +141,7 @@ skip-if = true # Bug 1258809
|
||||||
skip-if = true # Bug 1258809
|
skip-if = true # Bug 1258809
|
||||||
[browser_net_simple-request.js]
|
[browser_net_simple-request.js]
|
||||||
[browser_net_sort-01.js]
|
[browser_net_sort-01.js]
|
||||||
|
skip-if = true # Redundant for React/Redux version
|
||||||
[browser_net_sort-02.js]
|
[browser_net_sort-02.js]
|
||||||
[browser_net_sort-03.js]
|
[browser_net_sort-03.js]
|
||||||
[browser_net_statistics-01.js]
|
[browser_net_statistics-01.js]
|
||||||
|
|
@ -149,5 +151,6 @@ skip-if = true # Bug 1258809
|
||||||
[browser_net_streaming-response.js]
|
[browser_net_streaming-response.js]
|
||||||
[browser_net_throttle.js]
|
[browser_net_throttle.js]
|
||||||
[browser_net_timeline_ticks.js]
|
[browser_net_timeline_ticks.js]
|
||||||
|
skip-if = true # TODO: fix the test
|
||||||
[browser_net_timing-division.js]
|
[browser_net_timing-division.js]
|
||||||
[browser_net_persistent_logs.js]
|
[browser_net_persistent_logs.js]
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,13 @@ add_task(function* () {
|
||||||
// It seems that this test may be slow on Ubuntu builds running on ec2.
|
// It seems that this test may be slow on Ubuntu builds running on ec2.
|
||||||
requestLongerTimeout(2);
|
requestLongerTimeout(2);
|
||||||
|
|
||||||
let { NetMonitorView } = monitor.panelWin;
|
let { NetMonitorView, gStore, windowRequire } = monitor.panelWin;
|
||||||
let { RequestsMenu } = NetMonitorView;
|
let { RequestsMenu } = NetMonitorView;
|
||||||
|
|
||||||
RequestsMenu.lazyUpdate = false;
|
RequestsMenu.lazyUpdate = false;
|
||||||
|
|
||||||
|
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
function check(selectedIndex, paneVisibility) {
|
function check(selectedIndex, paneVisibility) {
|
||||||
info("Performing check " + (count++) + ".");
|
info("Performing check " + (count++) + ".");
|
||||||
|
|
@ -37,24 +39,19 @@ add_task(function* () {
|
||||||
|
|
||||||
check(-1, false);
|
check(-1, false);
|
||||||
|
|
||||||
RequestsMenu.focusLastVisibleItem();
|
gStore.dispatch(Actions.selectDelta(+Infinity));
|
||||||
check(1, true);
|
check(1, true);
|
||||||
RequestsMenu.focusFirstVisibleItem();
|
gStore.dispatch(Actions.selectDelta(-Infinity));
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
RequestsMenu.focusNextItem();
|
gStore.dispatch(Actions.selectDelta(+1));
|
||||||
check(1, true);
|
check(1, true);
|
||||||
RequestsMenu.focusPrevItem();
|
gStore.dispatch(Actions.selectDelta(-1));
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
RequestsMenu.focusItemAtDelta(+1);
|
gStore.dispatch(Actions.selectDelta(+10));
|
||||||
check(1, true);
|
check(1, true);
|
||||||
RequestsMenu.focusItemAtDelta(-1);
|
gStore.dispatch(Actions.selectDelta(-10));
|
||||||
check(0, true);
|
|
||||||
|
|
||||||
RequestsMenu.focusItemAtDelta(+10);
|
|
||||||
check(1, true);
|
|
||||||
RequestsMenu.focusItemAtDelta(-10);
|
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
wait = waitForNetworkEvents(monitor, 18);
|
wait = waitForNetworkEvents(monitor, 18);
|
||||||
|
|
@ -63,25 +60,25 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
RequestsMenu.focusLastVisibleItem();
|
gStore.dispatch(Actions.selectDelta(+Infinity));
|
||||||
check(19, true);
|
check(19, true);
|
||||||
RequestsMenu.focusFirstVisibleItem();
|
gStore.dispatch(Actions.selectDelta(-Infinity));
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
RequestsMenu.focusNextItem();
|
gStore.dispatch(Actions.selectDelta(+1));
|
||||||
check(1, true);
|
check(1, true);
|
||||||
RequestsMenu.focusPrevItem();
|
gStore.dispatch(Actions.selectDelta(-1));
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
RequestsMenu.focusItemAtDelta(+10);
|
gStore.dispatch(Actions.selectDelta(+10));
|
||||||
check(10, true);
|
check(10, true);
|
||||||
RequestsMenu.focusItemAtDelta(-10);
|
gStore.dispatch(Actions.selectDelta(-10));
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
RequestsMenu.focusItemAtDelta(+100);
|
gStore.dispatch(Actions.selectDelta(+100));
|
||||||
check(19, true);
|
check(19, true);
|
||||||
RequestsMenu.focusItemAtDelta(-100);
|
gStore.dispatch(Actions.selectDelta(-100));
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
yield teardown(monitor);
|
return teardown(monitor);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
|
$(".requests-menu-contents").focus();
|
||||||
|
|
||||||
check(-1, false);
|
check(-1, false);
|
||||||
|
|
||||||
EventUtils.sendKey("DOWN", window);
|
EventUtils.sendKey("DOWN", window);
|
||||||
|
|
@ -123,7 +125,7 @@ add_task(function* () {
|
||||||
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
|
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
|
||||||
check(-1, false);
|
check(-1, false);
|
||||||
|
|
||||||
EventUtils.sendMouseEvent({ type: "mousedown" }, $(".side-menu-widget-item"));
|
EventUtils.sendMouseEvent({ type: "mousedown" }, $(".request-list-item"));
|
||||||
check(0, true);
|
check(0, true);
|
||||||
|
|
||||||
yield teardown(monitor);
|
yield teardown(monitor);
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ add_task(function* () {
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
REQUEST_URIS.forEach(function (uri, index) {
|
REQUEST_URIS.forEach(function (uri, index) {
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(index), "GET", uri);
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(index), "GET", uri);
|
||||||
});
|
});
|
||||||
|
|
||||||
yield teardown(monitor);
|
yield teardown(monitor);
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,25 @@ add_task(function* () {
|
||||||
requestLongerTimeout(2);
|
requestLongerTimeout(2);
|
||||||
|
|
||||||
let { monitor } = yield initNetMonitor(INFINITE_GET_URL);
|
let { monitor } = yield initNetMonitor(INFINITE_GET_URL);
|
||||||
let win = monitor.panelWin;
|
let { $ } = monitor.panelWin;
|
||||||
let topNode = win.document.getElementById("requests-menu-contents");
|
|
||||||
let requestsContainer = topNode.getElementsByTagName("scrollbox")[0];
|
// Wait until the first request makes the empty notice disappear
|
||||||
ok(!!requestsContainer, "Container element exists as expected.");
|
yield waitForRequestListToAppear();
|
||||||
|
|
||||||
|
let requestsContainer = $(".requests-menu-contents");
|
||||||
|
ok(requestsContainer, "Container element exists as expected.");
|
||||||
|
|
||||||
// (1) Check that the scroll position is maintained at the bottom
|
// (1) Check that the scroll position is maintained at the bottom
|
||||||
// when the requests overflow the vertical size of the container.
|
// when the requests overflow the vertical size of the container.
|
||||||
yield waitForRequestsToOverflowContainer();
|
yield waitForRequestsToOverflowContainer();
|
||||||
yield waitForScroll();
|
yield waitForScroll();
|
||||||
ok(scrolledToBottom(requestsContainer), "Scrolled to bottom on overflow.");
|
ok(true, "Scrolled to bottom on overflow.");
|
||||||
|
|
||||||
// (2) Now set the scroll position somewhere in the middle and check
|
// (2) Now set the scroll position to the first item and check
|
||||||
// that additional requests do not change the scroll position.
|
// that additional requests do not change the scroll position.
|
||||||
let children = requestsContainer.childNodes;
|
let firstNode = requestsContainer.firstChild;
|
||||||
let middleNode = children.item(children.length / 2);
|
firstNode.scrollIntoView();
|
||||||
middleNode.scrollIntoView();
|
yield waitSomeTime();
|
||||||
ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
|
ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
|
||||||
// save for comparison later
|
// save for comparison later
|
||||||
let scrollTop = requestsContainer.scrollTop;
|
let scrollTop = requestsContainer.scrollTop;
|
||||||
|
|
@ -39,7 +42,7 @@ add_task(function* () {
|
||||||
ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
|
ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
|
||||||
yield waitForNetworkEvents(monitor, 8);
|
yield waitForNetworkEvents(monitor, 8);
|
||||||
yield waitForScroll();
|
yield waitForScroll();
|
||||||
ok(scrolledToBottom(requestsContainer), "Still scrolled to bottom.");
|
ok(true, "Still scrolled to bottom.");
|
||||||
|
|
||||||
// (4) Now select an item in the list and check that additional requests
|
// (4) Now select an item in the list and check that additional requests
|
||||||
// do not change the scroll position.
|
// do not change the scroll position.
|
||||||
|
|
@ -49,12 +52,20 @@ add_task(function* () {
|
||||||
is(requestsContainer.scrollTop, 0, "Did not scroll.");
|
is(requestsContainer.scrollTop, 0, "Did not scroll.");
|
||||||
|
|
||||||
// Done: clean up.
|
// Done: clean up.
|
||||||
yield teardown(monitor);
|
return teardown(monitor);
|
||||||
|
|
||||||
|
function waitForRequestListToAppear() {
|
||||||
|
info("Waiting until the empty notice disappears and is replaced with the list");
|
||||||
|
return waitUntil(() => !!$(".requests-menu-contents"));
|
||||||
|
}
|
||||||
|
|
||||||
function* waitForRequestsToOverflowContainer() {
|
function* waitForRequestsToOverflowContainer() {
|
||||||
|
info("Waiting for enough requests to overflow the container");
|
||||||
while (true) {
|
while (true) {
|
||||||
|
info("Waiting for one network request");
|
||||||
yield waitForNetworkEvents(monitor, 1);
|
yield waitForNetworkEvents(monitor, 1);
|
||||||
if (requestsContainer.scrollHeight > requestsContainer.clientHeight) {
|
if (requestsContainer.scrollHeight > requestsContainer.clientHeight) {
|
||||||
|
info("The list is long enough, returning");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +81,7 @@ add_task(function* () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForScroll() {
|
function waitForScroll() {
|
||||||
return monitor._view.RequestsMenu.widget.once("scroll-to-bottom");
|
info("Waiting for the list to scroll to bottom");
|
||||||
|
return waitUntil(() => scrolledToBottom(requestsContainer));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", {
|
"GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "Connected",
|
statusText: "Connected",
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,8 @@ add_task(function* () {
|
||||||
let item = RequestsMenu.getItemAtIndex(index);
|
let item = RequestsMenu.getItemAtIndex(index);
|
||||||
|
|
||||||
info("Verifying request #" + index);
|
info("Verifying request #" + index);
|
||||||
yield verifyRequestItemTarget(item, request.method, request.uri, request.details);
|
yield verifyRequestItemTarget(RequestsMenu, item,
|
||||||
|
request.method, request.uri, request.details);
|
||||||
|
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ const EXPECTED_REQUESTS = [
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: CAUSE_URL,
|
url: CAUSE_URL,
|
||||||
causeType: "document",
|
causeType: "document",
|
||||||
causeUri: "",
|
causeUri: null,
|
||||||
// The document load has internal privileged JS code on the stack
|
// The document load has internal privileged JS code on the stack
|
||||||
stack: true
|
stack: true
|
||||||
},
|
},
|
||||||
|
|
@ -103,11 +103,11 @@ add_task(function* () {
|
||||||
let { method, url, causeType, causeUri, stack } = spec;
|
let { method, url, causeType, causeUri, stack } = spec;
|
||||||
|
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(i);
|
let requestItem = RequestsMenu.getItemAtIndex(i);
|
||||||
verifyRequestItemTarget(requestItem,
|
verifyRequestItemTarget(RequestsMenu, requestItem,
|
||||||
method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
|
method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
|
||||||
);
|
);
|
||||||
|
|
||||||
let { stacktrace } = requestItem.attachment.cause;
|
let { stacktrace } = requestItem.cause;
|
||||||
let stackLen = stacktrace ? stacktrace.length : 0;
|
let stackLen = stacktrace ? stacktrace.length : 0;
|
||||||
|
|
||||||
if (stack) {
|
if (stack) {
|
||||||
|
|
@ -137,9 +137,7 @@ add_task(function* () {
|
||||||
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-cause-button"));
|
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-cause-button"));
|
||||||
let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort();
|
let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort();
|
||||||
expectedOrder.forEach((expectedCause, i) => {
|
expectedOrder.forEach((expectedCause, i) => {
|
||||||
let { target } = RequestsMenu.getItemAtIndex(i);
|
const cause = RequestsMenu.getItemAtIndex(i).cause.type;
|
||||||
let causeLabel = target.querySelector(".requests-menu-cause-label");
|
|
||||||
let cause = causeLabel.getAttribute("value");
|
|
||||||
is(cause, expectedCause, `The request #${i} has the expected cause after sorting`);
|
is(cause, expectedCause, `The request #${i} has the expected cause after sorting`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ add_task(function* () {
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
|
EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
|
||||||
let { attachment } = RequestsMenu.getItemAtIndex(i);
|
let item = RequestsMenu.getItemAtIndex(i);
|
||||||
|
|
||||||
is(attachment.status, status, `Request #${i} has the expected status`);
|
is(item.status, status, `Request #${i} has the expected status`);
|
||||||
|
|
||||||
let { stacktrace } = attachment.cause;
|
let { stacktrace } = item.cause;
|
||||||
let stackLen = stacktrace ? stacktrace.length : 0;
|
let stackLen = stacktrace ? stacktrace.length : 0;
|
||||||
|
|
||||||
if (hasStack) {
|
if (hasStack) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=xml", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=xml", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
@ -33,7 +33,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 42),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 42),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
@ -42,7 +42,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(2),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
@ -51,7 +51,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(3),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=json", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=json", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
@ -60,7 +60,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(4),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=bogus", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=bogus", {
|
||||||
status: 404,
|
status: 404,
|
||||||
statusText: "Not Found",
|
statusText: "Not Found",
|
||||||
|
|
@ -69,7 +69,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(5),
|
||||||
"GET", TEST_IMAGE, {
|
"GET", TEST_IMAGE, {
|
||||||
fuzzyUrl: true,
|
fuzzyUrl: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|
@ -79,7 +79,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 580),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 580),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(6),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=gzip", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=gzip", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ add_task(function* () {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
RequestsMenu.selectedItem = requestItem;
|
RequestsMenu.selectedItem = requestItem;
|
||||||
|
|
||||||
let { method, httpVersion, status, statusText } = requestItem.attachment;
|
let { method, httpVersion, status, statusText } = requestItem;
|
||||||
|
|
||||||
const EXPECTED_REQUEST_HEADERS = [
|
const EXPECTED_REQUEST_HEADERS = [
|
||||||
`${method} ${SIMPLE_URL} ${httpVersion}`,
|
`${method} ${SIMPLE_URL} ${httpVersion}`,
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ add_task(function* () {
|
||||||
|
|
||||||
yield waitForClipboardPromise(function setup() {
|
yield waitForClipboardPromise(function setup() {
|
||||||
RequestsMenu.contextMenu.copyUrl();
|
RequestsMenu.contextMenu.copyUrl();
|
||||||
}, requestItem.attachment.url);
|
}, requestItem.url);
|
||||||
|
|
||||||
yield teardown(monitor);
|
yield teardown(monitor);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,8 @@ add_task(function* () {
|
||||||
|
|
||||||
info("Checking the preflight and flight methods");
|
info("Checking the preflight and flight methods");
|
||||||
["OPTIONS", "POST"].forEach((method, i) => {
|
["OPTIONS", "POST"].forEach((method, i) => {
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), method, requestUrl);
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(i),
|
||||||
|
method, requestUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
yield teardown(monitor);
|
yield teardown(monitor);
|
||||||
|
|
|
||||||
|
|
@ -31,19 +31,19 @@ add_task(function* () {
|
||||||
multipartForm: RequestsMenu.getItemAtIndex(3)
|
multipartForm: RequestsMenu.getItemAtIndex(3)
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = yield createCurlData(requests.get.attachment, gNetwork);
|
let data = yield createCurlData(requests.get, gNetwork);
|
||||||
testFindHeader(data);
|
testFindHeader(data);
|
||||||
|
|
||||||
data = yield createCurlData(requests.post.attachment, gNetwork);
|
data = yield createCurlData(requests.post, gNetwork);
|
||||||
testIsUrlEncodedRequest(data);
|
testIsUrlEncodedRequest(data);
|
||||||
testWritePostDataTextParams(data);
|
testWritePostDataTextParams(data);
|
||||||
|
|
||||||
data = yield createCurlData(requests.multipart.attachment, gNetwork);
|
data = yield createCurlData(requests.multipart, gNetwork);
|
||||||
testIsMultipartRequest(data);
|
testIsMultipartRequest(data);
|
||||||
testGetMultipartBoundary(data);
|
testGetMultipartBoundary(data);
|
||||||
testRemoveBinaryDataFromMultipartText(data);
|
testRemoveBinaryDataFromMultipartText(data);
|
||||||
|
|
||||||
data = yield createCurlData(requests.multipartForm.attachment, gNetwork);
|
data = yield createCurlData(requests.multipartForm, gNetwork);
|
||||||
testGetHeadersFromMultipartText(data);
|
testGetHeadersFromMultipartText(data);
|
||||||
|
|
||||||
if (Services.appinfo.OS != "WINNT") {
|
if (Services.appinfo.OS != "WINNT") {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=txt", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=txt", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "DA DA DA"
|
statusText: "DA DA DA"
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ add_task(function* () {
|
||||||
tab.linkedBrowser.reload();
|
tab.linkedBrowser.reload();
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CYRILLIC_URL, {
|
"GET", CYRILLIC_URL, {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK"
|
statusText: "OK"
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,109 @@ const REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS = REQUESTS_WITH_MEDIA_AND_FLASH.conca
|
||||||
{ url: "sjs_content-type-test-server.sjs?fmt=ws" },
|
{ url: "sjs_content-type-test-server.sjs?fmt=ws" },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const EXPECTED_REQUESTS = [
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=html",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "html",
|
||||||
|
fullMimeType: "text/html; charset=utf-8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=css",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "css",
|
||||||
|
fullMimeType: "text/css; charset=utf-8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=js",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "js",
|
||||||
|
fullMimeType: "application/javascript; charset=utf-8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=font",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "woff",
|
||||||
|
fullMimeType: "font/woff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=image",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "png",
|
||||||
|
fullMimeType: "image/png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=audio",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "ogg",
|
||||||
|
fullMimeType: "audio/ogg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=video",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "webm",
|
||||||
|
fullMimeType: "video/webm"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=flash",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "x-shockwave-flash",
|
||||||
|
fullMimeType: "application/x-shockwave-flash"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=ws",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 101,
|
||||||
|
statusText: "Switching Protocols",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
add_task(function* () {
|
add_task(function* () {
|
||||||
let Actions = require("devtools/client/netmonitor/actions/index");
|
let Actions = require("devtools/client/netmonitor/actions/index");
|
||||||
|
|
||||||
let { monitor } = yield initNetMonitor(FILTERING_URL);
|
let { monitor } = yield initNetMonitor(FILTERING_URL);
|
||||||
let { gStore } = monitor.panelWin;
|
let { gStore } = monitor.panelWin;
|
||||||
|
|
||||||
|
|
@ -180,85 +281,25 @@ add_task(function* () {
|
||||||
is(NetMonitorView.detailsPaneHidden, false,
|
is(NetMonitorView.detailsPaneHidden, false,
|
||||||
"The details pane should still be visible after filtering.");
|
"The details pane should still be visible after filtering.");
|
||||||
|
|
||||||
is(RequestsMenu.items.length, visibility.length,
|
const items = RequestsMenu.items;
|
||||||
|
const visibleItems = RequestsMenu.visibleItems;
|
||||||
|
|
||||||
|
is(items.size, visibility.length,
|
||||||
"There should be a specific amount of items in the requests menu.");
|
"There should be a specific amount of items in the requests menu.");
|
||||||
is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length,
|
is(visibleItems.size, visibility.filter(e => e).length,
|
||||||
"There should be a specific amount of visbile items in the requests menu.");
|
"There should be a specific amount of visible items in the requests menu.");
|
||||||
|
|
||||||
for (let i = 0; i < visibility.length; i++) {
|
for (let i = 0; i < visibility.length; i++) {
|
||||||
is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i],
|
let itemId = items.get(i).id;
|
||||||
"The item at index " + i + " doesn't have the correct hidden state.");
|
let shouldBeVisible = !!visibility[i];
|
||||||
}
|
let isThere = visibleItems.some(r => r.id == itemId);
|
||||||
|
is(isThere, shouldBeVisible,
|
||||||
|
`The item at index ${i} has visibility=${shouldBeVisible}`);
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
if (shouldBeVisible) {
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
|
let { method, url, data } = EXPECTED_REQUESTS[i];
|
||||||
fuzzyUrl: true,
|
verifyRequestItemTarget(RequestsMenu, items.get(i), method, url, data);
|
||||||
status: 200,
|
}
|
||||||
statusText: "OK",
|
}
|
||||||
type: "html",
|
|
||||||
fullMimeType: "text/html; charset=utf-8"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "css",
|
|
||||||
fullMimeType: "text/css; charset=utf-8"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "js",
|
|
||||||
fullMimeType: "application/javascript; charset=utf-8"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=font", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "woff",
|
|
||||||
fullMimeType: "font/woff"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=image", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "png",
|
|
||||||
fullMimeType: "image/png"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=audio", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "ogg",
|
|
||||||
fullMimeType: "audio/ogg"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=video", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "webm",
|
|
||||||
fullMimeType: "video/webm"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(7),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=flash", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "x-shockwave-flash",
|
|
||||||
fullMimeType: "application/x-shockwave-flash"
|
|
||||||
});
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(8),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=ws", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 101,
|
|
||||||
statusText: "Switching Protocols",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,106 @@ const REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS = REQUESTS_WITH_MEDIA_AND_FLASH.conca
|
||||||
{ url: "sjs_content-type-test-server.sjs?fmt=ws" },
|
{ url: "sjs_content-type-test-server.sjs?fmt=ws" },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const EXPECTED_REQUESTS = [
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=html",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "html",
|
||||||
|
fullMimeType: "text/html; charset=utf-8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=css",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "css",
|
||||||
|
fullMimeType: "text/css; charset=utf-8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=js",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "js",
|
||||||
|
fullMimeType: "application/javascript; charset=utf-8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=font",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "woff",
|
||||||
|
fullMimeType: "font/woff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=image",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "png",
|
||||||
|
fullMimeType: "image/png"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=audio",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "ogg",
|
||||||
|
fullMimeType: "audio/ogg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=video",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "webm",
|
||||||
|
fullMimeType: "video/webm"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=flash",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
type: "x-shockwave-flash",
|
||||||
|
fullMimeType: "application/x-shockwave-flash"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
url: CONTENT_TYPE_SJS + "?fmt=ws",
|
||||||
|
data: {
|
||||||
|
fuzzyUrl: true,
|
||||||
|
status: 101,
|
||||||
|
statusText: "Switching Protocols",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
add_task(function* () {
|
add_task(function* () {
|
||||||
let { monitor } = yield initNetMonitor(FILTERING_URL);
|
let { monitor } = yield initNetMonitor(FILTERING_URL);
|
||||||
info("Starting test... ");
|
info("Starting test... ");
|
||||||
|
|
@ -98,103 +198,29 @@ add_task(function* () {
|
||||||
is(NetMonitorView.detailsPaneHidden, false,
|
is(NetMonitorView.detailsPaneHidden, false,
|
||||||
"The details pane should still be visible after filtering.");
|
"The details pane should still be visible after filtering.");
|
||||||
|
|
||||||
is(RequestsMenu.items.length, visibility.length,
|
const items = RequestsMenu.items;
|
||||||
|
const visibleItems = RequestsMenu.visibleItems;
|
||||||
|
|
||||||
|
is(items.size, visibility.length,
|
||||||
"There should be a specific amount of items in the requests menu.");
|
"There should be a specific amount of items in the requests menu.");
|
||||||
is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length,
|
is(visibleItems.size, visibility.filter(e => e).length,
|
||||||
"There should be a specific amount of visbile items in the requests menu.");
|
"There should be a specific amount of visible items in the requests menu.");
|
||||||
|
|
||||||
for (let i = 0; i < visibility.length; i++) {
|
for (let i = 0; i < visibility.length; i++) {
|
||||||
is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i],
|
let itemId = items.get(i).id;
|
||||||
"The item at index " + i + " doesn't have the correct hidden state.");
|
let shouldBeVisible = !!visibility[i];
|
||||||
|
let isThere = visibleItems.some(r => r.id == itemId);
|
||||||
|
is(isThere, shouldBeVisible,
|
||||||
|
`The item at index ${i} has visibility=${shouldBeVisible}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < visibility.length; i += 9) {
|
for (let i = 0; i < EXPECTED_REQUESTS.length; i++) {
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
let { method, url, data } = EXPECTED_REQUESTS[i];
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
|
for (let j = i; j < visibility.length; j += EXPECTED_REQUESTS.length) {
|
||||||
fuzzyUrl: true,
|
if (visibility[j]) {
|
||||||
status: 200,
|
verifyRequestItemTarget(RequestsMenu, items.get(j), method, url, data);
|
||||||
statusText: "OK",
|
}
|
||||||
type: "html",
|
}
|
||||||
fullMimeType: "text/html; charset=utf-8"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 1; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "css",
|
|
||||||
fullMimeType: "text/css; charset=utf-8"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 2; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "js",
|
|
||||||
fullMimeType: "application/javascript; charset=utf-8"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 3; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=font", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "woff",
|
|
||||||
fullMimeType: "font/woff"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 4; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=image", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "png",
|
|
||||||
fullMimeType: "image/png"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 5; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=audio", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "ogg",
|
|
||||||
fullMimeType: "audio/ogg"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 6; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=video", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "webm",
|
|
||||||
fullMimeType: "video/webm"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 7; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=flash", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "x-shockwave-flash",
|
|
||||||
fullMimeType: "application/x-shockwave-flash"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 8; i < visibility.length; i += 9) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=ws", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 101,
|
|
||||||
statusText: "Switching Protocols"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -104,82 +104,6 @@ add_task(function* () {
|
||||||
is(RequestsMenu.items.length, order.length,
|
is(RequestsMenu.items.length, order.length,
|
||||||
"There should be a specific amount of items in the requests menu.");
|
"There should be a specific amount of items in the requests menu.");
|
||||||
is(RequestsMenu.visibleItems.length, visible,
|
is(RequestsMenu.visibleItems.length, visible,
|
||||||
"There should be a specific amount of visbile items in the requests menu.");
|
"There should be a specific amount of visible items in the requests menu.");
|
||||||
|
|
||||||
for (let i = 0; i < order.length; i++) {
|
|
||||||
is(RequestsMenu.getItemAtIndex(i), RequestsMenu.items[i],
|
|
||||||
"The requests menu items aren't ordered correctly. Misplaced item " + i + ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0, len = order.length / 7; i < len; i++) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i]),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "html",
|
|
||||||
fullMimeType: "text/html; charset=utf-8"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0, len = order.length / 7; i < len; i++) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len]),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "css",
|
|
||||||
fullMimeType: "text/css; charset=utf-8"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0, len = order.length / 7; i < len; i++) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 2]),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "js",
|
|
||||||
fullMimeType: "application/javascript; charset=utf-8"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0, len = order.length / 7; i < len; i++) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 3]),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=font", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "woff",
|
|
||||||
fullMimeType: "font/woff"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0, len = order.length / 7; i < len; i++) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 4]),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=image", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "png",
|
|
||||||
fullMimeType: "image/png"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0, len = order.length / 7; i < len; i++) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 5]),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=audio", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "ogg",
|
|
||||||
fullMimeType: "audio/ogg"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0, len = order.length / 7; i < len; i++) {
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(order[i + len * 6]),
|
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=video", {
|
|
||||||
fuzzyUrl: true,
|
|
||||||
status: 200,
|
|
||||||
statusText: "OK",
|
|
||||||
type: "webm",
|
|
||||||
fullMimeType: "video/webm"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if the summary text displayed in the network requests menu footer
|
* Test if the summary text displayed in the network requests menu footer is correct.
|
||||||
* is correct.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
add_task(function* () {
|
add_task(function* () {
|
||||||
|
|
@ -18,7 +17,8 @@ add_task(function* () {
|
||||||
let { RequestsMenu } = NetMonitorView;
|
let { RequestsMenu } = NetMonitorView;
|
||||||
|
|
||||||
let winRequire = monitor.panelWin.require;
|
let winRequire = monitor.panelWin.require;
|
||||||
let { getSummary } = winRequire("devtools/client/netmonitor/selectors/index");
|
let { getDisplayedRequestsSummary } =
|
||||||
|
winRequire("devtools/client/netmonitor/selectors/index");
|
||||||
let { L10N } = winRequire("devtools/client/netmonitor/l10n");
|
let { L10N } = winRequire("devtools/client/netmonitor/l10n");
|
||||||
let { PluralForm } = winRequire("devtools/shared/plural-form");
|
let { PluralForm } = winRequire("devtools/shared/plural-form");
|
||||||
|
|
||||||
|
|
@ -46,26 +46,27 @@ add_task(function* () {
|
||||||
yield teardown(monitor);
|
yield teardown(monitor);
|
||||||
|
|
||||||
function testStatus() {
|
function testStatus() {
|
||||||
const { count, totalBytes, totalMillis } = getSummary(gStore.getState());
|
|
||||||
let value = $("#requests-menu-network-summary-button").textContent;
|
let value = $("#requests-menu-network-summary-button").textContent;
|
||||||
info("Current summary: " + value);
|
info("Current summary: " + value);
|
||||||
|
|
||||||
let totalRequestsCount = RequestsMenu.itemCount;
|
let state = gStore.getState();
|
||||||
info("Current requests: " + count + " of " + totalRequestsCount + ".");
|
let totalRequestsCount = state.requests.requests.size;
|
||||||
|
let requestsSummary = getDisplayedRequestsSummary(state);
|
||||||
|
info(`Current requests: ${requestsSummary.count} of ${totalRequestsCount}.`);
|
||||||
|
|
||||||
if (!totalRequestsCount || !count) {
|
if (!totalRequestsCount || !requestsSummary.count) {
|
||||||
is(value, L10N.getStr("networkMenu.empty"),
|
is(value, L10N.getStr("networkMenu.empty"),
|
||||||
"The current summary text is incorrect, expected an 'empty' label.");
|
"The current summary text is incorrect, expected an 'empty' label.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
info("Computed total bytes: " + totalBytes);
|
info(`Computed total bytes: ${requestsSummary.bytes}`);
|
||||||
info("Computed total millis: " + totalMillis);
|
info(`Computed total millis: ${requestsSummary.millis}`);
|
||||||
|
|
||||||
is(value, PluralForm.get(count, L10N.getStr("networkMenu.summary"))
|
is(value, PluralForm.get(requestsSummary.count, L10N.getStr("networkMenu.summary"))
|
||||||
.replace("#1", count)
|
.replace("#1", requestsSummary.count)
|
||||||
.replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, 2))
|
.replace("#2", L10N.numberWithDecimals(requestsSummary.bytes / 1024, 2))
|
||||||
.replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, 2))
|
.replace("#3", L10N.numberWithDecimals(requestsSummary.millis / 1000, 2))
|
||||||
, "The current summary text is incorrect.");
|
, "The current summary text is correct.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ const EXPECTED_REQUESTS_TOP = [
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: TOP_URL,
|
url: TOP_URL,
|
||||||
causeType: "document",
|
causeType: "document",
|
||||||
causeUri: "",
|
causeUri: null,
|
||||||
stack: true
|
stack: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -176,9 +176,8 @@ add_task(function* () {
|
||||||
for (let i = 0; i < REQUEST_COUNT; i++) {
|
for (let i = 0; i < REQUEST_COUNT; i++) {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(i);
|
let requestItem = RequestsMenu.getItemAtIndex(i);
|
||||||
|
|
||||||
let itemUrl = requestItem.attachment.url;
|
let itemUrl = requestItem.url;
|
||||||
let itemCauseUri = requestItem.target.querySelector(".requests-menu-cause-label")
|
let itemCauseUri = requestItem.cause.loadingDocumentUri;
|
||||||
.getAttribute("tooltiptext");
|
|
||||||
let spec;
|
let spec;
|
||||||
if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) {
|
if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) {
|
||||||
spec = EXPECTED_REQUESTS_SUB[currentSub++];
|
spec = EXPECTED_REQUESTS_SUB[currentSub++];
|
||||||
|
|
@ -187,11 +186,11 @@ add_task(function* () {
|
||||||
}
|
}
|
||||||
let { method, url, causeType, causeUri, stack } = spec;
|
let { method, url, causeType, causeUri, stack } = spec;
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem,
|
verifyRequestItemTarget(RequestsMenu, requestItem,
|
||||||
method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
|
method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
|
||||||
);
|
);
|
||||||
|
|
||||||
let { stacktrace } = requestItem.attachment.cause;
|
let { stacktrace } = requestItem.cause;
|
||||||
let stackLen = stacktrace ? stacktrace.length : 0;
|
let stackLen = stacktrace ? stacktrace.length : 0;
|
||||||
|
|
||||||
if (stack) {
|
if (stack) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
||||||
info("Checking the image thumbnail when all items are shown.");
|
info("Checking the image thumbnail when all items are shown.");
|
||||||
checkImageThumbnail();
|
checkImageThumbnail();
|
||||||
|
|
||||||
RequestsMenu.sortBy("size");
|
gStore.dispatch(Actions.sortBy("size"));
|
||||||
info("Checking the image thumbnail when all items are sorted.");
|
info("Checking the image thumbnail when all items are sorted.");
|
||||||
checkImageThumbnail();
|
checkImageThumbnail();
|
||||||
|
|
||||||
|
|
@ -61,11 +61,11 @@ add_task(function* () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkImageThumbnail() {
|
function checkImageThumbnail() {
|
||||||
is($all(".requests-menu-icon[type=thumbnail]").length, 1,
|
is($all(".requests-menu-icon[data-type=thumbnail]").length, 1,
|
||||||
"There should be only one image request with a thumbnail displayed.");
|
"There should be only one image request with a thumbnail displayed.");
|
||||||
is($(".requests-menu-icon[type=thumbnail]").src, TEST_IMAGE_DATA_URI,
|
is($(".requests-menu-icon[data-type=thumbnail]").src, TEST_IMAGE_DATA_URI,
|
||||||
"The image requests-menu-icon thumbnail is displayed correctly.");
|
"The image requests-menu-icon thumbnail is displayed correctly.");
|
||||||
is($(".requests-menu-icon[type=thumbnail]").hidden, false,
|
is($(".requests-menu-icon[data-type=thumbnail]").hidden, false,
|
||||||
"The image requests-menu-icon thumbnail should not be hidden.");
|
"The image requests-menu-icon thumbnail should not be hidden.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ add_task(function* test() {
|
||||||
yield onThumbnail;
|
yield onThumbnail;
|
||||||
|
|
||||||
info("Checking the image thumbnail after a few requests were made...");
|
info("Checking the image thumbnail after a few requests were made...");
|
||||||
yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[0]);
|
yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.getItemAtIndex(0));
|
||||||
|
|
||||||
// Hide tooltip before next test, to avoid the situation that tooltip covers
|
// Hide tooltip before next test, to avoid the situation that tooltip covers
|
||||||
// the icon for the request of the next test.
|
// the icon for the request of the next test.
|
||||||
info("Checking the image thumbnail gets hidden...");
|
info("Checking the image thumbnail gets hidden...");
|
||||||
yield hideTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[0]);
|
yield hideTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.getItemAtIndex(0));
|
||||||
|
|
||||||
// +1 extra document reload
|
// +1 extra document reload
|
||||||
onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1);
|
onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1);
|
||||||
|
|
@ -44,10 +44,10 @@ add_task(function* test() {
|
||||||
yield onThumbnail;
|
yield onThumbnail;
|
||||||
|
|
||||||
info("Checking the image thumbnail after a reload.");
|
info("Checking the image thumbnail after a reload.");
|
||||||
yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[1]);
|
yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.getItemAtIndex(1));
|
||||||
|
|
||||||
info("Checking if the image thumbnail is hidden when mouse leaves the menu widget");
|
info("Checking if the image thumbnail is hidden when mouse leaves the menu widget");
|
||||||
let requestsMenuEl = $("#requests-menu-contents");
|
let requestsMenuEl = $(".requests-menu-contents");
|
||||||
let onHidden = RequestsMenu.tooltip.once("hidden");
|
let onHidden = RequestsMenu.tooltip.once("hidden");
|
||||||
EventUtils.synthesizeMouse(requestsMenuEl, 0, 0, {type: "mouseout"}, monitor.panelWin);
|
EventUtils.synthesizeMouse(requestsMenuEl, 0, 0, {type: "mouseout"}, monitor.panelWin);
|
||||||
yield onHidden;
|
yield onHidden;
|
||||||
|
|
@ -65,7 +65,7 @@ add_task(function* test() {
|
||||||
* with the expected content.
|
* with the expected content.
|
||||||
*/
|
*/
|
||||||
function* showTooltipAndVerify(tooltip, requestItem) {
|
function* showTooltipAndVerify(tooltip, requestItem) {
|
||||||
let anchor = $(".requests-menu-file", requestItem.target);
|
let anchor = $(".requests-menu-file", getItemTarget(RequestsMenu, requestItem));
|
||||||
yield showTooltipOn(tooltip, anchor);
|
yield showTooltipOn(tooltip, anchor);
|
||||||
|
|
||||||
info("Tooltip was successfully opened for the image request.");
|
info("Tooltip was successfully opened for the image request.");
|
||||||
|
|
@ -88,13 +88,13 @@ add_task(function* test() {
|
||||||
* Hide a tooltip on the {requestItem} and verify that it was closed.
|
* Hide a tooltip on the {requestItem} and verify that it was closed.
|
||||||
*/
|
*/
|
||||||
function* hideTooltipAndVerify(tooltip, requestItem) {
|
function* hideTooltipAndVerify(tooltip, requestItem) {
|
||||||
// Hovering method hides tooltip.
|
// Hovering over the "method" column hides the tooltip.
|
||||||
let anchor = $(".requests-menu-method", requestItem.target);
|
let anchor = $(".requests-menu-method", getItemTarget(RequestsMenu, requestItem));
|
||||||
|
|
||||||
let onHidden = tooltip.once("hidden");
|
let onTooltipHidden = tooltip.once("hidden");
|
||||||
let win = anchor.ownerDocument.defaultView;
|
let win = anchor.ownerDocument.defaultView;
|
||||||
EventUtils.synthesizeMouseAtCenter(anchor, {type: "mousemove"}, win);
|
EventUtils.synthesizeMouseAtCenter(anchor, {type: "mousemove"}, win);
|
||||||
yield onHidden;
|
yield onTooltipHidden;
|
||||||
|
|
||||||
info("Tooltip was successfully closed.");
|
info("Tooltip was successfully closed.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-malformed", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=json-malformed", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-custom-mime", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=json-custom-mime", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-text-mime", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=json-text-mime", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
@ -34,7 +34,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp2&jsonp=$_4567Sad", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp2&jsonp=$_4567Sad", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=html-long", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=html-long", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK"
|
statusText: "OK"
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||||
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded", {
|
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "Och Aye",
|
statusText: "Och Aye",
|
||||||
|
|
@ -34,7 +34,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1),
|
||||||
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=multipart", {
|
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=multipart", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "Och Aye",
|
statusText: "Och Aye",
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
add_task(function* () {
|
add_task(function* () {
|
||||||
let Actions = require("devtools/client/netmonitor/actions/index");
|
let Actions = require("devtools/client/netmonitor/actions/index");
|
||||||
|
let { getActiveFilters } = require("devtools/client/netmonitor/selectors/index");
|
||||||
|
|
||||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||||
info("Starting test... ");
|
info("Starting test... ");
|
||||||
|
|
||||||
|
|
@ -19,8 +21,8 @@ add_task(function* () {
|
||||||
// Use these getters instead of caching instances inside the panel win,
|
// Use these getters instead of caching instances inside the panel win,
|
||||||
// since the tool is reopened a bunch of times during this test
|
// since the tool is reopened a bunch of times during this test
|
||||||
// and the instances will differ.
|
// and the instances will differ.
|
||||||
let getView = () => monitor.panelWin.NetMonitorView;
|
|
||||||
let getStore = () => monitor.panelWin.gStore;
|
let getStore = () => monitor.panelWin.gStore;
|
||||||
|
let getState = () => getStore().getState();
|
||||||
|
|
||||||
let prefsToCheck = {
|
let prefsToCheck = {
|
||||||
filters: {
|
filters: {
|
||||||
|
|
@ -28,7 +30,7 @@ add_task(function* () {
|
||||||
newValue: ["html", "css"],
|
newValue: ["html", "css"],
|
||||||
// Getter used to retrieve the current value from the frontend, in order
|
// Getter used to retrieve the current value from the frontend, in order
|
||||||
// to verify that the pref was applied properly.
|
// to verify that the pref was applied properly.
|
||||||
validateValue: ($) => getView().RequestsMenu._activeFilters,
|
validateValue: ($) => getActiveFilters(getState()),
|
||||||
// Predicate used to modify the frontend when setting the new pref value,
|
// Predicate used to modify the frontend when setting the new pref value,
|
||||||
// before trying to validate the changes.
|
// before trying to validate the changes.
|
||||||
modifyFrontend: ($, value) => value.forEach(e =>
|
modifyFrontend: ($, value) => value.forEach(e =>
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ add_task(function* () {
|
||||||
EventUtils.sendMouseEvent({ type: "click" },
|
EventUtils.sendMouseEvent({ type: "click" },
|
||||||
document.getElementById("toggle-raw-headers"));
|
document.getElementById("toggle-raw-headers"));
|
||||||
|
|
||||||
testShowRawHeaders(origItem.attachment);
|
testShowRawHeaders(origItem);
|
||||||
|
|
||||||
EventUtils.sendMouseEvent({ type: "click" },
|
EventUtils.sendMouseEvent({ type: "click" },
|
||||||
document.getElementById("toggle-raw-headers"));
|
document.getElementById("toggle-raw-headers"));
|
||||||
|
|
@ -46,12 +46,12 @@ add_task(function* () {
|
||||||
function testShowRawHeaders(data) {
|
function testShowRawHeaders(data) {
|
||||||
let requestHeaders = document.getElementById("raw-request-headers-textarea").value;
|
let requestHeaders = document.getElementById("raw-request-headers-textarea").value;
|
||||||
for (let header of data.requestHeaders.headers) {
|
for (let header of data.requestHeaders.headers) {
|
||||||
ok(requestHeaders.indexOf(header.name + ": " + header.value) >= 0,
|
ok(requestHeaders.includes(header.name + ": " + header.value),
|
||||||
"textarea contains request headers");
|
"textarea contains request headers");
|
||||||
}
|
}
|
||||||
let responseHeaders = document.getElementById("raw-response-headers-textarea").value;
|
let responseHeaders = document.getElementById("raw-response-headers-textarea").value;
|
||||||
for (let header of data.responseHeaders.headers) {
|
for (let header of data.responseHeaders.headers) {
|
||||||
ok(responseHeaders.indexOf(header.name + ": " + header.value) >= 0,
|
ok(responseHeaders.includes(header.name + ": " + header.value),
|
||||||
"textarea contains response headers");
|
"textarea contains response headers");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
add_task(function* () {
|
add_task(function* () {
|
||||||
let { monitor } = yield initNetMonitor(SINGLE_GET_URL);
|
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||||
info("Starting test... ");
|
info("Starting test... ");
|
||||||
|
|
||||||
let { document, NetMonitorView } = monitor.panelWin;
|
let { document, NetMonitorView } = monitor.panelWin;
|
||||||
let { RequestsMenu } = NetMonitorView;
|
let { RequestsMenu } = NetMonitorView;
|
||||||
|
|
||||||
let wait = waitForNetworkEvents(monitor, 2);
|
let wait = waitForNetworkEvents(monitor, 1);
|
||||||
let button = document.querySelector("#requests-menu-reload-notice-button");
|
let button = document.querySelector("#requests-menu-reload-notice-button");
|
||||||
button.click();
|
button.click();
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
is(RequestsMenu.itemCount, 2, "The request menu should have two items after reloading");
|
is(RequestsMenu.itemCount, 1, "The request menu should have one item after reloading");
|
||||||
|
|
||||||
return teardown(monitor);
|
return teardown(monitor);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
add_task(function* () {
|
add_task(function* () {
|
||||||
let { monitor } = yield initNetMonitor(SINGLE_GET_URL);
|
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||||
info("Starting test... ");
|
info("Starting test... ");
|
||||||
|
|
||||||
let { document, EVENTS } = monitor.panelWin;
|
let { document, EVENTS } = monitor.panelWin;
|
||||||
|
|
@ -21,7 +21,7 @@ add_task(function* () {
|
||||||
markers.push(marker);
|
markers.push(marker);
|
||||||
});
|
});
|
||||||
|
|
||||||
yield waitForNetworkEvents(monitor, 2);
|
yield waitForNetworkEvents(monitor, 1);
|
||||||
yield waitUntil(() => markers.length == 2);
|
yield waitUntil(() => markers.length == 2);
|
||||||
|
|
||||||
ok(true, "Reloading finished");
|
ok(true, "Reloading finished");
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ add_task(function* () {
|
||||||
return teardown(monitor);
|
return teardown(monitor);
|
||||||
|
|
||||||
function verifyRequest(offset) {
|
function verifyRequest(offset) {
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(offset),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(offset),
|
||||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
|
"GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
|
||||||
status: 200,
|
status: 200,
|
||||||
statusText: "OK",
|
statusText: "OK",
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,15 @@ add_task(function* () {
|
||||||
RequestsMenu.cloneSelectedRequest();
|
RequestsMenu.cloneSelectedRequest();
|
||||||
yield onPopulated;
|
yield onPopulated;
|
||||||
|
|
||||||
testCustomForm(origItem.attachment);
|
testCustomForm(origItem);
|
||||||
|
|
||||||
let customItem = RequestsMenu.selectedItem;
|
let customItem = RequestsMenu.selectedItem;
|
||||||
testCustomItem(customItem, origItem);
|
testCustomItem(customItem, origItem);
|
||||||
|
|
||||||
// edit the custom request
|
// edit the custom request
|
||||||
yield editCustomForm();
|
yield editCustomForm();
|
||||||
|
// FIXME: reread the customItem, it's been replaced by a new object (immutable!)
|
||||||
|
customItem = RequestsMenu.selectedItem;
|
||||||
testCustomItemChanged(customItem, origItem);
|
testCustomItemChanged(customItem, origItem);
|
||||||
|
|
||||||
// send the new request
|
// send the new request
|
||||||
|
|
@ -54,30 +56,20 @@ add_task(function* () {
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
let sentItem = RequestsMenu.selectedItem;
|
let sentItem = RequestsMenu.selectedItem;
|
||||||
testSentRequest(sentItem.attachment, origItem.attachment);
|
testSentRequest(sentItem, origItem);
|
||||||
|
|
||||||
return teardown(monitor);
|
return teardown(monitor);
|
||||||
|
|
||||||
function testCustomItem(item, orig) {
|
function testCustomItem(item, orig) {
|
||||||
let method = item.target.querySelector(".requests-menu-method").value;
|
is(item.method, orig.method, "item is showing the same method as original request");
|
||||||
let origMethod = orig.target.querySelector(".requests-menu-method").value;
|
is(item.url, orig.url, "item is showing the same URL as original request");
|
||||||
is(method, origMethod, "menu item is showing the same method as original request");
|
|
||||||
|
|
||||||
let file = item.target.querySelector(".requests-menu-file").value;
|
|
||||||
let origFile = orig.target.querySelector(".requests-menu-file").value;
|
|
||||||
is(file, origFile, "menu item is showing the same file name as original request");
|
|
||||||
|
|
||||||
let domain = item.target.querySelector(".requests-menu-domain").value;
|
|
||||||
let origDomain = orig.target.querySelector(".requests-menu-domain").value;
|
|
||||||
is(domain, origDomain, "menu item is showing the same domain as original request");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function testCustomItemChanged(item, orig) {
|
function testCustomItemChanged(item, orig) {
|
||||||
let file = item.target.querySelector(".requests-menu-file").value;
|
let url = item.url;
|
||||||
let expectedFile = orig.target.querySelector(".requests-menu-file").value +
|
let expectedUrl = orig.url + "&" + ADD_QUERY;
|
||||||
"&" + ADD_QUERY;
|
|
||||||
|
|
||||||
is(file, expectedFile, "menu item is updated to reflect url entered in form");
|
is(url, expectedUrl, "menu item is updated to reflect url entered in form");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ add_task(function* () {
|
||||||
|
|
||||||
// Check the requests that were sent
|
// Check the requests that were sent
|
||||||
for (let [i, method] of METHODS.entries()) {
|
for (let [i, method] of METHODS.entries()) {
|
||||||
let { attachment } = RequestsMenu.getItemAtIndex(i);
|
let item = RequestsMenu.getItemAtIndex(i);
|
||||||
is(attachment.method, method, `The ${method} request has the right method`);
|
is(item.method, method, `The ${method} request has the right method`);
|
||||||
is(attachment.url, requestUrl, `The ${method} request has the right URL`);
|
is(item.url, requestUrl, `The ${method} request has the right URL`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resend both requests without modification. Wait for resent OPTIONS, then POST.
|
// Resend both requests without modification. Wait for resent OPTIONS, then POST.
|
||||||
|
|
@ -61,7 +61,7 @@ add_task(function* () {
|
||||||
// Check the resent requests
|
// Check the resent requests
|
||||||
for (let [i, method] of METHODS.entries()) {
|
for (let [i, method] of METHODS.entries()) {
|
||||||
let index = i + 2;
|
let index = i + 2;
|
||||||
let item = RequestsMenu.getItemAtIndex(index).attachment;
|
let item = RequestsMenu.getItemAtIndex(index);
|
||||||
is(item.method, method, `The ${method} request has the right method`);
|
is(item.method, method, `The ${method} request has the right method`);
|
||||||
is(item.url, requestUrl, `The ${method} request has the right URL`);
|
is(item.url, requestUrl, `The ${method} request has the right URL`);
|
||||||
is(item.status, 200, `The ${method} response has the right status`);
|
is(item.status, 200, `The ${method} response has the right status`);
|
||||||
|
|
|
||||||
|
|
@ -35,21 +35,21 @@ add_task(function* () {
|
||||||
});
|
});
|
||||||
yield wait;
|
yield wait;
|
||||||
|
|
||||||
let { attachment } = RequestsMenu.getItemAtIndex(0);
|
let item = RequestsMenu.getItemAtIndex(0);
|
||||||
is(attachment.method, "POST", "The request has the right method");
|
is(item.method, "POST", "The request has the right method");
|
||||||
is(attachment.url, requestUrl, "The request has the right URL");
|
is(item.url, requestUrl, "The request has the right URL");
|
||||||
|
|
||||||
for (let { name, value } of attachment.requestHeaders.headers) {
|
for (let { name, value } of item.requestHeaders.headers) {
|
||||||
info(`Request header: ${name}: ${value}`);
|
info(`Request header: ${name}: ${value}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasRequestHeader(name, value) {
|
function hasRequestHeader(name, value) {
|
||||||
let { headers } = attachment.requestHeaders;
|
let { headers } = item.requestHeaders;
|
||||||
return headers.some(h => h.name === name && h.value === value);
|
return headers.some(h => h.name === name && h.value === value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasNotRequestHeader(name) {
|
function hasNotRequestHeader(name) {
|
||||||
let { headers } = attachment.requestHeaders;
|
let { headers } = item.requestHeaders;
|
||||||
return headers.every(h => h.name !== name);
|
return headers.every(h => h.name !== name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,13 @@ add_task(function* () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function* clickAndTestSecurityIcon() {
|
function* clickAndTestSecurityIcon() {
|
||||||
let item = RequestsMenu.items[0];
|
let item = RequestsMenu.getItemAtIndex(0);
|
||||||
let icon = $(".requests-security-state-icon", item.target);
|
let target = getItemTarget(RequestsMenu, item);
|
||||||
|
let icon = $(".requests-security-state-icon", target);
|
||||||
|
|
||||||
info("Clicking security icon of the first request and waiting for the " +
|
info("Clicking security icon of the first request and waiting for panel update.");
|
||||||
"panel to update.");
|
EventUtils.synthesizeMouseAtCenter(icon, {}, monitor.panelWin);
|
||||||
|
|
||||||
icon.click();
|
|
||||||
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
|
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
|
||||||
|
|
||||||
is(NetworkDetails.widget.selectedPanel, $("#security-tabpanel"),
|
is(NetworkDetails.widget.selectedPanel, $("#security-tabpanel"),
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,13 @@ add_task(function* () {
|
||||||
|
|
||||||
is(RequestsMenu.itemCount, 2, "There were two requests due to redirect.");
|
is(RequestsMenu.itemCount, 2, "There were two requests due to redirect.");
|
||||||
|
|
||||||
let initial = RequestsMenu.items[0];
|
let initial = RequestsMenu.getItemAtIndex(0);
|
||||||
let redirect = RequestsMenu.items[1];
|
let redirect = RequestsMenu.getItemAtIndex(1);
|
||||||
|
|
||||||
let initialSecurityIcon = $(".requests-security-state-icon", initial.target);
|
let initialSecurityIcon =
|
||||||
let redirectSecurityIcon = $(".requests-security-state-icon", redirect.target);
|
$(".requests-security-state-icon", getItemTarget(RequestsMenu, initial));
|
||||||
|
let redirectSecurityIcon =
|
||||||
|
$(".requests-security-state-icon", getItemTarget(RequestsMenu, redirect));
|
||||||
|
|
||||||
ok(initialSecurityIcon.classList.contains("security-state-insecure"),
|
ok(initialSecurityIcon.classList.contains("security-state-insecure"),
|
||||||
"Initial request was marked insecure.");
|
"Initial request was marked insecure.");
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,13 @@ add_task(function* () {
|
||||||
yield performRequests();
|
yield performRequests();
|
||||||
|
|
||||||
for (let item of RequestsMenu.items) {
|
for (let item of RequestsMenu.items) {
|
||||||
let domain = $(".requests-menu-domain", item.target).value;
|
let target = getItemTarget(RequestsMenu, item);
|
||||||
|
let domain = $(".requests-menu-domain", target).textContent;
|
||||||
|
|
||||||
info("Found a request to " + domain);
|
info("Found a request to " + domain);
|
||||||
ok(domain in EXPECTED_SECURITY_STATES, "Domain " + domain + " was expected.");
|
ok(domain in EXPECTED_SECURITY_STATES, "Domain " + domain + " was expected.");
|
||||||
|
|
||||||
let classes = $(".requests-security-state-icon", item.target).classList;
|
let classes = $(".requests-security-state-icon", target).classList;
|
||||||
let expectedClass = EXPECTED_SECURITY_STATES[domain];
|
let expectedClass = EXPECTED_SECURITY_STATES[domain];
|
||||||
|
|
||||||
info("Classes of security state icon are: " + classes);
|
info("Classes of security state icon are: " + classes);
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ add_task(function* () {
|
||||||
info("Selecting the request.");
|
info("Selecting the request.");
|
||||||
RequestsMenu.selectedIndex = 0;
|
RequestsMenu.selectedIndex = 0;
|
||||||
|
|
||||||
is(RequestsMenu.selectedItem.attachment.securityState, undefined,
|
is(RequestsMenu.selectedItem.securityState, undefined,
|
||||||
"Security state has not yet arrived.");
|
"Security state has not yet arrived.");
|
||||||
is(tabEl.hidden, !testcase.visibleOnNewEvent,
|
is(tabEl.hidden, !testcase.visibleOnNewEvent,
|
||||||
"Security tab is " +
|
"Security tab is " +
|
||||||
|
|
@ -70,7 +70,7 @@ add_task(function* () {
|
||||||
info("Waiting for security information to arrive.");
|
info("Waiting for security information to arrive.");
|
||||||
yield onSecurityInfo;
|
yield onSecurityInfo;
|
||||||
|
|
||||||
ok(RequestsMenu.selectedItem.attachment.securityState,
|
ok(RequestsMenu.selectedItem.securityState,
|
||||||
"Security state arrived.");
|
"Security state arrived.");
|
||||||
is(tabEl.hidden, !testcase.visibleOnSecurityInfo,
|
is(tabEl.hidden, !testcase.visibleOnSecurityInfo,
|
||||||
"Security tab is " +
|
"Security tab is " +
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ add_task(function* () {
|
||||||
|
|
||||||
is(RequestsMenu.itemCount, 1, "Only the reload should be recorded.");
|
is(RequestsMenu.itemCount, 1, "Only the reload should be recorded.");
|
||||||
let request = RequestsMenu.getItemAtIndex(0);
|
let request = RequestsMenu.getItemAtIndex(0);
|
||||||
is(request.attachment.method, "GET", "The method is correct.");
|
is(request.method, "GET", "The method is correct.");
|
||||||
is(request.attachment.status, "200", "The status is correct.");
|
is(request.status, "200", "The status is correct.");
|
||||||
|
|
||||||
yield removeTab(beaconTab);
|
yield removeTab(beaconTab);
|
||||||
return teardown(monitor);
|
return teardown(monitor);
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ add_task(function* () {
|
||||||
|
|
||||||
is(RequestsMenu.itemCount, 1, "The beacon should be recorded.");
|
is(RequestsMenu.itemCount, 1, "The beacon should be recorded.");
|
||||||
let request = RequestsMenu.getItemAtIndex(0);
|
let request = RequestsMenu.getItemAtIndex(0);
|
||||||
is(request.attachment.method, "POST", "The method is correct.");
|
is(request.method, "POST", "The method is correct.");
|
||||||
ok(request.attachment.url.endsWith("beacon_request"), "The URL is correct.");
|
ok(request.url.endsWith("beacon_request"), "The URL is correct.");
|
||||||
is(request.attachment.status, "404", "The status is correct.");
|
is(request.status, "404", "The status is correct.");
|
||||||
|
|
||||||
return teardown(monitor);
|
return teardown(monitor);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -51,9 +51,10 @@ add_task(function* () {
|
||||||
let item = RequestsMenu.getItemAtIndex(index);
|
let item = RequestsMenu.getItemAtIndex(index);
|
||||||
|
|
||||||
info(`Verifying request #${index}`);
|
info(`Verifying request #${index}`);
|
||||||
yield verifyRequestItemTarget(item, request.method, request.uri, request.details);
|
yield verifyRequestItemTarget(RequestsMenu, item,
|
||||||
|
request.method, request.uri, request.details);
|
||||||
|
|
||||||
let { stacktrace } = item.attachment.cause;
|
let { stacktrace } = item.cause;
|
||||||
let stackLen = stacktrace ? stacktrace.length : 0;
|
let stackLen = stacktrace ? stacktrace.length : 0;
|
||||||
|
|
||||||
ok(stacktrace, `Request #${index} has a stacktrace`);
|
ok(stacktrace, `Request #${index} has a stacktrace`);
|
||||||
|
|
|
||||||
|
|
@ -32,69 +32,63 @@ function test() {
|
||||||
|
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
is(typeof requestItem.value, "string",
|
is(typeof requestItem.id, "string",
|
||||||
"The attached request id is incorrect.");
|
"The attached request id is incorrect.");
|
||||||
isnot(requestItem.value, "",
|
isnot(requestItem.id, "",
|
||||||
"The attached request id should not be empty.");
|
"The attached request id should not be empty.");
|
||||||
|
|
||||||
is(typeof requestItem.attachment.startedDeltaMillis, "number",
|
is(typeof requestItem.startedMillis, "number",
|
||||||
"The attached startedDeltaMillis is incorrect.");
|
|
||||||
is(requestItem.attachment.startedDeltaMillis, 0,
|
|
||||||
"The attached startedDeltaMillis should be zero.");
|
|
||||||
|
|
||||||
is(typeof requestItem.attachment.startedMillis, "number",
|
|
||||||
"The attached startedMillis is incorrect.");
|
"The attached startedMillis is incorrect.");
|
||||||
isnot(requestItem.attachment.startedMillis, 0,
|
isnot(requestItem.startedMillis, 0,
|
||||||
"The attached startedMillis should not be zero.");
|
"The attached startedMillis should not be zero.");
|
||||||
|
|
||||||
is(requestItem.attachment.requestHeaders, undefined,
|
is(requestItem.requestHeaders, undefined,
|
||||||
"The requestHeaders should not yet be set.");
|
"The requestHeaders should not yet be set.");
|
||||||
is(requestItem.attachment.requestCookies, undefined,
|
is(requestItem.requestCookies, undefined,
|
||||||
"The requestCookies should not yet be set.");
|
"The requestCookies should not yet be set.");
|
||||||
is(requestItem.attachment.requestPostData, undefined,
|
is(requestItem.requestPostData, undefined,
|
||||||
"The requestPostData should not yet be set.");
|
"The requestPostData should not yet be set.");
|
||||||
|
|
||||||
is(requestItem.attachment.responseHeaders, undefined,
|
is(requestItem.responseHeaders, undefined,
|
||||||
"The responseHeaders should not yet be set.");
|
"The responseHeaders should not yet be set.");
|
||||||
is(requestItem.attachment.responseCookies, undefined,
|
is(requestItem.responseCookies, undefined,
|
||||||
"The responseCookies should not yet be set.");
|
"The responseCookies should not yet be set.");
|
||||||
|
|
||||||
is(requestItem.attachment.httpVersion, undefined,
|
is(requestItem.httpVersion, undefined,
|
||||||
"The httpVersion should not yet be set.");
|
"The httpVersion should not yet be set.");
|
||||||
is(requestItem.attachment.status, undefined,
|
is(requestItem.status, undefined,
|
||||||
"The status should not yet be set.");
|
"The status should not yet be set.");
|
||||||
is(requestItem.attachment.statusText, undefined,
|
is(requestItem.statusText, undefined,
|
||||||
"The statusText should not yet be set.");
|
"The statusText should not yet be set.");
|
||||||
|
|
||||||
is(requestItem.attachment.headersSize, undefined,
|
is(requestItem.headersSize, undefined,
|
||||||
"The headersSize should not yet be set.");
|
"The headersSize should not yet be set.");
|
||||||
is(requestItem.attachment.transferredSize, undefined,
|
is(requestItem.transferredSize, undefined,
|
||||||
"The transferredSize should not yet be set.");
|
"The transferredSize should not yet be set.");
|
||||||
is(requestItem.attachment.contentSize, undefined,
|
is(requestItem.contentSize, undefined,
|
||||||
"The contentSize should not yet be set.");
|
"The contentSize should not yet be set.");
|
||||||
|
|
||||||
is(requestItem.attachment.mimeType, undefined,
|
is(requestItem.mimeType, undefined,
|
||||||
"The mimeType should not yet be set.");
|
"The mimeType should not yet be set.");
|
||||||
is(requestItem.attachment.responseContent, undefined,
|
is(requestItem.responseContent, undefined,
|
||||||
"The responseContent should not yet be set.");
|
"The responseContent should not yet be set.");
|
||||||
|
|
||||||
is(requestItem.attachment.totalTime, undefined,
|
is(requestItem.totalTime, undefined,
|
||||||
"The totalTime should not yet be set.");
|
"The totalTime should not yet be set.");
|
||||||
is(requestItem.attachment.eventTimings, undefined,
|
is(requestItem.eventTimings, undefined,
|
||||||
"The eventTimings should not yet be set.");
|
"The eventTimings should not yet be set.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS);
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS);
|
||||||
});
|
});
|
||||||
|
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_HEADERS, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_HEADERS, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
ok(requestItem.requestHeaders,
|
||||||
ok(requestItem.attachment.requestHeaders,
|
"There should be a requestHeaders data available.");
|
||||||
"There should be a requestHeaders attachment available.");
|
is(requestItem.requestHeaders.headers.length, 10,
|
||||||
is(requestItem.attachment.requestHeaders.headers.length, 9,
|
"The requestHeaders data has an incorrect |headers| property.");
|
||||||
"The requestHeaders attachment has an incorrect |headers| property.");
|
isnot(requestItem.requestHeaders.headersSize, 0,
|
||||||
isnot(requestItem.attachment.requestHeaders.headersSize, 0,
|
"The requestHeaders data has an incorrect |headersSize| property.");
|
||||||
"The requestHeaders attachment has an incorrect |headersSize| property.");
|
|
||||||
// Can't test for the exact request headers size because the value may
|
// Can't test for the exact request headers size because the value may
|
||||||
// vary across platforms ("User-Agent" header differs).
|
// vary across platforms ("User-Agent" header differs).
|
||||||
|
|
||||||
|
|
@ -104,12 +98,12 @@ function test() {
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_COOKIES, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_COOKIES, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
ok(requestItem.attachment.requestCookies,
|
ok(requestItem.requestCookies,
|
||||||
"There should be a requestCookies attachment available.");
|
"There should be a requestCookies data available.");
|
||||||
is(requestItem.attachment.requestCookies.cookies.length, 2,
|
is(requestItem.requestCookies.cookies.length, 2,
|
||||||
"The requestCookies attachment has an incorrect |cookies| property.");
|
"The requestCookies data has an incorrect |cookies| property.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS);
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS);
|
||||||
});
|
});
|
||||||
|
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_POST_DATA, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_POST_DATA, () => {
|
||||||
|
|
@ -119,40 +113,40 @@ function test() {
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_HEADERS, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_HEADERS, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
ok(requestItem.attachment.responseHeaders,
|
ok(requestItem.responseHeaders,
|
||||||
"There should be a responseHeaders attachment available.");
|
"There should be a responseHeaders data available.");
|
||||||
is(requestItem.attachment.responseHeaders.headers.length, 10,
|
is(requestItem.responseHeaders.headers.length, 10,
|
||||||
"The responseHeaders attachment has an incorrect |headers| property.");
|
"The responseHeaders data has an incorrect |headers| property.");
|
||||||
is(requestItem.attachment.responseHeaders.headersSize, 330,
|
is(requestItem.responseHeaders.headersSize, 330,
|
||||||
"The responseHeaders attachment has an incorrect |headersSize| property.");
|
"The responseHeaders data has an incorrect |headersSize| property.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS);
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS);
|
||||||
});
|
});
|
||||||
|
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_COOKIES, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_COOKIES, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
ok(requestItem.attachment.responseCookies,
|
ok(requestItem.responseCookies,
|
||||||
"There should be a responseCookies attachment available.");
|
"There should be a responseCookies data available.");
|
||||||
is(requestItem.attachment.responseCookies.cookies.length, 2,
|
is(requestItem.responseCookies.cookies.length, 2,
|
||||||
"The responseCookies attachment has an incorrect |cookies| property.");
|
"The responseCookies data has an incorrect |cookies| property.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS);
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS);
|
||||||
});
|
});
|
||||||
|
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.STARTED_RECEIVING_RESPONSE, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.STARTED_RECEIVING_RESPONSE, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
is(requestItem.attachment.httpVersion, "HTTP/1.1",
|
is(requestItem.httpVersion, "HTTP/1.1",
|
||||||
"The httpVersion attachment has an incorrect value.");
|
"The httpVersion data has an incorrect value.");
|
||||||
is(requestItem.attachment.status, "200",
|
is(requestItem.status, "200",
|
||||||
"The status attachment has an incorrect value.");
|
"The status data has an incorrect value.");
|
||||||
is(requestItem.attachment.statusText, "Och Aye",
|
is(requestItem.statusText, "Och Aye",
|
||||||
"The statusText attachment has an incorrect value.");
|
"The statusText data has an incorrect value.");
|
||||||
is(requestItem.attachment.headersSize, 330,
|
is(requestItem.headersSize, 330,
|
||||||
"The headersSize attachment has an incorrect value.");
|
"The headersSize data has an incorrect value.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||||
status: "200",
|
status: "200",
|
||||||
statusText: "Och Aye"
|
statusText: "Och Aye"
|
||||||
});
|
});
|
||||||
|
|
@ -161,58 +155,53 @@ function test() {
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_RESPONSE_CONTENT, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_RESPONSE_CONTENT, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
is(requestItem.attachment.transferredSize, "12",
|
is(requestItem.transferredSize, "12",
|
||||||
"The transferredSize attachment has an incorrect value.");
|
"The transferredSize data has an incorrect value.");
|
||||||
is(requestItem.attachment.contentSize, "12",
|
is(requestItem.contentSize, "12",
|
||||||
"The contentSize attachment has an incorrect value.");
|
"The contentSize data has an incorrect value.");
|
||||||
is(requestItem.attachment.mimeType, "text/plain; charset=utf-8",
|
is(requestItem.mimeType, "text/plain; charset=utf-8",
|
||||||
"The mimeType attachment has an incorrect value.");
|
"The mimeType data has an incorrect value.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||||
type: "plain",
|
type: "plain",
|
||||||
fullMimeType: "text/plain; charset=utf-8",
|
fullMimeType: "text/plain; charset=utf-8",
|
||||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
ok(requestItem.attachment.responseContent,
|
ok(requestItem.responseContent,
|
||||||
"There should be a responseContent attachment available.");
|
"There should be a responseContent data available.");
|
||||||
is(requestItem.attachment.responseContent.content.mimeType,
|
is(requestItem.responseContent.content.mimeType,
|
||||||
"text/plain; charset=utf-8",
|
"text/plain; charset=utf-8",
|
||||||
"The responseContent attachment has an incorrect |content.mimeType| property.");
|
"The responseContent data has an incorrect |content.mimeType| property.");
|
||||||
is(requestItem.attachment.responseContent.content.text,
|
is(requestItem.responseContent.content.text,
|
||||||
"Hello world!",
|
"Hello world!",
|
||||||
"The responseContent attachment has an incorrect |content.text| property.");
|
"The responseContent data has an incorrect |content.text| property.");
|
||||||
is(requestItem.attachment.responseContent.content.size,
|
is(requestItem.responseContent.content.size,
|
||||||
12,
|
12,
|
||||||
"The responseContent attachment has an incorrect |content.size| property.");
|
"The responseContent data has an incorrect |content.size| property.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||||
type: "plain",
|
type: "plain",
|
||||||
fullMimeType: "text/plain; charset=utf-8",
|
fullMimeType: "text/plain; charset=utf-8",
|
||||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_EVENT_TIMINGS, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_EVENT_TIMINGS, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
is(typeof requestItem.attachment.totalTime, "number",
|
is(typeof requestItem.totalTime, "number",
|
||||||
"The attached totalTime is incorrect.");
|
"The attached totalTime is incorrect.");
|
||||||
ok(requestItem.attachment.totalTime >= 0,
|
ok(requestItem.totalTime >= 0,
|
||||||
"The attached totalTime should be positive.");
|
"The attached totalTime should be positive.");
|
||||||
|
|
||||||
is(typeof requestItem.attachment.endedMillis, "number",
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||||
"The attached endedMillis is incorrect.");
|
|
||||||
ok(requestItem.attachment.endedMillis >= 0,
|
|
||||||
"The attached endedMillis should be positive.");
|
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -220,24 +209,24 @@ function test() {
|
||||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_EVENT_TIMINGS, () => {
|
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_EVENT_TIMINGS, () => {
|
||||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||||
|
|
||||||
ok(requestItem.attachment.eventTimings,
|
ok(requestItem.eventTimings,
|
||||||
"There should be a eventTimings attachment available.");
|
"There should be a eventTimings data available.");
|
||||||
is(typeof requestItem.attachment.eventTimings.timings.blocked, "number",
|
is(typeof requestItem.eventTimings.timings.blocked, "number",
|
||||||
"The eventTimings attachment has an incorrect |timings.blocked| property.");
|
"The eventTimings data has an incorrect |timings.blocked| property.");
|
||||||
is(typeof requestItem.attachment.eventTimings.timings.dns, "number",
|
is(typeof requestItem.eventTimings.timings.dns, "number",
|
||||||
"The eventTimings attachment has an incorrect |timings.dns| property.");
|
"The eventTimings data has an incorrect |timings.dns| property.");
|
||||||
is(typeof requestItem.attachment.eventTimings.timings.connect, "number",
|
is(typeof requestItem.eventTimings.timings.connect, "number",
|
||||||
"The eventTimings attachment has an incorrect |timings.connect| property.");
|
"The eventTimings data has an incorrect |timings.connect| property.");
|
||||||
is(typeof requestItem.attachment.eventTimings.timings.send, "number",
|
is(typeof requestItem.eventTimings.timings.send, "number",
|
||||||
"The eventTimings attachment has an incorrect |timings.send| property.");
|
"The eventTimings data has an incorrect |timings.send| property.");
|
||||||
is(typeof requestItem.attachment.eventTimings.timings.wait, "number",
|
is(typeof requestItem.eventTimings.timings.wait, "number",
|
||||||
"The eventTimings attachment has an incorrect |timings.wait| property.");
|
"The eventTimings data has an incorrect |timings.wait| property.");
|
||||||
is(typeof requestItem.attachment.eventTimings.timings.receive, "number",
|
is(typeof requestItem.eventTimings.timings.receive, "number",
|
||||||
"The eventTimings attachment has an incorrect |timings.receive| property.");
|
"The eventTimings data has an incorrect |timings.receive| property.");
|
||||||
is(typeof requestItem.attachment.eventTimings.totalTime, "number",
|
is(typeof requestItem.eventTimings.totalTime, "number",
|
||||||
"The eventTimings attachment has an incorrect |totalTime| property.");
|
"The eventTimings data has an incorrect |totalTime| property.");
|
||||||
|
|
||||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ add_task(function* () {
|
||||||
"GET", "The method summary value is incorrect.");
|
"GET", "The method summary value is incorrect.");
|
||||||
is(tabpanel.querySelector("#headers-summary-address-value").getAttribute("value"),
|
is(tabpanel.querySelector("#headers-summary-address-value").getAttribute("value"),
|
||||||
"127.0.0.1:8888", "The remote address summary value is incorrect.");
|
"127.0.0.1:8888", "The remote address summary value is incorrect.");
|
||||||
is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("code"),
|
is(tabpanel.querySelector("#headers-summary-status-circle").getAttribute("data-code"),
|
||||||
"200", "The status summary code is incorrect.");
|
"200", "The status summary code is incorrect.");
|
||||||
is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"),
|
is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"),
|
||||||
"200 Och Aye", "The status summary value is incorrect.");
|
"200 Och Aye", "The status summary value is incorrect.");
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ add_task(function* () {
|
||||||
|
|
||||||
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true,
|
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true,
|
||||||
"The pane toggle button should be disabled when the frontend is opened.");
|
"The pane toggle button should be disabled when the frontend is opened.");
|
||||||
is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), false,
|
ok(document.querySelector("#requests-menu-empty-notice"),
|
||||||
"An empty notice should be displayed when the frontend is opened.");
|
"An empty notice should be displayed when the frontend is opened.");
|
||||||
is(RequestsMenu.itemCount, 0,
|
is(RequestsMenu.itemCount, 0,
|
||||||
"The requests menu should be empty when the frontend is opened.");
|
"The requests menu should be empty when the frontend is opened.");
|
||||||
|
|
@ -34,7 +34,7 @@ add_task(function* () {
|
||||||
|
|
||||||
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false,
|
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false,
|
||||||
"The pane toggle button should be enabled after the first request.");
|
"The pane toggle button should be enabled after the first request.");
|
||||||
is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), true,
|
ok(!document.querySelector("#requests-menu-empty-notice"),
|
||||||
"The empty notice should be hidden after the first request.");
|
"The empty notice should be hidden after the first request.");
|
||||||
is(RequestsMenu.itemCount, 1,
|
is(RequestsMenu.itemCount, 1,
|
||||||
"The requests menu should not be empty after the first request.");
|
"The requests menu should not be empty after the first request.");
|
||||||
|
|
@ -45,7 +45,7 @@ add_task(function* () {
|
||||||
|
|
||||||
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false,
|
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), false,
|
||||||
"The pane toggle button should be still be enabled after a reload.");
|
"The pane toggle button should be still be enabled after a reload.");
|
||||||
is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), true,
|
ok(!document.querySelector("#requests-menu-empty-notice"),
|
||||||
"The empty notice should be still hidden after a reload.");
|
"The empty notice should be still hidden after a reload.");
|
||||||
is(RequestsMenu.itemCount, 1,
|
is(RequestsMenu.itemCount, 1,
|
||||||
"The requests menu should not be empty after a reload.");
|
"The requests menu should not be empty after a reload.");
|
||||||
|
|
@ -56,7 +56,7 @@ add_task(function* () {
|
||||||
|
|
||||||
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true,
|
is(document.querySelector("#details-pane-toggle").hasAttribute("disabled"), true,
|
||||||
"The pane toggle button should be disabled when after clear.");
|
"The pane toggle button should be disabled when after clear.");
|
||||||
is(document.querySelector("#requests-menu-empty-notice").hasAttribute("hidden"), false,
|
ok(document.querySelector("#requests-menu-empty-notice"),
|
||||||
"An empty notice should be displayed again after clear.");
|
"An empty notice should be displayed again after clear.");
|
||||||
is(RequestsMenu.itemCount, 0,
|
is(RequestsMenu.itemCount, 0,
|
||||||
"The requests menu should be empty after clear.");
|
"The requests menu should be empty after clear.");
|
||||||
|
|
|
||||||
|
|
@ -162,21 +162,10 @@ add_task(function* () {
|
||||||
"There should be a total of 5 items in the requests menu.");
|
"There should be a total of 5 items in the requests menu.");
|
||||||
is(RequestsMenu.visibleItems.length, 5,
|
is(RequestsMenu.visibleItems.length, 5,
|
||||||
"There should be a total of 5 visbile items in the requests menu.");
|
"There should be a total of 5 visbile items in the requests menu.");
|
||||||
is($all(".side-menu-widget-item").length, 5,
|
is($all(".request-list-item").length, 5,
|
||||||
"The visible items in the requests menu are, in fact, visible!");
|
"The visible items in the requests menu are, in fact, visible!");
|
||||||
|
|
||||||
is(RequestsMenu.getItemAtIndex(0), RequestsMenu.items[0],
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(a),
|
||||||
"The requests menu items aren't ordered correctly. First item is misplaced.");
|
|
||||||
is(RequestsMenu.getItemAtIndex(1), RequestsMenu.items[1],
|
|
||||||
"The requests menu items aren't ordered correctly. Second item is misplaced.");
|
|
||||||
is(RequestsMenu.getItemAtIndex(2), RequestsMenu.items[2],
|
|
||||||
"The requests menu items aren't ordered correctly. Third item is misplaced.");
|
|
||||||
is(RequestsMenu.getItemAtIndex(3), RequestsMenu.items[3],
|
|
||||||
"The requests menu items aren't ordered correctly. Fourth item is misplaced.");
|
|
||||||
is(RequestsMenu.getItemAtIndex(4), RequestsMenu.items[4],
|
|
||||||
"The requests menu items aren't ordered correctly. Fifth item is misplaced.");
|
|
||||||
|
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(a),
|
|
||||||
"GET", STATUS_CODES_SJS + "?sts=100", {
|
"GET", STATUS_CODES_SJS + "?sts=100", {
|
||||||
status: 101,
|
status: 101,
|
||||||
statusText: "Switching Protocols",
|
statusText: "Switching Protocols",
|
||||||
|
|
@ -186,7 +175,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(b),
|
||||||
"GET", STATUS_CODES_SJS + "?sts=200", {
|
"GET", STATUS_CODES_SJS + "?sts=200", {
|
||||||
status: 202,
|
status: 202,
|
||||||
statusText: "Created",
|
statusText: "Created",
|
||||||
|
|
@ -196,7 +185,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(c),
|
||||||
"GET", STATUS_CODES_SJS + "?sts=300", {
|
"GET", STATUS_CODES_SJS + "?sts=300", {
|
||||||
status: 303,
|
status: 303,
|
||||||
statusText: "See Other",
|
statusText: "See Other",
|
||||||
|
|
@ -206,7 +195,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(d),
|
||||||
"GET", STATUS_CODES_SJS + "?sts=400", {
|
"GET", STATUS_CODES_SJS + "?sts=400", {
|
||||||
status: 404,
|
status: 404,
|
||||||
statusText: "Not Found",
|
statusText: "Not Found",
|
||||||
|
|
@ -216,7 +205,7 @@ add_task(function* () {
|
||||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22),
|
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22),
|
||||||
time: true
|
time: true
|
||||||
});
|
});
|
||||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e),
|
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(e),
|
||||||
"GET", STATUS_CODES_SJS + "?sts=500", {
|
"GET", STATUS_CODES_SJS + "?sts=500", {
|
||||||
status: 501,
|
status: 501,
|
||||||
statusText: "Not Implemented",
|
statusText: "Not Implemented",
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue