diff options
Diffstat (limited to 'devtools/client/debugger/src/components/SecondaryPanes/Frames/Frame.js')
-rw-r--r-- | devtools/client/debugger/src/components/SecondaryPanes/Frames/Frame.js | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/components/SecondaryPanes/Frames/Frame.js b/devtools/client/debugger/src/components/SecondaryPanes/Frames/Frame.js new file mode 100644 index 0000000000..fc75618930 --- /dev/null +++ b/devtools/client/debugger/src/components/SecondaryPanes/Frames/Frame.js @@ -0,0 +1,208 @@ +/* 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/>. */ + +// @flow +import React, { Component, memo } from "react"; +import PropTypes from "prop-types"; + +import classNames from "classnames"; + +import AccessibleImage from "../../shared/AccessibleImage"; +import { formatDisplayName } from "../../../utils/pause/frames"; +import { getFilename, getFileURL } from "../../../utils/source"; +import FrameMenu from "./FrameMenu"; +import FrameIndent from "./FrameIndent"; +import actions from "../../../actions"; + +import type { Frame, ThreadContext } from "../../../types"; + +type FrameTitleProps = { + frame: Frame, + options: Object, + l10n: Object, +}; + +function FrameTitle({ frame, options = {}, l10n }: FrameTitleProps) { + const displayName = formatDisplayName(frame, options, l10n); + return <span className="title">{displayName}</span>; +} + +type FrameLocationProps = { frame: Frame, displayFullUrl: boolean }; + +const FrameLocation = memo( + ({ frame, displayFullUrl = false }: FrameLocationProps) => { + if (!frame.source) { + return null; + } + + if (frame.library) { + return ( + <span className="location"> + {frame.library} + <AccessibleImage + className={`annotation-logo ${frame.library.toLowerCase()}`} + /> + </span> + ); + } + + const { location, source } = frame; + const filename = displayFullUrl + ? getFileURL(source, false) + : getFilename(source); + + return ( + <span className="location" title={source.url}> + <span className="filename">{filename}</span>: + <span className="line">{location.line}</span> + </span> + ); + } +); + +FrameLocation.displayName = "FrameLocation"; + +type FrameComponentProps = { + cx: ThreadContext, + frame: Frame, + selectedFrame: Frame, + copyStackTrace: Function, + toggleFrameworkGrouping: Function, + selectFrame: typeof actions.selectFrame, + selectLocation: typeof actions.selectLocation, + frameworkGroupingOn: boolean, + hideLocation: boolean, + shouldMapDisplayName: boolean, + toggleBlackBox: Function, + displayFullUrl: boolean, + getFrameTitle?: string => string, + disableContextMenu: boolean, + panel: "debugger" | "webconsole", + restart: typeof actions.restart, +}; + +export default class FrameComponent extends Component<FrameComponentProps> { + static defaultProps = { + hideLocation: false, + shouldMapDisplayName: true, + disableContextMenu: false, + }; + + get isSelectable() { + return this.props.panel == "webconsole"; + } + + get isDebugger() { + return this.props.panel == "debugger"; + } + + onContextMenu(event: SyntheticMouseEvent<HTMLElement>) { + const { + frame, + copyStackTrace, + toggleFrameworkGrouping, + toggleBlackBox, + frameworkGroupingOn, + cx, + restart, + } = this.props; + FrameMenu( + frame, + frameworkGroupingOn, + { copyStackTrace, toggleFrameworkGrouping, toggleBlackBox, restart }, + event, + cx + ); + } + + onMouseDown( + e: SyntheticMouseEvent<HTMLElement>, + frame: Frame, + selectedFrame: Frame + ) { + if (e.button !== 0) { + return; + } + + this.props.selectFrame(this.props.cx, frame); + } + + onKeyUp( + event: SyntheticKeyboardEvent<HTMLElement>, + frame: Frame, + selectedFrame: Frame + ) { + if (event.key != "Enter") { + return; + } + + this.props.selectFrame(this.props.cx, frame); + } + + render() { + const { + frame, + selectedFrame, + hideLocation, + shouldMapDisplayName, + displayFullUrl, + getFrameTitle, + disableContextMenu, + } = this.props; + const { l10n } = this.context; + + const className = classNames("frame", { + selected: selectedFrame && selectedFrame.id === frame.id, + }); + + if (!frame.source) { + throw new Error("no frame source"); + } + + const title = getFrameTitle + ? getFrameTitle( + `${getFileURL(frame.source, false)}:${frame.location.line}` + ) + : undefined; + + return ( + <div + role="listitem" + key={frame.id} + className={className} + onMouseDown={e => this.onMouseDown(e, frame, selectedFrame)} + onKeyUp={e => this.onKeyUp(e, frame, selectedFrame)} + onContextMenu={disableContextMenu ? null : e => this.onContextMenu(e)} + tabIndex={0} + title={title} + > + {frame.asyncCause && ( + <span className="location-async-cause"> + {this.isSelectable && <FrameIndent />} + {this.isDebugger ? ( + <span className="async-label">{frame.asyncCause}</span> + ) : ( + l10n.getFormatStr("stacktrace.asyncStack", frame.asyncCause) + )} + {this.isSelectable && <br className="clipboard-only" />} + </span> + )} + {this.isSelectable && <FrameIndent />} + <FrameTitle + frame={frame} + options={{ shouldMapDisplayName }} + l10n={l10n} + /> + {!hideLocation && <span className="clipboard-only"> </span>} + {!hideLocation && ( + <FrameLocation frame={frame} displayFullUrl={displayFullUrl} /> + )} + {this.isSelectable && <br className="clipboard-only" />} + </div> + ); + } +} + +FrameComponent.displayName = "Frame"; +FrameComponent.contextTypes = { l10n: PropTypes.object }; |