Commit 87fd5aa1 authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Commit Bot

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/+/2425484Reviewed-by: default avatarAnders Hartvoll Ruud <andruud@chromium.org>
Reviewed-by: default avatarChris Harrelson <chrishtr@chromium.org>
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810907}
parent 46941db5
......@@ -238,7 +238,14 @@ FontFace::FontFace(ExecutionContext* context,
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 @@ String FontFace::display() const {
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 @@ void FontFace::setDisplay(ExecutionContext* context,
&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,
......
......@@ -87,6 +87,9 @@ class CORE_EXPORT FontFace : public ScriptWrappable,
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 @@ class CORE_EXPORT FontFace : public ScriptWrappable,
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) {
......
......@@ -51,6 +51,9 @@ enum FontFaceLoadStatus {
[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;
......
......@@ -12,4 +12,7 @@ dictionary FontFaceDescriptors {
DOMString variant = "normal";
DOMString featureSettings = "normal";
DOMString display = "auto";
[RuntimeEnabled=CSSFontMetricsOverride] DOMString ascentOverride = "normal";
[RuntimeEnabled=CSSFontMetricsOverride] DOMString descentOverride = "normal";
[RuntimeEnabled=CSSFontMetricsOverride] DOMString lineGapOverride = "normal";
};
<!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>
<!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>
<!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>
......@@ -726,6 +726,7 @@ AHEM SYSTEM FONT: acid/acid3/test.html
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)
......
......@@ -592,9 +592,12 @@ interface FileSystemWritableFileStream : WritableStream
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 @@ interface FontFace
getter weight
method constructor
method load
setter ascentOverride
setter descentOverride
setter display
setter family
setter featureSettings
setter lineGapOverride
setter stretch
setter style
setter unicodeRange
......
......@@ -526,9 +526,12 @@ Starting worker: resources/global-interface-listing-worker.js
[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 @@ Starting worker: resources/global-interface-listing-worker.js
[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
......
......@@ -2493,9 +2493,12 @@ interface FocusEvent : UIEvent
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 @@ interface FontFace
getter weight
method constructor
method load
setter ascentOverride
setter descentOverride
setter display
setter family
setter featureSettings
setter lineGapOverride
setter stretch
setter style
setter unicodeRange
......
......@@ -521,9 +521,12 @@ Starting worker: resources/global-interface-listing-worker.js
[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 @@ Starting worker: resources/global-interface-listing-worker.js
[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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment