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