diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html')
-rw-r--r-- | chromium/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html | 327 |
1 files changed, 288 insertions, 39 deletions
diff --git a/chromium/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html b/chromium/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html index 2f9157c4a22..b5c7dbdb50f 100644 --- a/chromium/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html +++ b/chromium/third_party/blink/renderer/core/inspector/inspect_tool_highlight.html @@ -66,7 +66,7 @@ body { border-radius: 3px; box-sizing: border-box; min-width: 100px; - max-width: 300px; + max-width: min(300px, 100% - 4px); z-index: 1; background-clip: padding-box; will-change: transform; @@ -87,6 +87,20 @@ body { visibility: var(--arrow-visibility); } +.element-info-section { + margin-top: 12px; + margin-bottom: 6px; +} + +.section-name { + color: #333; + font-weight: 500; + font-size: 10px; + text-transform: uppercase; + letter-spacing: .05em; + line-height: 12px; +} + .element-info { display: flex; flex-direction: column; @@ -109,13 +123,20 @@ body { line-height: 19px; } -.element-info-contrast-row { +.separator-container { + display: flex; + align-items: center; + flex: auto; + margin-left: 7px; +} + +.separator { border-top: 1px solid #ddd; - padding-top: 5px; - margin-top: 5px; + width: 100%; } .element-info-name { + flex-shrink: 0; color: #666; } @@ -132,11 +153,21 @@ body { .element-info-value-contrast { display: flex; + align-items: center; text-align: right; color: rgb(48, 57, 66); margin-left: 10px; } +.element-info-value-contrast .a11y-icon { + margin-left: 8px; +} + +.element-info-value-icon { + display: flex; + align-items: center; +} + .element-info-value-text { text-align: right; color: rgb(48, 57, 66); @@ -205,16 +236,20 @@ body { .a11y-icon { width: 16px; height: 16px; - margin-left: 2px; background-repeat: no-repeat; + display: inline-block; +} + +.a11y-icon-not-ok { + background-image: url('data:image/svg+xml,<svg fill="none" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="m9 1.5c-4.14 0-7.5 3.36-7.5 7.5s3.36 7.5 7.5 7.5 7.5-3.36 7.5-7.5-3.36-7.5-7.5-7.5zm0 13.5c-3.315 0-6-2.685-6-6 0-1.3875.4725-2.6625 1.2675-3.675l8.4075 8.4075c-1.0125.795-2.2875 1.2675-3.675 1.2675zm4.7325-2.325-8.4075-8.4075c1.0125-.795 2.2875-1.2675 3.675-1.2675 3.315 0 6 2.685 6 6 0 1.3875-.4725 2.6625-1.2675 3.675z" fill="%239e9e9e"/></svg>'); } .a11y-icon-warning { - background-image: url("data:image/svg+xml;charset=UTF-8, %3csvg xmlns='http://www.w3.org/2000/svg' width='16px' height='16px' viewBox='0 0 18 18' fill='%23ffc107'%3e%3cpath d='M0 0h18v18H0z' fill='none'/%3e%3cpath d='M.5 16h17L9 1 .5 16zm9.5-2H8v-2h2v2zm0-3H8V7h2v4z'/%3e%3c/svg%3e "); + background-image: url('data:image/svg+xml,<svg fill="none" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="m8.25 11.25h1.5v1.5h-1.5zm0-6h1.5v4.5h-1.5zm.7425-3.75c-4.14 0-7.4925 3.36-7.4925 7.5s3.3525 7.5 7.4925 7.5c4.1475 0 7.5075-3.36 7.5075-7.5s-3.36-7.5-7.5075-7.5zm.0075 13.5c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6z" fill="%23e37400"/></svg>'); } .a11y-icon-ok { - background-image: url("data:image/svg+xml;charset=UTF-8, %3csvg xmlns='http://www.w3.org/2000/svg' width='16px' height='16px' viewBox='0 0 48 48' fill='%2300a000'%3e%3cpath d='M0 0h48v48H0z' fill='none'/%3e%3cpath d='M18 32.34L9.66 24l-2.83 2.83L18 38l24-24-2.83-2.83z'/%3e%3c/svg%3e"); + background-image: url('data:image/svg+xml,<svg fill="none" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="m9 1.5c-4.14 0-7.5 3.36-7.5 7.5s3.36 7.5 7.5 7.5 7.5-3.36 7.5-7.5-3.36-7.5-7.5-7.5zm0 13.5c-3.3075 0-6-2.6925-6-6s2.6925-6 6-6 6 2.6925 6 6-2.6925 6-6 6zm-1.5-4.35-1.95-1.95-1.05 1.05 3 3 6-6-1.05-1.05z" fill="%230ca40c"/></svg>'); } @media (forced-colors: active) { @@ -233,13 +268,14 @@ body { } .color-swatch-inner, .contrast-text, - .element-info-contrast-row { + .separator { border-color: Highlight; } .dimensions, .element-info-name, .element-info-value-color, .element-info-value-contrast, + .element-info-value-icon, .element-info-value-text, .material-tag-name, .material-class-name, @@ -407,14 +443,68 @@ function parseHexa(hexa) { } /** - * @param {!String} hexa + * TODO(alexrudenko): import this and other color helpers from DevTools + * @param {!Array<number>} rgba * @return {!Array<number>} */ -function normalizeColorString(hexa) { - if (hexa.endsWith("FF")) +function rgbaToHsla(rgba) { + const [r, g, b] = rgba; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const diff = max - min; + const sum = max + min; + + let h; + if (min === max) { + h = 0; + } else if (r === max) { + h = ((1 / 6 * (g - b) / diff) + 1) % 1; + } else if (g === max) { + h = (1 / 6 * (b - r) / diff) + 1 / 3; + } else { + h = (1 / 6 * (r - g) / diff) + 2 / 3; + } + + const l = 0.5 * sum; + + let s; + if (l === 0) { + s = 0; + } else if (l === 1) { + s = 0; + } else if (l <= 0.5) { + s = diff / sum; + } else { + s = diff / (2 - sum); + } + + return [h, s, l, rgba[3]]; +} + +/** + * @param {!String} hexa + * @param {!String} colorFormat + * @return {!String} + */ +function formatColor(hexa, colorFormat) { + if (colorFormat === 'rgb') { + const [r, g, b, a] = parseHexa(hexa); + // rgb(r g b [ / a]) + return `rgb(${(r * 255).toFixed()} ${(g * 255).toFixed()} ${(b * 255).toFixed()}${a === 1 ? '' : ' / ' + Math.round(a * 100) / 100})`; + } + + if (colorFormat === 'hsl') { + const [h, s, l, a] = rgbaToHsla(parseHexa(hexa)); + // hsl(hdeg s l [ / a]) + return `hsl(${Math.round(h * 360)}deg ${Math.round(s * 100)} ${Math.round(l * 100)}${a === 1 ? '' : ' / ' + Math.round(a * 100) / 100})`; + } + + if (hexa.endsWith("FF")) { + // short hex if no alpha return hexa.substr(0, 7); - const [r, g, b, a] = parseHexa(hexa); - return `rgba(${(r * 255).toFixed()}, ${(g * 255).toFixed()}, ${(b * 255).toFixed()}, ${Math.round(a * 100) / 100})`; + } + + return hexa; } /** @@ -484,7 +574,7 @@ function computeIsLargeFont(contrast) { return fontSizePt >= 18; } -function _createElementDescription(elementInfo) +function _createElementDescription(elementInfo, colorFormat) { const elementInfoElement = createElement("div", "element-info"); const elementInfoHeaderElement = elementInfoElement.createChild("div", "element-info-header"); @@ -513,7 +603,7 @@ function _createElementDescription(elementInfo) const color = style["color"]; if (color && color !== "#00000000") - addColorRow("Color", color); + addColorRow("Color", color, colorFormat); const fontFamily = style["font-family"]; const fontSize = style["font-size"]; @@ -522,7 +612,7 @@ function _createElementDescription(elementInfo) const bgcolor = style["background-color"]; if (bgcolor && bgcolor !== "#00000000") - addColorRow("Background", bgcolor); + addColorRow("Background", bgcolor, colorFormat); const margin = style["margin"]; if (margin && margin !== "0px") @@ -533,12 +623,36 @@ function _createElementDescription(elementInfo) addTextRow("Padding", padding); const cbgColor = elementInfo.contrast ? elementInfo.contrast.backgroundColor : null; - if (color && color !== "#00000000" && cbgColor && cbgColor !== "#00000000") - addContrastRow(style["color"], elementInfo.contrast); + const hasContrastInfo = color && color !== "#00000000" && cbgColor && cbgColor !== "#00000000"; - function addRow(name, rowClassName, valueClassName) { + if (elementInfo.showAccessibilityInfo) { + addSection("Accessibility"); + + if (hasContrastInfo) + addContrastRow(style["color"], elementInfo.contrast); + + addTextRow("Name", elementInfo.accessibleName); + addTextRow("Role", elementInfo.accessibleRole); + addIconRow("Keyboard-focusable", elementInfo.isKeyboardFocusable ? "a11y-icon a11y-icon-ok" : "a11y-icon a11y-icon-not-ok"); + } + + function ensureElementInfoBody() { if (!elementInfoBodyElement) elementInfoBodyElement = elementInfoElement.createChild("div", "element-info-body") + } + + function addSection(name) { + ensureElementInfoBody(); + const rowElement = elementInfoBodyElement.createChild("div", "element-info-row element-info-section"); + const nameElement = rowElement.createChild("div", "section-name"); + nameElement.textContent = name; + const separatorElement = rowElement + .createChild("div", "separator-container") + .createChild("div", "separator"); + } + + function addRow(name, rowClassName, valueClassName) { + ensureElementInfoBody(); const rowElement = elementInfoBodyElement.createChild("div", "element-info-row"); if (rowClassName) rowElement.classList.add(rowClassName); @@ -548,22 +662,26 @@ function _createElementDescription(elementInfo) return rowElement.createChild("div", valueClassName || ""); } + function addIconRow(name, value) { + addRow(name, "", "element-info-value-icon").createChild('div', value); + } + function addTextRow(name, value) { addRow(name, "", "element-info-value-text").createTextChild(value); } - function addColorRow(name, color) { + function addColorRow(name, color, colorFormat) { const valueElement = addRow(name, "", "element-info-value-color"); const swatch = valueElement.createChild("div", "color-swatch"); const inner = swatch.createChild("div", "color-swatch-inner"); inner.style.backgroundColor = color; - valueElement.createTextChild(normalizeColorString(color)); + valueElement.createTextChild(formatColor(color, colorFormat)); } function addContrastRow(fgColor, contrast) { const ratio = contrastRatio(parseHexa(fgColor), parseHexa(contrast.backgroundColor)); const threshold = computeIsLargeFont(contrast) ? 3.0 : 4.5; - const valueElement = addRow("Contrast", "element-info-contrast-row", "element-info-value-contrast"); + const valueElement = addRow("Contrast", "", "element-info-value-contrast"); const sampleText = valueElement.createChild("div", "contrast-text"); sampleText.style.color = fgColor; sampleText.style.backgroundColor = contrast.backgroundColor; @@ -576,11 +694,11 @@ function _createElementDescription(elementInfo) return elementInfoElement; } -function _drawElementTitle(elementInfo, bounds) +function _drawElementTitle(elementInfo, bounds, colorFormat) { const tooltipContainer = document.getElementById("tooltip-container"); tooltipContainer.removeChildren(); - _createMaterialTooltip(tooltipContainer, bounds, _createElementDescription(elementInfo), true); + _createMaterialTooltip(tooltipContainer, bounds, _createElementDescription(elementInfo, colorFormat), true); } function _createMaterialTooltip(parentElement, bounds, contentElement, withArrow) @@ -651,14 +769,17 @@ function _createMaterialTooltip(parentElement, bounds, contentElement, withArrow tooltipContent.style.setProperty('--arrow-left', (arrowX - boxX) + 'px'); } -function _drawRulers(context, bounds, rulerAtRight, rulerAtBottom) +function _drawRulers(context, bounds, rulerAtRight, rulerAtBottom, color, dash) { context.save(); var width = canvasWidth; var height = canvasHeight; - context.strokeStyle = "rgba(128, 128, 128, 0.3)"; + context.strokeStyle = color || "rgba(128, 128, 128, 0.3)"; context.lineWidth = 1; context.translate(0.5, 0.5); + if (dash) { + context.setLineDash([3, 3]); + } if (rulerAtRight) { for (var y in bounds.rightmostXForY) { @@ -779,22 +900,150 @@ function emptyBounds() function _drawLayoutGridHighlight(highlight, context) { + // Draw Grid border + if (highlight.gridHighlightConfig.gridBorderColor) { + context.save(); + context.translate(0.5, 0.5); + context.lineWidth = 0; + if (highlight.gridHighlightConfig.gridBorderDash) { + context.setLineDash([3, 3]); + } + context.strokeStyle = highlight.gridHighlightConfig.gridBorderColor; + context.stroke(buildPath(highlight.gridBorder, emptyBounds())); + context.restore(); + } + + // Draw Cell Border + if (highlight.gridHighlightConfig.cellBorderColor) { + const rowBounds = emptyBounds(); + const columnBounds = emptyBounds(); + const rowPath = buildPath(highlight.rows, rowBounds); + const columnPath = buildPath(highlight.columns, columnBounds); + context.save(); + context.translate(0.5, 0.5); + if (highlight.gridHighlightConfig.cellBorderDash) { + context.setLineDash([3, 3]); + } + context.lineWidth = 0; + context.strokeStyle = highlight.gridHighlightConfig.cellBorderColor; + + context.save(); + context.clip(columnPath); + context.stroke(rowPath); + context.restore(); + + context.save(); + context.clip(rowPath); + context.stroke(columnPath); + context.restore(); + + context.restore(); + + if (highlight.gridHighlightConfig.showGridExtensionLines) { + // Extend row gap lines left/up. + _drawRulers(context, rowBounds, /* rulerAtRight */ false, /* rulerAtBottom */ false, highlight.gridHighlightConfig.cellBorderColor, highlight.gridHighlightConfig.cellBorderDash); + // Extend row gap right/down. + _drawRulers(context, rowBounds, /* rulerAtRight */ true, /* rulerAtBottom */ true, highlight.gridHighlightConfig.cellBorderColor, highlight.gridHighlightConfig.cellBorderDash); + // Extend column lines left/up. + _drawRulers(context, columnBounds, /* rulerAtRight */ false, /* rulerAtBottom */ false, highlight.gridHighlightConfig.cellBorderColor, highlight.gridHighlightConfig.cellBorderDash); + // Extend column right/down. + _drawRulers(context, columnBounds, /* rulerAtRight */ true, /* rulerAtBottom */ true, highlight.gridHighlightConfig.cellBorderColor, highlight.gridHighlightConfig.cellBorderDash); + } + } + + // Row Gaps + if (highlight.gridHighlightConfig.rowGapColor) { + context.save(); + context.translate(0.5, 0.5); + context.lineWidth = 0; + context.fillStyle = highlight.gridHighlightConfig.rowGapColor; + let bounds = emptyBounds(); + const path = buildPath(highlight.rowGaps, bounds); + if (highlight.gridHighlightConfig.rowHatchColor) { + hatchFillPath(context, path, bounds, 10, highlight.gridHighlightConfig.rowHatchColor, /* flipDirection */ true); + } + context.fill(path); + context.restore(); + } + + // Column Gaps + if (highlight.gridHighlightConfig.columnGapColor) { + context.save(); + context.translate(0.5, 0.5); + context.lineWidth = 0; + context.fillStyle = highlight.gridHighlightConfig.columnGapColor; + let bounds = emptyBounds(); + const path = buildPath(highlight.columnGaps, bounds); + if (highlight.gridHighlightConfig.columnHatchColor) { + hatchFillPath(context, path, bounds, 10, highlight.gridHighlightConfig.columnHatchColor); + } + context.fill(path); + context.restore(); + } +} + +/** + * Draw line hatching at a 45 degree angle for a given + * path. + * __________ + * |\ \ \ | + * | \ \ \| + * | \ \ | + * |\ \ \ | + * ********** + * + * @param {CanvasRenderingContext2D} context + * @param {Path2D} path + * @param {Object} bounds + * @param {delta} delta - vertical gap between hatching lines in pixels + * @param {string} color + * @param {boolean=} flipDirection - lines are drawn from top right to bottom left + * + */ +function hatchFillPath(context, path, bounds, delta, color, flipDirection) { + const dx = bounds.maxX - bounds.minX; + const dy = bounds.maxY - bounds.minY; + context.rect(bounds.minX, bounds.minY, dx, dy); context.save(); - context.translate(0.5, 0.5); - context.setLineDash([3, 3]); - context.lineWidth = 0; - context.strokeStyle = highlight.color; - context.stroke(buildPath(highlight.cells, emptyBounds())); + context.clip(path); + context.setLineDash([5, 3]); + const majorAxis = Math.max(dx, dy); + context.strokeStyle = color; + if (flipDirection) { + for (let i = -majorAxis; i < majorAxis; i += delta) { + context.beginPath(); + context.moveTo(bounds.maxX - i , bounds.minY); + context.lineTo(bounds.maxX - dy - i, bounds.maxY); + context.stroke(); + } + } else { + for (let i = -majorAxis; i < majorAxis; i += delta) { + context.beginPath(); + context.moveTo(i + bounds.minX, bounds.minY); + context.lineTo(dy + i + bounds.minX, bounds.maxY); + context.stroke(); + } + } context.restore(); } -function clipLayoutGridCells(highlight) { +function clipLayoutGridCells(highlight, context) { + // It may seem simpler to, before drawing the desired path, call context.clip() + // with the rows and then with the columns. However, the 2nd context.clip() call + // would try to find the intersection of the rows and columns, which is way too + // expensive if the grid is huge, e.g. a 1000x1000 grid has 1M cells. + // Therefore, it's better to draw the path first, set the globalCompositeOperation + // so that the existing canvas content is kept where it overlaps with new content, + // and then draw the rows and columns. if (highlight.gridInfo) { for (const grid of highlight.gridInfo) { if (!grid.isPrimaryGrid) continue; - const path = buildPath(grid.cells, emptyBounds()); - context.clip(path); + context.save(); + context.globalCompositeOperation = "destination-in"; + drawPath(context, grid.rows, "red", null, emptyBounds()); + drawPath(context, grid.columns, "red", null, emptyBounds()); + context.restore(); } } } @@ -808,15 +1057,15 @@ function drawHighlight(highlight, context) for (var paths = highlight.paths.slice(); paths.length;) { var path = paths.pop(); - // Clip content quad using the data grid cells info to create white stripes. context.save(); - if (path.name === "content") - clipLayoutGridCells(highlight); drawPath(context, path.path, path.fillColor, path.outlineColor, bounds); if (paths.length) { context.globalCompositeOperation = "destination-out"; drawPath(context, paths[paths.length - 1].path, "red", null, bounds); } + // Clip content quad using the data grid cells info to create white stripes. + if (path.name === "content") + clipLayoutGridCells(highlight, context); context.restore(); } context.restore(); @@ -834,7 +1083,7 @@ function drawHighlight(highlight, context) _drawRulers(context, bounds, rulerAtRight, rulerAtBottom); if (highlight.elementInfo) - _drawElementTitle(highlight.elementInfo, bounds); + _drawElementTitle(highlight.elementInfo, bounds, highlight.colorFormat); } if (highlight.gridInfo) { for (var grid of highlight.gridInfo) @@ -851,7 +1100,7 @@ function test() { drawHighlight( {"paths":[{"path":["M",122,133.796875,"L",822,133.796875,"L",822,208.796875,"L",122,208.796875,"Z"], "fillColor":"rgba(111, 168, 220, 0.658823529411765)","name":"content"}, {"path":["M",122,113.796875,"L",822,113.796875,"L",822,228.796875,"L",122,228.796875,"Z"],"fillColor":"rgba(246, 178, 107, 0.66)","name":"margin"}],"showRulers":false,"showExtensionLines":false, - "elementInfo":{"tagName":"button","className":"class.name", "idValue":"download-hero","nodeWidth":"700","nodeHeight":"75","style":{"color":"#FFFFFFFF","font-family":"\"Product Sans\", \"Open Sans\", Roboto, Arial, \"Product Sans\", \"Open Sans\", Roboto, Arial","font-size":"20px","line-height":"25px","padding":"0px","margin":"20px 0px","background-color":"#00000000"},"contrast":{"fontSize":"20px","fontWeight":"400","backgroundColor":"#F9B826BF"}}, showExtensionLines: true, showRulers: true}); + "elementInfo":{"tagName":"button","className":"class.name", "idValue":"download-hero","nodeWidth":"700","nodeHeight":"75","style":{"color":"#FFFFFFFF","font-family":"\"Product Sans\", \"Open Sans\", Roboto, Arial, \"Product Sans\", \"Open Sans\", Roboto, Arial","font-size":"20px","line-height":"25px","padding":"0px","margin":"20px 0px","background-color":"#00000000"},"contrast":{"fontSize":"20px","fontWeight":"400","backgroundColor":"#F9B826BF"},"isKeyboardFocusable":false,"accessibleName":"name","accessibleRole":"role","showAccessibilityInfo":true}, showExtensionLines: true, showRulers: true, colorFormat: "hsl"}); } </script> |