TabboxPanel.js (8422B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { 8 Component, 9 createFactory, 10 } = require("resource://devtools/client/shared/vendor/react.mjs"); 11 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 12 const { 13 connect, 14 } = require("resource://devtools/client/shared/vendor/react-redux.js"); 15 const { 16 L10N, 17 } = require("resource://devtools/client/netmonitor/src/utils/l10n.js"); 18 const { 19 PANELS, 20 } = require("resource://devtools/client/netmonitor/src/constants.js"); 21 const { 22 getOverriddenUrl, 23 } = require("resource://devtools/client/netmonitor/src/selectors/index.js"); 24 25 // Components 26 const Tabbar = createFactory( 27 require("resource://devtools/client/shared/components/tabs/TabBar.js") 28 ); 29 const TabPanel = createFactory( 30 ChromeUtils.importESModule( 31 "resource://devtools/client/shared/components/tabs/Tabs.mjs" 32 ).TabPanel 33 ); 34 const CookiesPanel = createFactory( 35 require("resource://devtools/client/netmonitor/src/components/request-details/CookiesPanel.js") 36 ); 37 const HeadersPanel = createFactory( 38 require("resource://devtools/client/netmonitor/src/components/request-details/HeadersPanel.js") 39 ); 40 const RequestPanel = createFactory( 41 require("resource://devtools/client/netmonitor/src/components/request-details/RequestPanel.js") 42 ); 43 const CachePanel = createFactory( 44 require("resource://devtools/client/netmonitor/src/components/request-details/CachePanel.js") 45 ); 46 const ResponsePanel = createFactory( 47 require("resource://devtools/client/netmonitor/src/components/request-details/ResponsePanel.js") 48 ); 49 const SecurityPanel = createFactory( 50 require("resource://devtools/client/netmonitor/src/components/request-details/SecurityPanel.js") 51 ); 52 const StackTracePanel = createFactory( 53 require("resource://devtools/client/netmonitor/src/components/request-details/StackTracePanel.js") 54 ); 55 const TimingsPanel = createFactory( 56 require("resource://devtools/client/netmonitor/src/components/request-details/TimingsPanel.js") 57 ); 58 59 const COLLAPSE_DETAILS_PANE = L10N.getStr("collapseDetailsPane"); 60 const ALL_TABS_MENU_BUTTON_TOOLTIP = L10N.getStr("allTabsMenuButton.tooltip"); 61 const CACHE_TITLE = L10N.getStr("netmonitor.tab.cache"); 62 const COOKIES_TITLE = L10N.getStr("netmonitor.tab.cookies"); 63 const HEADERS_TITLE = L10N.getStr("netmonitor.tab.headers"); 64 const REQUEST_TITLE = L10N.getStr("netmonitor.tab.request"); 65 const RESPONSE_TITLE = L10N.getStr("netmonitor.tab.response"); 66 const SECURITY_TITLE = L10N.getStr("netmonitor.tab.security"); 67 const STACK_TRACE_TITLE = L10N.getStr("netmonitor.tab.stackTrace"); 68 const TIMINGS_TITLE = L10N.getStr("netmonitor.tab.timings"); 69 70 /** 71 * Tabbox panel component 72 * Display the network request details 73 */ 74 class TabboxPanel extends Component { 75 static get propTypes() { 76 return { 77 activeTabId: PropTypes.string, 78 cloneSelectedRequest: PropTypes.func, 79 connector: PropTypes.object.isRequired, 80 openLink: PropTypes.func, 81 request: PropTypes.object, 82 selectTab: PropTypes.func.isRequired, 83 sourceMapURLService: PropTypes.object, 84 hideToggleButton: PropTypes.bool, 85 toggleNetworkDetails: PropTypes.func, 86 openNetworkDetails: PropTypes.func.isRequired, 87 showMessagesView: PropTypes.bool, 88 targetSearchResult: PropTypes.object, 89 defaultRawResponse: PropTypes.bool, 90 setDefaultRawResponse: PropTypes.func, 91 isOverridden: PropTypes.bool, 92 overriddenUrl: PropTypes.string, 93 }; 94 } 95 static get defaultProps() { 96 return { 97 showMessagesView: true, 98 }; 99 } 100 componentDidMount() { 101 this.closeOnEscRef = this.closeOnEsc.bind(this); 102 window.addEventListener("keydown", this.closeOnEscRef); 103 } 104 105 componentWillUnmount() { 106 window.removeEventListener("keydown", this.closeOnEscRef); 107 } 108 109 closeOnEsc(event) { 110 if (event.key == "Escape") { 111 event.preventDefault(); 112 // Don't take focus when the keyboard shortcut is triggered in a CodeMirror instance, 113 // so the CodeMirror search UI is closed. 114 if (event.target.closest(".cm-search")) { 115 return; 116 } 117 this.props.openNetworkDetails(false); 118 } 119 } 120 121 render() { 122 const { 123 activeTabId, 124 cloneSelectedRequest = () => {}, 125 connector, 126 hideToggleButton, 127 openLink, 128 defaultRawResponse, 129 isOverridden, 130 overriddenUrl, 131 request, 132 selectTab, 133 setDefaultRawResponse, 134 sourceMapURLService, 135 toggleNetworkDetails, 136 targetSearchResult, 137 } = this.props; 138 139 if (!request) { 140 return null; 141 } 142 143 const isWs = request.cause.type === "websocket"; 144 const isSse = request.isEventStream; 145 146 const showMessagesView = (isWs || isSse) && this.props.showMessagesView; 147 return Tabbar( 148 { 149 activeTabId, 150 menuDocument: window.parent.document, 151 onSelect: selectTab, 152 renderOnlySelected: true, 153 showAllTabsMenu: true, 154 allTabsMenuButtonTooltip: ALL_TABS_MENU_BUTTON_TOOLTIP, 155 sidebarToggleButton: hideToggleButton 156 ? null 157 : { 158 collapsed: false, 159 collapsePaneTitle: COLLAPSE_DETAILS_PANE, 160 expandPaneTitle: "", 161 onClick: toggleNetworkDetails, 162 }, 163 }, 164 TabPanel( 165 { 166 id: PANELS.HEADERS, 167 title: HEADERS_TITLE, 168 className: "panel-with-code", 169 }, 170 HeadersPanel({ 171 cloneSelectedRequest, 172 connector, 173 openLink, 174 request, 175 targetSearchResult, 176 }) 177 ), 178 TabPanel( 179 { 180 id: PANELS.COOKIES, 181 title: COOKIES_TITLE, 182 className: "panel-with-code", 183 }, 184 CookiesPanel({ 185 connector, 186 openLink, 187 request, 188 targetSearchResult, 189 }) 190 ), 191 TabPanel( 192 { 193 id: PANELS.REQUEST, 194 title: REQUEST_TITLE, 195 className: "panel-with-code", 196 }, 197 RequestPanel({ 198 connector, 199 openLink, 200 request, 201 targetSearchResult, 202 }) 203 ), 204 TabPanel( 205 { 206 id: PANELS.RESPONSE, 207 title: RESPONSE_TITLE, 208 className: 209 "panel-with-code" + 210 (isOverridden ? " tab-response-overridden" : ""), 211 tooltip: isOverridden 212 ? L10N.getFormatStr( 213 "netmonitor.tab.response-overridden.tooltip", 214 overriddenUrl 215 ) 216 : RESPONSE_TITLE, 217 }, 218 ResponsePanel({ 219 request, 220 openLink, 221 connector, 222 showMessagesView, 223 targetSearchResult, 224 defaultRawResponse, 225 setDefaultRawResponse, 226 }) 227 ), 228 (request.fromCache || request.status == "304") && 229 TabPanel( 230 { 231 id: PANELS.CACHE, 232 title: CACHE_TITLE, 233 }, 234 CachePanel({ request, openLink, connector }) 235 ), 236 TabPanel( 237 { 238 id: PANELS.TIMINGS, 239 title: TIMINGS_TITLE, 240 }, 241 TimingsPanel({ 242 connector, 243 request, 244 }) 245 ), 246 request.cause?.stacktraceAvailable && 247 TabPanel( 248 { 249 id: PANELS.STACK_TRACE, 250 title: STACK_TRACE_TITLE, 251 className: "panel-with-code", 252 }, 253 StackTracePanel({ connector, openLink, request, sourceMapURLService }) 254 ), 255 request.securityState && 256 request.securityState !== "insecure" && 257 TabPanel( 258 { 259 id: PANELS.SECURITY, 260 title: SECURITY_TITLE, 261 }, 262 SecurityPanel({ 263 connector, 264 openLink, 265 request, 266 }) 267 ) 268 ); 269 } 270 } 271 272 module.exports = { 273 ConnectedTabboxPanel: connect( 274 (state, props) => { 275 const overriddenUrl = getOverriddenUrl( 276 state, 277 props.request.urlDetails?.url 278 ); 279 return { 280 isOverridden: !!overriddenUrl, 281 overriddenUrl, 282 }; 283 }, 284 {}, 285 undefined, 286 { storeKey: "toolbox-store" } 287 )(TabboxPanel), 288 // Export the non-connected variant of the component for the browser console 289 // which might not have toolbox store available. 290 TabboxPanel, 291 };