/* 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 . */ import React, { Component } from "react"; import PropTypes from "prop-types"; import { connect } from "../../utils/connect"; import { features, prefs } from "../../utils/prefs"; import { getIsWaitingOnBreak, getSkipPausing, getCurrentThread, isTopFrameSelected, getThreadContext, getIsCurrentThreadPaused, getIsThreadCurrentlyTracing, getJavascriptTracingLogMethod, } from "../../selectors"; import { formatKeyShortcut } from "../../utils/text"; import actions from "../../actions"; import { debugBtn } from "../shared/Button/CommandBarButton"; import AccessibleImage from "../shared/AccessibleImage"; import "./CommandBar.css"; import { showMenu } from "../../context-menu/menu"; const classnames = require("devtools/client/shared/classnames.js"); const MenuButton = require("devtools/client/shared/components/menu/MenuButton"); const MenuItem = require("devtools/client/shared/components/menu/MenuItem"); const MenuList = require("devtools/client/shared/components/menu/MenuList"); const isMacOS = Services.appinfo.OS === "Darwin"; // NOTE: the "resume" command will call either the resume or breakOnNext action // depending on whether or not the debugger is paused or running const COMMANDS = ["resume", "stepOver", "stepIn", "stepOut"]; const KEYS = { WINNT: { resume: "F8", stepOver: "F10", stepIn: "F11", stepOut: "Shift+F11", }, Darwin: { resume: "Cmd+\\", stepOver: "Cmd+'", stepIn: "Cmd+;", stepOut: "Cmd+Shift+:", stepOutDisplay: "Cmd+Shift+;", }, Linux: { resume: "F8", stepOver: "F10", stepIn: "F11", stepOut: "Shift+F11", }, }; const LOG_METHODS = { CONSOLE: "console", STDOUT: "stdout", }; function getKey(action) { return getKeyForOS(Services.appinfo.OS, action); } function getKeyForOS(os, action) { const osActions = KEYS[os] || KEYS.Linux; return osActions[action]; } function formatKey(action) { const key = getKey(`${action}Display`) || getKey(action); if (isMacOS) { const winKey = getKeyForOS("WINNT", `${action}Display`) || getKeyForOS("WINNT", action); // display both Windows type and Mac specific keys return formatKeyShortcut([key, winKey].join(" ")); } return formatKeyShortcut(key); } class CommandBar extends Component { constructor() { super(); this.state = {}; } static get propTypes() { return { breakOnNext: PropTypes.func.isRequired, cx: PropTypes.object.isRequired, horizontal: PropTypes.bool.isRequired, isPaused: PropTypes.bool.isRequired, isTracingEnabled: PropTypes.bool.isRequired, isWaitingOnBreak: PropTypes.bool.isRequired, javascriptEnabled: PropTypes.bool.isRequired, trace: PropTypes.func.isRequired, resume: PropTypes.func.isRequired, skipPausing: PropTypes.bool.isRequired, stepIn: PropTypes.func.isRequired, stepOut: PropTypes.func.isRequired, stepOver: PropTypes.func.isRequired, toggleEditorWrapping: PropTypes.func.isRequired, toggleInlinePreview: PropTypes.func.isRequired, toggleJavaScriptEnabled: PropTypes.func.isRequired, toggleSkipPausing: PropTypes.any.isRequired, toggleSourceMapsEnabled: PropTypes.func.isRequired, topFrameSelected: PropTypes.bool.isRequired, toggleTracing: PropTypes.func.isRequired, logMethod: PropTypes.string.isRequired, setJavascriptTracingLogMethod: PropTypes.func.isRequired, setHideOrShowIgnoredSources: PropTypes.func.isRequired, toggleSourceMapIgnoreList: PropTypes.func.isRequired, }; } componentWillUnmount() { const { shortcuts } = this.context; COMMANDS.forEach(action => shortcuts.off(getKey(action))); if (isMacOS) { COMMANDS.forEach(action => shortcuts.off(getKeyForOS("WINNT", action))); } } componentDidMount() { const { shortcuts } = this.context; COMMANDS.forEach(action => shortcuts.on(getKey(action), e => this.handleEvent(e, action)) ); if (isMacOS) { // The Mac supports both the Windows Function keys // as well as the Mac non-Function keys COMMANDS.forEach(action => shortcuts.on(getKeyForOS("WINNT", action), e => this.handleEvent(e, action) ) ); } } handleEvent(e, action) { const { cx } = this.props; e.preventDefault(); e.stopPropagation(); if (action === "resume") { this.props.isPaused ? this.props.resume() : this.props.breakOnNext(cx); } else { this.props[action](cx); } } renderStepButtons() { const { isPaused, topFrameSelected } = this.props; const className = isPaused ? "active" : "disabled"; const isDisabled = !isPaused; return [ this.renderTraceButton(), this.renderPauseButton(), debugBtn( () => this.props.stepOver(), "stepOver", className, L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")), isDisabled ), debugBtn( () => this.props.stepIn(), "stepIn", className, L10N.getFormatStr("stepInTooltip", formatKey("stepIn")), isDisabled || !topFrameSelected ), debugBtn( () => this.props.stepOut(), "stepOut", className, L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")), isDisabled ), ]; } resume() { this.props.resume(); } renderTraceButton() { if (!features.javascriptTracing) { return null; } // Display a button which: // - on left click, would toggle on/off javascript tracing // - on right click, would display a context menu allowing to choose the loggin output (console or stdout) return ( ); } renderSettingsButton() { const { toolboxDoc } = this.context; return ( {() => this.renderSettingsMenuItems()} ); } renderSettingsMenuItems() { return ( { this.props.toggleJavaScriptEnabled(!this.props.javascriptEnabled); }} /> this.props.toggleInlinePreview(!features.inlinePreview) } /> this.props.toggleEditorWrapping(!prefs.editorWrapping)} /> this.props.toggleSourceMapsEnabled(!prefs.clientSourceMapsEnabled) } /> this.props.setHideOrShowIgnoredSources(!prefs.hideIgnoredSources) } /> this.props.toggleSourceMapIgnoreList( this.props.cx, !prefs.sourceMapIgnoreListEnabled ) } /> ); } render() { return (
{this.renderStepButtons()}
{this.renderSkipPausingButton()}
{this.renderSettingsButton()}
); } } CommandBar.contextTypes = { shortcuts: PropTypes.object, toolboxDoc: PropTypes.object, }; const mapStateToProps = state => ({ cx: getThreadContext(state), isWaitingOnBreak: getIsWaitingOnBreak(state, getCurrentThread(state)), skipPausing: getSkipPausing(state), topFrameSelected: isTopFrameSelected(state, getCurrentThread(state)), javascriptEnabled: state.ui.javascriptEnabled, isPaused: getIsCurrentThreadPaused(state), isTracingEnabled: getIsThreadCurrentlyTracing(state, getCurrentThread(state)), logMethod: getJavascriptTracingLogMethod(state), }); export default connect(mapStateToProps, { toggleTracing: actions.toggleTracing, setJavascriptTracingLogMethod: actions.setJavascriptTracingLogMethod, resume: actions.resume, stepIn: actions.stepIn, stepOut: actions.stepOut, stepOver: actions.stepOver, breakOnNext: actions.breakOnNext, pauseOnExceptions: actions.pauseOnExceptions, toggleSkipPausing: actions.toggleSkipPausing, toggleInlinePreview: actions.toggleInlinePreview, toggleEditorWrapping: actions.toggleEditorWrapping, toggleSourceMapsEnabled: actions.toggleSourceMapsEnabled, toggleJavaScriptEnabled: actions.toggleJavaScriptEnabled, setHideOrShowIgnoredSources: actions.setHideOrShowIgnoredSources, toggleSourceMapIgnoreList: actions.toggleSourceMapIgnoreList, })(CommandBar);