--- a/devtools/client/netmonitor/src/components/request-list-column-cause.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-cause.js
@@ -12,43 +12,43 @@ const {
const { div } = DOM;
const RequestListColumnCause = createClass({
displayName: "RequestListColumnCause",
propTypes: {
item: PropTypes.object.isRequired,
- onCauseBadgeClick: PropTypes.func.isRequired,
+ onCauseBadgeMouseDown: PropTypes.func.isRequired,
},
shouldComponentUpdate(nextProps) {
return this.props.item.cause !== nextProps.item.cause;
},
render() {
let {
item: { cause },
- onCauseBadgeClick,
+ onCauseBadgeMouseDown,
} = this.props;
let causeType = "unknown";
let causeHasStack = false;
if (cause) {
// Legacy server might send a numeric value. Display it as "unknown"
causeType = typeof cause.type === "string" ? cause.type : "unknown";
causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
}
return (
div({ className: "requests-list-column requests-list-cause", title: causeType },
causeHasStack && div({
className: "requests-list-cause-stack",
- onClick: onCauseBadgeClick,
+ onMouseDown: onCauseBadgeMouseDown,
}, "JS"),
causeType
)
);
}
});
module.exports = RequestListColumnCause;
--- a/devtools/client/netmonitor/src/components/request-list-column-domain.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-domain.js
@@ -21,25 +21,25 @@ const UPDATED_DOMAIN_PROPS = [
"urlDetails",
];
const RequestListColumnDomain = createClass({
displayName: "RequestListColumnDomain",
propTypes: {
item: PropTypes.object.isRequired,
- onSecurityIconClick: PropTypes.func.isRequired,
+ onSecurityIconMouseDown: PropTypes.func.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
},
render() {
- let { item, onSecurityIconClick } = this.props;
+ let { item, onSecurityIconMouseDown } = this.props;
let { remoteAddress, remotePort, securityState,
urlDetails: { host, isLocal } } = item;
let iconClassList = ["requests-security-state-icon"];
let iconTitle;
let title = host + (remoteAddress ?
` (${getFormattedIPAndPort(remoteAddress, remotePort)})` : "");
if (isLocal) {
@@ -49,17 +49,17 @@ const RequestListColumnDomain = createCl
iconClassList.push(`security-state-${securityState}`);
iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
}
return (
div({ className: "requests-list-column requests-list-domain", title },
div({
className: iconClassList.join(" "),
- onMouseDown: onSecurityIconClick,
+ onMouseDown: onSecurityIconMouseDown,
title: iconTitle,
}),
host,
)
);
}
});
--- a/devtools/client/netmonitor/src/components/request-list-column-file.js
+++ b/devtools/client/netmonitor/src/components/request-list-column-file.js
@@ -18,33 +18,38 @@ const UPDATED_FILE_PROPS = [
"urlDetails",
];
const RequestListColumnFile = createClass({
displayName: "RequestListColumnFile",
propTypes: {
item: PropTypes.object.isRequired,
+ onThumbnailMouseDown: PropTypes.func.isRequired,
},
shouldComponentUpdate(nextProps) {
return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
},
render() {
- let { responseContentDataUri, urlDetails } = this.props.item;
+ let {
+ item: { responseContentDataUri, urlDetails },
+ onThumbnailMouseDown
+ } = this.props;
return (
div({
className: "requests-list-column requests-list-file",
title: urlDetails.unicodeUrl,
},
img({
className: "requests-list-icon",
src: responseContentDataUri,
+ onMouseDown: onThumbnailMouseDown,
}),
urlDetails.baseNameWithQuery
)
);
}
});
module.exports = RequestListColumnFile;
--- a/devtools/client/netmonitor/src/components/request-list-content.js
+++ b/devtools/client/netmonitor/src/components/request-list-content.js
@@ -35,20 +35,21 @@ const RequestListContent = createClass({
displayName: "RequestListContent",
propTypes: {
columns: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
displayedRequests: PropTypes.object.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
fromCache: PropTypes.bool,
- onCauseBadgeClick: PropTypes.func.isRequired,
+ onCauseBadgeMouseDown: PropTypes.func.isRequired,
onItemMouseDown: PropTypes.func.isRequired,
- onSecurityIconClick: PropTypes.func.isRequired,
+ onSecurityIconMouseDown: PropTypes.func.isRequired,
onSelectDelta: PropTypes.func.isRequired,
+ onThumbnailMouseDown: PropTypes.func.isRequired,
scale: PropTypes.number,
selectedRequestId: PropTypes.string,
},
componentWillMount() {
const { dispatch } = this.props;
this.contextMenu = new RequestListContextMenu({
cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
@@ -219,19 +220,20 @@ const RequestListContent = createClass({
this.shouldScrollBottom = false;
},
render() {
const {
columns,
displayedRequests,
firstRequestStartedMillis,
- onCauseBadgeClick,
+ onCauseBadgeMouseDown,
onItemMouseDown,
- onSecurityIconClick,
+ onSecurityIconMouseDown,
+ onThumbnailMouseDown,
selectedRequestId,
} = this.props;
return (
div({ className: "requests-list-wrapper"},
div({ className: "requests-list-table"},
div({
ref: "contentEl",
@@ -245,18 +247,19 @@ const RequestListContent = createClass({
columns,
item,
index,
isSelected: item.id === selectedRequestId,
key: item.id,
onContextMenu: this.onContextMenu,
onFocusedNodeChange: this.onFocusedNodeChange,
onMouseDown: () => onItemMouseDown(item.id),
- onCauseBadgeClick: () => onCauseBadgeClick(item.cause),
- onSecurityIconClick: () => onSecurityIconClick(item.securityState),
+ onCauseBadgeMouseDown: () => onCauseBadgeMouseDown(item.cause),
+ onSecurityIconMouseDown: () => onSecurityIconMouseDown(item.securityState),
+ onThumbnailMouseDown: () => onThumbnailMouseDown(),
}))
)
)
)
);
},
});
@@ -268,26 +271,33 @@ module.exports = connect(
selectedRequestId: state.requests.selectedId,
scale: getWaterfallScale(state),
}),
(dispatch) => ({
dispatch,
/**
* A handler that opens the stack trace tab when a stack trace is available
*/
- onCauseBadgeClick: (cause) => {
+ onCauseBadgeMouseDown: (cause) => {
if (cause.stacktrace && cause.stacktrace.length > 0) {
dispatch(Actions.selectDetailsPanelTab("stack-trace"));
}
},
onItemMouseDown: (id) => dispatch(Actions.selectRequest(id)),
/**
* A handler that opens the security tab in the details view if secure or
* broken security indicator is clicked.
*/
- onSecurityIconClick: (securityState) => {
+ onSecurityIconMouseDown: (securityState) => {
if (securityState && securityState !== "insecure") {
dispatch(Actions.selectDetailsPanelTab("security"));
}
},
onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
+ /**
+ * A handler that opens the response tab in the details view if
+ * the thumbnail is clicked.
+ */
+ onThumbnailMouseDown: () => {
+ dispatch(Actions.selectDetailsPanelTab("response"));
+ },
}),
)(RequestListContent);
--- a/devtools/client/netmonitor/src/components/request-list-item.js
+++ b/devtools/client/netmonitor/src/components/request-list-item.js
@@ -71,21 +71,22 @@ const RequestListItem = createClass({
propTypes: {
columns: PropTypes.object.isRequired,
item: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
isSelected: PropTypes.bool.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
fromCache: PropTypes.bool,
- onCauseBadgeClick: PropTypes.func.isRequired,
+ onCauseBadgeMouseDown: PropTypes.func.isRequired,
onContextMenu: PropTypes.func.isRequired,
onFocusedNodeChange: PropTypes.func,
onMouseDown: PropTypes.func.isRequired,
- onSecurityIconClick: PropTypes.func.isRequired,
+ onSecurityIconMouseDown: PropTypes.func.isRequired,
+ onThumbnailMouseDown: PropTypes.func.isRequired,
waterfallWidth: PropTypes.number,
},
componentDidMount() {
if (this.props.isSelected) {
this.refs.listItem.focus();
}
},
@@ -110,18 +111,19 @@ const RequestListItem = createClass({
columns,
item,
index,
isSelected,
firstRequestStartedMillis,
fromCache,
onContextMenu,
onMouseDown,
- onCauseBadgeClick,
- onSecurityIconClick,
+ onCauseBadgeMouseDown,
+ onSecurityIconMouseDown,
+ onThumbnailMouseDown,
} = this.props;
let classList = ["request-list-item", index % 2 ? "odd" : "even"];
isSelected && classList.push("selected");
fromCache && classList.push("fromCache");
return (
div({
@@ -129,22 +131,22 @@ const RequestListItem = createClass({
className: classList.join(" "),
"data-id": item.id,
tabIndex: 0,
onContextMenu,
onMouseDown,
},
columns.get("status") && RequestListColumnStatus({ item }),
columns.get("method") && RequestListColumnMethod({ item }),
- columns.get("file") && RequestListColumnFile({ item }),
+ columns.get("file") && RequestListColumnFile({ item, onThumbnailMouseDown }),
columns.get("protocol") && RequestListColumnProtocol({ item }),
columns.get("scheme") && RequestListColumnScheme({ item }),
- columns.get("domain") && RequestListColumnDomain({ item, onSecurityIconClick }),
+ columns.get("domain") && RequestListColumnDomain({ item, onSecurityIconMouseDown }),
columns.get("remoteip") && RequestListColumnRemoteIP({ item }),
- columns.get("cause") && RequestListColumnCause({ item, onCauseBadgeClick }),
+ columns.get("cause") && RequestListColumnCause({ item, onCauseBadgeMouseDown }),
columns.get("type") && RequestListColumnType({ item }),
columns.get("cookies") && RequestListColumnCookies({ item }),
columns.get("setCookies") && RequestListColumnSetCookies({ item }),
columns.get("transferred") && RequestListColumnTransferredSize({ item }),
columns.get("contentSize") && RequestListColumnContentSize({ item }),
columns.get("waterfall") &&
RequestListColumnWaterfall({ item, firstRequestStartedMillis }),
)
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -154,13 +154,14 @@ skip-if = true # Bug 1258809
[browser_net_simple-request.js]
[browser_net_sort-01.js]
[browser_net_sort-02.js]
[browser_net_statistics-01.js]
[browser_net_statistics-02.js]
[browser_net_status-codes.js]
[browser_net_streaming-response.js]
[browser_net_throttle.js]
+[browser_net_thumbnail-click.js]
[browser_net_timeline_ticks.js]
skip-if = true # TODO: fix the test
[browser_net_timing-division.js]
[browser_net_truncate.js]
[browser_net_persistent_logs.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_thumbnail-click.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Test that clicking on the file thumbnail opens the response details tab.
+ */
+
+add_task(function* () {
+ let { tab, monitor } = yield initNetMonitor(CONTENT_TYPE_WITHOUT_CACHE_URL);
+ let { document } = monitor.panelWin;
+
+ yield performRequestsAndWait();
+
+ let wait = waitForDOM(document, "#response-panel");
+
+ let request = document.querySelectorAll(".request-list-item")[5];
+ let icon = request.querySelector(".requests-list-icon");
+
+ info("Clicking thumbnail of the sixth request.");
+ EventUtils.synthesizeMouseAtCenter(icon, {}, monitor.panelWin);
+
+ yield wait;
+
+ ok(document.querySelector("#response-tab[aria-selected=true]"),
+ "Response tab is selected.");
+ ok(document.querySelector(".response-image-box"),
+ "Response image preview is shown.");
+
+ yield teardown(monitor);
+
+ function* performRequestsAndWait() {
+ let onAllEvents = waitForNetworkEvents(monitor, CONTENT_TYPE_WITHOUT_CACHE_REQUESTS);
+ yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
+ content.wrappedJSObject.performRequests();
+ });
+ yield onAllEvents;
+ }
+});