Toolbar.js (9538B)
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 file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 "use strict"; 5 6 const { assert } = require("resource://devtools/shared/DevToolsUtils.js"); 7 const { 8 Component, 9 } = require("resource://devtools/client/shared/vendor/react.mjs"); 10 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs"); 11 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); 12 const { L10N } = require("resource://devtools/client/memory/utils.js"); 13 const models = require("resource://devtools/client/memory/models.js"); 14 const { viewState } = require("resource://devtools/client/memory/constants.js"); 15 16 class Toolbar extends Component { 17 static get propTypes() { 18 return { 19 censusDisplays: PropTypes.arrayOf(models.censusDisplay).isRequired, 20 censusDisplay: models.censusDisplay.isRequired, 21 onTakeSnapshotClick: PropTypes.func.isRequired, 22 onImportClick: PropTypes.func.isRequired, 23 onClearSnapshotsClick: PropTypes.func.isRequired, 24 onCensusDisplayChange: PropTypes.func.isRequired, 25 onToggleRecordAllocationStacks: PropTypes.func.isRequired, 26 allocations: models.allocations, 27 filterString: PropTypes.string, 28 setFilterString: PropTypes.func.isRequired, 29 diffing: models.diffingModel, 30 onToggleDiffing: PropTypes.func.isRequired, 31 view: models.view.isRequired, 32 onViewChange: PropTypes.func.isRequired, 33 labelDisplays: PropTypes.arrayOf(models.labelDisplay).isRequired, 34 labelDisplay: models.labelDisplay.isRequired, 35 onLabelDisplayChange: PropTypes.func.isRequired, 36 treeMapDisplays: PropTypes.arrayOf(models.treeMapDisplay).isRequired, 37 onTreeMapDisplayChange: PropTypes.func.isRequired, 38 snapshots: PropTypes.arrayOf(models.snapshot).isRequired, 39 }; 40 } 41 42 render() { 43 const { 44 onTakeSnapshotClick, 45 onImportClick, 46 onClearSnapshotsClick, 47 onCensusDisplayChange, 48 censusDisplays, 49 censusDisplay, 50 labelDisplays, 51 labelDisplay, 52 onLabelDisplayChange, 53 treeMapDisplays, 54 onTreeMapDisplayChange, 55 onToggleRecordAllocationStacks, 56 allocations, 57 filterString, 58 setFilterString, 59 snapshots, 60 diffing, 61 onToggleDiffing, 62 view, 63 onViewChange, 64 } = this.props; 65 66 let viewToolbarOptions; 67 if (view.state == viewState.CENSUS || view.state === viewState.DIFFING) { 68 viewToolbarOptions = dom.div( 69 { 70 className: "toolbar-group", 71 }, 72 73 dom.label( 74 { 75 className: "display-by", 76 title: L10N.getStr("toolbar.displayBy.tooltip"), 77 }, 78 L10N.getStr("toolbar.displayBy"), 79 dom.select( 80 { 81 id: "select-display", 82 className: "devtools-toolbar-select select-display", 83 onChange: e => { 84 const newDisplay = censusDisplays.find( 85 b => b.displayName === e.target.value 86 ); 87 onCensusDisplayChange(newDisplay); 88 }, 89 value: censusDisplay.displayName, 90 }, 91 censusDisplays.map(({ tooltip, displayName }) => 92 dom.option( 93 { 94 key: `display-${displayName}`, 95 value: displayName, 96 title: tooltip, 97 }, 98 displayName 99 ) 100 ) 101 ) 102 ), 103 104 dom.span({ className: "devtools-separator" }), 105 106 dom.input({ 107 id: "filter", 108 type: "search", 109 className: "devtools-filterinput", 110 placeholder: L10N.getStr("filter.placeholder"), 111 title: L10N.getStr("filter.tooltip"), 112 onChange: event => setFilterString(event.target.value), 113 value: filterString || undefined, 114 }) 115 ); 116 } else if (view.state == viewState.TREE_MAP) { 117 assert( 118 treeMapDisplays.length >= 1, 119 "Should always have at least one tree map display" 120 ); 121 122 // Only show the dropdown if there are multiple display options 123 viewToolbarOptions = 124 treeMapDisplays.length > 1 125 ? dom.div( 126 { 127 className: "toolbar-group", 128 }, 129 130 dom.label( 131 { 132 className: "display-by", 133 title: L10N.getStr("toolbar.displayBy.tooltip"), 134 }, 135 L10N.getStr("toolbar.displayBy"), 136 dom.select( 137 { 138 id: "select-tree-map-display", 139 onChange: e => { 140 const newDisplay = treeMapDisplays.find( 141 b => b.displayName === e.target.value 142 ); 143 onTreeMapDisplayChange(newDisplay); 144 }, 145 }, 146 treeMapDisplays.map(({ tooltip, displayName }) => 147 dom.option( 148 { 149 key: `tree-map-display-${displayName}`, 150 value: displayName, 151 title: tooltip, 152 }, 153 displayName 154 ) 155 ) 156 ) 157 ) 158 ) 159 : null; 160 } else { 161 assert( 162 view.state === viewState.DOMINATOR_TREE || 163 view.state === viewState.INDIVIDUALS 164 ); 165 166 viewToolbarOptions = dom.div( 167 { 168 className: "toolbar-group", 169 }, 170 171 dom.label( 172 { 173 className: "label-by", 174 title: L10N.getStr("toolbar.labelBy.tooltip"), 175 }, 176 L10N.getStr("toolbar.labelBy"), 177 dom.select( 178 { 179 id: "select-label-display", 180 className: "devtools-toolbar-select select-label-display", 181 onChange: e => { 182 const newDisplay = labelDisplays.find( 183 b => b.displayName === e.target.value 184 ); 185 onLabelDisplayChange(newDisplay); 186 }, 187 value: labelDisplay.displayName, 188 }, 189 labelDisplays.map(({ tooltip, displayName }) => 190 dom.option( 191 { 192 key: `label-display-${displayName}`, 193 value: displayName, 194 title: tooltip, 195 }, 196 displayName 197 ) 198 ) 199 ) 200 ) 201 ); 202 } 203 204 let viewSelect; 205 if ( 206 view.state !== viewState.DIFFING && 207 view.state !== viewState.INDIVIDUALS 208 ) { 209 viewSelect = dom.label( 210 { 211 title: L10N.getStr("toolbar.view.tooltip"), 212 }, 213 L10N.getStr("toolbar.view"), 214 dom.select( 215 { 216 id: "select-view", 217 className: "devtools-toolbar-select select-view", 218 onChange: e => onViewChange(e.target.value), 219 value: view.state, 220 }, 221 dom.option( 222 { 223 value: viewState.TREE_MAP, 224 title: L10N.getStr("toolbar.view.treemap.tooltip"), 225 }, 226 L10N.getStr("toolbar.view.treemap") 227 ), 228 dom.option( 229 { 230 value: viewState.CENSUS, 231 title: L10N.getStr("toolbar.view.census.tooltip"), 232 }, 233 L10N.getStr("toolbar.view.census") 234 ), 235 dom.option( 236 { 237 value: viewState.DOMINATOR_TREE, 238 title: L10N.getStr("toolbar.view.dominators.tooltip"), 239 }, 240 L10N.getStr("toolbar.view.dominators") 241 ) 242 ) 243 ); 244 } 245 246 return dom.div( 247 { 248 className: "devtools-toolbar", 249 }, 250 251 dom.div( 252 { 253 className: "toolbar-group", 254 }, 255 256 dom.button({ 257 id: "clear-snapshots", 258 className: "clear-snapshots devtools-button", 259 disabled: !snapshots.length, 260 onClick: onClearSnapshotsClick, 261 title: L10N.getStr("clear-snapshots.tooltip"), 262 }), 263 264 dom.button({ 265 id: "take-snapshot", 266 className: "take-snapshot devtools-button", 267 onClick: onTakeSnapshotClick, 268 title: L10N.getStr("take-snapshot"), 269 }), 270 271 dom.button({ 272 id: "diff-snapshots", 273 className: 274 "devtools-button devtools-monospace" + (diffing ? " checked" : ""), 275 disabled: snapshots.length < 2, 276 onClick: onToggleDiffing, 277 title: L10N.getStr("diff-snapshots.tooltip"), 278 }), 279 280 dom.button({ 281 id: "import-snapshot", 282 className: "import-snapshot devtools-button", 283 onClick: onImportClick, 284 title: L10N.getStr("import-snapshot"), 285 }) 286 ), 287 288 dom.label( 289 { 290 id: "record-allocation-stacks-label", 291 title: L10N.getStr("checkbox.recordAllocationStacks.tooltip"), 292 }, 293 dom.input({ 294 id: "record-allocation-stacks-checkbox", 295 type: "checkbox", 296 checked: allocations.recording, 297 disabled: allocations.togglingInProgress, 298 onChange: onToggleRecordAllocationStacks, 299 }), 300 L10N.getStr("checkbox.recordAllocationStacks") 301 ), 302 303 viewSelect, 304 viewToolbarOptions 305 ); 306 } 307 } 308 309 module.exports = Toolbar;