chrome-devtools-frontend
Version:
Chrome DevTools UI
1,390 lines (1,337 loc) • 42.7 kB
text/typescript
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/*
* Copyright (C) 2010 Nikita Vasilyev. All rights reserved.
* Copyright (C) 2010 Joseph Pecoraro. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the #name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import * as Protocol from '../../generated/protocol.js';
import * as SupportedCSSProperties from '../../generated/SupportedCSSProperties.js';
import * as Common from '../common/common.js';
import * as Platform from '../platform/platform.js';
export class CSSMetadata {
readonly #values: string[];
readonly #longhands: Map<string, string[]>;
readonly #shorthands: Map<string, string[]>;
readonly #inherited: Set<string>;
readonly #svgProperties: Set<string>;
readonly #propertyValues: Map<string, string[]>;
readonly #aliasesFor: Map<string, string>;
#valuesSet: Set<string>;
readonly #nameValuePresetsInternal: string[];
readonly #nameValuePresetsIncludingSVG: string[];
constructor(properties: CSSPropertyDefinition[], aliasesFor: Map<string, string>) {
this.#values = [];
this.#longhands = new Map();
this.#shorthands = new Map();
this.#inherited = new Set();
this.#svgProperties = new Set();
this.#propertyValues = new Map();
this.#aliasesFor = aliasesFor;
for (let i = 0; i < properties.length; ++i) {
const property = properties[i];
const propertyName = property.name;
if (!CSS.supports(propertyName, 'initial')) {
continue;
}
this.#values.push(propertyName);
if (property.inherited) {
this.#inherited.add(propertyName);
}
if (property.svg) {
this.#svgProperties.add(propertyName);
}
const longhands = properties[i].longhands;
if (longhands) {
this.#longhands.set(propertyName, longhands);
for (let j = 0; j < longhands.length; ++j) {
const longhandName = longhands[j];
let shorthands = this.#shorthands.get(longhandName);
if (!shorthands) {
shorthands = [];
this.#shorthands.set(longhandName, shorthands);
}
shorthands.push(propertyName);
}
}
}
this.#values.sort(CSSMetadata.sortPrefixesToEnd);
this.#valuesSet = new Set(this.#values);
// Reads in auto-generated property names and #values from blink/public/renderer/core/css/css_properties.json5
// treats _generatedPropertyValues as basis
const propertyValueSets = new Map<string, Set<string>>();
for (const [propertyName, basisValueObj] of Object.entries(SupportedCSSProperties.generatedPropertyValues)) {
propertyValueSets.set(propertyName, new Set(basisValueObj.values));
}
// and add manually maintained map of extra prop-value pairs
for (const [propertyName, extraValueObj] of Object.entries(extraPropertyValues)) {
const propertyValueSet = propertyValueSets.get(propertyName);
if (propertyValueSet) {
Platform.SetUtilities.addAll(propertyValueSet, extraValueObj.values);
} else {
propertyValueSets.set(propertyName, new Set(extraValueObj.values));
}
}
// finally add common keywords to value sets and convert property #values
// into arrays since callers expect arrays
for (const [propertyName, values] of propertyValueSets) {
for (const commonKeyword of CommonKeywords) {
if (!values.has(commonKeyword) && CSS.supports(propertyName, commonKeyword)) {
values.add(commonKeyword);
}
}
this.#propertyValues.set(propertyName, [...values]);
}
this.#nameValuePresetsInternal = [];
this.#nameValuePresetsIncludingSVG = [];
for (const name of this.#valuesSet) {
const values = this.specificPropertyValues(name)
.filter(value => CSS.supports(name, value))
.sort(CSSMetadata.sortPrefixesToEnd);
const presets = values.map(value => `${name}: ${value}`);
if (!this.isSVGProperty(name)) {
this.#nameValuePresetsInternal.push(...presets);
}
this.#nameValuePresetsIncludingSVG.push(...presets);
}
}
private static sortPrefixesToEnd(a: string, b: string): 1|- 1|0 {
const aIsPrefixed = a.startsWith('-webkit-');
const bIsPrefixed = b.startsWith('-webkit-');
if (aIsPrefixed && !bIsPrefixed) {
return 1;
}
if (!aIsPrefixed && bIsPrefixed) {
return -1;
}
return a < b ? -1 : (a > b ? 1 : 0);
}
allProperties(): string[] {
return this.#values;
}
aliasesFor(): Map<string, string> {
return this.#aliasesFor;
}
nameValuePresets(includeSVG?: boolean): string[] {
return includeSVG ? this.#nameValuePresetsIncludingSVG : this.#nameValuePresetsInternal;
}
isSVGProperty(name: string): boolean {
name = name.toLowerCase();
return this.#svgProperties.has(name);
}
getLonghands(shorthand: string): string[]|null {
return this.#longhands.get(shorthand) || null;
}
getShorthands(longhand: string): string[]|null {
return this.#shorthands.get(longhand) || null;
}
isColorAwareProperty(propertyName: string): boolean {
return colorAwareProperties.has(propertyName.toLowerCase()) || this.isCustomProperty(propertyName.toLowerCase());
}
isFontFamilyProperty(propertyName: string): boolean {
return propertyName.toLowerCase() === 'font-family';
}
isAngleAwareProperty(propertyName: string): boolean {
const lowerCasedName = propertyName.toLowerCase();
// TODO: @Yisi, parse hsl(), hsla(), hwb() and lch()
// See also https://drafts.csswg.org/css-color/#hue-syntax
return colorAwareProperties.has(lowerCasedName) || angleAwareProperties.has(lowerCasedName);
}
isGridAreaDefiningProperty(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
return propertyName === 'grid' || propertyName === 'grid-template' || propertyName === 'grid-template-areas';
}
isLengthProperty(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
if (propertyName === 'line-height') {
return false;
}
return distanceProperties.has(propertyName) || propertyName.startsWith('margin') ||
propertyName.startsWith('padding') || propertyName.indexOf('width') !== -1 ||
propertyName.indexOf('height') !== -1;
}
isBezierAwareProperty(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
return bezierAwareProperties.has(propertyName) || this.isCustomProperty(propertyName);
}
isFontAwareProperty(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
return fontAwareProperties.has(propertyName) || this.isCustomProperty(propertyName);
}
isCustomProperty(propertyName: string): boolean {
return propertyName.startsWith('--');
}
isShadowProperty(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
return propertyName === 'box-shadow' || propertyName === 'text-shadow' || propertyName === '-webkit-box-shadow';
}
isStringProperty(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
// TODO(crbug.com/1033910): Generalize this to all CSS properties
// that accept <string> #values.
return propertyName === 'content';
}
canonicalPropertyName(name: string): string {
if (this.isCustomProperty(name)) {
return name;
}
name = name.toLowerCase();
const aliasFor = this.#aliasesFor.get(name);
if (aliasFor) {
return aliasFor;
}
if (!name || name.length < 9 || name.charAt(0) !== '-') {
return name;
}
const match = name.match(/(?:-webkit-)(.+)/);
if (!match || !this.#valuesSet.has(match[1])) {
return name;
}
return match[1];
}
isCSSPropertyName(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
if ((propertyName.startsWith('--') && propertyName.length > 2) || propertyName.startsWith('-moz-') ||
propertyName.startsWith('-ms-') || propertyName.startsWith('-o-') || propertyName.startsWith('-webkit-')) {
return true;
}
return this.#valuesSet.has(propertyName);
}
isPropertyInherited(propertyName: string): boolean {
propertyName = propertyName.toLowerCase();
return propertyName.startsWith('--') || this.#inherited.has(this.canonicalPropertyName(propertyName)) ||
this.#inherited.has(propertyName);
}
private specificPropertyValues(propertyName: string): string[] {
const unprefixedName = propertyName.replace(/^-webkit-/, '');
const propertyValues = this.#propertyValues;
// #propertyValues acts like cache; missing properties are added with possible common keywords
let keywords: (string[]|undefined) = propertyValues.get(propertyName) || propertyValues.get(unprefixedName);
if (!keywords) {
keywords = [];
for (const commonKeyword of CommonKeywords) {
if (CSS.supports(propertyName, commonKeyword)) {
keywords.push(commonKeyword);
}
}
propertyValues.set(propertyName, keywords);
}
return keywords;
}
getPropertyValues(propertyName: string): string[] {
const acceptedKeywords = ['inherit', 'initial', 'revert', 'unset'];
propertyName = propertyName.toLowerCase();
acceptedKeywords.push(...this.specificPropertyValues(propertyName));
if (this.isColorAwareProperty(propertyName)) {
acceptedKeywords.push('currentColor');
for (const color of Common.Color.Nicknames.keys()) {
acceptedKeywords.push(color);
}
}
return acceptedKeywords.sort(CSSMetadata.sortPrefixesToEnd);
}
propertyUsageWeight(property: string): number {
return Weight.get(property) || Weight.get(this.canonicalPropertyName(property)) || 0;
}
getValuePreset(key: string, value: string): {
text: string,
startColumn: number,
endColumn: number,
}|null {
const values = valuePresets.get(key);
let text: string|(string | null | undefined) = values ? values.get(value) : null;
if (!text) {
return null;
}
let startColumn: number = text.length;
let endColumn: number = text.length;
if (text) {
startColumn = text.indexOf('|');
endColumn = text.lastIndexOf('|');
endColumn = startColumn === endColumn ? endColumn : endColumn - 1;
text = text.replace(/\|/g, '');
}
return {text, startColumn, endColumn};
}
isHighlightPseudoType(pseudoType: Protocol.DOM.PseudoType): boolean {
return (
pseudoType === Protocol.DOM.PseudoType.Highlight || pseudoType === Protocol.DOM.PseudoType.Selection ||
pseudoType === Protocol.DOM.PseudoType.TargetText || pseudoType === Protocol.DOM.PseudoType.GrammarError ||
pseudoType === Protocol.DOM.PseudoType.SpellingError);
}
}
export const VariableRegex = /(var\(\s*--.*?\))/g;
export const CustomVariableRegex = /(var\(*--[\w\d]+-([\w]+-[\w]+)\))/g;
export const URLRegex = /url\(\s*('.+?'|".+?"|[^)]+)\s*\)/g;
/**
* Matches an instance of a grid area 'row' definition.
* 'grid-template-areas', e.g.
* "a a ."
*
* 'grid', 'grid-template', e.g.
* [track-#name] "a a ." minmax(50px, auto) [track-#name]
*/
export const GridAreaRowRegex = /((?:\[[\w\- ]+\]\s*)*(?:"[^"]+"|'[^']+'))[^'"\[]*\[?[^'"\[]*/;
let cssMetadataInstance: CSSMetadata|null = null;
export function cssMetadata(): CSSMetadata {
if (!cssMetadataInstance) {
const supportedProperties = (SupportedCSSProperties.generatedProperties as CSSPropertyDefinition[]);
cssMetadataInstance = new CSSMetadata(supportedProperties, SupportedCSSProperties.generatedAliasesFor);
}
return cssMetadataInstance;
}
/**
* The pipe character '|' indicates where text selection should be set.
*/
const imageValuePresetMap = new Map([
['linear-gradient', 'linear-gradient(|45deg, black, transparent|)'],
['radial-gradient', 'radial-gradient(|black, transparent|)'],
['repeating-linear-gradient', 'repeating-linear-gradient(|45deg, black, transparent 100px|)'],
['repeating-radial-gradient', 'repeating-radial-gradient(|black, transparent 100px|)'],
['url', 'url(||)'],
]);
const filterValuePresetMap = new Map([
['blur', 'blur(|1px|)'],
['brightness', 'brightness(|0.5|)'],
['contrast', 'contrast(|0.5|)'],
['drop-shadow', 'drop-shadow(|2px 4px 6px black|)'],
['grayscale', 'grayscale(|1|)'],
['hue-rotate', 'hue-rotate(|45deg|)'],
['invert', 'invert(|1|)'],
['opacity', 'opacity(|0.5|)'],
['saturate', 'saturate(|0.5|)'],
['sepia', 'sepia(|1|)'],
['url', 'url(||)'],
]);
const valuePresets = new Map([
['filter', filterValuePresetMap],
['backdrop-filter', filterValuePresetMap],
['background', imageValuePresetMap],
['background-image', imageValuePresetMap],
['-webkit-mask-image', imageValuePresetMap],
[
'transform',
new Map([
['scale', 'scale(|1.5|)'],
['scaleX', 'scaleX(|1.5|)'],
['scaleY', 'scaleY(|1.5|)'],
['scale3d', 'scale3d(|1.5, 1.5, 1.5|)'],
['rotate', 'rotate(|45deg|)'],
['rotateX', 'rotateX(|45deg|)'],
['rotateY', 'rotateY(|45deg|)'],
['rotateZ', 'rotateZ(|45deg|)'],
['rotate3d', 'rotate3d(|1, 1, 1, 45deg|)'],
['skew', 'skew(|10deg, 10deg|)'],
['skewX', 'skewX(|10deg|)'],
['skewY', 'skewY(|10deg|)'],
['translate', 'translate(|10px, 10px|)'],
['translateX', 'translateX(|10px|)'],
['translateY', 'translateY(|10px|)'],
['translateZ', 'translateZ(|10px|)'],
['translate3d', 'translate3d(|10px, 10px, 10px|)'],
['matrix', 'matrix(|1, 0, 0, 1, 0, 0|)'],
['matrix3d', 'matrix3d(|1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1|)'],
['perspective', 'perspective(|10px|)'],
]),
],
]);
const distanceProperties = new Set<string>([
'background-position',
'border-spacing',
'bottom',
'font-size',
'height',
'left',
'letter-spacing',
'max-height',
'max-width',
'min-height',
'min-width',
'right',
'text-indent',
'top',
'width',
'word-spacing',
'grid-row-gap',
'grid-column-gap',
'row-gap',
]);
const bezierAwareProperties = new Set<string>([
'animation',
'animation-timing-function',
'transition',
'transition-timing-function',
'-webkit-animation',
'-webkit-animation-timing-function',
'-webkit-transition',
'-webkit-transition-timing-function',
]);
const fontAwareProperties =
new Set<string>(['font-size', 'line-height', 'font-weight', 'font-family', 'letter-spacing']);
const colorAwareProperties = new Set<string>([
'accent-color',
'background',
'background-color',
'background-image',
'border',
'border-color',
'border-image',
'border-image-source',
'border-bottom',
'border-bottom-color',
'border-left',
'border-left-color',
'border-right',
'border-right-color',
'border-top',
'border-top-color',
'box-shadow',
'caret-color',
'color',
'column-rule',
'column-rule-color',
'content',
'fill',
'list-style-image',
'outline',
'outline-color',
'stop-color',
'stroke',
'text-decoration-color',
'text-shadow',
'-webkit-border-after',
'-webkit-border-after-color',
'-webkit-border-before',
'-webkit-border-before-color',
'-webkit-border-end',
'-webkit-border-end-color',
'-webkit-border-start',
'-webkit-border-start-color',
'-webkit-box-reflect',
'-webkit-box-shadow',
'-webkit-column-rule-color',
'-webkit-mask',
'-webkit-mask-box-image',
'-webkit-mask-box-image-source',
'-webkit-mask-image',
'-webkit-tap-highlight-color',
'-webkit-text-decoration-color',
'-webkit-text-emphasis',
'-webkit-text-emphasis-color',
'-webkit-text-fill-color',
'-webkit-text-stroke',
'-webkit-text-stroke-color',
]);
// In addition to `_colorAwareProperties`, the following properties contain CSS <angle> units.
const angleAwareProperties = new Set<string>([
'-webkit-border-image',
'transform',
'-webkit-transform',
'rotate',
'filter',
'-webkit-filter',
'backdrop-filter',
'offset',
'offset-rotate',
'font-style',
]);
// manually maintained list of property #values to add into autocomplete list
const extraPropertyValues = {
'background-repeat': {values: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'space', 'round']},
'content': {values: ['normal', 'close-quote', 'no-close-quote', 'no-open-quote', 'open-quote']},
'baseline-shift': {values: ['baseline']},
'max-height': {values: ['min-content', 'max-content', '-webkit-fill-available', 'fit-content']},
'color': {values: ['black']},
'background-color': {values: ['white']},
'box-shadow': {values: ['inset']},
'text-shadow': {values: ['0 0 black']},
'-webkit-writing-mode': {values: ['horizontal-tb', 'vertical-rl', 'vertical-lr']},
'writing-mode': {values: ['lr', 'rl', 'tb', 'lr-tb', 'rl-tb', 'tb-rl']},
'page-break-inside': {values: ['avoid']},
'cursor': {values: ['-webkit-zoom-in', '-webkit-zoom-out', '-webkit-grab', '-webkit-grabbing']},
'border-width': {values: ['medium', 'thick', 'thin']},
'border-style': {values: ['hidden', 'inset', 'groove', 'ridge', 'outset', 'dotted', 'dashed', 'solid', 'double']},
'size': {values: ['a3', 'a4', 'a5', 'b4', 'b5', 'landscape', 'ledger', 'legal', 'letter', 'portrait']},
'overflow': {values: ['hidden', 'visible', 'overlay', 'scroll']},
'overscroll-behavior': {values: ['contain']},
'text-rendering': {values: ['optimizeSpeed', 'optimizeLegibility', 'geometricPrecision']},
'text-align': {values: ['-webkit-auto', '-webkit-match-parent']},
'clip-path': {values: ['circle', 'ellipse', 'inset', 'polygon', 'url']},
'color-interpolation': {values: ['sRGB', 'linearRGB']},
'word-wrap': {values: ['normal', 'break-word']},
'font-weight': {values: ['100', '200', '300', '400', '500', '600', '700', '800', '900']},
'-webkit-text-emphasis': {values: ['circle', 'filled', 'open', 'dot', 'double-circle', 'triangle', 'sesame']},
'color-rendering': {values: ['optimizeSpeed', 'optimizeQuality']},
'-webkit-text-combine': {values: ['horizontal']},
'text-orientation': {values: ['sideways-right']},
'outline': {
values: ['inset', 'groove', 'ridge', 'outset', 'dotted', 'dashed', 'solid', 'double', 'medium', 'thick', 'thin'],
},
'font': {
values: [
'caption',
'icon',
'menu',
'message-box',
'small-caption',
'-webkit-mini-control',
'-webkit-small-control',
'-webkit-control',
'status-bar',
],
},
'dominant-baseline': {values: ['text-before-edge', 'text-after-edge', 'use-script', 'no-change', 'reset-size']},
'-webkit-text-emphasis-position': {values: ['over', 'under']},
'alignment-baseline': {values: ['before-edge', 'after-edge', 'text-before-edge', 'text-after-edge', 'hanging']},
'page-break-before': {values: ['left', 'right', 'always', 'avoid']},
'border-image': {values: ['repeat', 'stretch', 'space', 'round']},
'text-decoration':
{values: ['blink', 'line-through', 'overline', 'underline', 'wavy', 'double', 'solid', 'dashed', 'dotted']},
// List taken from https://drafts.csswg.org/css-fonts-4/#generic-font-families
'font-family': {
values: [
'serif',
'sans-serif',
'cursive',
'fantasy',
'monospace',
'system-ui',
'emoji',
'math',
'fangsong',
'ui-serif',
'ui-sans-serif',
'ui-monospace',
'ui-rounded',
'-webkit-body',
],
},
'zoom': {values: ['normal']},
'max-width': {values: ['min-content', 'max-content', '-webkit-fill-available', 'fit-content']},
'-webkit-font-smoothing': {values: ['antialiased', 'subpixel-antialiased']},
'border': {
values: [
'hidden',
'inset',
'groove',
'ridge',
'outset',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'font-variant': {
values: [
'small-caps',
'normal',
'common-ligatures',
'no-common-ligatures',
'discretionary-ligatures',
'no-discretionary-ligatures',
'historical-ligatures',
'no-historical-ligatures',
'contextual',
'no-contextual',
'all-small-caps',
'petite-caps',
'all-petite-caps',
'unicase',
'titling-caps',
'lining-nums',
'oldstyle-nums',
'proportional-nums',
'tabular-nums',
'diagonal-fractions',
'stacked-fractions',
'ordinal',
'slashed-zero',
'jis78',
'jis83',
'jis90',
'jis04',
'simplified',
'traditional',
'full-width',
'proportional-width',
'ruby',
],
},
'vertical-align': {values: ['top', 'bottom', '-webkit-baseline-middle']},
'page-break-after': {values: ['left', 'right', 'always', 'avoid']},
'-webkit-text-emphasis-style': {values: ['circle', 'filled', 'open', 'dot', 'double-circle', 'triangle', 'sesame']},
'transform': {
values: [
'scale', 'scaleX', 'scaleY', 'scale3d', 'rotate', 'rotateX', 'rotateY',
'rotateZ', 'rotate3d', 'skew', 'skewX', 'skewY', 'translate', 'translateX',
'translateY', 'translateZ', 'translate3d', 'matrix', 'matrix3d', 'perspective',
],
},
'align-content': {
values: [
'normal',
'baseline',
'space-between',
'space-around',
'space-evenly',
'stretch',
'center',
'start',
'end',
'flex-start',
'flex-end',
],
},
'justify-content': {
values: [
'normal',
'space-between',
'space-around',
'space-evenly',
'stretch',
'center',
'start',
'end',
'flex-start',
'flex-end',
'left',
'right',
],
},
'place-content': {
values: [
'normal',
'space-between',
'space-around',
'space-evenly',
'stretch',
'center',
'start',
'end',
'flex-start',
'flex-end',
'baseline',
],
},
'align-items': {
values:
['normal', 'stretch', 'baseline', 'center', 'start', 'end', 'self-start', 'self-end', 'flex-start', 'flex-end'],
},
'justify-items': {
values: [
'normal',
'stretch',
'baseline',
'center',
'start',
'end',
'self-start',
'self-end',
'flex-start',
'flex-end',
'left',
'right',
'legacy',
],
},
'place-items': {
values:
['normal', 'stretch', 'baseline', 'center', 'start', 'end', 'self-start', 'self-end', 'flex-start', 'flex-end'],
},
'align-self': {
values:
['normal', 'stretch', 'baseline', 'center', 'start', 'end', 'self-start', 'self-end', 'flex-start', 'flex-end'],
},
'justify-self': {
values: [
'normal',
'stretch',
'baseline',
'center',
'start',
'end',
'self-start',
'self-end',
'flex-start',
'flex-end',
'left',
'right',
],
},
'place-self': {
values:
['normal', 'stretch', 'baseline', 'center', 'start', 'end', 'self-start', 'self-end', 'flex-start', 'flex-end'],
},
'perspective-origin': {values: ['left', 'center', 'right', 'top', 'bottom']},
'transform-origin': {values: ['left', 'center', 'right', 'top', 'bottom']},
'transition-timing-function': {values: ['cubic-bezier', 'steps']},
'animation-timing-function': {values: ['cubic-bezier', 'steps']},
'-webkit-backface-visibility': {values: ['visible', 'hidden']},
'-webkit-column-break-after': {values: ['always', 'avoid']},
'-webkit-column-break-before': {values: ['always', 'avoid']},
'-webkit-column-break-inside': {values: ['avoid']},
'-webkit-column-span': {values: ['all']},
'-webkit-column-gap': {values: ['normal']},
'filter': {
values: [
'url',
'blur',
'brightness',
'contrast',
'drop-shadow',
'grayscale',
'hue-rotate',
'invert',
'opacity',
'saturate',
'sepia',
],
},
'backdrop-filter': {
values: [
'url',
'blur',
'brightness',
'contrast',
'drop-shadow',
'grayscale',
'hue-rotate',
'invert',
'opacity',
'saturate',
'sepia',
],
},
'mix-blend-mode': {values: ['unset']},
'background-blend-mode': {values: ['unset']},
'grid-template-columns': {values: ['min-content', 'max-content']},
'grid-template-rows': {values: ['min-content', 'max-content']},
'grid-auto-flow': {values: ['dense']},
'background': {
values: [
'repeat',
'repeat-x',
'repeat-y',
'no-repeat',
'top',
'bottom',
'left',
'right',
'center',
'fixed',
'local',
'scroll',
'space',
'round',
'border-box',
'content-box',
'padding-box',
'linear-gradient',
'radial-gradient',
'repeating-linear-gradient',
'repeating-radial-gradient',
'url',
],
},
'background-image':
{values: ['linear-gradient', 'radial-gradient', 'repeating-linear-gradient', 'repeating-radial-gradient', 'url']},
'background-position': {values: ['top', 'bottom', 'left', 'right', 'center']},
'background-position-x': {values: ['left', 'right', 'center']},
'background-position-y': {values: ['top', 'bottom', 'center']},
'background-repeat-x': {values: ['repeat', 'no-repeat']},
'background-repeat-y': {values: ['repeat', 'no-repeat']},
'border-bottom': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'border-left': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'border-right': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'border-top': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'buffered-rendering': {values: ['static', 'dynamic']},
'color-interpolation-filters': {values: ['srgb', 'linearrgb']},
'column-rule': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'flex-flow': {values: ['nowrap', 'row', 'row-reverse', 'column', 'column-reverse', 'wrap', 'wrap-reverse']},
'height': {values: ['-webkit-fill-available']},
'inline-size': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'list-style': {
values: [
'outside',
'inside',
'disc',
'circle',
'square',
'decimal',
'decimal-leading-zero',
'arabic-indic',
'bengali',
'cambodian',
'khmer',
'devanagari',
'gujarati',
'gurmukhi',
'kannada',
'lao',
'malayalam',
'mongolian',
'myanmar',
'oriya',
'persian',
'urdu',
'telugu',
'tibetan',
'thai',
'lower-roman',
'upper-roman',
'lower-greek',
'lower-alpha',
'lower-latin',
'upper-alpha',
'upper-latin',
'cjk-earthly-branch',
'cjk-heavenly-stem',
'ethiopic-halehame',
'ethiopic-halehame-am',
'ethiopic-halehame-ti-er',
'ethiopic-halehame-ti-et',
'hangul',
'hangul-consonant',
'korean-hangul-formal',
'korean-hanja-formal',
'korean-hanja-informal',
'hebrew',
'armenian',
'lower-armenian',
'upper-armenian',
'georgian',
'cjk-ideographic',
'simp-chinese-formal',
'simp-chinese-informal',
'trad-chinese-formal',
'trad-chinese-informal',
'hiragana',
'katakana',
'hiragana-iroha',
'katakana-iroha',
],
},
'max-block-size': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'max-inline-size': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'min-block-size': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'min-height': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'min-inline-size': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'min-width': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'object-position': {values: ['top', 'bottom', 'left', 'right', 'center']},
'shape-outside': {values: ['border-box', 'content-box', 'padding-box', 'margin-box']},
'-webkit-appearance': {
values: [
'checkbox',
'radio',
'push-button',
'square-button',
'button',
'inner-spin-button',
'listbox',
'media-slider',
'media-sliderthumb',
'media-volume-slider',
'media-volume-sliderthumb',
'menulist',
'menulist-button',
'meter',
'progress-bar',
'slider-horizontal',
'slider-vertical',
'sliderthumb-horizontal',
'sliderthumb-vertical',
'searchfield',
'searchfield-cancel-button',
'textfield',
'textarea',
],
},
'-webkit-border-after': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'-webkit-border-after-style':
{values: ['hidden', 'inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double']},
'-webkit-border-after-width': {values: ['medium', 'thick', 'thin']},
'-webkit-border-before': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'-webkit-border-before-style':
{values: ['hidden', 'inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double']},
'-webkit-border-before-width': {values: ['medium', 'thick', 'thin']},
'-webkit-border-end': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'-webkit-border-end-style':
{values: ['hidden', 'inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double']},
'-webkit-border-end-width': {values: ['medium', 'thick', 'thin']},
'-webkit-border-start': {
values: [
'hidden',
'inset',
'groove',
'outset',
'ridge',
'dotted',
'dashed',
'solid',
'double',
'medium',
'thick',
'thin',
],
},
'-webkit-border-start-style':
{values: ['hidden', 'inset', 'groove', 'outset', 'ridge', 'dotted', 'dashed', 'solid', 'double']},
'-webkit-border-start-width': {values: ['medium', 'thick', 'thin']},
'-webkit-logical-height': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'-webkit-logical-width': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'-webkit-margin-collapse': {values: ['collapse', 'separate', 'discard']},
'-webkit-mask-box-image': {values: ['repeat', 'stretch', 'space', 'round']},
'-webkit-mask-box-image-repeat': {values: ['repeat', 'stretch', 'space', 'round']},
'-webkit-mask-clip': {values: ['text', 'border', 'border-box', 'content', 'content-box', 'padding', 'padding-box']},
'-webkit-mask-composite': {
values: [
'clear',
'copy',
'source-over',
'source-in',
'source-out',
'source-atop',
'destination-over',
'destination-in',
'destination-out',
'destination-atop',
'xor',
'plus-lighter',
],
},
'-webkit-mask-image':
{values: ['linear-gradient', 'radial-gradient', 'repeating-linear-gradient', 'repeating-radial-gradient', 'url']},
'-webkit-mask-origin': {values: ['border', 'border-box', 'content', 'content-box', 'padding', 'padding-box']},
'-webkit-mask-position': {values: ['top', 'bottom', 'left', 'right', 'center']},
'-webkit-mask-position-x': {values: ['left', 'right', 'center']},
'-webkit-mask-position-y': {values: ['top', 'bottom', 'center']},
'-webkit-mask-repeat': {values: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'space', 'round']},
'-webkit-mask-size': {values: ['contain', 'cover']},
'-webkit-max-logical-height': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'-webkit-max-logical-width': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'-webkit-min-logical-height': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'-webkit-min-logical-width': {values: ['-webkit-fill-available', 'min-content', 'max-content', 'fit-content']},
'-webkit-perspective-origin-x': {values: ['left', 'right', 'center']},
'-webkit-perspective-origin-y': {values: ['top', 'bottom', 'center']},
'-webkit-text-decorations-in-effect': {values: ['blink', 'line-through', 'overline', 'underline']},
'-webkit-text-stroke': {values: ['medium', 'thick', 'thin']},
'-webkit-text-stroke-width': {values: ['medium', 'thick', 'thin']},
'-webkit-transform-origin-x': {values: ['left', 'right', 'center']},
'-webkit-transform-origin-y': {values: ['top', 'bottom', 'center']},
'width': {values: ['-webkit-fill-available']},
};
// Weight of CSS properties based on their usage from https://www.chromestatus.com/metrics/css/popularity
const Weight = new Map([
['align-content', 57],
['align-items', 129],
['align-self', 55],
['animation', 175],
['animation-delay', 114],
['animation-direction', 113],
['animation-duration', 137],
['animation-fill-mode', 132],
['animation-iteration-count', 124],
['animation-name', 139],
['animation-play-state', 104],
['animation-timing-function', 141],
['backface-visibility', 123],
['background', 260],
['background-attachment', 119],
['background-clip', 165],
['background-color', 259],
['background-image', 246],
['background-origin', 107],
['background-position', 237],
['background-position-x', 108],
['background-position-y', 93],
['background-repeat', 234],
['background-size', 203],
['border', 263],
['border-bottom', 233],
['border-bottom-color', 190],
['border-bottom-left-radius', 186],
['border-bottom-right-radius', 185],
['border-bottom-style', 150],
['border-bottom-width', 179],
['border-collapse', 209],
['border-color', 226],
['border-image', 89],
['border-image-outset', 50],
['border-image-repeat', 49],
['border-image-slice', 58],
['border-image-source', 32],
['border-image-width', 52],
['border-left', 221],
['border-left-color', 174],
['border-left-style', 142],
['border-left-width', 172],
['border-radius', 224],
['border-right', 223],
['border-right-color', 182],
['border-right-style', 130],
['border-right-width', 178],
['border-spacing', 198],
['border-style', 206],
['border-top', 231],
['border-top-color', 192],
['border-top-left-radius', 187],
['border-top-right-radius', 189],
['border-top-style', 152],
['border-top-width', 180],
['border-width', 214],
['bottom', 227],
['box-shadow', 213],
['box-sizing', 216],
['caption-side', 96],
['clear', 229],
['clip', 173],
['clip-rule', 5],
['color', 256],
['content', 219],
['counter-increment', 111],
['counter-reset', 110],
['cursor', 250],
['direction', 176],
['display', 262],
['empty-cells', 99],
['fill', 140],
['fill-opacity', 82],
['fill-rule', 22],
['filter', 160],
['flex', 133],
['flex-basis', 66],
['flex-direction', 85],
['flex-flow', 94],
['flex-grow', 112],
['flex-shrink', 61],
['flex-wrap', 68],
['float', 252],
['font', 211],
['font-family', 254],
['font-kerning', 18],
['font-size', 264],
['font-stretch', 77],
['font-style', 220],
['font-variant', 161],
['font-weight', 257],
['height', 266],
['image-rendering', 90],
['justify-content', 127],
['left', 248],
['letter-spacing', 188],
['line-height', 244],
['list-style', 215],
['list-style-image', 145],
['list-style-position', 149],
['list-style-type', 199],
['margin', 267],
['margin-bottom', 241],
['margin-left', 243],
['margin-right', 238],
['margin-top', 253],
['mask', 20],
['max-height', 205],
['max-width', 225],
['min-height', 217],
['min-width', 218],
['object-fit', 33],
['opacity', 251],
['order', 117],
['orphans', 146],
['outline', 222],
['outline-color', 153],
['outline-offset', 147],
['outline-style', 151],
['outline-width', 148],
['overflow', 255],
['overflow-wrap', 105],
['overflow-x', 184],
['overflow-y', 196],
['padding', 265],
['padding-bottom', 230],
['padding-left', 235],
['padding-right', 232],
['padding-top', 240],
['page', 8],
['page-break-after', 120],
['page-break-before', 69],
['page-break-inside', 121],
['perspective', 92],
['perspective-origin', 103],
['pointer-events', 183],
['position', 261],
['quotes', 158],
['resize', 168],
['right', 245],
['shape-rendering', 38],
['size', 64],
['speak', 118],
['src', 170],
['stop-color', 42],
['stop-opacity', 31],
['stroke', 98],
['stroke-dasharray', 36],
['stroke-dashoffset', 3],
['stroke-linecap', 30],
['stroke-linejoin', 21],
['stroke-miterlimit', 12],
['stroke-opacity', 34],
['stroke-width', 87],
['table-layout', 171],
['tab-size', 46],
['text-align', 260],
['text-anchor', 35],
['text-decoration', 247],
['text-indent', 207],
['text-overflow', 204],
['text-rendering', 155],
['text-shadow', 208],
['text-transform', 202],
['top', 258],
['touch-action', 80],
['transform', 181],
['transform-origin', 162],
['transform-style', 86],
['transition', 193],
['transition-delay', 134],
['transition-duration', 135],
['transition-property', 131],
['transition-timing-function', 122],
['unicode-bidi', 156],
['unicode-range', 136],
['vertical-align', 236],
['visibility', 242],
['-webkit-appearance', 191],
['-webkit-backface-visibility', 154],
['-webkit-background-clip', 164],
['-webkit-background-origin', 40],
['-webkit-background-size', 163],
['-webkit-border-end', 9],
['-webkit-border-horizontal-spacing', 81],
['-webkit-border-image', 75],
['-webkit-border-radius', 212],
['-webkit-border-start', 10],
['-webkit-border-start-color', 16],
['-webkit-border-start-width', 13],
['-webkit-border-vertical-spacing', 43],
['-webkit-box-align', 101],
['-webkit-box-direction', 51],
['-webkit-box-flex', 128],
['-webkit-box-ordinal-group', 91],
['-webkit-box-orient', 144],
['-webkit-box-pack', 106],
['-webkit-box-reflect', 39],
['-webkit-box-shadow', 210],
['-webkit-column-break-inside', 60],
['-webkit-column-count', 84],
['-webkit-column-gap', 76],
['-webkit-column-rule', 25],
['-webkit-column-rule-color', 23],
['-webkit-columns', 44],
['-webkit-column-span', 29],
['-webkit-column-width', 47],
['-webkit-filter', 159],
['-webkit-font-feature-settings', 59],
['-webkit-font-smoothing', 177],
['-webkit-highlight', 1],
['-webkit-line-break', 45],
['-webkit-line-clamp', 126],
['-webkit-margin-after', 67],
['-webkit-margin-before', 70],
['-webkit-margin-collapse', 14],
['-webkit-margin-end', 65],
['-webkit-margin-start', 100],
['-webkit-margin-top-collapse', 78],
['-webkit-mask', 19],
['-webkit-mask-box-image', 72],
['-webkit-mask-image', 88],
['-webkit-mask-position', 54],
['-webkit-mask-repeat', 63],
['-webkit-mask-size', 79],
['-webkit-padding-after', 15],
['-webkit-padding-before', 28],
['-webkit-padding-end', 48],
['-webkit-padding-start', 73],
['-webkit-print-color-adjust', 83],
['-webkit-rtl-ordering', 7],
['-webkit-tap-highlight-color', 169],
['-webkit-text-emphasis-color', 11],
['-webkit-text-fill-color', 71],
['-webkit-text-security', 17],
['-webkit-text-stroke', 56],
['-webkit-text-stroke-color', 37],
['-webkit-text-stroke-width', 53],
['-webkit-user-drag', 95],
['-webkit-user-modify', 62],
['-webkit-user-select', 194],
['-webkit-writing-mode', 4],
['white-space', 228],
['widows', 115],
['width', 268],
['will-change', 74],
['word-break', 166],
['word-spacing', 157],
['word-wrap', 197],
['writing-mode', 41],
['z-index', 239],
['zoom', 200],
]);
// Common keywords to CSS properties
const CommonKeywords = ['auto', 'none'];
export interface CSSPropertyDefinition {
name: string;
longhands: string[]|null;
inherited: boolean|null;
svg: boolean|null;
}