summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/components/Editor/Footer.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/components/Editor/Footer.js')
-rw-r--r--devtools/client/debugger/src/components/Editor/Footer.js208
1 files changed, 199 insertions, 9 deletions
diff --git a/devtools/client/debugger/src/components/Editor/Footer.js b/devtools/client/debugger/src/components/Editor/Footer.js
index c4ff02caf4..69c7b52b68 100644
--- a/devtools/client/debugger/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/src/components/Editor/Footer.js
@@ -7,6 +7,7 @@ import {
div,
button,
span,
+ hr,
} from "devtools/client/shared/vendor/react-dom-factories";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
import { connect } from "devtools/client/shared/vendor/react-redux";
@@ -23,14 +24,23 @@ import {
isSourceOnSourceMapIgnoreList,
isSourceMapIgnoreListEnabled,
getSelectedMappedSource,
+ getSourceMapErrorForSourceActor,
+ areSourceMapsEnabled,
+ getShouldSelectOriginalLocation,
+ isSourceActorWithSourceMap,
+ getSourceMapResolvedURL,
+ isSelectedMappedSourceLoading,
} from "../../selectors/index";
-import { isPretty, getFilename, shouldBlackbox } from "../../utils/source";
+import { isPretty, shouldBlackbox } from "../../utils/source";
import { PaneToggleButton } from "../shared/Button/index";
import AccessibleImage from "../shared/AccessibleImage";
const classnames = require("resource://devtools/client/shared/classnames.js");
+const MenuButton = require("resource://devtools/client/shared/components/menu/MenuButton.js");
+const MenuItem = require("resource://devtools/client/shared/components/menu/MenuItem.js");
+const MenuList = require("resource://devtools/client/shared/components/menu/MenuList.js");
class SourceFooter extends PureComponent {
static get propTypes() {
@@ -155,9 +165,11 @@ class SourceFooter extends PureComponent {
}
renderCommands() {
- const commands = [this.blackBoxButton(), this.prettyPrintButton()].filter(
- Boolean
- );
+ const commands = [
+ this.blackBoxButton(),
+ this.prettyPrintButton(),
+ this.renderSourceMapButton(),
+ ].filter(Boolean);
return commands.length
? div(
@@ -169,7 +181,7 @@ class SourceFooter extends PureComponent {
: null;
}
- renderSourceSummary() {
+ renderMappedSource() {
const { mappedSource, jumpToMappedLocation, selectedLocation } = this.props;
if (!mappedSource) {
@@ -182,12 +194,11 @@ class SourceFooter extends PureComponent {
: "sourceFooter.mappedGeneratedSource.tooltip",
mappedSource.url
);
- const filename = getFilename(mappedSource);
const label = L10N.getFormatStr(
mappedSource.isOriginal
? "sourceFooter.mappedOriginalSource.title"
: "sourceFooter.mappedGeneratedSource.title",
- filename
+ mappedSource.shortName
);
return button(
{
@@ -227,6 +238,148 @@ class SourceFooter extends PureComponent {
);
}
+ getSourceMapLabel() {
+ if (!this.props.selectedLocation) {
+ return undefined;
+ }
+ if (!this.props.areSourceMapsEnabled) {
+ return L10N.getStr("sourceFooter.sourceMapButton.disabled");
+ }
+ if (this.props.sourceMapError) {
+ return undefined;
+ }
+ if (!this.props.isSourceActorWithSourceMap) {
+ return L10N.getStr("sourceFooter.sourceMapButton.sourceNotMapped");
+ }
+ if (this.props.selectedLocation.source.isOriginal) {
+ return L10N.getStr("sourceFooter.sourceMapButton.isOriginalSource");
+ }
+ return L10N.getStr("sourceFooter.sourceMapButton.isBundleSource");
+ }
+
+ getSourceMapTitle() {
+ if (this.props.sourceMapError) {
+ return L10N.getFormatStr(
+ "sourceFooter.sourceMapButton.errorTitle",
+ this.props.sourceMapError
+ );
+ }
+ if (this.props.isSourceMapLoading) {
+ return L10N.getStr("sourceFooter.sourceMapButton.loadingTitle");
+ }
+ return L10N.getStr("sourceFooter.sourceMapButton.title");
+ }
+
+ renderSourceMapButton() {
+ const { toolboxDoc } = this.context;
+
+ return React.createElement(
+ MenuButton,
+ {
+ menuId: "debugger-source-map-button",
+ toolboxDoc,
+ className: classnames("devtools-button", "debugger-source-map-button", {
+ error: !!this.props.sourceMapError,
+ loading: this.props.isSourceMapLoading,
+ disabled: !this.props.areSourceMapsEnabled,
+ "not-mapped":
+ !this.props.selectedLocation?.source.isOriginal &&
+ !this.props.isSourceActorWithSourceMap,
+ original: this.props.selectedLocation?.source.isOriginal,
+ }),
+ title: this.getSourceMapTitle(),
+ label: this.getSourceMapLabel(),
+ icon: true,
+ },
+ () => this.renderSourceMapMenuItems()
+ );
+ }
+
+ renderSourceMapMenuItems() {
+ const items = [
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-source-map-enabled",
+ checked: this.props.areSourceMapsEnabled,
+ label: L10N.getStr("sourceFooter.sourceMapButton.enable"),
+ onClick: this.toggleSourceMaps,
+ }),
+ hr(),
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-source-map-open-original",
+ checked: this.props.shouldSelectOriginalLocation,
+ label: L10N.getStr(
+ "sourceFooter.sourceMapButton.showOriginalSourceByDefault"
+ ),
+ onClick: this.toggleSelectOriginalByDefault,
+ }),
+ ];
+
+ if (this.props.mappedSource) {
+ items.push(
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-jump-mapped-source",
+ label: this.props.mappedSource.isOriginal
+ ? L10N.getStr("sourceFooter.sourceMapButton.jumpToGeneratedSource")
+ : L10N.getStr("sourceFooter.sourceMapButton.jumpToOriginalSource"),
+ tooltip: this.props.mappedSource.url,
+ onClick: () =>
+ this.props.jumpToMappedLocation(this.props.selectedLocation),
+ })
+ );
+ }
+
+ if (this.props.resolvedSourceMapURL) {
+ items.push(
+ React.createElement(MenuItem, {
+ className: "menu-item debugger-source-map-link",
+ label: L10N.getStr(
+ "sourceFooter.sourceMapButton.openSourceMapInNewTab"
+ ),
+ onClick: this.openSourceMap,
+ })
+ );
+ }
+ return React.createElement(
+ MenuList,
+ {
+ id: "debugger-source-map-list",
+ },
+ items
+ );
+ }
+
+ openSourceMap = () => {
+ let line, column;
+ if (
+ this.props.sourceMapError &&
+ this.props.sourceMapError.includes("JSON.parse")
+ ) {
+ const match = this.props.sourceMapError.match(
+ /at line (\d+) column (\d+)/
+ );
+ if (match) {
+ line = match[1];
+ column = match[2];
+ }
+ }
+ this.props.openSourceMap(
+ this.props.resolvedSourceMapURL || this.props.selectedLocation.source.url,
+ line,
+ column
+ );
+ };
+
+ toggleSourceMaps = () => {
+ this.props.toggleSourceMapsEnabled(!this.props.areSourceMapsEnabled);
+ };
+
+ toggleSelectOriginalByDefault = () => {
+ this.props.setDefaultSelectedLocation(
+ !this.props.shouldSelectOriginalLocation
+ );
+ this.props.jumpToMappedSelectedLocation();
+ };
+
render() {
return div(
{
@@ -242,19 +395,40 @@ class SourceFooter extends PureComponent {
{
className: "source-footer-end",
},
- this.renderSourceSummary(),
+ this.renderMappedSource(),
this.renderCursorPosition(),
this.renderToggleButton()
)
);
}
}
+SourceFooter.contextTypes = {
+ toolboxDoc: PropTypes.object,
+};
const mapStateToProps = state => {
const selectedSource = getSelectedSource(state);
const selectedLocation = getSelectedLocation(state);
const sourceTextContent = getSelectedSourceTextContent(state);
+ const areSourceMapsEnabledProp = areSourceMapsEnabled(state);
+ const isSourceActorWithSourceMapProp = isSourceActorWithSourceMap(
+ state,
+ selectedLocation?.sourceActor.id
+ );
+ const sourceMapError = selectedLocation?.sourceActor
+ ? getSourceMapErrorForSourceActor(state, selectedLocation.sourceActor.id)
+ : null;
+ const mappedSource = getSelectedMappedSource(state);
+
+ const isSourceMapLoading =
+ areSourceMapsEnabledProp &&
+ isSourceActorWithSourceMapProp &&
+ // `mappedSource` will be null while loading, we need another way to know when it is done computing
+ !mappedSource &&
+ isSelectedMappedSourceLoading(state) &&
+ !sourceMapError;
+
return {
selectedSource,
selectedLocation,
@@ -265,7 +439,8 @@ const mapStateToProps = state => {
isSourceMapIgnoreListEnabled(state) &&
isSourceOnSourceMapIgnoreList(state, selectedSource),
sourceLoaded: !!sourceTextContent,
- mappedSource: getSelectedMappedSource(state),
+ mappedSource,
+ isSourceMapLoading,
prettySource: getPrettySource(
state,
selectedSource ? selectedSource.id : null
@@ -277,6 +452,17 @@ const mapStateToProps = state => {
prettyPrintMessage: selectedLocation
? getPrettyPrintMessage(state, selectedLocation)
: null,
+
+ sourceMapError,
+ resolvedSourceMapURL: selectedLocation?.sourceActor
+ ? getSourceMapResolvedURL(state, selectedLocation.sourceActor.id)
+ : null,
+ isSourceActorWithSourceMap: isSourceActorWithSourceMapProp,
+
+ sourceMapURL: selectedLocation?.sourceActor.sourceMapURL,
+
+ areSourceMapsEnabled: areSourceMapsEnabledProp,
+ shouldSelectOriginalLocation: getShouldSelectOriginalLocation(state),
};
};
@@ -285,4 +471,8 @@ export default connect(mapStateToProps, {
toggleBlackBox: actions.toggleBlackBox,
jumpToMappedLocation: actions.jumpToMappedLocation,
togglePaneCollapse: actions.togglePaneCollapse,
+ toggleSourceMapsEnabled: actions.toggleSourceMapsEnabled,
+ setDefaultSelectedLocation: actions.setDefaultSelectedLocation,
+ jumpToMappedSelectedLocation: actions.jumpToMappedSelectedLocation,
+ openSourceMap: actions.openSourceMap,
})(SourceFooter);