VariableTooltipHelper.js (7108B)
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 XHTML_NS = "http://www.w3.org/1999/xhtml"; 8 9 const OutputParser = require("resource://devtools/client/shared/output-parser.js"); 10 loader.lazyGetter(this, "L10N", function () { 11 const { LocalizationHelper } = require("resource://devtools/shared/l10n.js"); 12 return new LocalizationHelper( 13 "devtools/shared/locales/styleinspector.properties" 14 ); 15 }); 16 loader.lazyGetter(this, "L10N_EMPTY", function () { 17 return L10N.getStr("rule.variableEmpty"); 18 }); 19 loader.lazyGetter(this, "L10N_COMPUTED_VALUE", function () { 20 return L10N.getStr("rule.variableComputedValue"); 21 }); 22 23 /** 24 * Set the tooltip content of a provided HTMLTooltip instance to display a 25 * variable preview matching the provided text. 26 * 27 * @param {HTMLTooltip} tooltip 28 * The tooltip instance on which the text preview content should be set. 29 * @param {Document} doc 30 * A document element to create the HTML elements needed for the tooltip. 31 * @param {object} params 32 * @param {string} params.computed 33 * The computed value for the variable. 34 * @param {object} params.outputParserOptions 35 * Options to pass to the OutputParser. At the moment, this is the same object that 36 * we use in the Rules view, so we have the same output in the variable tooltip 37 * than in the Rules view. 38 * @param {object} params.registeredProperty 39 * Contains the registered property data, if the variable was registered (@property or CSS.registerProperty) 40 * @param {string} params.registeredProperty.syntax 41 * The registered property `syntax` value 42 * @param {boolean} params.registeredProperty.inherits 43 * The registered property `inherits` value 44 * @param {string} params.registeredProperty.initialValue 45 * The registered property `initial-value` 46 * @param {string} params.startingStyle 47 * The text for @starting-style value (e.g. `red`) 48 * @param {string} params.topSectionText 49 * Text to display in the top section of tooltip (e.g. "blue" or "--x is not defined"). 50 * @param {string} params.variableName 51 * The name of the variable we're showing the tooltip for 52 */ 53 function setVariableTooltip( 54 tooltip, 55 doc, 56 { 57 computed, 58 cssProperties, 59 outputParserOptions, 60 registeredProperty, 61 startingStyle, 62 topSectionText, 63 variableName, 64 } 65 ) { 66 // Create tooltip content 67 const div = doc.createElementNS(XHTML_NS, "div"); 68 div.classList.add("devtools-monospace", "devtools-tooltip-css-variable"); 69 70 const outputParser = new OutputParser(doc, cssProperties); 71 const parse = value => 72 outputParser.parseCssProperty(variableName, value, { 73 ...outputParserOptions, 74 colorSwatchReadOnly: true, 75 // At the moment, we can't hover the tooltip, so the button couldn't be clicked/activated 76 showJumpToVariableButton: false, 77 }); 78 79 const valueEl = doc.createElementNS(XHTML_NS, "section"); 80 valueEl.classList.add("variable-value"); 81 const varData = outputParserOptions.getVariableData(variableName); 82 // If the variable is not defined, append the text as is so we don't get the additional 83 // class added by appendValue. 84 if ( 85 typeof varData.value !== "string" && 86 typeof registeredProperty?.initialValue !== "string" 87 ) { 88 valueEl.append(doc.createTextNode(topSectionText)); 89 } else { 90 appendValue(doc, valueEl, topSectionText, parse); 91 } 92 div.appendChild(valueEl); 93 94 if (typeof computed !== "undefined") { 95 const section = doc.createElementNS(XHTML_NS, "section"); 96 section.classList.add("computed", "variable-tooltip-section"); 97 98 const h2 = doc.createElementNS(XHTML_NS, "h2"); 99 h2.append(doc.createTextNode(L10N_COMPUTED_VALUE)); 100 const computedValueEl = doc.createElementNS(XHTML_NS, "div"); 101 appendValue(doc, computedValueEl, computed, parse); 102 section.append(h2, computedValueEl); 103 104 div.appendChild(section); 105 } 106 107 if (typeof startingStyle !== "undefined") { 108 const section = doc.createElementNS(XHTML_NS, "section"); 109 section.classList.add("starting-style", "variable-tooltip-section"); 110 111 const h2 = doc.createElementNS(XHTML_NS, "h2"); 112 h2.append(doc.createTextNode("@starting-style")); 113 const startingStyleValue = doc.createElementNS(XHTML_NS, "div"); 114 appendValue(doc, startingStyleValue, startingStyle, parse); 115 section.append(h2, startingStyleValue); 116 117 div.appendChild(section); 118 } 119 120 // A registered property always have a non-falsy syntax 121 if (registeredProperty?.syntax) { 122 const section = doc.createElementNS(XHTML_NS, "section"); 123 section.classList.add("registered-property", "variable-tooltip-section"); 124 125 const h2 = doc.createElementNS(XHTML_NS, "h2"); 126 h2.append(doc.createTextNode("@property")); 127 128 const dl = doc.createElementNS(XHTML_NS, "dl"); 129 const addProperty = ({ label, value, parseValue, lineBreak }) => { 130 const dt = doc.createElementNS(XHTML_NS, "dt"); 131 dt.append(doc.createTextNode(label)); 132 const dd = doc.createElementNS(XHTML_NS, "dd"); 133 appendValue(doc, dd, value, parseValue ? parse : null); 134 dl.append(dt, dd); 135 if (lineBreak) { 136 dl.append(doc.createElementNS(XHTML_NS, "br")); 137 } 138 }; 139 140 const hasInitialValue = typeof registeredProperty.initialValue === "string"; 141 142 addProperty({ 143 label: "syntax:", 144 value: `"${registeredProperty.syntax}"`, 145 parseValue: false, 146 lineBreak: true, 147 }); 148 addProperty({ 149 label: "inherits:", 150 value: registeredProperty.inherits, 151 parseValue: false, 152 lineBreak: hasInitialValue, 153 }); 154 if (hasInitialValue) { 155 addProperty({ 156 label: "initial-value:", 157 value: registeredProperty.initialValue, 158 parseValue: true, 159 lineBreak: false, 160 }); 161 } 162 163 section.append(h2, dl); 164 div.appendChild(section); 165 } 166 167 tooltip.panel.innerHTML = ""; 168 tooltip.panel.appendChild(div); 169 tooltip.setContentSize({ width: "auto", height: "auto" }); 170 } 171 172 /** 173 * Append a value into the passed element. 174 * 175 * @param {Document} doc: A document that will be used to create elements 176 * @param {Element} el: The element into which the rendered value will be appended 177 * @param {string} value: The value we want to append 178 * @param {Function} parse: An optional function that will be called with `value`, and whose 179 * result will be appended to `el`. If not passed, `value` will be appended 180 * as is in `el`, as a text node (if it's not empty). 181 */ 182 function appendValue(doc, el, value, parse) { 183 if (value !== "") { 184 const frag = parse && parse(value); 185 if (frag) { 186 el.append(frag); 187 } else { 188 el.append(doc.createTextNode(value)); 189 } 190 el.classList.add("theme-fg-color1"); 191 } else { 192 el.append(doc.createTextNode(`<${L10N_EMPTY}>`)); 193 el.classList.add("empty-css-variable"); 194 } 195 } 196 197 module.exports.setVariableTooltip = setVariableTooltip;