зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
ebeb98b7b8
Коммит
bcce5eb099
|
@ -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.
|
||||
*
|
||||
* @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 {
|
||||
type: SET_FILTER_TEXT,
|
||||
url,
|
||||
text,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,20 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const batching = require("./batching");
|
||||
const filters = require("./filters");
|
||||
const requests = require("./requests");
|
||||
const selection = require("./selection");
|
||||
const sort = require("./sort");
|
||||
const timingMarkers = require("./timing-markers");
|
||||
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/.
|
||||
|
||||
DevToolsModules(
|
||||
'batching.js',
|
||||
'filters.js',
|
||||
'index.js',
|
||||
'requests.js',
|
||||
'selection.js',
|
||||
'sort.js',
|
||||
'timing-markers.js',
|
||||
'ui.js',
|
||||
)
|
||||
|
|
|
@ -5,21 +5,61 @@
|
|||
"use strict";
|
||||
|
||||
const {
|
||||
UPDATE_REQUESTS,
|
||||
ADD_REQUEST,
|
||||
UPDATE_REQUEST,
|
||||
CLONE_SELECTED_REQUEST,
|
||||
REMOVE_SELECTED_CUSTOM_REQUEST,
|
||||
CLEAR_REQUESTS,
|
||||
} = require("../constants");
|
||||
|
||||
/**
|
||||
* Update request items
|
||||
*
|
||||
* @param {array} requests - visible request items
|
||||
*/
|
||||
function updateRequests(items) {
|
||||
function addRequest(id, data, batch) {
|
||||
return {
|
||||
type: UPDATE_REQUESTS,
|
||||
items,
|
||||
type: ADD_REQUEST,
|
||||
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 = {
|
||||
updateRequests,
|
||||
addRequest,
|
||||
updateRequest,
|
||||
cloneSelectedRequest,
|
||||
removeSelectedCustomRequest,
|
||||
clearRequests,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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
|
||||
};
|
|
@ -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 {
|
||||
OPEN_SIDEBAR,
|
||||
TOGGLE_SIDEBAR,
|
||||
WATERFALL_RESIZE,
|
||||
} = require("../constants");
|
||||
|
||||
/**
|
||||
|
@ -25,12 +25,21 @@ function openSidebar(open) {
|
|||
* Toggle sidebar open state.
|
||||
*/
|
||||
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 {
|
||||
type: TOGGLE_SIDEBAR,
|
||||
type: WATERFALL_RESIZE,
|
||||
width
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
openSidebar,
|
||||
toggleSidebar,
|
||||
resizeWaterfall,
|
||||
};
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
* 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 { DOM } = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const { L10N } = require("../l10n");
|
||||
const Actions = require("../actions/index");
|
||||
|
||||
const { button } = DOM;
|
||||
|
||||
|
@ -15,15 +15,18 @@ const { button } = DOM;
|
|||
* Clear button component
|
||||
* A type of tool button is responsible for cleaning network requests.
|
||||
*/
|
||||
function ClearButton() {
|
||||
function ClearButton({ onClick }) {
|
||||
return button({
|
||||
id: "requests-menu-clear-button",
|
||||
className: "devtools-button devtools-clear-icon",
|
||||
title: L10N.getStr("netmonitor.toolbar.clear"),
|
||||
onClick: () => {
|
||||
NetMonitorView.RequestsMenu.clear();
|
||||
},
|
||||
onClick,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = ClearButton;
|
||||
module.exports = connect(
|
||||
undefined,
|
||||
dispatch => ({
|
||||
onClick: () => dispatch(Actions.clearRequests())
|
||||
})
|
||||
)(ClearButton);
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
DevToolsModules(
|
||||
'clear-button.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',
|
||||
'summary-button.js',
|
||||
'toggle-button.js',
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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;
|
|
@ -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,
|
||||
};
|
|
@ -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 { PluralForm } = require("devtools/shared/plural-form");
|
||||
const { L10N } = require("../l10n");
|
||||
const { getSummary } = require("../selectors/index");
|
||||
const { getDisplayedRequestsSummary } = require("../selectors/index");
|
||||
|
||||
const { button, span } = DOM;
|
||||
|
||||
|
@ -22,14 +22,12 @@ function SummaryButton({
|
|||
summary,
|
||||
triggerSummary,
|
||||
}) {
|
||||
let { count, totalBytes, totalMillis } = summary;
|
||||
let { count, bytes, millis } = summary;
|
||||
const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
|
||||
PluralForm.get(count, L10N.getStr("networkMenu.summary"))
|
||||
.replace("#1", count)
|
||||
.replace("#2", L10N.numberWithDecimals(totalBytes / 1024,
|
||||
CONTENT_SIZE_DECIMALS))
|
||||
.replace("#3", L10N.numberWithDecimals(totalMillis / 1000,
|
||||
REQUEST_TIME_DECIMALS));
|
||||
.replace("#2", L10N.numberWithDecimals(bytes / 1024, CONTENT_SIZE_DECIMALS))
|
||||
.replace("#3", L10N.numberWithDecimals(millis / 1000, REQUEST_TIME_DECIMALS));
|
||||
|
||||
return button({
|
||||
id: "requests-menu-network-summary-button",
|
||||
|
@ -47,7 +45,7 @@ SummaryButton.propTypes = {
|
|||
|
||||
module.exports = connect(
|
||||
(state) => ({
|
||||
summary: getSummary(state),
|
||||
summary: getDisplayedRequestsSummary(state),
|
||||
}),
|
||||
(dispatch) => ({
|
||||
triggerSummary: () => {
|
||||
|
|
|
@ -2,21 +2,20 @@
|
|||
* 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 { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const { L10N } = require("../l10n");
|
||||
const Actions = require("../actions/index");
|
||||
const { isSidebarToggleButtonDisabled } = require("../selectors/index");
|
||||
|
||||
const { button } = DOM;
|
||||
|
||||
function ToggleButton({
|
||||
disabled,
|
||||
open,
|
||||
triggerSidebar,
|
||||
onToggle,
|
||||
}) {
|
||||
let className = ["devtools-button"];
|
||||
if (!open) {
|
||||
|
@ -32,34 +31,21 @@ function ToggleButton({
|
|||
title,
|
||||
disabled,
|
||||
tabIndex: "0",
|
||||
onMouseDown: triggerSidebar,
|
||||
onMouseDown: onToggle,
|
||||
});
|
||||
}
|
||||
|
||||
ToggleButton.propTypes = {
|
||||
disabled: PropTypes.bool.isRequired,
|
||||
triggerSidebar: PropTypes.func.isRequired,
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
module.exports = connect(
|
||||
(state) => ({
|
||||
disabled: state.requests.items.length === 0,
|
||||
open: state.ui.sidebar.open,
|
||||
disabled: isSidebarToggleButtonDisabled(state),
|
||||
open: state.ui.sidebarOpen,
|
||||
}),
|
||||
(dispatch) => ({
|
||||
triggerSidebar: () => {
|
||||
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;
|
||||
}
|
||||
},
|
||||
onToggle: () => dispatch(Actions.toggleSidebar())
|
||||
})
|
||||
)(ToggleButton);
|
||||
|
|
|
@ -11,12 +11,23 @@ const general = {
|
|||
};
|
||||
|
||||
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",
|
||||
ENABLE_FILTER_TYPE_ONLY: "ENABLE_FILTER_TYPE_ONLY",
|
||||
SET_FILTER_TEXT: "SET_FILTER_TEXT",
|
||||
OPEN_SIDEBAR: "OPEN_SIDEBAR",
|
||||
TOGGLE_SIDEBAR: "TOGGLE_SIDEBAR",
|
||||
UPDATE_REQUESTS: "UPDATE_REQUESTS",
|
||||
WATERFALL_RESIZE: "WATERFALL_RESIZE",
|
||||
};
|
||||
|
||||
module.exports = Object.assign({}, general, actionTypes);
|
||||
|
|
|
@ -7,12 +7,11 @@
|
|||
"use strict";
|
||||
|
||||
const { Task } = require("devtools/shared/task");
|
||||
const {
|
||||
writeHeaderText,
|
||||
getKeyWithEvent,
|
||||
getUrlQuery,
|
||||
parseQueryString,
|
||||
} = require("./request-utils");
|
||||
const { writeHeaderText,
|
||||
getKeyWithEvent,
|
||||
getUrlQuery,
|
||||
parseQueryString } = require("./request-utils");
|
||||
const Actions = require("./actions/index");
|
||||
|
||||
/**
|
||||
* Functions handling the custom request view.
|
||||
|
@ -76,37 +75,41 @@ CustomRequestView.prototype = {
|
|||
*/
|
||||
onUpdate: function (field) {
|
||||
let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
|
||||
let store = NetMonitorView.RequestsMenu.store;
|
||||
let value;
|
||||
|
||||
switch (field) {
|
||||
case "method":
|
||||
value = $("#custom-method-value").value.trim();
|
||||
selectedItem.attachment.method = value;
|
||||
store.dispatch(Actions.updateRequest(selectedItem.id, { method: value }));
|
||||
break;
|
||||
case "url":
|
||||
value = $("#custom-url-value").value;
|
||||
this.updateCustomQuery(value);
|
||||
selectedItem.attachment.url = value;
|
||||
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
|
||||
break;
|
||||
case "query":
|
||||
let query = $("#custom-query-value").value;
|
||||
this.updateCustomUrl(query);
|
||||
field = "url";
|
||||
value = $("#custom-url-value").value;
|
||||
selectedItem.attachment.url = value;
|
||||
store.dispatch(Actions.updateRequest(selectedItem.id, { url: value }));
|
||||
break;
|
||||
case "body":
|
||||
value = $("#custom-postdata-value").value;
|
||||
selectedItem.attachment.requestPostData = { postData: { text: value } };
|
||||
store.dispatch(Actions.updateRequest(selectedItem.id, {
|
||||
requestPostData: {
|
||||
postData: { text: value }
|
||||
}
|
||||
}));
|
||||
break;
|
||||
case "headers":
|
||||
let headersText = $("#custom-headers-value").value;
|
||||
value = parseHeadersText(headersText);
|
||||
selectedItem.attachment.requestHeaders = { headers: value };
|
||||
store.dispatch(Actions.updateRequest(selectedItem.id, {
|
||||
requestHeaders: { headers: value }
|
||||
}));
|
||||
break;
|
||||
}
|
||||
|
||||
NetMonitorView.RequestsMenu.updateMenuView(selectedItem, field, value);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -161,7 +164,7 @@ function parseHeadersText(text) {
|
|||
* Parse readable text list of a query string.
|
||||
*
|
||||
* @param string text
|
||||
* Text of query string represetation
|
||||
* Text of query string representation
|
||||
* @return array
|
||||
* Array of query params {name, value}
|
||||
*/
|
||||
|
|
|
@ -267,10 +267,6 @@ DetailsView.prototype = {
|
|||
// Tab is selected but not dirty. We're done here.
|
||||
populated[tab] = true;
|
||||
window.emit(EVENTS.TAB_UPDATED);
|
||||
|
||||
if (NetMonitorController.isConnected()) {
|
||||
NetMonitorView.RequestsMenu.ensureSelectedItemIsVisible();
|
||||
}
|
||||
}
|
||||
} else if (viewState.dirty[tab]) {
|
||||
// Tab is dirty but no longer selected. Don't refresh it now, it'll be
|
||||
|
@ -328,7 +324,7 @@ DetailsView.prototype = {
|
|||
} else {
|
||||
code = data.status;
|
||||
}
|
||||
$("#headers-summary-status-circle").setAttribute("code", code);
|
||||
$("#headers-summary-status-circle").setAttribute("data-code", code);
|
||||
$("#headers-summary-status-value").setAttribute("value",
|
||||
data.status + " " + data.statusText);
|
||||
$("#headers-summary-status").removeAttribute("hidden");
|
||||
|
|
|
@ -63,9 +63,7 @@ HarBuilder.prototype = {
|
|||
let log = this.buildLog();
|
||||
|
||||
// Build entries.
|
||||
let items = this._options.items;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let file = items[i].attachment;
|
||||
for (let file of this._options.items) {
|
||||
log.entries.push(this.buildEntry(log, file));
|
||||
}
|
||||
|
||||
|
|
|
@ -201,9 +201,7 @@ HarCollector.prototype = {
|
|||
this.files.set(actor, file);
|
||||
|
||||
// Mimic the Net panel data structure
|
||||
this.items.push({
|
||||
attachment: file
|
||||
});
|
||||
this.items.push(file);
|
||||
},
|
||||
|
||||
onNetworkEventUpdate: function (type, packet) {
|
||||
|
|
|
@ -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;
|
|
@ -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',
|
||||
'components',
|
||||
'har',
|
||||
'middleware',
|
||||
'reducers',
|
||||
'selectors'
|
||||
'selectors',
|
||||
'utils',
|
||||
]
|
||||
|
||||
DevToolsModules(
|
||||
|
@ -27,6 +29,7 @@ DevToolsModules(
|
|||
'sort-predicates.js',
|
||||
'store.js',
|
||||
'toolbar-view.js',
|
||||
'waterfall-background.js',
|
||||
)
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* eslint-disable mozilla/reject-some-requires */
|
||||
/* globals window, document, NetMonitorView, gStore, Actions */
|
||||
/* globals window, NetMonitorView, gStore, Actions */
|
||||
/* exported loader */
|
||||
|
||||
"use strict";
|
||||
|
@ -41,21 +41,17 @@ const EventEmitter = require("devtools/shared/event-emitter");
|
|||
const Editor = require("devtools/client/sourceeditor/editor");
|
||||
const {TimelineFront} = require("devtools/shared/fronts/timeline");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const {Prefs} = require("./prefs");
|
||||
const {EVENTS} = require("./events");
|
||||
const Actions = require("./actions/index");
|
||||
const { getDisplayedRequestById } = require("./selectors/index");
|
||||
|
||||
XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
|
||||
XPCOMUtils.defineConstant(this, "ACTIVITY_TYPE", ACTIVITY_TYPE);
|
||||
XPCOMUtils.defineConstant(this, "Editor", Editor);
|
||||
XPCOMUtils.defineConstant(this, "Prefs", Prefs);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Chart",
|
||||
"resource://devtools/client/shared/widgets/Chart.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
|
||||
"@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
|
||||
|
||||
/**
|
||||
* Object defining the network monitor controller components.
|
||||
*/
|
||||
|
@ -91,6 +87,7 @@ var NetMonitorController = {
|
|||
}
|
||||
this._shutdown = promise.defer();
|
||||
{
|
||||
gStore.dispatch(Actions.batchReset());
|
||||
NetMonitorView.destroy();
|
||||
this.TargetEventsHandler.disconnect();
|
||||
this.NetworkEventsHandler.disconnect();
|
||||
|
@ -287,19 +284,18 @@ var NetMonitorController = {
|
|||
let deferred = promise.defer();
|
||||
let request = null;
|
||||
let inspector = function () {
|
||||
let predicate = i => i.value === requestId;
|
||||
request = NetMonitorView.RequestsMenu.getItemForPredicate(predicate);
|
||||
request = getDisplayedRequestById(gStore.getState(), requestId);
|
||||
if (!request) {
|
||||
// Reset filters so that the request is visible.
|
||||
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
|
||||
// called again once new requests arrive.
|
||||
if (request) {
|
||||
window.off(EVENTS.REQUEST_ADDED, inspector);
|
||||
NetMonitorView.RequestsMenu.selectedItem = request;
|
||||
gStore.dispatch(Actions.selectRequest(request.id));
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
|
@ -398,14 +394,14 @@ TargetEventsHandler.prototype = {
|
|||
// Reset UI.
|
||||
if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
|
||||
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.
|
||||
if (NetMonitorController.getCurrentActivity() == ACTIVITY_TYPE.NONE) {
|
||||
NetMonitorView.showNetworkInspectorView();
|
||||
}
|
||||
// Clear any accumulated markers.
|
||||
NetMonitorController.NetworkEventsHandler.clearMarkers();
|
||||
|
||||
window.emit(EVENTS.TARGET_WILL_NAVIGATE);
|
||||
break;
|
||||
|
@ -429,8 +425,6 @@ TargetEventsHandler.prototype = {
|
|||
* Functions handling target network events.
|
||||
*/
|
||||
function NetworkEventsHandler() {
|
||||
this._markers = [];
|
||||
|
||||
this._onNetworkEvent = this._onNetworkEvent.bind(this);
|
||||
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
|
||||
this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
|
||||
|
@ -456,19 +450,6 @@ NetworkEventsHandler.prototype = {
|
|||
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.
|
||||
*/
|
||||
|
@ -525,7 +506,7 @@ NetworkEventsHandler.prototype = {
|
|||
*/
|
||||
_onDocLoadingMarker: function (marker) {
|
||||
window.emit(EVENTS.TIMELINE_EVENT, marker);
|
||||
this._markers.push(marker);
|
||||
gStore.dispatch(Actions.addTimingMarker(marker));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -547,8 +528,7 @@ NetworkEventsHandler.prototype = {
|
|||
} = networkInfo;
|
||||
|
||||
NetMonitorView.RequestsMenu.addRequest(
|
||||
actor, startedDateTime, method, url, isXHR, cause, fromCache,
|
||||
fromServiceWorker
|
||||
actor, {startedDateTime, method, url, isXHR, cause, fromCache, fromServiceWorker}
|
||||
);
|
||||
window.emit(EVENTS.NETWORK_EVENT, actor);
|
||||
},
|
||||
|
@ -637,7 +617,7 @@ NetworkEventsHandler.prototype = {
|
|||
_onRequestHeaders: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
requestHeaders: response
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
|
||||
});
|
||||
},
|
||||
|
@ -651,7 +631,7 @@ NetworkEventsHandler.prototype = {
|
|||
_onRequestCookies: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
requestCookies: response
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
|
||||
});
|
||||
},
|
||||
|
@ -665,7 +645,7 @@ NetworkEventsHandler.prototype = {
|
|||
_onRequestPostData: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
requestPostData: response
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
|
||||
});
|
||||
},
|
||||
|
@ -679,7 +659,7 @@ NetworkEventsHandler.prototype = {
|
|||
_onSecurityInfo: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
securityInfo: response.securityInfo
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
|
||||
});
|
||||
},
|
||||
|
@ -693,7 +673,7 @@ NetworkEventsHandler.prototype = {
|
|||
_onResponseHeaders: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
responseHeaders: response
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
|
||||
});
|
||||
},
|
||||
|
@ -707,7 +687,7 @@ NetworkEventsHandler.prototype = {
|
|||
_onResponseCookies: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
responseCookies: response
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
|
||||
});
|
||||
},
|
||||
|
@ -721,7 +701,7 @@ NetworkEventsHandler.prototype = {
|
|||
_onResponseContent: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
responseContent: response
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
|
||||
});
|
||||
},
|
||||
|
@ -735,18 +715,11 @@ NetworkEventsHandler.prototype = {
|
|||
_onEventTimings: function (response) {
|
||||
NetMonitorView.RequestsMenu.updateRequest(response.from, {
|
||||
eventTimings: response
|
||||
}, () => {
|
||||
}).then(() => {
|
||||
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.
|
||||
*
|
||||
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,7 @@ const { ToolbarView } = require("./toolbar-view");
|
|||
const { SidebarView } = require("./sidebar-view");
|
||||
const { DetailsView } = require("./details-view");
|
||||
const { PerformanceStatisticsView } = require("./performance-statistics-view");
|
||||
var {Prefs} = require("./prefs");
|
||||
|
||||
// Initialize the global redux variables
|
||||
var gStore = configureStore();
|
||||
|
@ -80,12 +81,6 @@ var NetMonitorView = {
|
|||
this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
|
||||
this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
|
||||
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 () {
|
||||
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
|
||||
// populating the statistics view.
|
||||
// • The response mime type is used for categorization.
|
||||
yield whenDataAvailable(requestsView, [
|
||||
yield whenDataAvailable(requestsView.store, [
|
||||
"responseHeaders", "status", "contentSize", "mimeType", "totalTime"
|
||||
]);
|
||||
} catch (ex) {
|
||||
|
@ -200,8 +194,9 @@ var NetMonitorView = {
|
|||
console.error(ex);
|
||||
}
|
||||
|
||||
statisticsView.createPrimedCacheChart(requestsView.items);
|
||||
statisticsView.createEmptyCacheChart(requestsView.items);
|
||||
const requests = requestsView.store.getState().requests.requests;
|
||||
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.
|
||||
*
|
||||
* @param array dataStore
|
||||
* The request view object from which to fetch the item list.
|
||||
* @param Store dataStore
|
||||
* A Redux store for which to check the availability of properties.
|
||||
* @param array mandatoryFields
|
||||
* A list of strings representing properties of objects in dataStore.
|
||||
* @return object
|
||||
* A promise resolved when all objects in dataStore contain the
|
||||
* properties defined in mandatoryFields.
|
||||
*/
|
||||
function whenDataAvailable(requestsView, mandatoryFields) {
|
||||
let deferred = promise.defer();
|
||||
function whenDataAvailable(dataStore, mandatoryFields) {
|
||||
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(() => {
|
||||
const { attachments } = requestsView;
|
||||
if (attachments.length > 0 && attachments.every(item => {
|
||||
return mandatoryFields.every(field => field in item);
|
||||
})) {
|
||||
if (allFieldsPresent) {
|
||||
clearInterval(interval);
|
||||
clearTimeout(timer);
|
||||
resolve();
|
||||
}
|
||||
}, WDA_DEFAULT_VERIFY_INTERVAL);
|
||||
|
||||
let timer = setTimeout(() => {
|
||||
clearInterval(interval);
|
||||
clearTimeout(timer);
|
||||
deferred.resolve();
|
||||
}
|
||||
}, 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;
|
||||
reject(new Error("Timed out while waiting for data"));
|
||||
}, WDA_DEFAULT_GIVE_UP_TIMEOUT);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,188 +26,10 @@
|
|||
<hbox id="network-table-and-sidebar"
|
||||
class="devtools-responsive-container"
|
||||
flex="1">
|
||||
<vbox id="network-table" flex="1" class="devtools-main-content">
|
||||
<toolbar id="requests-menu-toolbar"
|
||||
class="devtools-toolbar"
|
||||
align="center">
|
||||
<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>
|
||||
<html:div xmlns="http://www.w3.org/1999/xhtml"
|
||||
id="network-table"
|
||||
class="devtools-main-content">
|
||||
</html:div>
|
||||
|
||||
<splitter id="network-inspector-view-splitter"
|
||||
class="devtools-side-splitter"/>
|
||||
|
|
|
@ -172,7 +172,7 @@ PerformanceStatisticsView.prototype = {
|
|||
}));
|
||||
|
||||
for (let requestItem of items) {
|
||||
let details = requestItem.attachment;
|
||||
let details = requestItem;
|
||||
let type;
|
||||
|
||||
if (Filters.html(details)) {
|
||||
|
@ -237,11 +237,8 @@ function responseIsFresh({ responseHeaders, status }) {
|
|||
}
|
||||
|
||||
let list = responseHeaders.headers;
|
||||
let cacheControl = list.filter(e => {
|
||||
return e.name.toLowerCase() == "cache-control";
|
||||
})[0];
|
||||
|
||||
let expires = list.filter(e => e.name.toLowerCase() == "expires")[0];
|
||||
let cacheControl = list.find(e => e.name.toLowerCase() == "cache-control");
|
||||
let expires = list.find(e => e.name.toLowerCase() == "expires");
|
||||
|
||||
// Check the "Cache-Control" header for a maximum age value.
|
||||
if (cacheControl) {
|
||||
|
|
|
@ -13,6 +13,5 @@ const {PrefsHelper} = require("devtools/client/shared/prefs");
|
|||
exports.Prefs = new PrefsHelper("devtools.netmonitor", {
|
||||
networkDetailsWidth: ["Int", "panes-network-details-width"],
|
||||
networkDetailsHeight: ["Int", "panes-network-details-height"],
|
||||
statistics: ["Bool", "statistics"],
|
||||
filters: ["Json", "filters"]
|
||||
});
|
||||
|
|
|
@ -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({
|
||||
types: new FilterTypes({ all: true }),
|
||||
url: "",
|
||||
text: "",
|
||||
});
|
||||
|
||||
function toggleFilterType(state, action) {
|
||||
|
@ -72,7 +72,7 @@ function filters(state = new Filters(), action) {
|
|||
case ENABLE_FILTER_TYPE_ONLY:
|
||||
return state.set("types", enableFilterTypeOnly(state.types, action));
|
||||
case SET_FILTER_TEXT:
|
||||
return state.set("url", action.url);
|
||||
return state.set("text", action.text);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -5,12 +5,19 @@
|
|||
"use strict";
|
||||
|
||||
const { combineReducers } = require("devtools/client/shared/vendor/redux");
|
||||
const filters = require("./filters");
|
||||
const batchingReducer = require("./batching");
|
||||
const requests = require("./requests");
|
||||
const sort = require("./sort");
|
||||
const filters = require("./filters");
|
||||
const timingMarkers = require("./timing-markers");
|
||||
const ui = require("./ui");
|
||||
|
||||
module.exports = combineReducers({
|
||||
filters,
|
||||
requests,
|
||||
ui,
|
||||
});
|
||||
module.exports = batchingReducer(
|
||||
combineReducers({
|
||||
requests,
|
||||
sort,
|
||||
filters,
|
||||
timingMarkers,
|
||||
ui,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'batching.js',
|
||||
'filters.js',
|
||||
'index.js',
|
||||
'requests.js',
|
||||
'sort.js',
|
||||
'timing-markers.js',
|
||||
'ui.js',
|
||||
)
|
||||
|
|
|
@ -5,25 +5,242 @@
|
|||
"use strict";
|
||||
|
||||
const I = require("devtools/client/shared/vendor/immutable");
|
||||
const { getUrlDetails } = require("../request-utils");
|
||||
const {
|
||||
UPDATE_REQUESTS,
|
||||
ADD_REQUEST,
|
||||
UPDATE_REQUEST,
|
||||
CLEAR_REQUESTS,
|
||||
SELECT_REQUEST,
|
||||
PRESELECT_REQUEST,
|
||||
CLONE_SELECTED_REQUEST,
|
||||
REMOVE_SELECTED_CUSTOM_REQUEST,
|
||||
OPEN_SIDEBAR
|
||||
} = require("../constants");
|
||||
|
||||
const Requests = I.Record({
|
||||
items: [],
|
||||
const Request = I.Record({
|
||||
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) {
|
||||
return state.set("items", action.items || state.items);
|
||||
}
|
||||
const Requests = I.Record({
|
||||
// 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) {
|
||||
case UPDATE_REQUESTS:
|
||||
return updateRequests(state, action);
|
||||
case ADD_REQUEST: {
|
||||
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:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = requests;
|
||||
module.exports = requestsReducer;
|
||||
|
|
|
@ -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;
|
|
@ -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 {
|
||||
OPEN_SIDEBAR,
|
||||
TOGGLE_SIDEBAR,
|
||||
WATERFALL_RESIZE,
|
||||
} = require("../constants");
|
||||
|
||||
const Sidebar = I.Record({
|
||||
open: false,
|
||||
});
|
||||
|
||||
const UI = I.Record({
|
||||
sidebar: new Sidebar(),
|
||||
sidebarOpen: false,
|
||||
waterfallWidth: 300,
|
||||
});
|
||||
|
||||
function openSidebar(state, action) {
|
||||
return state.setIn(["sidebar", "open"], action.open);
|
||||
return state.set("sidebarOpen", action.open);
|
||||
}
|
||||
|
||||
function toggleSidebar(state, action) {
|
||||
return state.setIn(["sidebar", "open"], !state.sidebar.open);
|
||||
// Safe bounds for waterfall width (px)
|
||||
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) {
|
||||
switch (action.type) {
|
||||
case OPEN_SIDEBAR:
|
||||
return openSidebar(state, action);
|
||||
case TOGGLE_SIDEBAR:
|
||||
return toggleSidebar(state, action);
|
||||
case WATERFALL_RESIZE:
|
||||
return resizeWaterfall(state, action);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ RequestListContextMenu.prototype = {
|
|||
id: "request-menu-context-copy-url-params",
|
||||
label: L10N.getStr("netmonitor.context.copyUrlParams"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyUrlParams.accesskey"),
|
||||
visible: !!(selectedItem && getUrlQuery(selectedItem.attachment.url)),
|
||||
visible: !!(selectedItem && getUrlQuery(selectedItem.url)),
|
||||
click: () => this.copyUrlParams(),
|
||||
}));
|
||||
|
||||
|
@ -66,7 +66,7 @@ RequestListContextMenu.prototype = {
|
|||
id: "request-menu-context-copy-post-data",
|
||||
label: L10N.getStr("netmonitor.context.copyPostData"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
|
||||
visible: !!(selectedItem && selectedItem.attachment.requestPostData),
|
||||
visible: !!(selectedItem && selectedItem.requestPostData),
|
||||
click: () => this.copyPostData(),
|
||||
}));
|
||||
|
||||
|
@ -74,7 +74,7 @@ RequestListContextMenu.prototype = {
|
|||
id: "request-menu-context-copy-as-curl",
|
||||
label: L10N.getStr("netmonitor.context.copyAsCurl"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
|
||||
visible: !!(selectedItem && selectedItem.attachment),
|
||||
visible: !!selectedItem,
|
||||
click: () => this.copyAsCurl(),
|
||||
}));
|
||||
|
||||
|
@ -87,7 +87,7 @@ RequestListContextMenu.prototype = {
|
|||
id: "request-menu-context-copy-request-headers",
|
||||
label: L10N.getStr("netmonitor.context.copyRequestHeaders"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyRequestHeaders.accesskey"),
|
||||
visible: !!(selectedItem && selectedItem.attachment.requestHeaders),
|
||||
visible: !!(selectedItem && selectedItem.requestHeaders),
|
||||
click: () => this.copyRequestHeaders(),
|
||||
}));
|
||||
|
||||
|
@ -95,7 +95,7 @@ RequestListContextMenu.prototype = {
|
|||
id: "response-menu-context-copy-response-headers",
|
||||
label: L10N.getStr("netmonitor.context.copyResponseHeaders"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyResponseHeaders.accesskey"),
|
||||
visible: !!(selectedItem && selectedItem.attachment.responseHeaders),
|
||||
visible: !!(selectedItem && selectedItem.responseHeaders),
|
||||
click: () => this.copyResponseHeaders(),
|
||||
}));
|
||||
|
||||
|
@ -104,9 +104,9 @@ RequestListContextMenu.prototype = {
|
|||
label: L10N.getStr("netmonitor.context.copyResponse"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyResponse.accesskey"),
|
||||
visible: !!(selectedItem &&
|
||||
selectedItem.attachment.responseContent &&
|
||||
selectedItem.attachment.responseContent.content.text &&
|
||||
selectedItem.attachment.responseContent.content.text.length !== 0),
|
||||
selectedItem.responseContent &&
|
||||
selectedItem.responseContent.content.text &&
|
||||
selectedItem.responseContent.content.text.length !== 0),
|
||||
click: () => this.copyResponse(),
|
||||
}));
|
||||
|
||||
|
@ -115,9 +115,8 @@ RequestListContextMenu.prototype = {
|
|||
label: L10N.getStr("netmonitor.context.copyImageAsDataUri"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyImageAsDataUri.accesskey"),
|
||||
visible: !!(selectedItem &&
|
||||
selectedItem.attachment.responseContent &&
|
||||
selectedItem.attachment.responseContent.content
|
||||
.mimeType.includes("image/")),
|
||||
selectedItem.responseContent &&
|
||||
selectedItem.responseContent.content.mimeType.includes("image/")),
|
||||
click: () => this.copyImageAsDataUri(),
|
||||
}));
|
||||
|
||||
|
@ -130,7 +129,7 @@ RequestListContextMenu.prototype = {
|
|||
id: "request-menu-context-copy-all-as-har",
|
||||
label: L10N.getStr("netmonitor.context.copyAllAsHar"),
|
||||
accesskey: L10N.getStr("netmonitor.context.copyAllAsHar.accesskey"),
|
||||
visible: !!this.items.length,
|
||||
visible: this.items.size > 0,
|
||||
click: () => this.copyAllAsHar(),
|
||||
}));
|
||||
|
||||
|
@ -138,7 +137,7 @@ RequestListContextMenu.prototype = {
|
|||
id: "request-menu-context-save-all-as-har",
|
||||
label: L10N.getStr("netmonitor.context.saveAllAsHar"),
|
||||
accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
|
||||
visible: !!this.items.length,
|
||||
visible: this.items.size > 0,
|
||||
click: () => this.saveAllAsHar(),
|
||||
}));
|
||||
|
||||
|
@ -152,8 +151,7 @@ RequestListContextMenu.prototype = {
|
|||
label: L10N.getStr("netmonitor.context.editAndResend"),
|
||||
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
|
||||
visible: !!(NetMonitorController.supportsCustomRequest &&
|
||||
selectedItem &&
|
||||
!selectedItem.attachment.isCustom),
|
||||
selectedItem && !selectedItem.isCustom),
|
||||
click: () => NetMonitorView.RequestsMenu.cloneSelectedRequest(),
|
||||
}));
|
||||
|
||||
|
@ -187,15 +185,14 @@ RequestListContextMenu.prototype = {
|
|||
*/
|
||||
openRequestInTab() {
|
||||
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
|
||||
let { url } = this.selectedItem.attachment;
|
||||
win.openUILinkIn(url, "tab", { relatedToCurrent: true });
|
||||
win.openUILinkIn(this.selectedItem.url, "tab", { relatedToCurrent: true });
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy the request url from the currently selected item.
|
||||
*/
|
||||
copyUrl() {
|
||||
clipboardHelper.copyString(this.selectedItem.attachment.url);
|
||||
clipboardHelper.copyString(this.selectedItem.url);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -203,7 +200,7 @@ RequestListContextMenu.prototype = {
|
|||
* selected item.
|
||||
*/
|
||||
copyUrlParams() {
|
||||
let { url } = this.selectedItem.attachment;
|
||||
let { url } = this.selectedItem;
|
||||
let params = getUrlQuery(url).split("&");
|
||||
let string = params.join(Services.appinfo.OS === "WINNT" ? "\r\n" : "\n");
|
||||
clipboardHelper.copyString(string);
|
||||
|
@ -214,7 +211,7 @@ RequestListContextMenu.prototype = {
|
|||
* the currently selected item.
|
||||
*/
|
||||
copyPostData: Task.async(function* () {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let selected = this.selectedItem;
|
||||
|
||||
// Try to extract any form data parameters.
|
||||
let formDataSections = yield getFormDataSections(
|
||||
|
@ -251,7 +248,7 @@ RequestListContextMenu.prototype = {
|
|||
* Copy a cURL command from the currently selected item.
|
||||
*/
|
||||
copyAsCurl: Task.async(function* () {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let selected = this.selectedItem;
|
||||
|
||||
// Create a sanitized object for the Curl command generator.
|
||||
let data = {
|
||||
|
@ -281,8 +278,7 @@ RequestListContextMenu.prototype = {
|
|||
* Copy the raw request headers from the currently selected item.
|
||||
*/
|
||||
copyRequestHeaders() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let rawHeaders = selected.requestHeaders.rawHeaders.trim();
|
||||
let rawHeaders = this.selectedItem.requestHeaders.rawHeaders.trim();
|
||||
if (Services.appinfo.OS !== "WINNT") {
|
||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||
}
|
||||
|
@ -293,8 +289,7 @@ RequestListContextMenu.prototype = {
|
|||
* Copy the raw response headers from the currently selected item.
|
||||
*/
|
||||
copyResponseHeaders() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let rawHeaders = selected.responseHeaders.rawHeaders.trim();
|
||||
let rawHeaders = this.selectedItem.responseHeaders.rawHeaders.trim();
|
||||
if (Services.appinfo.OS !== "WINNT") {
|
||||
rawHeaders = rawHeaders.replace(/\r/g, "");
|
||||
}
|
||||
|
@ -305,8 +300,7 @@ RequestListContextMenu.prototype = {
|
|||
* Copy image as data uri.
|
||||
*/
|
||||
copyImageAsDataUri() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let { mimeType, text, encoding } = selected.responseContent.content;
|
||||
const { mimeType, text, encoding } = this.selectedItem.responseContent.content;
|
||||
|
||||
gNetwork.getString(text).then(string => {
|
||||
let data = formDataURI(mimeType, encoding, string);
|
||||
|
@ -318,8 +312,7 @@ RequestListContextMenu.prototype = {
|
|||
* Copy response data as a string.
|
||||
*/
|
||||
copyResponse() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let text = selected.responseContent.content.text;
|
||||
const { text } = this.selectedItem.responseContent.content;
|
||||
|
||||
gNetwork.getString(text).then(string => {
|
||||
clipboardHelper.copyString(string);
|
||||
|
@ -348,8 +341,7 @@ RequestListContextMenu.prototype = {
|
|||
|
||||
return {
|
||||
getString: gNetwork.getString.bind(gNetwork),
|
||||
view: NetMonitorView.RequestsMenu,
|
||||
items: NetMonitorView.RequestsMenu.items,
|
||||
items: this.items,
|
||||
title: title
|
||||
};
|
||||
}
|
||||
|
|
|
@ -50,8 +50,8 @@ const getFormDataSections = Task.async(function* (headers, uploadHeaders, postDa
|
|||
getString) {
|
||||
let formDataSections = [];
|
||||
|
||||
let { headers: requestHeaders } = headers;
|
||||
let { headers: payloadHeaders } = uploadHeaders;
|
||||
let requestHeaders = headers.headers;
|
||||
let payloadHeaders = uploadHeaders ? uploadHeaders.headers : [];
|
||||
let allHeaders = [...payloadHeaders, ...requestHeaders];
|
||||
|
||||
let contentTypeHeader = allHeaders.find(e => {
|
||||
|
@ -188,6 +188,37 @@ function getUrlHost(url) {
|
|||
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
|
||||
*
|
||||
|
@ -253,6 +284,7 @@ module.exports = {
|
|||
getUrlBaseNameWithQuery,
|
||||
getUrlHostName,
|
||||
getUrlHost,
|
||||
getUrlDetails,
|
||||
parseQueryString,
|
||||
loadCauseString,
|
||||
};
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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";
|
||||
|
||||
const { createSelector } = require("devtools/client/shared/vendor/reselect");
|
||||
const filters = require("./filters");
|
||||
const requests = require("./requests");
|
||||
const ui = require("./ui");
|
||||
|
||||
/**
|
||||
* Gets the total number of bytes representing the cumulated content size of
|
||||
* a set of requests. Returns 0 for an empty set.
|
||||
*
|
||||
* @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),
|
||||
})
|
||||
Object.assign(exports,
|
||||
filters,
|
||||
requests,
|
||||
ui
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
getSummary,
|
||||
};
|
||||
|
|
|
@ -3,5 +3,8 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'index.js'
|
||||
'filters.js',
|
||||
'index.js',
|
||||
'requests.js',
|
||||
'ui.js',
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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) {
|
||||
NetMonitorView.toggleDetailsPane({ visible: visibleFlag });
|
||||
NetMonitorView.RequestsMenu._flushWaterfallViews(true);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -8,7 +8,6 @@ const {
|
|||
getAbbreviatedMimeType,
|
||||
getUrlBaseNameWithQuery,
|
||||
getUrlHost,
|
||||
loadCauseString,
|
||||
} = require("./request-utils");
|
||||
|
||||
/**
|
||||
|
@ -60,8 +59,8 @@ function domain(first, second) {
|
|||
}
|
||||
|
||||
function cause(first, second) {
|
||||
let firstCause = loadCauseString(first.cause.type);
|
||||
let secondCause = loadCauseString(second.cause.type);
|
||||
let firstCause = first.cause.type;
|
||||
let secondCause = second.cause.type;
|
||||
if (firstCause == secondCause) {
|
||||
return first.startedMillis - second.startedMillis;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,19 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const createStore = require("devtools/client/shared/redux/create-store");
|
||||
const reducers = require("./reducers/index");
|
||||
const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
|
||||
const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
|
||||
const batching = require("./middleware/batching");
|
||||
const rootReducer = require("./reducers/index");
|
||||
|
||||
function configureStore() {
|
||||
return createStore()(reducers);
|
||||
return createStore(
|
||||
rootReducer,
|
||||
applyMiddleware(
|
||||
thunk,
|
||||
batching
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
exports.configureStore = configureStore;
|
||||
|
|
|
@ -54,7 +54,6 @@ support-files =
|
|||
skip-if = (toolkit == "cocoa" && e10s) # bug 1252254
|
||||
[browser_net_api-calls.js]
|
||||
[browser_net_autoscroll.js]
|
||||
skip-if = true # Bug 1309191 - replace with rewritten version in React
|
||||
[browser_net_cached-status.js]
|
||||
[browser_net_cause.js]
|
||||
[browser_net_cause_redirect.js]
|
||||
|
@ -89,9 +88,11 @@ subsuite = clipboard
|
|||
[browser_net_cyrillic-01.js]
|
||||
[browser_net_cyrillic-02.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]
|
||||
skip-if = (os == 'linux' && debug && bits == 32) # Bug 1321434
|
||||
[browser_net_filter-01.js]
|
||||
skip-if = (os == 'linux' && debug && bits == 32) # Bug 1303439
|
||||
[browser_net_filter-02.js]
|
||||
[browser_net_filter-03.js]
|
||||
[browser_net_filter-04.js]
|
||||
|
@ -140,6 +141,7 @@ skip-if = true # Bug 1258809
|
|||
skip-if = true # Bug 1258809
|
||||
[browser_net_simple-request.js]
|
||||
[browser_net_sort-01.js]
|
||||
skip-if = true # Redundant for React/Redux version
|
||||
[browser_net_sort-02.js]
|
||||
[browser_net_sort-03.js]
|
||||
[browser_net_statistics-01.js]
|
||||
|
@ -149,5 +151,6 @@ skip-if = true # Bug 1258809
|
|||
[browser_net_streaming-response.js]
|
||||
[browser_net_throttle.js]
|
||||
[browser_net_timeline_ticks.js]
|
||||
skip-if = true # TODO: fix the test
|
||||
[browser_net_timing-division.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.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
let { NetMonitorView } = monitor.panelWin;
|
||||
let { NetMonitorView, gStore, windowRequire } = monitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
let Actions = windowRequire("devtools/client/netmonitor/actions/index");
|
||||
|
||||
let count = 0;
|
||||
function check(selectedIndex, paneVisibility) {
|
||||
info("Performing check " + (count++) + ".");
|
||||
|
@ -37,24 +39,19 @@ add_task(function* () {
|
|||
|
||||
check(-1, false);
|
||||
|
||||
RequestsMenu.focusLastVisibleItem();
|
||||
gStore.dispatch(Actions.selectDelta(+Infinity));
|
||||
check(1, true);
|
||||
RequestsMenu.focusFirstVisibleItem();
|
||||
gStore.dispatch(Actions.selectDelta(-Infinity));
|
||||
check(0, true);
|
||||
|
||||
RequestsMenu.focusNextItem();
|
||||
gStore.dispatch(Actions.selectDelta(+1));
|
||||
check(1, true);
|
||||
RequestsMenu.focusPrevItem();
|
||||
gStore.dispatch(Actions.selectDelta(-1));
|
||||
check(0, true);
|
||||
|
||||
RequestsMenu.focusItemAtDelta(+1);
|
||||
gStore.dispatch(Actions.selectDelta(+10));
|
||||
check(1, true);
|
||||
RequestsMenu.focusItemAtDelta(-1);
|
||||
check(0, true);
|
||||
|
||||
RequestsMenu.focusItemAtDelta(+10);
|
||||
check(1, true);
|
||||
RequestsMenu.focusItemAtDelta(-10);
|
||||
gStore.dispatch(Actions.selectDelta(-10));
|
||||
check(0, true);
|
||||
|
||||
wait = waitForNetworkEvents(monitor, 18);
|
||||
|
@ -63,25 +60,25 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
RequestsMenu.focusLastVisibleItem();
|
||||
gStore.dispatch(Actions.selectDelta(+Infinity));
|
||||
check(19, true);
|
||||
RequestsMenu.focusFirstVisibleItem();
|
||||
gStore.dispatch(Actions.selectDelta(-Infinity));
|
||||
check(0, true);
|
||||
|
||||
RequestsMenu.focusNextItem();
|
||||
gStore.dispatch(Actions.selectDelta(+1));
|
||||
check(1, true);
|
||||
RequestsMenu.focusPrevItem();
|
||||
gStore.dispatch(Actions.selectDelta(-1));
|
||||
check(0, true);
|
||||
|
||||
RequestsMenu.focusItemAtDelta(+10);
|
||||
gStore.dispatch(Actions.selectDelta(+10));
|
||||
check(10, true);
|
||||
RequestsMenu.focusItemAtDelta(-10);
|
||||
gStore.dispatch(Actions.selectDelta(-10));
|
||||
check(0, true);
|
||||
|
||||
RequestsMenu.focusItemAtDelta(+100);
|
||||
gStore.dispatch(Actions.selectDelta(+100));
|
||||
check(19, true);
|
||||
RequestsMenu.focusItemAtDelta(-100);
|
||||
gStore.dispatch(Actions.selectDelta(-100));
|
||||
check(0, true);
|
||||
|
||||
yield teardown(monitor);
|
||||
return teardown(monitor);
|
||||
});
|
||||
|
|
|
@ -35,6 +35,8 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
$(".requests-menu-contents").focus();
|
||||
|
||||
check(-1, false);
|
||||
|
||||
EventUtils.sendKey("DOWN", window);
|
||||
|
@ -123,7 +125,7 @@ add_task(function* () {
|
|||
EventUtils.sendMouseEvent({ type: "mousedown" }, $("#details-pane-toggle"));
|
||||
check(-1, false);
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, $(".side-menu-widget-item"));
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, $(".request-list-item"));
|
||||
check(0, true);
|
||||
|
||||
yield teardown(monitor);
|
||||
|
|
|
@ -32,7 +32,7 @@ add_task(function* () {
|
|||
yield wait;
|
||||
|
||||
REQUEST_URIS.forEach(function (uri, index) {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(index), "GET", uri);
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(index), "GET", uri);
|
||||
});
|
||||
|
||||
yield teardown(monitor);
|
||||
|
|
|
@ -10,22 +10,25 @@ add_task(function* () {
|
|||
requestLongerTimeout(2);
|
||||
|
||||
let { monitor } = yield initNetMonitor(INFINITE_GET_URL);
|
||||
let win = monitor.panelWin;
|
||||
let topNode = win.document.getElementById("requests-menu-contents");
|
||||
let requestsContainer = topNode.getElementsByTagName("scrollbox")[0];
|
||||
ok(!!requestsContainer, "Container element exists as expected.");
|
||||
let { $ } = monitor.panelWin;
|
||||
|
||||
// Wait until the first request makes the empty notice disappear
|
||||
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
|
||||
// when the requests overflow the vertical size of the container.
|
||||
yield waitForRequestsToOverflowContainer();
|
||||
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.
|
||||
let children = requestsContainer.childNodes;
|
||||
let middleNode = children.item(children.length / 2);
|
||||
middleNode.scrollIntoView();
|
||||
let firstNode = requestsContainer.firstChild;
|
||||
firstNode.scrollIntoView();
|
||||
yield waitSomeTime();
|
||||
ok(!scrolledToBottom(requestsContainer), "Not scrolled to bottom.");
|
||||
// save for comparison later
|
||||
let scrollTop = requestsContainer.scrollTop;
|
||||
|
@ -39,7 +42,7 @@ add_task(function* () {
|
|||
ok(scrolledToBottom(requestsContainer), "Set scroll position to bottom.");
|
||||
yield waitForNetworkEvents(monitor, 8);
|
||||
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
|
||||
// do not change the scroll position.
|
||||
|
@ -49,12 +52,20 @@ add_task(function* () {
|
|||
is(requestsContainer.scrollTop, 0, "Did not scroll.");
|
||||
|
||||
// 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() {
|
||||
info("Waiting for enough requests to overflow the container");
|
||||
while (true) {
|
||||
info("Waiting for one network request");
|
||||
yield waitForNetworkEvents(monitor, 1);
|
||||
if (requestsContainer.scrollHeight > requestsContainer.clientHeight) {
|
||||
info("The list is long enough, returning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +81,7 @@ add_task(function* () {
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", HTTPS_CONTENT_TYPE_SJS + "?fmt=br", {
|
||||
status: 200,
|
||||
statusText: "Connected",
|
||||
|
|
|
@ -93,7 +93,8 @@ add_task(function* () {
|
|||
let item = RequestsMenu.getItemAtIndex(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++;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ const EXPECTED_REQUESTS = [
|
|||
method: "GET",
|
||||
url: CAUSE_URL,
|
||||
causeType: "document",
|
||||
causeUri: "",
|
||||
causeUri: null,
|
||||
// The document load has internal privileged JS code on the stack
|
||||
stack: true
|
||||
},
|
||||
|
@ -103,11 +103,11 @@ add_task(function* () {
|
|||
let { method, url, causeType, causeUri, stack } = spec;
|
||||
|
||||
let requestItem = RequestsMenu.getItemAtIndex(i);
|
||||
verifyRequestItemTarget(requestItem,
|
||||
verifyRequestItemTarget(RequestsMenu, requestItem,
|
||||
method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
|
||||
);
|
||||
|
||||
let { stacktrace } = requestItem.attachment.cause;
|
||||
let { stacktrace } = requestItem.cause;
|
||||
let stackLen = stacktrace ? stacktrace.length : 0;
|
||||
|
||||
if (stack) {
|
||||
|
@ -137,9 +137,7 @@ add_task(function* () {
|
|||
EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-cause-button"));
|
||||
let expectedOrder = EXPECTED_REQUESTS.map(r => r.causeType).sort();
|
||||
expectedOrder.forEach((expectedCause, i) => {
|
||||
let { target } = RequestsMenu.getItemAtIndex(i);
|
||||
let causeLabel = target.querySelector(".requests-menu-cause-label");
|
||||
let cause = causeLabel.getAttribute("value");
|
||||
const cause = RequestsMenu.getItemAtIndex(i).cause.type;
|
||||
is(cause, expectedCause, `The request #${i} has the expected cause after sorting`);
|
||||
});
|
||||
|
||||
|
|
|
@ -27,11 +27,11 @@ add_task(function* () {
|
|||
yield wait;
|
||||
|
||||
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;
|
||||
|
||||
if (hasStack) {
|
||||
|
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=xml", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
@ -33,7 +33,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 42),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=css", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
@ -42,7 +42,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(2),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(2),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=js", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
@ -51,7 +51,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 34),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(3),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(3),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
@ -60,7 +60,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 29),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(4),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(4),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=bogus", {
|
||||
status: 404,
|
||||
statusText: "Not Found",
|
||||
|
@ -69,7 +69,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 24),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(5),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(5),
|
||||
"GET", TEST_IMAGE, {
|
||||
fuzzyUrl: true,
|
||||
status: 200,
|
||||
|
@ -79,7 +79,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 580),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(6),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(6),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=gzip", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
|
|
@ -23,7 +23,7 @@ add_task(function* () {
|
|||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
RequestsMenu.selectedItem = requestItem;
|
||||
|
||||
let { method, httpVersion, status, statusText } = requestItem.attachment;
|
||||
let { method, httpVersion, status, statusText } = requestItem;
|
||||
|
||||
const EXPECTED_REQUEST_HEADERS = [
|
||||
`${method} ${SIMPLE_URL} ${httpVersion}`,
|
||||
|
|
|
@ -25,7 +25,7 @@ add_task(function* () {
|
|||
|
||||
yield waitForClipboardPromise(function setup() {
|
||||
RequestsMenu.contextMenu.copyUrl();
|
||||
}, requestItem.attachment.url);
|
||||
}, requestItem.url);
|
||||
|
||||
yield teardown(monitor);
|
||||
});
|
||||
|
|
|
@ -25,7 +25,8 @@ add_task(function* () {
|
|||
|
||||
info("Checking the preflight and flight methods");
|
||||
["OPTIONS", "POST"].forEach((method, i) => {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i), method, requestUrl);
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(i),
|
||||
method, requestUrl);
|
||||
});
|
||||
|
||||
yield teardown(monitor);
|
||||
|
|
|
@ -31,19 +31,19 @@ add_task(function* () {
|
|||
multipartForm: RequestsMenu.getItemAtIndex(3)
|
||||
};
|
||||
|
||||
let data = yield createCurlData(requests.get.attachment, gNetwork);
|
||||
let data = yield createCurlData(requests.get, gNetwork);
|
||||
testFindHeader(data);
|
||||
|
||||
data = yield createCurlData(requests.post.attachment, gNetwork);
|
||||
data = yield createCurlData(requests.post, gNetwork);
|
||||
testIsUrlEncodedRequest(data);
|
||||
testWritePostDataTextParams(data);
|
||||
|
||||
data = yield createCurlData(requests.multipart.attachment, gNetwork);
|
||||
data = yield createCurlData(requests.multipart, gNetwork);
|
||||
testIsMultipartRequest(data);
|
||||
testGetMultipartBoundary(data);
|
||||
testRemoveBinaryDataFromMultipartText(data);
|
||||
|
||||
data = yield createCurlData(requests.multipartForm.attachment, gNetwork);
|
||||
data = yield createCurlData(requests.multipartForm, gNetwork);
|
||||
testGetHeadersFromMultipartText(data);
|
||||
|
||||
if (Services.appinfo.OS != "WINNT") {
|
||||
|
|
|
@ -22,7 +22,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=txt", {
|
||||
status: 200,
|
||||
statusText: "DA DA DA"
|
||||
|
|
|
@ -21,7 +21,7 @@ add_task(function* () {
|
|||
tab.linkedBrowser.reload();
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CYRILLIC_URL, {
|
||||
status: 200,
|
||||
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" },
|
||||
]);
|
||||
|
||||
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* () {
|
||||
let Actions = require("devtools/client/netmonitor/actions/index");
|
||||
|
||||
let { monitor } = yield initNetMonitor(FILTERING_URL);
|
||||
let { gStore } = monitor.panelWin;
|
||||
|
||||
|
@ -180,85 +281,25 @@ add_task(function* () {
|
|||
is(NetMonitorView.detailsPaneHidden, false,
|
||||
"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.");
|
||||
is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length,
|
||||
"There should be a specific amount of visbile items in the requests menu.");
|
||||
is(visibleItems.size, visibility.filter(e => e).length,
|
||||
"There should be a specific amount of visible items in the requests menu.");
|
||||
|
||||
for (let i = 0; i < visibility.length; i++) {
|
||||
is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i],
|
||||
"The item at index " + i + " doesn't have the correct hidden state.");
|
||||
}
|
||||
let itemId = items.get(i).id;
|
||||
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),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
|
||||
fuzzyUrl: true,
|
||||
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",
|
||||
});
|
||||
if (shouldBeVisible) {
|
||||
let { method, url, data } = EXPECTED_REQUESTS[i];
|
||||
verifyRequestItemTarget(RequestsMenu, items.get(i), method, url, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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" },
|
||||
]);
|
||||
|
||||
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* () {
|
||||
let { monitor } = yield initNetMonitor(FILTERING_URL);
|
||||
info("Starting test... ");
|
||||
|
@ -98,103 +198,29 @@ add_task(function* () {
|
|||
is(NetMonitorView.detailsPaneHidden, false,
|
||||
"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.");
|
||||
is(RequestsMenu.visibleItems.length, visibility.filter(e => e).length,
|
||||
"There should be a specific amount of visbile items in the requests menu.");
|
||||
is(visibleItems.size, visibility.filter(e => e).length,
|
||||
"There should be a specific amount of visible items in the requests menu.");
|
||||
|
||||
for (let i = 0; i < visibility.length; i++) {
|
||||
is(RequestsMenu.getItemAtIndex(i).target.hidden, !visibility[i],
|
||||
"The item at index " + i + " doesn't have the correct hidden state.");
|
||||
let itemId = items.get(i).id;
|
||||
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) {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(i),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=html", {
|
||||
fuzzyUrl: true,
|
||||
status: 200,
|
||||
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"
|
||||
});
|
||||
for (let i = 0; i < EXPECTED_REQUESTS.length; i++) {
|
||||
let { method, url, data } = EXPECTED_REQUESTS[i];
|
||||
for (let j = i; j < visibility.length; j += EXPECTED_REQUESTS.length) {
|
||||
if (visibility[j]) {
|
||||
verifyRequestItemTarget(RequestsMenu, items.get(j), method, url, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -104,82 +104,6 @@ add_task(function* () {
|
|||
is(RequestsMenu.items.length, order.length,
|
||||
"There should be a specific amount of items in the requests menu.");
|
||||
is(RequestsMenu.visibleItems.length, visible,
|
||||
"There should be a specific amount of visbile 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"
|
||||
});
|
||||
}
|
||||
"There should be a specific amount of visible items in the requests menu.");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Test if the summary text displayed in the network requests menu footer
|
||||
* is correct.
|
||||
* Test if the summary text displayed in the network requests menu footer is correct.
|
||||
*/
|
||||
|
||||
add_task(function* () {
|
||||
|
@ -18,7 +17,8 @@ add_task(function* () {
|
|||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
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 { PluralForm } = winRequire("devtools/shared/plural-form");
|
||||
|
||||
|
@ -46,26 +46,27 @@ add_task(function* () {
|
|||
yield teardown(monitor);
|
||||
|
||||
function testStatus() {
|
||||
const { count, totalBytes, totalMillis } = getSummary(gStore.getState());
|
||||
let value = $("#requests-menu-network-summary-button").textContent;
|
||||
info("Current summary: " + value);
|
||||
|
||||
let totalRequestsCount = RequestsMenu.itemCount;
|
||||
info("Current requests: " + count + " of " + totalRequestsCount + ".");
|
||||
let state = gStore.getState();
|
||||
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"),
|
||||
"The current summary text is incorrect, expected an 'empty' label.");
|
||||
return;
|
||||
}
|
||||
|
||||
info("Computed total bytes: " + totalBytes);
|
||||
info("Computed total millis: " + totalMillis);
|
||||
info(`Computed total bytes: ${requestsSummary.bytes}`);
|
||||
info(`Computed total millis: ${requestsSummary.millis}`);
|
||||
|
||||
is(value, PluralForm.get(count, L10N.getStr("networkMenu.summary"))
|
||||
.replace("#1", count)
|
||||
.replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, 2))
|
||||
.replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, 2))
|
||||
, "The current summary text is incorrect.");
|
||||
is(value, PluralForm.get(requestsSummary.count, L10N.getStr("networkMenu.summary"))
|
||||
.replace("#1", requestsSummary.count)
|
||||
.replace("#2", L10N.numberWithDecimals(requestsSummary.bytes / 1024, 2))
|
||||
.replace("#3", L10N.numberWithDecimals(requestsSummary.millis / 1000, 2))
|
||||
, "The current summary text is correct.");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ const EXPECTED_REQUESTS_TOP = [
|
|||
method: "GET",
|
||||
url: TOP_URL,
|
||||
causeType: "document",
|
||||
causeUri: "",
|
||||
causeUri: null,
|
||||
stack: true
|
||||
},
|
||||
{
|
||||
|
@ -176,9 +176,8 @@ add_task(function* () {
|
|||
for (let i = 0; i < REQUEST_COUNT; i++) {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(i);
|
||||
|
||||
let itemUrl = requestItem.attachment.url;
|
||||
let itemCauseUri = requestItem.target.querySelector(".requests-menu-cause-label")
|
||||
.getAttribute("tooltiptext");
|
||||
let itemUrl = requestItem.url;
|
||||
let itemCauseUri = requestItem.cause.loadingDocumentUri;
|
||||
let spec;
|
||||
if (itemUrl == SUB_URL || itemCauseUri == SUB_URL) {
|
||||
spec = EXPECTED_REQUESTS_SUB[currentSub++];
|
||||
|
@ -187,11 +186,11 @@ add_task(function* () {
|
|||
}
|
||||
let { method, url, causeType, causeUri, stack } = spec;
|
||||
|
||||
verifyRequestItemTarget(requestItem,
|
||||
verifyRequestItemTarget(RequestsMenu, requestItem,
|
||||
method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
|
||||
);
|
||||
|
||||
let { stacktrace } = requestItem.attachment.cause;
|
||||
let { stacktrace } = requestItem.cause;
|
||||
let stackLen = stacktrace ? stacktrace.length : 0;
|
||||
|
||||
if (stack) {
|
||||
|
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
|||
info("Checking the image thumbnail when all items are shown.");
|
||||
checkImageThumbnail();
|
||||
|
||||
RequestsMenu.sortBy("size");
|
||||
gStore.dispatch(Actions.sortBy("size"));
|
||||
info("Checking the image thumbnail when all items are sorted.");
|
||||
checkImageThumbnail();
|
||||
|
||||
|
@ -61,11 +61,11 @@ add_task(function* () {
|
|||
}
|
||||
|
||||
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.");
|
||||
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.");
|
||||
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.");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -26,12 +26,12 @@ add_task(function* test() {
|
|||
yield onThumbnail;
|
||||
|
||||
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
|
||||
// the icon for the request of the next test.
|
||||
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
|
||||
onEvents = waitForNetworkEvents(monitor, IMAGE_TOOLTIP_REQUESTS + 1);
|
||||
|
@ -44,10 +44,10 @@ add_task(function* test() {
|
|||
yield onThumbnail;
|
||||
|
||||
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");
|
||||
let requestsMenuEl = $("#requests-menu-contents");
|
||||
let requestsMenuEl = $(".requests-menu-contents");
|
||||
let onHidden = RequestsMenu.tooltip.once("hidden");
|
||||
EventUtils.synthesizeMouse(requestsMenuEl, 0, 0, {type: "mouseout"}, monitor.panelWin);
|
||||
yield onHidden;
|
||||
|
@ -65,7 +65,7 @@ add_task(function* test() {
|
|||
* with the expected content.
|
||||
*/
|
||||
function* showTooltipAndVerify(tooltip, requestItem) {
|
||||
let anchor = $(".requests-menu-file", requestItem.target);
|
||||
let anchor = $(".requests-menu-file", getItemTarget(RequestsMenu, requestItem));
|
||||
yield showTooltipOn(tooltip, anchor);
|
||||
|
||||
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.
|
||||
*/
|
||||
function* hideTooltipAndVerify(tooltip, requestItem) {
|
||||
// Hovering method hides tooltip.
|
||||
let anchor = $(".requests-menu-method", requestItem.target);
|
||||
// Hovering over the "method" column hides the tooltip.
|
||||
let anchor = $(".requests-menu-method", getItemTarget(RequestsMenu, requestItem));
|
||||
|
||||
let onHidden = tooltip.once("hidden");
|
||||
let onTooltipHidden = tooltip.once("hidden");
|
||||
let win = anchor.ownerDocument.defaultView;
|
||||
EventUtils.synthesizeMouseAtCenter(anchor, {type: "mousemove"}, win);
|
||||
yield onHidden;
|
||||
yield onTooltipHidden;
|
||||
|
||||
info("Tooltip was successfully closed.");
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
|
|
@ -22,7 +22,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-malformed", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-custom-mime", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
|
|
@ -24,7 +24,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-text-mime", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
|
|
@ -25,7 +25,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
@ -34,7 +34,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 41),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp2&jsonp=$_4567Sad", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
|
|
@ -28,7 +28,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=html-long", {
|
||||
status: 200,
|
||||
statusText: "OK"
|
||||
|
|
|
@ -25,7 +25,7 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(0),
|
||||
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=urlencoded", {
|
||||
status: 200,
|
||||
statusText: "Och Aye",
|
||||
|
@ -34,7 +34,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(1),
|
||||
"POST", SIMPLE_SJS + "?foo=bar&baz=42&type=multipart", {
|
||||
status: 200,
|
||||
statusText: "Och Aye",
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
add_task(function* () {
|
||||
let Actions = require("devtools/client/netmonitor/actions/index");
|
||||
let { getActiveFilters } = require("devtools/client/netmonitor/selectors/index");
|
||||
|
||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
|
@ -19,8 +21,8 @@ add_task(function* () {
|
|||
// Use these getters instead of caching instances inside the panel win,
|
||||
// since the tool is reopened a bunch of times during this test
|
||||
// and the instances will differ.
|
||||
let getView = () => monitor.panelWin.NetMonitorView;
|
||||
let getStore = () => monitor.panelWin.gStore;
|
||||
let getState = () => getStore().getState();
|
||||
|
||||
let prefsToCheck = {
|
||||
filters: {
|
||||
|
@ -28,7 +30,7 @@ add_task(function* () {
|
|||
newValue: ["html", "css"],
|
||||
// Getter used to retrieve the current value from the frontend, in order
|
||||
// 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,
|
||||
// before trying to validate the changes.
|
||||
modifyFrontend: ($, value) => value.forEach(e =>
|
||||
|
|
|
@ -31,7 +31,7 @@ add_task(function* () {
|
|||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
document.getElementById("toggle-raw-headers"));
|
||||
|
||||
testShowRawHeaders(origItem.attachment);
|
||||
testShowRawHeaders(origItem);
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
document.getElementById("toggle-raw-headers"));
|
||||
|
@ -46,12 +46,12 @@ add_task(function* () {
|
|||
function testShowRawHeaders(data) {
|
||||
let requestHeaders = document.getElementById("raw-request-headers-textarea").value;
|
||||
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");
|
||||
}
|
||||
let responseHeaders = document.getElementById("raw-response-headers-textarea").value;
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,18 +8,18 @@
|
|||
*/
|
||||
|
||||
add_task(function* () {
|
||||
let { monitor } = yield initNetMonitor(SINGLE_GET_URL);
|
||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, NetMonitorView } = monitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
let wait = waitForNetworkEvents(monitor, 2);
|
||||
let wait = waitForNetworkEvents(monitor, 1);
|
||||
let button = document.querySelector("#requests-menu-reload-notice-button");
|
||||
button.click();
|
||||
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);
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
add_task(function* () {
|
||||
let { monitor } = yield initNetMonitor(SINGLE_GET_URL);
|
||||
let { monitor } = yield initNetMonitor(SIMPLE_URL);
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, EVENTS } = monitor.panelWin;
|
||||
|
@ -21,7 +21,7 @@ add_task(function* () {
|
|||
markers.push(marker);
|
||||
});
|
||||
|
||||
yield waitForNetworkEvents(monitor, 2);
|
||||
yield waitForNetworkEvents(monitor, 1);
|
||||
yield waitUntil(() => markers.length == 2);
|
||||
|
||||
ok(true, "Reloading finished");
|
||||
|
|
|
@ -54,7 +54,7 @@ add_task(function* () {
|
|||
return teardown(monitor);
|
||||
|
||||
function verifyRequest(offset) {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(offset),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(offset),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-long", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
|
|
|
@ -39,13 +39,15 @@ add_task(function* () {
|
|||
RequestsMenu.cloneSelectedRequest();
|
||||
yield onPopulated;
|
||||
|
||||
testCustomForm(origItem.attachment);
|
||||
testCustomForm(origItem);
|
||||
|
||||
let customItem = RequestsMenu.selectedItem;
|
||||
testCustomItem(customItem, origItem);
|
||||
|
||||
// edit the custom request
|
||||
yield editCustomForm();
|
||||
// FIXME: reread the customItem, it's been replaced by a new object (immutable!)
|
||||
customItem = RequestsMenu.selectedItem;
|
||||
testCustomItemChanged(customItem, origItem);
|
||||
|
||||
// send the new request
|
||||
|
@ -54,30 +56,20 @@ add_task(function* () {
|
|||
yield wait;
|
||||
|
||||
let sentItem = RequestsMenu.selectedItem;
|
||||
testSentRequest(sentItem.attachment, origItem.attachment);
|
||||
testSentRequest(sentItem, origItem);
|
||||
|
||||
return teardown(monitor);
|
||||
|
||||
function testCustomItem(item, orig) {
|
||||
let method = item.target.querySelector(".requests-menu-method").value;
|
||||
let origMethod = orig.target.querySelector(".requests-menu-method").value;
|
||||
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");
|
||||
is(item.method, orig.method, "item is showing the same method as original request");
|
||||
is(item.url, orig.url, "item is showing the same URL as original request");
|
||||
}
|
||||
|
||||
function testCustomItemChanged(item, orig) {
|
||||
let file = item.target.querySelector(".requests-menu-file").value;
|
||||
let expectedFile = orig.target.querySelector(".requests-menu-file").value +
|
||||
"&" + ADD_QUERY;
|
||||
let url = item.url;
|
||||
let expectedUrl = orig.url + "&" + 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
|
||||
for (let [i, method] of METHODS.entries()) {
|
||||
let { attachment } = RequestsMenu.getItemAtIndex(i);
|
||||
is(attachment.method, method, `The ${method} request has the right method`);
|
||||
is(attachment.url, requestUrl, `The ${method} request has the right URL`);
|
||||
let item = RequestsMenu.getItemAtIndex(i);
|
||||
is(item.method, method, `The ${method} request has the right method`);
|
||||
is(item.url, requestUrl, `The ${method} request has the right URL`);
|
||||
}
|
||||
|
||||
// Resend both requests without modification. Wait for resent OPTIONS, then POST.
|
||||
|
@ -61,7 +61,7 @@ add_task(function* () {
|
|||
// Check the resent requests
|
||||
for (let [i, method] of METHODS.entries()) {
|
||||
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.url, requestUrl, `The ${method} request has the right URL`);
|
||||
is(item.status, 200, `The ${method} response has the right status`);
|
||||
|
|
|
@ -35,21 +35,21 @@ add_task(function* () {
|
|||
});
|
||||
yield wait;
|
||||
|
||||
let { attachment } = RequestsMenu.getItemAtIndex(0);
|
||||
is(attachment.method, "POST", "The request has the right method");
|
||||
is(attachment.url, requestUrl, "The request has the right URL");
|
||||
let item = RequestsMenu.getItemAtIndex(0);
|
||||
is(item.method, "POST", "The request has the right method");
|
||||
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}`);
|
||||
}
|
||||
|
||||
function hasRequestHeader(name, value) {
|
||||
let { headers } = attachment.requestHeaders;
|
||||
let { headers } = item.requestHeaders;
|
||||
return headers.some(h => h.name === name && h.value === value);
|
||||
}
|
||||
|
||||
function hasNotRequestHeader(name) {
|
||||
let { headers } = attachment.requestHeaders;
|
||||
let { headers } = item.requestHeaders;
|
||||
return headers.every(h => h.name !== name);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,13 +42,13 @@ add_task(function* () {
|
|||
}
|
||||
|
||||
function* clickAndTestSecurityIcon() {
|
||||
let item = RequestsMenu.items[0];
|
||||
let icon = $(".requests-security-state-icon", item.target);
|
||||
let item = RequestsMenu.getItemAtIndex(0);
|
||||
let target = getItemTarget(RequestsMenu, item);
|
||||
let icon = $(".requests-security-state-icon", target);
|
||||
|
||||
info("Clicking security icon of the first request and waiting for the " +
|
||||
"panel to update.");
|
||||
info("Clicking security icon of the first request and waiting for panel update.");
|
||||
EventUtils.synthesizeMouseAtCenter(icon, {}, monitor.panelWin);
|
||||
|
||||
icon.click();
|
||||
yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
|
||||
|
||||
is(NetworkDetails.widget.selectedPanel, $("#security-tabpanel"),
|
||||
|
|
|
@ -22,11 +22,13 @@ add_task(function* () {
|
|||
|
||||
is(RequestsMenu.itemCount, 2, "There were two requests due to redirect.");
|
||||
|
||||
let initial = RequestsMenu.items[0];
|
||||
let redirect = RequestsMenu.items[1];
|
||||
let initial = RequestsMenu.getItemAtIndex(0);
|
||||
let redirect = RequestsMenu.getItemAtIndex(1);
|
||||
|
||||
let initialSecurityIcon = $(".requests-security-state-icon", initial.target);
|
||||
let redirectSecurityIcon = $(".requests-security-state-icon", redirect.target);
|
||||
let initialSecurityIcon =
|
||||
$(".requests-security-state-icon", getItemTarget(RequestsMenu, initial));
|
||||
let redirectSecurityIcon =
|
||||
$(".requests-security-state-icon", getItemTarget(RequestsMenu, redirect));
|
||||
|
||||
ok(initialSecurityIcon.classList.contains("security-state-insecure"),
|
||||
"Initial request was marked insecure.");
|
||||
|
|
|
@ -24,12 +24,13 @@ add_task(function* () {
|
|||
yield performRequests();
|
||||
|
||||
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);
|
||||
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];
|
||||
|
||||
info("Classes of security state icon are: " + classes);
|
||||
|
|
|
@ -58,7 +58,7 @@ add_task(function* () {
|
|||
info("Selecting the request.");
|
||||
RequestsMenu.selectedIndex = 0;
|
||||
|
||||
is(RequestsMenu.selectedItem.attachment.securityState, undefined,
|
||||
is(RequestsMenu.selectedItem.securityState, undefined,
|
||||
"Security state has not yet arrived.");
|
||||
is(tabEl.hidden, !testcase.visibleOnNewEvent,
|
||||
"Security tab is " +
|
||||
|
@ -70,7 +70,7 @@ add_task(function* () {
|
|||
info("Waiting for security information to arrive.");
|
||||
yield onSecurityInfo;
|
||||
|
||||
ok(RequestsMenu.selectedItem.attachment.securityState,
|
||||
ok(RequestsMenu.selectedItem.securityState,
|
||||
"Security state arrived.");
|
||||
is(tabEl.hidden, !testcase.visibleOnSecurityInfo,
|
||||
"Security tab is " +
|
||||
|
|
|
@ -26,8 +26,8 @@ add_task(function* () {
|
|||
|
||||
is(RequestsMenu.itemCount, 1, "Only the reload should be recorded.");
|
||||
let request = RequestsMenu.getItemAtIndex(0);
|
||||
is(request.attachment.method, "GET", "The method is correct.");
|
||||
is(request.attachment.status, "200", "The status is correct.");
|
||||
is(request.method, "GET", "The method is correct.");
|
||||
is(request.status, "200", "The status is correct.");
|
||||
|
||||
yield removeTab(beaconTab);
|
||||
return teardown(monitor);
|
||||
|
|
|
@ -23,9 +23,9 @@ add_task(function* () {
|
|||
|
||||
is(RequestsMenu.itemCount, 1, "The beacon should be recorded.");
|
||||
let request = RequestsMenu.getItemAtIndex(0);
|
||||
is(request.attachment.method, "POST", "The method is correct.");
|
||||
ok(request.attachment.url.endsWith("beacon_request"), "The URL is correct.");
|
||||
is(request.attachment.status, "404", "The status is correct.");
|
||||
is(request.method, "POST", "The method is correct.");
|
||||
ok(request.url.endsWith("beacon_request"), "The URL is correct.");
|
||||
is(request.status, "404", "The status is correct.");
|
||||
|
||||
return teardown(monitor);
|
||||
});
|
||||
|
|
|
@ -51,9 +51,10 @@ add_task(function* () {
|
|||
let item = RequestsMenu.getItemAtIndex(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;
|
||||
|
||||
ok(stacktrace, `Request #${index} has a stacktrace`);
|
||||
|
|
|
@ -32,69 +32,63 @@ function test() {
|
|||
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
is(typeof requestItem.value, "string",
|
||||
is(typeof requestItem.id, "string",
|
||||
"The attached request id is incorrect.");
|
||||
isnot(requestItem.value, "",
|
||||
isnot(requestItem.id, "",
|
||||
"The attached request id should not be empty.");
|
||||
|
||||
is(typeof requestItem.attachment.startedDeltaMillis, "number",
|
||||
"The attached startedDeltaMillis is incorrect.");
|
||||
is(requestItem.attachment.startedDeltaMillis, 0,
|
||||
"The attached startedDeltaMillis should be zero.");
|
||||
|
||||
is(typeof requestItem.attachment.startedMillis, "number",
|
||||
is(typeof requestItem.startedMillis, "number",
|
||||
"The attached startedMillis is incorrect.");
|
||||
isnot(requestItem.attachment.startedMillis, 0,
|
||||
isnot(requestItem.startedMillis, 0,
|
||||
"The attached startedMillis should not be zero.");
|
||||
|
||||
is(requestItem.attachment.requestHeaders, undefined,
|
||||
is(requestItem.requestHeaders, undefined,
|
||||
"The requestHeaders should not yet be set.");
|
||||
is(requestItem.attachment.requestCookies, undefined,
|
||||
is(requestItem.requestCookies, undefined,
|
||||
"The requestCookies should not yet be set.");
|
||||
is(requestItem.attachment.requestPostData, undefined,
|
||||
is(requestItem.requestPostData, undefined,
|
||||
"The requestPostData should not yet be set.");
|
||||
|
||||
is(requestItem.attachment.responseHeaders, undefined,
|
||||
is(requestItem.responseHeaders, undefined,
|
||||
"The responseHeaders should not yet be set.");
|
||||
is(requestItem.attachment.responseCookies, undefined,
|
||||
is(requestItem.responseCookies, undefined,
|
||||
"The responseCookies should not yet be set.");
|
||||
|
||||
is(requestItem.attachment.httpVersion, undefined,
|
||||
is(requestItem.httpVersion, undefined,
|
||||
"The httpVersion should not yet be set.");
|
||||
is(requestItem.attachment.status, undefined,
|
||||
is(requestItem.status, undefined,
|
||||
"The status should not yet be set.");
|
||||
is(requestItem.attachment.statusText, undefined,
|
||||
is(requestItem.statusText, undefined,
|
||||
"The statusText should not yet be set.");
|
||||
|
||||
is(requestItem.attachment.headersSize, undefined,
|
||||
is(requestItem.headersSize, undefined,
|
||||
"The headersSize should not yet be set.");
|
||||
is(requestItem.attachment.transferredSize, undefined,
|
||||
is(requestItem.transferredSize, undefined,
|
||||
"The transferredSize should not yet be set.");
|
||||
is(requestItem.attachment.contentSize, undefined,
|
||||
is(requestItem.contentSize, undefined,
|
||||
"The contentSize should not yet be set.");
|
||||
|
||||
is(requestItem.attachment.mimeType, undefined,
|
||||
is(requestItem.mimeType, undefined,
|
||||
"The mimeType should not yet be set.");
|
||||
is(requestItem.attachment.responseContent, undefined,
|
||||
is(requestItem.responseContent, undefined,
|
||||
"The responseContent should not yet be set.");
|
||||
|
||||
is(requestItem.attachment.totalTime, undefined,
|
||||
is(requestItem.totalTime, undefined,
|
||||
"The totalTime should not yet be set.");
|
||||
is(requestItem.attachment.eventTimings, undefined,
|
||||
is(requestItem.eventTimings, undefined,
|
||||
"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, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
ok(requestItem.attachment.requestHeaders,
|
||||
"There should be a requestHeaders attachment available.");
|
||||
is(requestItem.attachment.requestHeaders.headers.length, 9,
|
||||
"The requestHeaders attachment has an incorrect |headers| property.");
|
||||
isnot(requestItem.attachment.requestHeaders.headersSize, 0,
|
||||
"The requestHeaders attachment has an incorrect |headersSize| property.");
|
||||
ok(requestItem.requestHeaders,
|
||||
"There should be a requestHeaders data available.");
|
||||
is(requestItem.requestHeaders.headers.length, 10,
|
||||
"The requestHeaders data has an incorrect |headers| property.");
|
||||
isnot(requestItem.requestHeaders.headersSize, 0,
|
||||
"The requestHeaders data has an incorrect |headersSize| property.");
|
||||
// Can't test for the exact request headers size because the value may
|
||||
// vary across platforms ("User-Agent" header differs).
|
||||
|
||||
|
@ -104,12 +98,12 @@ function test() {
|
|||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_REQUEST_COOKIES, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
ok(requestItem.attachment.requestCookies,
|
||||
"There should be a requestCookies attachment available.");
|
||||
is(requestItem.attachment.requestCookies.cookies.length, 2,
|
||||
"The requestCookies attachment has an incorrect |cookies| property.");
|
||||
ok(requestItem.requestCookies,
|
||||
"There should be a requestCookies data available.");
|
||||
is(requestItem.requestCookies.cookies.length, 2,
|
||||
"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, () => {
|
||||
|
@ -119,40 +113,40 @@ function test() {
|
|||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_HEADERS, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
ok(requestItem.attachment.responseHeaders,
|
||||
"There should be a responseHeaders attachment available.");
|
||||
is(requestItem.attachment.responseHeaders.headers.length, 10,
|
||||
"The responseHeaders attachment has an incorrect |headers| property.");
|
||||
is(requestItem.attachment.responseHeaders.headersSize, 330,
|
||||
"The responseHeaders attachment has an incorrect |headersSize| property.");
|
||||
ok(requestItem.responseHeaders,
|
||||
"There should be a responseHeaders data available.");
|
||||
is(requestItem.responseHeaders.headers.length, 10,
|
||||
"The responseHeaders data has an incorrect |headers| property.");
|
||||
is(requestItem.responseHeaders.headersSize, 330,
|
||||
"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, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
ok(requestItem.attachment.responseCookies,
|
||||
"There should be a responseCookies attachment available.");
|
||||
is(requestItem.attachment.responseCookies.cookies.length, 2,
|
||||
"The responseCookies attachment has an incorrect |cookies| property.");
|
||||
ok(requestItem.responseCookies,
|
||||
"There should be a responseCookies data available.");
|
||||
is(requestItem.responseCookies.cookies.length, 2,
|
||||
"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, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
is(requestItem.attachment.httpVersion, "HTTP/1.1",
|
||||
"The httpVersion attachment has an incorrect value.");
|
||||
is(requestItem.attachment.status, "200",
|
||||
"The status attachment has an incorrect value.");
|
||||
is(requestItem.attachment.statusText, "Och Aye",
|
||||
"The statusText attachment has an incorrect value.");
|
||||
is(requestItem.attachment.headersSize, 330,
|
||||
"The headersSize attachment has an incorrect value.");
|
||||
is(requestItem.httpVersion, "HTTP/1.1",
|
||||
"The httpVersion data has an incorrect value.");
|
||||
is(requestItem.status, "200",
|
||||
"The status data has an incorrect value.");
|
||||
is(requestItem.statusText, "Och Aye",
|
||||
"The statusText data has an incorrect value.");
|
||||
is(requestItem.headersSize, 330,
|
||||
"The headersSize data has an incorrect value.");
|
||||
|
||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
||||
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||
status: "200",
|
||||
statusText: "Och Aye"
|
||||
});
|
||||
|
@ -161,58 +155,53 @@ function test() {
|
|||
monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_RESPONSE_CONTENT, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
is(requestItem.attachment.transferredSize, "12",
|
||||
"The transferredSize attachment has an incorrect value.");
|
||||
is(requestItem.attachment.contentSize, "12",
|
||||
"The contentSize attachment has an incorrect value.");
|
||||
is(requestItem.attachment.mimeType, "text/plain; charset=utf-8",
|
||||
"The mimeType attachment has an incorrect value.");
|
||||
is(requestItem.transferredSize, "12",
|
||||
"The transferredSize data has an incorrect value.");
|
||||
is(requestItem.contentSize, "12",
|
||||
"The contentSize data has an incorrect value.");
|
||||
is(requestItem.mimeType, "text/plain; charset=utf-8",
|
||||
"The mimeType data has an incorrect value.");
|
||||
|
||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
||||
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||
type: "plain",
|
||||
fullMimeType: "text/plain; charset=utf-8",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||
});
|
||||
});
|
||||
|
||||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_RESPONSE_CONTENT, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
ok(requestItem.attachment.responseContent,
|
||||
"There should be a responseContent attachment available.");
|
||||
is(requestItem.attachment.responseContent.content.mimeType,
|
||||
ok(requestItem.responseContent,
|
||||
"There should be a responseContent data available.");
|
||||
is(requestItem.responseContent.content.mimeType,
|
||||
"text/plain; charset=utf-8",
|
||||
"The responseContent attachment has an incorrect |content.mimeType| property.");
|
||||
is(requestItem.attachment.responseContent.content.text,
|
||||
"The responseContent data has an incorrect |content.mimeType| property.");
|
||||
is(requestItem.responseContent.content.text,
|
||||
"Hello world!",
|
||||
"The responseContent attachment has an incorrect |content.text| property.");
|
||||
is(requestItem.attachment.responseContent.content.size,
|
||||
"The responseContent data has an incorrect |content.text| property.");
|
||||
is(requestItem.responseContent.content.size,
|
||||
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",
|
||||
fullMimeType: "text/plain; charset=utf-8",
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.01),
|
||||
transferred: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 12),
|
||||
});
|
||||
});
|
||||
|
||||
monitor.panelWin.once(monitor.panelWin.EVENTS.UPDATING_EVENT_TIMINGS, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
is(typeof requestItem.attachment.totalTime, "number",
|
||||
is(typeof requestItem.totalTime, "number",
|
||||
"The attached totalTime is incorrect.");
|
||||
ok(requestItem.attachment.totalTime >= 0,
|
||||
ok(requestItem.totalTime >= 0,
|
||||
"The attached totalTime should be positive.");
|
||||
|
||||
is(typeof requestItem.attachment.endedMillis, "number",
|
||||
"The attached endedMillis is incorrect.");
|
||||
ok(requestItem.attachment.endedMillis >= 0,
|
||||
"The attached endedMillis should be positive.");
|
||||
|
||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
||||
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||
time: true
|
||||
});
|
||||
});
|
||||
|
@ -220,24 +209,24 @@ function test() {
|
|||
monitor.panelWin.once(monitor.panelWin.EVENTS.RECEIVED_EVENT_TIMINGS, () => {
|
||||
let requestItem = RequestsMenu.getItemAtIndex(0);
|
||||
|
||||
ok(requestItem.attachment.eventTimings,
|
||||
"There should be a eventTimings attachment available.");
|
||||
is(typeof requestItem.attachment.eventTimings.timings.blocked, "number",
|
||||
"The eventTimings attachment has an incorrect |timings.blocked| property.");
|
||||
is(typeof requestItem.attachment.eventTimings.timings.dns, "number",
|
||||
"The eventTimings attachment has an incorrect |timings.dns| property.");
|
||||
is(typeof requestItem.attachment.eventTimings.timings.connect, "number",
|
||||
"The eventTimings attachment has an incorrect |timings.connect| property.");
|
||||
is(typeof requestItem.attachment.eventTimings.timings.send, "number",
|
||||
"The eventTimings attachment has an incorrect |timings.send| property.");
|
||||
is(typeof requestItem.attachment.eventTimings.timings.wait, "number",
|
||||
"The eventTimings attachment has an incorrect |timings.wait| property.");
|
||||
is(typeof requestItem.attachment.eventTimings.timings.receive, "number",
|
||||
"The eventTimings attachment has an incorrect |timings.receive| property.");
|
||||
is(typeof requestItem.attachment.eventTimings.totalTime, "number",
|
||||
"The eventTimings attachment has an incorrect |totalTime| property.");
|
||||
ok(requestItem.eventTimings,
|
||||
"There should be a eventTimings data available.");
|
||||
is(typeof requestItem.eventTimings.timings.blocked, "number",
|
||||
"The eventTimings data has an incorrect |timings.blocked| property.");
|
||||
is(typeof requestItem.eventTimings.timings.dns, "number",
|
||||
"The eventTimings data has an incorrect |timings.dns| property.");
|
||||
is(typeof requestItem.eventTimings.timings.connect, "number",
|
||||
"The eventTimings data has an incorrect |timings.connect| property.");
|
||||
is(typeof requestItem.eventTimings.timings.send, "number",
|
||||
"The eventTimings data has an incorrect |timings.send| property.");
|
||||
is(typeof requestItem.eventTimings.timings.wait, "number",
|
||||
"The eventTimings data has an incorrect |timings.wait| property.");
|
||||
is(typeof requestItem.eventTimings.timings.receive, "number",
|
||||
"The eventTimings data has an incorrect |timings.receive| property.");
|
||||
is(typeof requestItem.eventTimings.totalTime, "number",
|
||||
"The eventTimings data has an incorrect |totalTime| property.");
|
||||
|
||||
verifyRequestItemTarget(requestItem, "GET", SIMPLE_SJS, {
|
||||
verifyRequestItemTarget(RequestsMenu, requestItem, "GET", SIMPLE_SJS, {
|
||||
time: true
|
||||
});
|
||||
});
|
||||
|
|
|
@ -62,7 +62,7 @@ add_task(function* () {
|
|||
"GET", "The method summary value is incorrect.");
|
||||
is(tabpanel.querySelector("#headers-summary-address-value").getAttribute("value"),
|
||||
"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.");
|
||||
is(tabpanel.querySelector("#headers-summary-status-value").getAttribute("value"),
|
||||
"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,
|
||||
"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.");
|
||||
is(RequestsMenu.itemCount, 0,
|
||||
"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,
|
||||
"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.");
|
||||
is(RequestsMenu.itemCount, 1,
|
||||
"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,
|
||||
"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.");
|
||||
is(RequestsMenu.itemCount, 1,
|
||||
"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,
|
||||
"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.");
|
||||
is(RequestsMenu.itemCount, 0,
|
||||
"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.");
|
||||
is(RequestsMenu.visibleItems.length, 5,
|
||||
"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!");
|
||||
|
||||
is(RequestsMenu.getItemAtIndex(0), RequestsMenu.items[0],
|
||||
"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),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(a),
|
||||
"GET", STATUS_CODES_SJS + "?sts=100", {
|
||||
status: 101,
|
||||
statusText: "Switching Protocols",
|
||||
|
@ -186,7 +175,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(b),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(b),
|
||||
"GET", STATUS_CODES_SJS + "?sts=200", {
|
||||
status: 202,
|
||||
statusText: "Created",
|
||||
|
@ -196,7 +185,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(c),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(c),
|
||||
"GET", STATUS_CODES_SJS + "?sts=300", {
|
||||
status: 303,
|
||||
statusText: "See Other",
|
||||
|
@ -206,7 +195,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 0),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(d),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(d),
|
||||
"GET", STATUS_CODES_SJS + "?sts=400", {
|
||||
status: 404,
|
||||
statusText: "Not Found",
|
||||
|
@ -216,7 +205,7 @@ add_task(function* () {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeB", 22),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(e),
|
||||
verifyRequestItemTarget(RequestsMenu, RequestsMenu.getItemAtIndex(e),
|
||||
"GET", STATUS_CODES_SJS + "?sts=500", {
|
||||
status: 501,
|
||||
statusText: "Not Implemented",
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче