EagerEvaluation.js (4377B)
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 "use strict"; 6 7 const { 8 Component, 9 } = require("resource://devtools/client/shared/vendor/react.mjs"); 10 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 11 const { 12 connect, 13 } = require("resource://devtools/client/shared/vendor/react-redux.js"); 14 15 const { 16 getTerminalEagerResult, 17 } = require("resource://devtools/client/webconsole/selectors/history.js"); 18 19 const actions = require("resource://devtools/client/webconsole/actions/index.js"); 20 21 loader.lazyGetter(this, "REPS", function () { 22 return ChromeUtils.importESModule( 23 "resource://devtools/client/shared/components/reps/index.mjs", 24 { global: "current" } 25 ).REPS; 26 }); 27 loader.lazyGetter(this, "MODE", function () { 28 return ChromeUtils.importESModule( 29 "resource://devtools/client/shared/components/reps/index.mjs", 30 { global: "current" } 31 ).MODE; 32 }); 33 loader.lazyRequireGetter( 34 this, 35 "PropTypes", 36 "resource://devtools/client/shared/vendor/react-prop-types.js" 37 ); 38 39 /** 40 * Show the results of evaluating the current terminal text, if possible. 41 */ 42 class EagerEvaluation extends Component { 43 static get propTypes() { 44 return { 45 terminalEagerResult: PropTypes.any, 46 highlightDomElement: PropTypes.func.isRequired, 47 unHighlightDomElement: PropTypes.func.isRequired, 48 }; 49 } 50 51 static getDerivedStateFromError() { 52 return { hasError: true }; 53 } 54 55 componentDidUpdate(prevProps) { 56 const { highlightDomElement, unHighlightDomElement, terminalEagerResult } = 57 this.props; 58 59 if (canHighlightObject(prevProps.terminalEagerResult)) { 60 unHighlightDomElement(prevProps.terminalEagerResult.getGrip()); 61 } 62 63 if (canHighlightObject(terminalEagerResult)) { 64 highlightDomElement(terminalEagerResult.getGrip()); 65 } 66 67 if (this.state?.hasError) { 68 // If the render function threw at some point, clear the error after 1s so the 69 // component has a chance to render again. 70 // This way, we don't block instant evaluation for the whole session, in case the 71 // input changed in the meantime. If the input didn't change, we'll hit 72 // getDerivatedStateFromError again (and this won't render anything), so it's safe. 73 setTimeout(() => { 74 this.setState({ hasError: false }); 75 }, 1000); 76 } 77 } 78 79 componentWillUnmount() { 80 const { unHighlightDomElement, terminalEagerResult } = this.props; 81 82 if (canHighlightObject(terminalEagerResult)) { 83 unHighlightDomElement(terminalEagerResult.getGrip()); 84 } 85 } 86 87 renderRepsResult() { 88 const { terminalEagerResult } = this.props; 89 90 const result = terminalEagerResult.getGrip 91 ? terminalEagerResult.getGrip() 92 : terminalEagerResult; 93 const { isError } = result || {}; 94 95 return REPS.Rep({ 96 key: "rep", 97 object: result, 98 mode: isError ? MODE.SHORT : MODE.LONG, 99 }); 100 } 101 102 render() { 103 const hasResult = 104 this.props.terminalEagerResult !== null && 105 this.props.terminalEagerResult !== undefined && 106 !this.state?.hasError; 107 108 return dom.div( 109 { className: "eager-evaluation-result", key: "eager-evaluation-result" }, 110 hasResult 111 ? dom.span( 112 { className: "eager-evaluation-result__row" }, 113 dom.span({ 114 className: "eager-evaluation-result__icon", 115 key: "icon", 116 }), 117 dom.span( 118 { className: "eager-evaluation-result__text", key: "text" }, 119 this.renderRepsResult() 120 ) 121 ) 122 : null 123 ); 124 } 125 } 126 127 function canHighlightObject(obj) { 128 const grip = obj?.getGrip && obj.getGrip(); 129 return ( 130 grip && 131 (REPS.ElementNode.supportsObject(grip) || 132 REPS.TextNode.supportsObject(grip)) && 133 grip.preview.isConnected 134 ); 135 } 136 137 function mapStateToProps(state) { 138 return { 139 terminalEagerResult: getTerminalEagerResult(state), 140 }; 141 } 142 143 function mapDispatchToProps(dispatch) { 144 return { 145 highlightDomElement: grip => dispatch(actions.highlightDomElement(grip)), 146 unHighlightDomElement: grip => 147 dispatch(actions.unHighlightDomElement(grip)), 148 }; 149 } 150 module.exports = connect(mapStateToProps, mapDispatchToProps)(EagerEvaluation);