Frame.js (6561B)
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 import React, { Component, memo } from "devtools/client/shared/vendor/react"; 6 import PropTypes from "devtools/client/shared/vendor/react-prop-types"; 7 8 import DebuggerImage from "../../shared/DebuggerImage"; 9 import { formatDisplayName } from "../../../utils/pause/frames/index"; 10 import { getFileURL } from "../../../utils/source"; 11 import FrameIndent from "./FrameIndent"; 12 const classnames = require("resource://devtools/client/shared/classnames.js"); 13 14 function FrameTitle({ frame, options = {}, l10n }) { 15 const displayName = formatDisplayName(frame, options, l10n); 16 return React.createElement( 17 "span", 18 { 19 className: "title", 20 }, 21 displayName 22 ); 23 } 24 25 FrameTitle.propTypes = { 26 frame: PropTypes.object.isRequired, 27 options: PropTypes.object.isRequired, 28 l10n: PropTypes.object.isRequired, 29 }; 30 31 function getFrameLocation(frame, shouldDisplayOriginalLocation) { 32 if (shouldDisplayOriginalLocation) { 33 return frame.location; 34 } 35 return frame.generatedLocation || frame.location; 36 } 37 const FrameLocation = memo( 38 ({ frame, displayFullUrl = false, shouldDisplayOriginalLocation }) => { 39 if (frame.library) { 40 return React.createElement( 41 "span", 42 { 43 className: "location", 44 }, 45 frame.library, 46 React.createElement(DebuggerImage, { 47 name: frame.library.toLowerCase(), 48 className: "annotation-logo", 49 }) 50 ); 51 } 52 const location = getFrameLocation(frame, shouldDisplayOriginalLocation); 53 const filename = displayFullUrl 54 ? getFileURL(location.source, false) 55 : location.source.shortName; 56 return React.createElement( 57 "span", 58 { 59 className: "location", 60 title: location.source.url, 61 }, 62 React.createElement( 63 "span", 64 { 65 className: "filename", 66 }, 67 filename 68 ), 69 ":", 70 React.createElement( 71 "span", 72 { 73 className: "line", 74 }, 75 location.line 76 ) 77 ); 78 } 79 ); 80 FrameLocation.displayName = "FrameLocation"; 81 82 FrameLocation.propTypes = { 83 frame: PropTypes.object.isRequired, 84 displayFullUrl: PropTypes.bool.isRequired, 85 }; 86 87 export default class FrameComponent extends Component { 88 static defaultProps = { 89 hideLocation: false, 90 shouldMapDisplayName: true, 91 disableContextMenu: false, 92 }; 93 94 static get propTypes() { 95 return { 96 disableContextMenu: PropTypes.bool.isRequired, 97 displayFullUrl: PropTypes.bool.isRequired, 98 frame: PropTypes.object.isRequired, 99 getFrameTitle: PropTypes.func, 100 hideLocation: PropTypes.bool.isRequired, 101 isInGroup: PropTypes.bool, 102 panel: PropTypes.oneOf(["debugger", "webconsole"]).isRequired, 103 selectFrame: PropTypes.func.isRequired, 104 selectedFrame: PropTypes.object, 105 isTracerFrameSelected: PropTypes.bool.isRequired, 106 shouldMapDisplayName: PropTypes.bool.isRequired, 107 shouldDisplayOriginalLocation: PropTypes.bool, 108 showFrameContextMenu: PropTypes.func.isRequired, 109 }; 110 } 111 112 get isSelectable() { 113 return this.props.panel == "webconsole"; 114 } 115 116 get isDebugger() { 117 return this.props.panel == "debugger"; 118 } 119 120 render() { 121 const { 122 frame, 123 selectedFrame, 124 isTracerFrameSelected, 125 hideLocation, 126 shouldMapDisplayName, 127 displayFullUrl, 128 getFrameTitle, 129 shouldDisplayOriginalLocation, 130 isInGroup, 131 } = this.props; 132 const { l10n } = this.context; 133 134 const isSelected = 135 !isTracerFrameSelected && selectedFrame && selectedFrame.id === frame.id; 136 137 const className = classnames("frame", { 138 selected: isSelected, 139 // When a JS Tracer frame is selected, the frame will still be considered as selected, 140 // and switch from a blue to a grey background. It will still be considered as selected 141 // from the point of view of stepping buttons. 142 inactive: 143 isTracerFrameSelected && selectedFrame && selectedFrame.id === frame.id, 144 // Dead frames will likely not have inspectable scope 145 dead: frame.state && frame.state !== "on-stack", 146 }); 147 148 const location = getFrameLocation(frame, shouldDisplayOriginalLocation); 149 const title = getFrameTitle 150 ? getFrameTitle(`${getFileURL(location.source, false)}:${location.line}`) 151 : undefined; 152 153 return React.createElement( 154 React.Fragment, 155 null, 156 frame.asyncCause && 157 React.createElement( 158 "div", 159 { 160 className: "location-async-cause", 161 tabIndex: -1, 162 role: "presentation", 163 }, 164 this.isSelectable && React.createElement(FrameIndent, null), 165 this.isDebugger 166 ? React.createElement( 167 "span", 168 { 169 className: "async-label", 170 }, 171 frame.asyncCause 172 ) 173 : l10n.getFormatStr("stacktrace.asyncStack", frame.asyncCause), 174 this.isSelectable && 175 React.createElement("br", { 176 className: "clipboard-only", 177 }) 178 ), 179 React.createElement( 180 "div", 181 { 182 title, 183 className, 184 tabIndex: -1, 185 role: "option", 186 id: frame.id, 187 "aria-selected": isSelected ? "true" : "false", 188 // used by test helpers 189 "data-url": location.source.url, 190 "data-line": location.line, 191 "data-column": location.column + 1, 192 }, 193 this.isSelectable && 194 React.createElement(FrameIndent, { 195 indentLevel: isInGroup ? 2 : 1, 196 }), 197 React.createElement(FrameTitle, { 198 frame, 199 options: { 200 shouldMapDisplayName, 201 }, 202 l10n, 203 }), 204 !hideLocation && 205 React.createElement( 206 "span", 207 { 208 className: "clipboard-only", 209 }, 210 " " 211 ), 212 !hideLocation && 213 React.createElement(FrameLocation, { 214 frame, 215 displayFullUrl, 216 shouldDisplayOriginalLocation, 217 }), 218 this.isSelectable && 219 React.createElement("br", { 220 className: "clipboard-only", 221 }) 222 ) 223 ); 224 } 225 } 226 227 FrameComponent.displayName = "Frame"; 228 FrameComponent.contextTypes = { l10n: PropTypes.object };