chrome-devtools-frontend 1.0.979699 → 1.0.980166

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,10 +33,11 @@
33
33
 
34
34
  import * as Platform from '../platform/platform.js';
35
35
 
36
- import {blendColors, contrastRatioAPCA, desiredLuminanceAPCA, luminance, luminanceAPCA, rgbaToHsla} from './ColorUtils.js';
36
+ import {blendColors, contrastRatioAPCA, desiredLuminanceAPCA, luminance, luminanceAPCA, rgbaToHsla, rgbaToHwba} from './ColorUtils.js';
37
37
 
38
38
  export class Color {
39
39
  #hslaInternal: number[]|undefined;
40
+ #hwbaInternal: number[]|undefined;
40
41
  #rgbaInternal: number[];
41
42
  #originalText: string|null;
42
43
  readonly #originalTextIsValid: boolean;
@@ -44,6 +45,7 @@ export class Color {
44
45
 
45
46
  constructor(rgba: number[], format: Format, originalText?: string) {
46
47
  this.#hslaInternal = undefined;
48
+ this.#hwbaInternal = undefined;
47
49
  this.#rgbaInternal = rgba;
48
50
  this.#originalText = originalText || null;
49
51
  this.#originalTextIsValid = Boolean(this.#originalText);
@@ -110,29 +112,12 @@ export class Color {
110
112
  return null;
111
113
  }
112
114
 
113
- // rgb/rgba(), hsl/hsla()
114
- match = text.toLowerCase().match(/^\s*(?:(rgba?)|(hsla?))\((.*)\)\s*$/);
115
-
115
+ // rgb/rgba(), hsl/hsla(), hwb/hwba()
116
+ match = text.toLowerCase().match(/^\s*(?:(rgba?)|(hsla?)|(hwba?))\((.*)\)\s*$/);
116
117
  if (match) {
117
- const components = match[3].trim();
118
- let values = components.split(/\s*,\s*/);
119
- if (values.length === 1) {
120
- values = components.split(/\s+/);
121
- if (values[3] === '/') {
122
- values.splice(3, 1);
123
- if (values.length !== 4) {
124
- return null;
125
- }
126
- } else if (
127
- (values.length > 2 && values[2].indexOf('/') !== -1) ||
128
- (values.length > 3 && values[3].indexOf('/') !== -1)) {
129
- const alpha = values.slice(2, 4).join('');
130
- values = values.slice(0, 2).concat(alpha.split(/\//)).concat(values.slice(4));
131
- } else if (values.length >= 4) {
132
- return null;
133
- }
134
- }
135
- if (values.length !== 3 && values.length !== 4 || values.indexOf('') > -1) {
118
+ // hwb(a) must have white space delimiters between its parameters.
119
+ const values = this.splitColorFunctionParameters(match[4], !match[3]);
120
+ if (!values) {
136
121
  return null;
137
122
  }
138
123
  const hasAlpha = (values[3] !== undefined);
@@ -150,19 +135,23 @@ export class Color {
150
135
  return new Color((rgba as number[]), hasAlpha ? Format.RGBA : Format.RGB, text);
151
136
  }
152
137
 
153
- if (match[2]) { // hsl/hsla
154
- const hsla = [
138
+ if (match[2] || match[3]) { // hsl/hsla or hwb/hwba
139
+ const parameters = [
155
140
  Color.parseHueNumeric(values[0]),
156
141
  Color.parseSatLightNumeric(values[1]),
157
142
  Color.parseSatLightNumeric(values[2]),
158
143
  hasAlpha ? Color.parseAlphaNumeric(values[3]) : 1,
159
144
  ];
160
- if (hsla.indexOf(null) > -1) {
145
+ if (parameters.indexOf(null) > -1) {
161
146
  return null;
162
147
  }
163
148
  const rgba: number[] = [];
164
- Color.hsl2rgb((hsla as number[]), rgba);
165
- return new Color(rgba, hasAlpha ? Format.HSLA : Format.HSL, text);
149
+ if (match[2]) {
150
+ Color.hsl2rgb((parameters as number[]), rgba);
151
+ return new Color(rgba, hasAlpha ? Format.HSLA : Format.HSL, text);
152
+ }
153
+ Color.hwb2rgb((parameters as number[]), rgba);
154
+ return new Color(rgba, hasAlpha ? Format.HWBA : Format.HWB, text);
166
155
  }
167
156
  }
168
157
 
@@ -179,6 +168,38 @@ export class Color {
179
168
  return new Color(rgba, Format.HSLA);
180
169
  }
181
170
 
171
+ /**
172
+ * Split the color parameters of (e.g.) rgb(a), hsl(a), hwb(a) functions.
173
+ */
174
+ static splitColorFunctionParameters(content: string, allowCommas: boolean): string[]|null {
175
+ const components = content.trim();
176
+ let values: string[] = [];
177
+
178
+ if (allowCommas) {
179
+ values = components.split(/\s*,\s*/);
180
+ }
181
+ if (!allowCommas || values.length === 1) {
182
+ values = components.split(/\s+/);
183
+ if (values[3] === '/') {
184
+ values.splice(3, 1);
185
+ if (values.length !== 4) {
186
+ return null;
187
+ }
188
+ } else if (
189
+ (values.length > 2 && values[2].indexOf('/') !== -1) ||
190
+ (values.length > 3 && values[3].indexOf('/') !== -1)) {
191
+ const alpha = values.slice(2, 4).join('');
192
+ values = values.slice(0, 2).concat(alpha.split(/\//)).concat(values.slice(4));
193
+ } else if (values.length >= 4) {
194
+ return null;
195
+ }
196
+ }
197
+ if (values.length !== 3 && values.length !== 4 || values.indexOf('') > -1) {
198
+ return null;
199
+ }
200
+ return values;
201
+ }
202
+
182
203
  static parsePercentOrNumber(value: string): number|null {
183
204
  // @ts-ignore: isNaN can accept strings
184
205
  if (isNaN(value.replace('%', ''))) {
@@ -309,6 +330,24 @@ export class Color {
309
330
  out_rgb[3] = hsl[3];
310
331
  }
311
332
 
333
+ // See https://drafts.csswg.org/css-color-4/#hwb-to-rgb for formula reference.
334
+ // eslint-disable-next-line @typescript-eslint/naming-convention
335
+ static hwb2rgb(hwb: number[], out_rgb: number[]): void {
336
+ const h = hwb[0];
337
+ const w = hwb[1];
338
+ const b = hwb[2];
339
+
340
+ if (w + b >= 1) {
341
+ out_rgb[0] = out_rgb[1] = out_rgb[2] = w / (w + b);
342
+ out_rgb[3] = hwb[3];
343
+ } else {
344
+ Color.hsl2rgb([h, 1, 0.5, hwb[3]], out_rgb);
345
+ for (let i = 0; i < 3; ++i) {
346
+ out_rgb[i] += w - (w + b) * out_rgb[i];
347
+ }
348
+ }
349
+ }
350
+
312
351
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
313
352
  // eslint-disable-next-line @typescript-eslint/naming-convention
314
353
  static hsva2rgba(hsva: number[], out_rgba: number[]): void {
@@ -478,6 +517,21 @@ export class Color {
478
517
  return [h, s !== 0 ? 2 * s / (l + s) : 0, (l + s), hsla[3]];
479
518
  }
480
519
 
520
+ /** HWBA with components within [0..1]
521
+ */
522
+ hwba(): number[] {
523
+ if (this.#hwbaInternal) {
524
+ return this.#hwbaInternal;
525
+ }
526
+ this.#hwbaInternal = rgbaToHwba(this.#rgbaInternal);
527
+ return this.#hwbaInternal;
528
+ }
529
+
530
+ canonicalHWBA(): number[] {
531
+ const hwba = this.hwba();
532
+ return [Math.round(hwba[0] * 360), Math.round(hwba[1] * 100), Math.round(hwba[2] * 100), hwba[3]];
533
+ }
534
+
481
535
  hasAlpha(): boolean {
482
536
  return this.#rgbaInternal[3] !== 1;
483
537
  }
@@ -546,6 +600,16 @@ export class Color {
546
600
  }
547
601
  return start + ')';
548
602
  }
603
+ case Format.HWB:
604
+ case Format.HWBA: {
605
+ const hwba = this.hwba();
606
+ const start = Platform.StringUtilities.sprintf(
607
+ 'hwb(%ddeg %d% %d%', Math.round(hwba[0] * 360), Math.round(hwba[1] * 100), Math.round(hwba[2] * 100));
608
+ if (this.hasAlpha()) {
609
+ return start + Platform.StringUtilities.sprintf(' / %d%)', Math.round(hwba[3] * 100));
610
+ }
611
+ return start + ')';
612
+ }
549
613
  case Format.HEXA: {
550
614
  return Platform.StringUtilities
551
615
  .sprintf(
@@ -670,7 +734,7 @@ export class Color {
670
734
  }
671
735
 
672
736
  export const Regex: RegExp =
673
- /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{8}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,4}|\b[a-zA-Z]+\b(?!-))/g;
737
+ /((?:rgb|hsl|hwb)a?\([^)]+\)|#[0-9a-fA-F]{8}|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3,4}|\b[a-zA-Z]+\b(?!-))/g;
674
738
 
675
739
  // TODO(crbug.com/1167717): Make this a const enum again
676
740
  // eslint-disable-next-line rulesdir/const_enum
@@ -685,6 +749,8 @@ export enum Format {
685
749
  RGBA = 'rgba',
686
750
  HSL = 'hsl',
687
751
  HSLA = 'hsla',
752
+ HWB = 'hwb',
753
+ HWBA = 'hwba',
688
754
  }
689
755
 
690
756
  const COLOR_TO_RGBA_ENTRIES: Array<readonly[string, number[]]> = [
@@ -15,11 +15,10 @@ export function blendColors(fgRGBA: number[], bgRGBA: number[]): number[] {
15
15
  ];
16
16
  }
17
17
 
18
- export function rgbaToHsla([r, g, b, a]: number[]): number[] {
18
+ function rgbToHue([r, g, b]: number[]): number {
19
19
  const max = Math.max(r, g, b);
20
20
  const min = Math.min(r, g, b);
21
21
  const diff = max - min;
22
- const sum = max + min;
23
22
 
24
23
  let h;
25
24
  if (min === max) {
@@ -31,7 +30,16 @@ export function rgbaToHsla([r, g, b, a]: number[]): number[] {
31
30
  } else {
32
31
  h = (1 / 6 * (r - g) / diff) + 2 / 3;
33
32
  }
33
+ return h;
34
+ }
35
+
36
+ export function rgbaToHsla([r, g, b, a]: number[]): number[] {
37
+ const max = Math.max(r, g, b);
38
+ const min = Math.min(r, g, b);
39
+ const diff = max - min;
40
+ const sum = max + min;
34
41
 
42
+ const h = rgbToHue([r, g, b]);
35
43
  const l = 0.5 * sum;
36
44
 
37
45
  let s;
@@ -48,6 +56,14 @@ export function rgbaToHsla([r, g, b, a]: number[]): number[] {
48
56
  return [h, s, l, a];
49
57
  }
50
58
 
59
+ export function rgbaToHwba([r, g, b, a]: number[]): number[] {
60
+ const h = rgbToHue([r, g, b]);
61
+ const max = Math.max(r, g, b);
62
+ const min = Math.min(r, g, b);
63
+
64
+ return [h, min, 1 - max, a];
65
+ }
66
+
51
67
  /**
52
68
  * Calculate the luminance of this color using the WCAG algorithm.
53
69
  * See http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
@@ -1113,6 +1113,8 @@ export function detectColorFormat(color: Color): Format {
1113
1113
  format = cf.RGB;
1114
1114
  } else if (formatSetting === cf.HSL) {
1115
1115
  format = cf.HSL;
1116
+ } else if (formatSetting === cf.HWB) {
1117
+ format = cf.HWB;
1116
1118
  } else if (formatSetting === cf.HEX) {
1117
1119
  format = color.detectHEXFormat();
1118
1120
  } else {
@@ -53,9 +53,8 @@ export class PersistenceUtils {
53
53
  }
54
54
  const icon = UI.Icon.Icon.create('mediumicon-file-sync');
55
55
  UI.Tooltip.Tooltip.install(icon, PersistenceUtils.tooltipForUISourceCode(binding.network));
56
- // TODO(allada) This will not work properly with dark theme.
57
56
  if (NetworkPersistenceManager.instance().project() === binding.fileSystem.project()) {
58
- icon.style.filter = 'hue-rotate(160deg)';
57
+ icon.classList.add('purple-dot');
59
58
  }
60
59
  return icon;
61
60
  }
@@ -224,7 +224,7 @@ export class Spectrum extends Common.ObjectWrapper.eventMixin<EventTypes, typeof
224
224
  this.alphaElementBackground = this.alphaElement.createChild('div', 'spectrum-alpha-background');
225
225
  this.alphaSlider = this.alphaElement.createChild('div', 'spectrum-slider');
226
226
 
227
- // RGBA/HSLA display.
227
+ // RGBA/HSLA/HWBA display.
228
228
  this.displayContainer = toolsContainer.createChild('div', 'spectrum-text source-code');
229
229
  UI.ARIAUtils.markAsPoliteLiveRegion(this.displayContainer, true);
230
230
  this.textValues = [];
@@ -937,6 +937,8 @@ export class Spectrum extends Common.ObjectWrapper.eventMixin<EventTypes, typeof
937
937
  colorFormat = cf.RGB;
938
938
  } else if (colorFormat === cf.HSLA) {
939
939
  colorFormat = cf.HSL;
940
+ } else if (colorFormat === cf.HWBA) {
941
+ colorFormat = cf.HWB;
940
942
  } else if (colorFormat === cf.HEXA) {
941
943
  colorFormat = cf.HEX;
942
944
  } else if (colorFormat === cf.ShortHEXA) {
@@ -988,6 +990,8 @@ export class Spectrum extends Common.ObjectWrapper.eventMixin<EventTypes, typeof
988
990
  colorString = color.asString(cf.HEXA);
989
991
  } else if (this.colorFormat === cf.HSL) {
990
992
  colorString = color.asString(cf.HSLA);
993
+ } else if (this.colorFormat === cf.HWB) {
994
+ colorString = color.asString(cf.HWBA);
991
995
  } else {
992
996
  colorString = color.asString(cf.RGBA);
993
997
  }
@@ -1033,12 +1037,21 @@ export class Spectrum extends Common.ObjectWrapper.eventMixin<EventTypes, typeof
1033
1037
  this.hexValue.value = String(this.color().asString(this.color().hasAlpha() ? cf.HEXA : cf.HEX));
1034
1038
  }
1035
1039
  } else {
1036
- // RGBA, HSLA display.
1040
+ // RGBA, HSLA, HWBA display.
1037
1041
  this.hexContainer.hidden = true;
1038
1042
  this.displayContainer.hidden = false;
1039
- const isRgb = this.colorFormat === cf.RGB;
1040
- this.textLabels.textContent = isRgb ? 'RGBA' : 'HSLA';
1041
- const colorValues = isRgb ? this.color().canonicalRGBA() : this.color().canonicalHSLA();
1043
+ let colorValues;
1044
+ if (this.colorFormat === cf.RGB) {
1045
+ this.textLabels.textContent = 'RGBA';
1046
+ colorValues = this.color().canonicalRGBA();
1047
+ } else if (this.colorFormat === cf.HSL) {
1048
+ this.textLabels.textContent = 'HSLA';
1049
+ colorValues = this.color().canonicalHSLA();
1050
+ } else {
1051
+ this.textLabels.textContent = 'HWBA';
1052
+ colorValues = this.color().canonicalHWBA();
1053
+ }
1054
+
1042
1055
  for (let i = 0; i < 3; ++i) {
1043
1056
  UI.ARIAUtils.setAccessibleName(
1044
1057
  this.textValues[i],
@@ -1047,7 +1060,7 @@ export class Spectrum extends Common.ObjectWrapper.eventMixin<EventTypes, typeof
1047
1060
  PH2: this.textLabels.textContent,
1048
1061
  }));
1049
1062
  this.textValues[i].value = String(colorValues[i]);
1050
- if (!isRgb && (i === 1 || i === 2)) {
1063
+ if (this.colorFormat !== cf.RGB && (i === 1 || i === 2)) {
1051
1064
  this.textValues[i].value += '%';
1052
1065
  }
1053
1066
  }
@@ -1081,6 +1094,8 @@ export class Spectrum extends Common.ObjectWrapper.eventMixin<EventTypes, typeof
1081
1094
  if (this.colorFormat === cf.RGB) {
1082
1095
  format = cf.HSL;
1083
1096
  } else if (this.colorFormat === cf.HSL) {
1097
+ format = cf.HWB;
1098
+ } else if (this.colorFormat === cf.HWB) {
1084
1099
  format = (this.originalFormat === cf.ShortHEX || this.originalFormat === cf.ShortHEXA) ? cf.ShortHEX : cf.HEX;
1085
1100
  }
1086
1101
  this.innerSetColor(undefined, '', undefined /* colorName */, format, ChangeSource.Other);
@@ -1121,10 +1136,9 @@ export class Spectrum extends Common.ObjectWrapper.eventMixin<EventTypes, typeof
1121
1136
  if (this.colorFormat === cf.Nickname || this.colorFormat === cf.HEX || this.colorFormat === cf.ShortHEX) {
1122
1137
  colorString = this.hexValue.value;
1123
1138
  } else {
1124
- const format = this.colorFormat === cf.RGB ? 'rgb' : 'hsl';
1125
1139
  const values = this.textValues.slice(0, -1).map(elementValue).join(' ');
1126
1140
  const alpha = this.textValues.slice(-1).map(elementValue).join(' ');
1127
- colorString = Platform.StringUtilities.sprintf('%s(%s)', format, [values, alpha].join(' / '));
1141
+ colorString = Platform.StringUtilities.sprintf('%s(%s)', this.colorFormat, [values, alpha].join(' / '));
1128
1142
  }
1129
1143
 
1130
1144
  const color = Common.Color.Color.parse(colorString);
@@ -1291,7 +1305,7 @@ export class PaletteGenerator {
1291
1305
  private async processStylesheet(stylesheet: SDK.CSSStyleSheetHeader.CSSStyleSheetHeader): Promise<void> {
1292
1306
  let text: string = (await stylesheet.requestContent()).content || '';
1293
1307
  text = text.toLowerCase();
1294
- const regexResult = text.match(/((?:rgb|hsl)a?\([^)]+\)|#[0-9a-f]{6}|#[0-9a-f]{3})/g) || [];
1308
+ const regexResult = text.match(/((?:rgb|hsl|hwb)a?\([^)]+\)|#[0-9a-f]{6}|#[0-9a-f]{3})/g) || [];
1295
1309
  for (const c of regexResult) {
1296
1310
  let frequency = this.frequencyMap.get(c) || 0;
1297
1311
  this.frequencyMap.set(c, ++frequency);
@@ -180,6 +180,7 @@ function nextColorFormat(color: Common.Color.Color, curFormat: string): string {
180
180
  // * original
181
181
  // * rgb(a)
182
182
  // * hsl(a)
183
+ // * hwb(a)
183
184
  // * nickname (if the color has a nickname)
184
185
  // * shorthex (if has short hex)
185
186
  // * hex
@@ -195,6 +196,10 @@ function nextColorFormat(color: Common.Color.Color, curFormat: string): string {
195
196
 
196
197
  case cf.HSL:
197
198
  case cf.HSLA:
199
+ return !color.hasAlpha() ? cf.HWB : cf.HWBA;
200
+
201
+ case cf.HWB:
202
+ case cf.HWBA:
198
203
  if (color.nickname()) {
199
204
  return cf.Nickname;
200
205
  }
@@ -550,6 +550,15 @@ span[is="dt-icon-label"] {
550
550
  background: var(--override-force-white-icons-background) !important; /* stylelint-disable-line declaration-no-important */
551
551
  }
552
552
 
553
+ .mediumicon-file-sync.purple-dot {
554
+ filter: hue-rotate(160deg);
555
+ }
556
+
557
+ .-theme-with-dark-background [is="ui-icon"].icon-invert.mediumicon-file-sync.purple-dot,
558
+ :host-context(.-theme-with-dark-background) [is="ui-icon"].icon-invert.mediumicon-file-sync.purple-dot {
559
+ filter: invert(80%) hue-rotate(350deg);
560
+ }
561
+
553
562
  .expandable-inline-button {
554
563
  background-color: var(--color-background-elevation-2);
555
564
  color: var(--color-text-primary);
@@ -28,7 +28,7 @@
28
28
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29
29
  // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
30
 
31
- import {rgbaToHsla} from '../front_end/core/common/ColorUtils.js'; // eslint-disable-line rulesdir/es_modules_import
31
+ import {rgbaToHsla, rgbaToHwba} from '../front_end/core/common/ColorUtils.js'; // eslint-disable-line rulesdir/es_modules_import
32
32
 
33
33
  import type {Bounds, PathCommands, Quad} from './common.js';
34
34
 
@@ -258,7 +258,7 @@ export function parseHexa(hexa: string): Array<number> {
258
258
  return (hexa.match(/#(\w\w)(\w\w)(\w\w)(\w\w)/) || []).slice(1).map(c => parseInt(c, 16) / 255);
259
259
  }
260
260
 
261
- export function formatRgba(rgba: number[], colorFormat: 'rgb'|'hsl'): string {
261
+ export function formatRgba(rgba: number[], colorFormat: 'rgb'|'hsl'|'hwb'): string {
262
262
  if (colorFormat === 'rgb') {
263
263
  const [r, g, b, a] = rgba;
264
264
  // rgb(r g b [ / a])
@@ -273,11 +273,18 @@ export function formatRgba(rgba: number[], colorFormat: 'rgb'|'hsl'): string {
273
273
  a === 1 ? '' : ' / ' + Math.round(a * 100) / 100})`;
274
274
  }
275
275
 
276
+ if (colorFormat === 'hwb') {
277
+ const [h, w, b, a] = rgbaToHwba(rgba);
278
+ // hwb(hdeg w b [ / a])
279
+ return `hwb(${Math.round(h * 360)}deg ${Math.round(w * 100)} ${Math.round(b * 100)}${
280
+ a === 1 ? '' : ' / ' + Math.round(a * 100) / 100})`;
281
+ }
282
+
276
283
  throw new Error('NOT_REACHED');
277
284
  }
278
285
 
279
286
  export function formatColor(hexa: string, colorFormat: string): string {
280
- if (colorFormat === 'rgb' || colorFormat === 'hsl') {
287
+ if (colorFormat === 'rgb' || colorFormat === 'hsl' || colorFormat === 'hwb') {
281
288
  return formatRgba(parseHexa(hexa), colorFormat);
282
289
  }
283
290
 
package/package.json CHANGED
@@ -54,5 +54,5 @@
54
54
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
55
55
  "watch": "third_party/node/node.py --output scripts/watch_build.js"
56
56
  },
57
- "version": "1.0.979699"
57
+ "version": "1.0.980166"
58
58
  }