/* 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 actions from "../../actions"; import { CloseButton } from "../shared/Button"; import "./XHRBreakpoints.css"; import { getXHRBreakpoints, shouldPauseOnAnyXHR } from "../../selectors"; import ExceptionOption from "./Breakpoints/ExceptionOption"; const classnames = require("devtools/client/shared/classnames.js"); // At present, the "Pause on any URL" checkbox creates an xhrBreakpoint // of "ANY" with no path, so we can remove that before creating the list function getExplicitXHRBreakpoints(xhrBreakpoints) { return xhrBreakpoints.filter(bp => bp.path !== ""); } const xhrMethods = [ "ANY", "GET", "POST", "PUT", "HEAD", "DELETE", "PATCH", "OPTIONS", ]; class XHRBreakpoints extends Component { constructor(props) { super(props); this.state = { editing: false, inputValue: "", inputMethod: "ANY", focused: false, editIndex: -1, clickedOnFormElement: false, }; } static get propTypes() { return { disableXHRBreakpoint: PropTypes.func.isRequired, enableXHRBreakpoint: PropTypes.func.isRequired, onXHRAdded: PropTypes.func.isRequired, removeXHRBreakpoint: PropTypes.func.isRequired, setXHRBreakpoint: PropTypes.func.isRequired, shouldPauseOnAny: PropTypes.bool.isRequired, showInput: PropTypes.bool.isRequired, togglePauseOnAny: PropTypes.func.isRequired, updateXHRBreakpoint: PropTypes.func.isRequired, xhrBreakpoints: PropTypes.array.isRequired, }; } componentDidMount() { const { showInput } = this.props; // Ensures that the input is focused when the "+" // is clicked while the panel is collapsed if (this._input && showInput) { this._input.focus(); } } componentDidUpdate(prevProps, prevState) { const input = this._input; if (!input) { return; } if (!prevState.editing && this.state.editing) { input.setSelectionRange(0, input.value.length); input.focus(); } else if (this.props.showInput && !this.state.focused) { input.focus(); } } handleNewSubmit = e => { e.preventDefault(); e.stopPropagation(); const setXHRBreakpoint = function () { this.props.setXHRBreakpoint( this.state.inputValue, this.state.inputMethod ); this.hideInput(); }; // force update inputMethod in state for mochitest purposes // before setting XHR breakpoint this.setState( { inputMethod: e.target.children[1].value }, setXHRBreakpoint ); }; handleExistingSubmit = e => { e.preventDefault(); e.stopPropagation(); const { editIndex, inputValue, inputMethod } = this.state; const { xhrBreakpoints } = this.props; const { path, method } = xhrBreakpoints[editIndex]; if (path !== inputValue || method != inputMethod) { this.props.updateXHRBreakpoint(editIndex, inputValue, inputMethod); } this.hideInput(); }; handleChange = e => { this.setState({ inputValue: e.target.value }); }; handleMethodChange = e => { this.setState({ focused: true, editing: true, inputMethod: e.target.value, }); }; hideInput = () => { if (this.state.clickedOnFormElement) { this.setState({ focused: true, clickedOnFormElement: false, }); } else { this.setState({ focused: false, editing: false, editIndex: -1, inputValue: "", inputMethod: "ANY", }); this.props.onXHRAdded(); } }; onFocus = () => { this.setState({ focused: true, editing: true }); }; onMouseDown = e => { this.setState({ editing: false, clickedOnFormElement: true }); }; handleTab = e => { if (e.key !== "Tab") { return; } if (e.currentTarget.nodeName === "INPUT") { this.setState({ clickedOnFormElement: true, editing: false, }); } else if (e.currentTarget.nodeName === "SELECT" && !e.shiftKey) { // The user has tabbed off the select and we should // cancel the edit this.hideInput(); } }; editExpression = index => { const { xhrBreakpoints } = this.props; const { path, method } = xhrBreakpoints[index]; this.setState({ inputValue: path, inputMethod: method, editing: true, editIndex: index, }); }; renderXHRInput(onSubmit) { const { focused, inputValue } = this.state; const placeholder = L10N.getStr("xhrBreakpoints.placeholder"); return (
(this._input = c)} /> {this.renderMethodSelectElement()}
); } handleCheckbox = index => { const { xhrBreakpoints, enableXHRBreakpoint, disableXHRBreakpoint } = this.props; const breakpoint = xhrBreakpoints[index]; if (breakpoint.disabled) { enableXHRBreakpoint(index); } else { disableXHRBreakpoint(index); } }; renderBreakpoint = breakpoint => { const { path, disabled, method } = breakpoint; const { editIndex } = this.state; const { removeXHRBreakpoint, xhrBreakpoints } = this.props; // The "pause on any" checkbox if (!path) { return null; } // Finds the xhrbreakpoint so as to not make assumptions about position const index = xhrBreakpoints.findIndex( bp => bp.path === path && bp.method === method ); if (index === editIndex) { return this.renderXHRInput(this.handleExistingSubmit); } return (
  • this.editExpression(index)} >
  • ); }; renderBreakpoints = explicitXhrBreakpoints => { const { showInput } = this.props; return ( <> {showInput && this.renderXHRInput(this.handleNewSubmit)} ); }; renderCheckbox = explicitXhrBreakpoints => { const { shouldPauseOnAny, togglePauseOnAny } = this.props; return (
    togglePauseOnAny()} />
    ); }; renderMethodOption = method => { return ( ); }; renderMethodSelectElement = () => { return ( ); }; render() { const { xhrBreakpoints } = this.props; const explicitXhrBreakpoints = getExplicitXHRBreakpoints(xhrBreakpoints); return ( <> {this.renderCheckbox(explicitXhrBreakpoints)} {explicitXhrBreakpoints.length === 0 ? this.renderXHRInput(this.handleNewSubmit) : this.renderBreakpoints(explicitXhrBreakpoints)} ); } } const mapStateToProps = state => ({ xhrBreakpoints: getXHRBreakpoints(state), shouldPauseOnAny: shouldPauseOnAnyXHR(state), }); export default connect(mapStateToProps, { setXHRBreakpoint: actions.setXHRBreakpoint, removeXHRBreakpoint: actions.removeXHRBreakpoint, enableXHRBreakpoint: actions.enableXHRBreakpoint, disableXHRBreakpoint: actions.disableXHRBreakpoint, updateXHRBreakpoint: actions.updateXHRBreakpoint, togglePauseOnAny: actions.togglePauseOnAny, })(XHRBreakpoints);