[go: nahoru, domu]

Add metric override descriptors to FontFace JS interface

Following the CSSWG resolution to add metric override descriptors
to @font-face, this patch also adds these descriptors as attributes
to the JavaScript FontFace interface.

Bug: 1098355
Change-Id: Ia5ef4ad870559457834fe9fafaaab79b24a0e08b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2425484
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: Chris Harrelson <chrishtr@chromium.org>
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810907}
diff --git a/third_party/blink/renderer/core/css/font_face.cc b/third_party/blink/renderer/core/css/font_face.cc
index 9277455..61acf2a 100644
--- a/third_party/blink/renderer/core/css/font_face.cc
+++ b/third_party/blink/renderer/core/css/font_face.cc
@@ -238,7 +238,14 @@
                         AtRuleDescriptorID::FontFeatureSettings);
   SetPropertyFromString(context, descriptors->display(),
                         AtRuleDescriptorID::FontDisplay);
-  // TODO(xiaochengh): Add override descriptors to FontFaceDescriptors
+  if (RuntimeEnabledFeatures::CSSFontMetricsOverrideEnabled()) {
+    SetPropertyFromString(context, descriptors->ascentOverride(),
+                          AtRuleDescriptorID::AscentOverride);
+    SetPropertyFromString(context, descriptors->descentOverride(),
+                          AtRuleDescriptorID::DescentOverride);
+    SetPropertyFromString(context, descriptors->lineGapOverride(),
+                          AtRuleDescriptorID::LineGapOverride);
+  }
 }
 
 FontFace::~FontFace() = default;
@@ -271,6 +278,18 @@
   return display_ ? display_->CssText() : "auto";
 }
 
+String FontFace::ascentOverride() const {
+  return ascent_override_ ? ascent_override_->CssText() : "normal";
+}
+
+String FontFace::descentOverride() const {
+  return descent_override_ ? descent_override_->CssText() : "normal";
+}
+
+String FontFace::lineGapOverride() const {
+  return line_gap_override_ ? line_gap_override_->CssText() : "normal";
+}
+
 void FontFace::setStyle(ExecutionContext* context,
                         const String& s,
                         ExceptionState& exception_state) {
@@ -320,6 +339,27 @@
                         &exception_state);
 }
 
+void FontFace::setAscentOverride(ExecutionContext* context,
+                                 const String& s,
+                                 ExceptionState& exception_state) {
+  SetPropertyFromString(context, s, AtRuleDescriptorID::AscentOverride,
+                        &exception_state);
+}
+
+void FontFace::setDescentOverride(ExecutionContext* context,
+                                  const String& s,
+                                  ExceptionState& exception_state) {
+  SetPropertyFromString(context, s, AtRuleDescriptorID::DescentOverride,
+                        &exception_state);
+}
+
+void FontFace::setLineGapOverride(ExecutionContext* context,
+                                  const String& s,
+                                  ExceptionState& exception_state) {
+  SetPropertyFromString(context, s, AtRuleDescriptorID::LineGapOverride,
+                        &exception_state);
+}
+
 void FontFace::SetPropertyFromString(const ExecutionContext* context,
                                      const String& s,
                                      AtRuleDescriptorID descriptor_id,
diff --git a/third_party/blink/renderer/core/css/font_face.h b/third_party/blink/renderer/core/css/font_face.h
index 52168d1..39194852 100644
--- a/third_party/blink/renderer/core/css/font_face.h
+++ b/third_party/blink/renderer/core/css/font_face.h
@@ -87,6 +87,9 @@
   String variant() const;
   String featureSettings() const;
   String display() const;
+  String ascentOverride() const;
+  String descentOverride() const;
+  String lineGapOverride() const;
 
   // FIXME: Changing these attributes should affect font matching.
   void setFamily(ExecutionContext*, const AtomicString& s, ExceptionState&) {
@@ -99,6 +102,9 @@
   void setVariant(ExecutionContext*, const String&, ExceptionState&);
   void setFeatureSettings(ExecutionContext*, const String&, ExceptionState&);
   void setDisplay(ExecutionContext*, const String&, ExceptionState&);
+  void setAscentOverride(ExecutionContext*, const String&, ExceptionState&);
+  void setDescentOverride(ExecutionContext*, const String&, ExceptionState&);
+  void setLineGapOverride(ExecutionContext*, const String&, ExceptionState&);
 
   String status() const;
   ScriptPromise loaded(ScriptState* script_state) {
diff --git a/third_party/blink/renderer/core/css/font_face.idl b/third_party/blink/renderer/core/css/font_face.idl
index 90baf8a..1c24740 100644
--- a/third_party/blink/renderer/core/css/font_face.idl
+++ b/third_party/blink/renderer/core/css/font_face.idl
@@ -51,6 +51,9 @@
     [RaisesException=Setter, SetterCallWith=ExecutionContext] attribute DOMString variant;
     [RaisesException=Setter, SetterCallWith=ExecutionContext] attribute DOMString featureSettings;
     [RaisesException=Setter, SetterCallWith=ExecutionContext] attribute DOMString display;
+    [RaisesException=Setter, SetterCallWith=ExecutionContext, RuntimeEnabled=CSSFontMetricsOverride] attribute DOMString ascentOverride;
+    [RaisesException=Setter, SetterCallWith=ExecutionContext, RuntimeEnabled=CSSFontMetricsOverride] attribute DOMString descentOverride;
+    [RaisesException=Setter, SetterCallWith=ExecutionContext, RuntimeEnabled=CSSFontMetricsOverride] attribute DOMString lineGapOverride;
 
     readonly attribute FontFaceLoadStatus status;
 
diff --git a/third_party/blink/renderer/core/css/font_face_descriptors.idl b/third_party/blink/renderer/core/css/font_face_descriptors.idl
index 3c7e6c20..895b0b9 100644
--- a/third_party/blink/renderer/core/css/font_face_descriptors.idl
+++ b/third_party/blink/renderer/core/css/font_face_descriptors.idl
@@ -12,4 +12,7 @@
     DOMString variant = "normal";
     DOMString featureSettings = "normal";
     DOMString display = "auto";
+    [RuntimeEnabled=CSSFontMetricsOverride] DOMString ascentOverride = "normal";
+    [RuntimeEnabled=CSSFontMetricsOverride] DOMString descentOverride = "normal";
+    [RuntimeEnabled=CSSFontMetricsOverride] DOMString lineGapOverride = "normal";
 };
diff --git a/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptor-getter-setter.html b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptor-getter-setter.html
new file mode 100644
index 0000000..a674c22
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptor-getter-setter.html
@@ -0,0 +1,222 @@
+<!DOCTYPE html>
+<title>Tests getters and setters of the font metrics override descriptors of FontFace</title>
+<link rel="author" href="mailto:xiaochengh@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-font-loading/#fontface-interface">
+<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#font-metrics-override-desc">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function rejection(promise) {
+  return new Promise((resolve, reject) => promise.then(reject, resolve));
+}
+
+// ascentOverride
+
+test(() => {
+  const face = new FontFace(
+      'ascent-override-initial',
+      'url(https://example.com/font.woff)');
+  assert_equals(face.ascentOverride, 'normal');
+}, "Initial value of ascentOverride should be 'normal'");
+
+test(() => {
+  const face = new FontFace(
+    'ascent-override-initialize-with-normal',
+    'url(https://example.com/font.woff)',
+    {ascentOverride: 'normal'});
+  assert_equals(face.ascentOverride, 'normal');
+}, "Initialize ascentOverride with 'normal' should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'ascent-override-initialize-with-percentage',
+    'url(https://example.com/font.woff)',
+    {ascentOverride: '50%'});
+  assert_equals(face.ascentOverride, '50%');
+}, "Initialize ascentOverride with a percentage should succeed");
+
+promise_test(async () => {
+  const face = new FontFace(
+      'ascent-override-initialize-with-negative-percentage',
+      'url(https://example.com/font.woff)',
+      {ascentOverride: '-50%'});
+  const error = await rejection(face.load());
+  assert_equals('error', face.status);
+  assert_throws_dom('SyntaxError', () => {throw error});
+}, "Initialize ascentOverride with a negative percentage should fail");
+
+promise_test(async () => {
+  const face = new FontFace(
+      'ascent-override-initialize-with-non-percentage',
+      'url(https://example.com/font.woff)',
+      {ascentOverride: '10px'});
+  const error = await rejection(face.load());
+  assert_equals('error', face.status);
+  assert_throws_dom('SyntaxError', () => {throw error});
+}, "Initialize ascentOverride with a non-percentage should fail");
+
+test(() => {
+  const face = new FontFace(
+    'ascent-override-normal-to-percentage',
+    'url(https://example.com/font.woff)',
+    {ascentOverride: 'normal'});
+  face.ascentOverride = '50%';
+  assert_equals(face.ascentOverride, '50%');
+}, "Changing ascentOverride from 'normal' to percentage should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'ascent-override-percentage-to-normal',
+    'url(https://example.com/font.woff)',
+    {ascentOverride: '50%'});
+  face.ascentOverride = 'normal';
+  assert_equals(face.ascentOverride, 'normal');
+}, "Changing ascentOverride from percentage to 'normal' should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'ascent-override-set-to-invalid',
+    'url(https://example.com/font.woff)');
+  assert_throws_dom('SyntaxError', () => {face.ascentOverride = '10px'});
+}, "Changing ascentOverride to invalid value should fail");
+
+// descentOverride
+
+test(() => {
+  const face = new FontFace(
+      'descent-override-initial',
+      'url(https://example.com/font.woff)');
+  assert_equals(face.descentOverride, 'normal');
+}, "Initial value of descentOverride should be 'normal'");
+
+test(() => {
+  const face = new FontFace(
+    'descent-override-initialize-with-normal',
+    'url(https://example.com/font.woff)',
+    {descentOverride: 'normal'});
+  assert_equals(face.descentOverride, 'normal');
+}, "Initialize descentOverride with 'normal' should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'descent-override-initialize-with-percentage',
+    'url(https://example.com/font.woff)',
+    {descentOverride: '50%'});
+  assert_equals(face.descentOverride, '50%');
+}, "Initialize descentOverride with a percentage should succeed");
+
+promise_test(async () => {
+  const face = new FontFace(
+      'descent-override-initialize-with-negative-percentage',
+      'url(https://example.com/font.woff)',
+      {descentOverride: '-50%'});
+  const error = await rejection(face.load());
+  assert_equals('error', face.status);
+  assert_throws_dom('SyntaxError', () => {throw error});
+}, "Initialize descentOverride with a negative percentage should fail");
+
+promise_test(async () => {
+  const face = new FontFace(
+      'descent-override-initialize-with-non-percentage',
+      'url(https://example.com/font.woff)',
+      {descentOverride: '10px'});
+  const error = await rejection(face.load());
+  assert_equals('error', face.status);
+  assert_throws_dom('SyntaxError', () => {throw error});
+}, "Initialize descentOverride with a non-percentage should fail");
+
+test(() => {
+  const face = new FontFace(
+    'descent-override-normal-to-percentage',
+    'url(https://example.com/font.woff)',
+    {descentOverride: 'normal'});
+  face.descentOverride = '50%';
+  assert_equals(face.descentOverride, '50%');
+}, "Changing descentOverride from 'normal' to percentage should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'descent-override-percentage-to-normal',
+    'url(https://example.com/font.woff)',
+    {descentOverride: '50%'});
+  face.descentOverride = 'normal';
+  assert_equals(face.descentOverride, 'normal');
+}, "Changing descentOverride from percentage to 'normal' should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'descent-override-set-to-invalid',
+    'url(https://example.com/font.woff)');
+  assert_throws_dom('SyntaxError', () => {face.descentOverride = '10px'});
+}, "Changing descentOverride to invalid value should fail");
+
+// lineGapOverride
+
+test(() => {
+  const face = new FontFace(
+      'lineGap-override-initial',
+      'url(https://example.com/font.woff)');
+  assert_equals(face.lineGapOverride, 'normal');
+}, "Initial value of lineGapOverride should be 'normal'");
+
+test(() => {
+  const face = new FontFace(
+    'lineGap-override-initialize-with-normal',
+    'url(https://example.com/font.woff)',
+    {lineGapOverride: 'normal'});
+  assert_equals(face.lineGapOverride, 'normal');
+}, "Initialize lineGapOverride with 'normal' should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'lineGap-override-initialize-with-percentage',
+    'url(https://example.com/font.woff)',
+    {lineGapOverride: '50%'});
+  assert_equals(face.lineGapOverride, '50%');
+}, "Initialize lineGapOverride with a percentage should succeed");
+
+promise_test(async () => {
+  const face = new FontFace(
+      'lineGap-override-initialize-with-negative-percentage',
+      'url(https://example.com/font.woff)',
+      {lineGapOverride: '-50%'});
+  const error = await rejection(face.load());
+  assert_equals('error', face.status);
+  assert_throws_dom('SyntaxError', () => {throw error});
+}, "Initialize lineGapOverride with a negative percentage should fail");
+
+promise_test(async () => {
+  const face = new FontFace(
+      'lineGap-override-initialize-with-non-percentage',
+      'url(https://example.com/font.woff)',
+      {lineGapOverride: '10px'});
+  const error = await rejection(face.load());
+  assert_equals('error', face.status);
+  assert_throws_dom('SyntaxError', () => {throw error});
+}, "Initialize lineGapOverride with a non-percentage should fail");
+
+test(() => {
+  const face = new FontFace(
+    'lineGap-override-normal-to-percentage',
+    'url(https://example.com/font.woff)',
+    {lineGapOverride: 'normal'});
+  face.lineGapOverride = '50%';
+  assert_equals(face.lineGapOverride, '50%');
+}, "Changing lineGapOverride from 'normal' to percentage should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'lineGap-override-percentage-to-normal',
+    'url(https://example.com/font.woff)',
+    {lineGapOverride: '50%'});
+  face.lineGapOverride = 'normal';
+  assert_equals(face.lineGapOverride, 'normal');
+}, "Changing lineGapOverride from percentage to 'normal' should succeed");
+
+test(() => {
+  const face = new FontFace(
+    'lineGap-override-set-to-invalid',
+    'url(https://example.com/font.woff)');
+  assert_throws_dom('SyntaxError', () => {face.lineGapOverride = '10px'});
+}, "Changing lineGapOverride to invalid value should fail");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptors-ref.html b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptors-ref.html
new file mode 100644
index 0000000..4a6d26ea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptors-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Tests that the ascentOverride, descentOverride and lineGapOverride attributes of FontFace work</title>
+<link rel="stylesheet" herf="/fonts/ahem.css">
+<style>
+#target {
+  position: absolute;
+  font-family: Ahem;
+  font-size: 20px;
+}
+
+#first-line {
+  position: absolute;
+  left: 0;
+  top: 0.7em;
+}
+
+#second-line {
+  position: absolute;
+  left: 0;
+  top: 3.7em;
+}
+</style>
+
+<div id="target">
+  <div id="first-line">XXXXX</div>
+  <div id="second-line">XXXXX</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptors.html b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptors.html
new file mode 100644
index 0000000..40de722
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-font-loading/fontface-override-descriptors.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Tests that the ascentOverride, descentOverride and lineGapOverride attributes of FontFace work</title>
+<link rel="help" href="https://drafts.csswg.org/css-font-loading/#fontface-interface">
+<link rel="help" href="https://drafts.csswg.org/css-fonts-4/#font-metrics-override-desc">
+<link rel="match" href="fontface-override-descriptors-ref.html">
+<script>
+const face = new FontFace(
+    'ahem-overridden',
+    'local("Ahem"), url("/fonts/Ahem.ttf")',
+    {ascentOverride: '100%', descentOverride: '100%', lineGapOverride: '100%'});
+document.fonts.add(face);
+
+// Line height is ascent + descent + lineGap = 3em
+// Baseline is placed at lineGap * 0.5 + ascent = 1.5em below line box top
+// Since each Ahem 'X' glyph has 0.8em above baseline, the top of each glyph
+// should be placed at 0.7em below line box top
+</script>
+<style>
+#target {
+  position: absolute;
+  font-family: ahem-overridden;
+  font-size: 20px;
+}
+</style>
+
+<div id="target">
+  XXXXX<br>
+  XXXXX
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/lint.ignore b/third_party/blink/web_tests/external/wpt/lint.ignore
index 94ee0fdb..77939b4 100644
--- a/third_party/blink/web_tests/external/wpt/lint.ignore
+++ b/third_party/blink/web_tests/external/wpt/lint.ignore
@@ -726,6 +726,7 @@
 AHEM SYSTEM FONT: resource-timing/resources/all_resource_types.htm
 AHEM SYSTEM FONT: resource-timing/resources/iframe-reload-TAO.sub.html
 AHEM SYSTEM FONT: html/canvas/element/drawing-text-to-the-canvas/2d.text.measure.fontBoundingBox.ahem.html
+AHEM SYSTEM FONT: css/css-font-loading/fontface-override-descriptors.html
 
 # TODO: The following should be deleted along with the Ahem web font cleanup
 # PR (https://github.com/web-platform-tests/wpt/pull/18702)
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index e34bdb2..42bd99c 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -592,9 +592,12 @@
     method write
 interface FontFace
     attribute @@toStringTag
+    getter ascentOverride
+    getter descentOverride
     getter display
     getter family
     getter featureSettings
+    getter lineGapOverride
     getter loaded
     getter status
     getter stretch
@@ -604,9 +607,12 @@
     getter weight
     method constructor
     method load
+    setter ascentOverride
+    setter descentOverride
     setter display
     setter family
     setter featureSettings
+    setter lineGapOverride
     setter stretch
     setter style
     setter unicodeRange
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 3feb85f..95cd442d 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -526,9 +526,12 @@
 [Worker]     method write
 [Worker] interface FontFace
 [Worker]     attribute @@toStringTag
+[Worker]     getter ascentOverride
+[Worker]     getter descentOverride
 [Worker]     getter display
 [Worker]     getter family
 [Worker]     getter featureSettings
+[Worker]     getter lineGapOverride
 [Worker]     getter loaded
 [Worker]     getter status
 [Worker]     getter stretch
@@ -538,9 +541,12 @@
 [Worker]     getter weight
 [Worker]     method constructor
 [Worker]     method load
+[Worker]     setter ascentOverride
+[Worker]     setter descentOverride
 [Worker]     setter display
 [Worker]     setter family
 [Worker]     setter featureSettings
+[Worker]     setter lineGapOverride
 [Worker]     setter stretch
 [Worker]     setter style
 [Worker]     setter unicodeRange
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 582f972..f4eccd8 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -2493,9 +2493,12 @@
     method constructor
 interface FontFace
     attribute @@toStringTag
+    getter ascentOverride
+    getter descentOverride
     getter display
     getter family
     getter featureSettings
+    getter lineGapOverride
     getter loaded
     getter status
     getter stretch
@@ -2505,9 +2508,12 @@
     getter weight
     method constructor
     method load
+    setter ascentOverride
+    setter descentOverride
     setter display
     setter family
     setter featureSettings
+    setter lineGapOverride
     setter stretch
     setter style
     setter unicodeRange
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 27cbbab..da511fa 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -521,9 +521,12 @@
 [Worker]     method write
 [Worker] interface FontFace
 [Worker]     attribute @@toStringTag
+[Worker]     getter ascentOverride
+[Worker]     getter descentOverride
 [Worker]     getter display
 [Worker]     getter family
 [Worker]     getter featureSettings
+[Worker]     getter lineGapOverride
 [Worker]     getter loaded
 [Worker]     getter status
 [Worker]     getter stretch
@@ -533,9 +536,12 @@
 [Worker]     getter weight
 [Worker]     method constructor
 [Worker]     method load
+[Worker]     setter ascentOverride
+[Worker]     setter descentOverride
 [Worker]     setter display
 [Worker]     setter family
 [Worker]     setter featureSettings
+[Worker]     setter lineGapOverride
 [Worker]     setter stretch
 [Worker]     setter style
 [Worker]     setter unicodeRange