Title: [213436] trunk
Revision
213436
Author
mmaxfi...@apple.com
Date
2017-03-05 12:14:02 -0800 (Sun, 05 Mar 2017)

Log Message

Update CSSFontSelector's matching algorithm to understand ranges
https://bugs.webkit.org/show_bug.cgi?id=168892

Reviewed by Jon Lee.

Source/WebCore:

This patch migrates the font selection algorithm out of FontCacheCoreText and into its own file which can be shared
among all ports. It then migrates our web font selection algorithm to use it.

This patch doesn't actually change the parsing rules; it just changes the internal machinery for how fonts get
selected. Therefore, this patch simply makes zero-length ranges from the discrete values the parser emits, and passes
those zero-length ranges to the range-based font selection routine. This means that this patch doesn't actually
change the results of the font selection algorithm.

One of the inputs to the new font selection algorithm is font-stretch, which previously was not parsed inside
@font-face blocks or the CSS Font Loading API. This patch therefore adds parsing support and modifies the existing
tests for these pieces to expect parsing to work. Because the font selection algorithm itself is shared between
installed fonts and webfonts, this patch doesn't add any additional tests for it (because it is already covered under
existing tests).

No new tests because there is no behavior change.

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj: Add new file for the font selection algorithm.
* css/CSSFontFace.cpp:
(WebCore::CSSFontFace::calculateStretch): Used on @font-face blocks and the CSS Font Loading API.
(WebCore::CSSFontFace::setStretch): Fill out the previously stubbed function.
* css/CSSFontFace.h: Add the range member variable to CSSFontFaces.
* css/CSSFontFaceSet.cpp:
(WebCore::CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered): Now that we care about font-stretch, we need to
look it up for local fonts too. This current approach of an extra FontSelectionValue hanging around with a
FontTraitsMask is very ugly and will be removed soon (https://bugs.webkit.org/show_bug.cgi?id=168889 and
https://bugs.webkit.org/show_bug.cgi?id=168890).
(WebCore::computeFontStretch): Used on @font-face blocks and the CSS Font Loading API.
(WebCore::CSSFontFaceSet::matchingFaces): Educate about font-stretch.
(WebCore::CSSFontFaceSet::fontFace): Migrate to the new font-selection algorithm.
(WebCore::fontFaceComparator): Deleted.
* css/CSSFontFaceSet.h:
* css/CSSFontSelector.cpp:
(WebCore::CSSFontSelector::addFontFaceRule): Educate about font-stretch.
(WebCore::CSSFontSelector::fontRangesForFamily): Ditto.
* css/FontFace.cpp:
(WebCore::FontFace::setStretch): Ditto.
(WebCore::FontFace::stretch): Ditto.
* css/parser/CSSPropertyParser.cpp:
(WebCore::CSSPropertyParser::parseFontFaceDescriptor): Ditto.
* platform/graphics/FontCache.h: Ditto.
* platform/graphics/FontDescription.h:
* platform/graphics/FontSelectionAlgorithm.cpp: Added.
(WebCore::FontSelectionAlgorithm::filterCapability):
(WebCore::FontSelectionAlgorithm::indexOfBestCapabilities):
(WebCore::fontSelectionRequestForTraitsMask):
(WebCore::initialFontSelectionCapabilitiesForTraitsMask):
(WebCore::fontSelectionCapabilitiesForTraitsMask):
* platform/graphics/FontSelectionAlgorithm.h: Added. Taken from FontCacheCoreText.cpp.
(WebCore::FontSelectionValue::FontSelectionValue):
(WebCore::FontSelectionValue::operator float):
(WebCore::FontSelectionValue::rawValue):
(WebCore::FontSelectionValue::maximumValue):
(WebCore::FontSelectionValue::minimumValue):
(WebCore::FontSelectionValue::operator+):
(WebCore::FontSelectionValue::operator-):
(WebCore::FontSelectionValue::operator*):
(WebCore::FontSelectionValue::operator/):
(WebCore::FontSelectionValue::operator==):
(WebCore::FontSelectionValue::operator!=):
(WebCore::FontSelectionValue::operator<):
(WebCore::FontSelectionValue::operator<=):
(WebCore::FontSelectionValue::operator>):
(WebCore::FontSelectionValue::operator>=):
(WebCore::FontSelectionRange::isValid):
(WebCore::FontSelectionRange::expand):
(WebCore::FontSelectionRange::includes):
(WebCore::FontSelectionRequest::operator==):
(WebCore::FontSelectionRequest::operator!=):
(WebCore::FontSelectionRequestKey::FontSelectionRequestKey):
(WebCore::FontSelectionRequestKey::isHashTableDeletedValue):
(WebCore::FontSelectionRequestKey::operator==):
(WebCore::FontSelectionRequestKeyHash::hash):
(WebCore::FontSelectionRequestKeyHash::equal):
(WebCore::FontSelectionCapabilities::expand):
(WebCore::FontSelectionAlgorithm::FontSelectionAlgorithm):
(WebCore::FontSelectionAlgorithm::iterateActiveCapabilitiesWithReturn):
(WebCore::FontSelectionAlgorithm::iterateActiveCapabilities):
* platform/graphics/cocoa/FontCacheCoreText.cpp: Moved to FontSelectionAlgorithm.
(WebCore::stretchFromCoreTextTraits):
(WebCore::FontDatabase::capabilitiesForFontDescriptor):
(WebCore::findClosestFont):
(WebCore::calculateFontSelectionRequest):
(WebCore::platformFontLookupWithFamily):
(WebCore::FontCache::getTraitsInFamily): Deleted.
(WebCore::iterateActiveFontsWithReturn): Deleted.
(WebCore::iterateActiveFonts): Deleted.
(WebCore::findClosestStretch): Deleted.
(WebCore::filterStretch): Deleted.
(WebCore::findClosestStyle): Deleted.
(WebCore::filterStyle): Deleted.
(WebCore::findClosestWeight): Deleted.
(WebCore::filterWeight): Deleted.
(WebCore::computeTargetWeight): Deleted.
* platform/text/TextFlags.h: Moved to FontSelectionAlgorithm.
(WebCore::FontSelectionValue::FontSelectionValue): Deleted.
(WebCore::FontSelectionValue::operator float): Deleted.
(WebCore::FontSelectionValue::operator+): Deleted.
(WebCore::FontSelectionValue::operator-): Deleted.
(WebCore::FontSelectionValue::operator*): Deleted.
(WebCore::FontSelectionValue::operator/): Deleted.
(WebCore::FontSelectionValue::operator==): Deleted.
(WebCore::FontSelectionValue::operator!=): Deleted.
(WebCore::FontSelectionValue::operator<): Deleted.
(WebCore::FontSelectionValue::operator<=): Deleted.
(WebCore::FontSelectionValue::operator>): Deleted.
(WebCore::FontSelectionValue::operator>=): Deleted.
(WebCore::FontSelectionValue::rawValue): Deleted.
(WebCore::FontSelectionValue::maximumValue): Deleted.
(WebCore::FontSelectionValue::minimumValue): Deleted.
(WebCore::FontSelectionRange::isValid): Deleted.
(WebCore::FontSelectionRange::expand): Deleted.
(WebCore::FontSelectionRange::includes): Deleted.
(WebCore::FontSelectionCapabilities::expand): Deleted.

LayoutTests:

Update CSS Font Loading API test to accept font-stretch values.

* fast/text/font-face-_javascript_-expected.txt:
* fast/text/font-face-_javascript_.html:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (213435 => 213436)


--- trunk/LayoutTests/ChangeLog	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/LayoutTests/ChangeLog	2017-03-05 20:14:02 UTC (rev 213436)
@@ -1,3 +1,15 @@
+2017-03-04  Myles C. Maxfield  <mmaxfi...@apple.com>
+
+        Update CSSFontSelector's matching algorithm to understand ranges
+        https://bugs.webkit.org/show_bug.cgi?id=168892
+
+        Reviewed by Jon Lee.
+
+        Update CSS Font Loading API test to accept font-stretch values.
+
+        * fast/text/font-face-_javascript_-expected.txt:
+        * fast/text/font-face-_javascript_.html:
+
 2017-03-05  Carlos Garcia Campos  <cgar...@igalia.com>
 
         [GTK] Two file reset tests are failing in the bots since they were added in r213042

Modified: trunk/LayoutTests/fast/text/font-face-_javascript_-expected.txt (213435 => 213436)


--- trunk/LayoutTests/fast/text/font-face-_javascript_-expected.txt	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/LayoutTests/fast/text/font-face-_javascript_-expected.txt	2017-03-05 20:14:02 UTC (rev 213436)
@@ -16,7 +16,7 @@
 PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'bolder'}).weight is "bold"
 PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'lighter'}).weight is "200"
 PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'inherit'}).weight threw exception SyntaxError (DOM Exception 12): The string did not match the expected pattern..
-PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'stretch_name'}).stretch is "normal"
+PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'ultra-expanded'}).stretch is "ultra-expanded"
 PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+26'}).unicodeRange is "U+26-26"
 PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+0-7F'}).unicodeRange is "U+0-7f"
 PASS new FontFace('family_name', 'url(\'asdf\')', {'variant': 'variant_name'}).variant threw exception SyntaxError (DOM Exception 12): The string did not match the expected pattern..
@@ -25,7 +25,7 @@
 PASS new FontFace('family_name', 'url(\'asdf\')', {'featureSettings': '\'titl\''}).featureSettings is "'titl' 1"
 PASS everything.style is "italic"
 PASS everything.weight is "bold"
-PASS everything.stretch is "normal"
+PASS everything.stretch is "extra-expanded"
 PASS everything.unicodeRange is "U+26-26"
 PASS everything.variant is "small-caps"
 PASS everything.featureSettings is "'titl' 1"
@@ -32,7 +32,7 @@
 PASS everything.family is "other_family_name"
 PASS everything.style is "normal"
 PASS everything.weight is "300"
-PASS everything.stretch is "normal"
+PASS everything.stretch is "condensed"
 PASS everything.unicodeRange is "U+0-7f"
 PASS everything.variant is "stacked-fractions"
 PASS everything.featureSettings is "'smcp' 1"

Modified: trunk/LayoutTests/fast/text/font-face-_javascript_.html (213435 => 213436)


--- trunk/LayoutTests/fast/text/font-face-_javascript_.html	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/LayoutTests/fast/text/font-face-_javascript_.html	2017-03-05 20:14:02 UTC (rev 213436)
@@ -24,7 +24,7 @@
 shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'bolder'}).weight", "bold");
 shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'lighter'}).weight", "200");
 shouldThrow("new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'inherit'}).weight");
-shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'stretch_name'}).stretch", "normal");
+shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'ultra-expanded'}).stretch", "ultra-expanded");
 shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+26'}).unicodeRange", "U+26-26");
 shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+0-7F'}).unicodeRange", "U+0-7f");
 shouldThrow("new FontFace('family_name', 'url(\\'asdf\\')', {'variant': 'variant_name'}).variant");
@@ -32,10 +32,10 @@
 shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'variant': 'small-caps common-ligatures'}).variant", "common-ligatures small-caps");
 shouldBeEqualToString("new FontFace('family_name', 'url(\\'asdf\\')', {'featureSettings': '\\'titl\\''}).featureSettings", "'titl' 1");
 
-var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'stretch_name', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
+var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'extra-expanded', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
 shouldBeEqualToString("everything.style", "italic");
 shouldBeEqualToString("everything.weight", "bold");
-shouldBeEqualToString("everything.stretch", "normal");
+shouldBeEqualToString("everything.stretch", "extra-expanded");
 shouldBeEqualToString("everything.unicodeRange", "U+26-26");
 shouldBeEqualToString("everything.variant", "small-caps");
 shouldBeEqualToString("everything.featureSettings", "'titl' 1");
@@ -46,8 +46,8 @@
 shouldBeEqualToString("everything.style", "normal");
 everything.weight = "300";
 shouldBeEqualToString("everything.weight", "300");
-everything.stretch = "other_stretch_name";
-shouldBeEqualToString("everything.stretch", "normal");
+everything.stretch = "condensed";
+shouldBeEqualToString("everything.stretch", "condensed");
 everything.unicodeRange = "U+0-7F";
 shouldBeEqualToString("everything.unicodeRange", "U+0-7f");
 everything.variant = "stacked-fractions";

Modified: trunk/Source/WebCore/CMakeLists.txt (213435 => 213436)


--- trunk/Source/WebCore/CMakeLists.txt	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/CMakeLists.txt	2017-03-05 20:14:02 UTC (rev 213436)
@@ -2200,6 +2200,7 @@
     platform/graphics/FontCascade.cpp
     platform/graphics/FontCascadeFonts.cpp
     platform/graphics/FontDescription.cpp
+    platform/graphics/FontSelectionAlgorithm.cpp
     platform/graphics/FontTaggedSettings.cpp
     platform/graphics/FontGenericFamilies.cpp
     platform/graphics/FontPlatformData.cpp

Modified: trunk/Source/WebCore/ChangeLog (213435 => 213436)


--- trunk/Source/WebCore/ChangeLog	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/ChangeLog	2017-03-05 20:14:02 UTC (rev 213436)
@@ -1,3 +1,125 @@
+2017-03-04  Myles C. Maxfield  <mmaxfi...@apple.com>
+
+        Update CSSFontSelector's matching algorithm to understand ranges
+        https://bugs.webkit.org/show_bug.cgi?id=168892
+
+        Reviewed by Jon Lee.
+
+        This patch migrates the font selection algorithm out of FontCacheCoreText and into its own file which can be shared
+        among all ports. It then migrates our web font selection algorithm to use it.
+
+        This patch doesn't actually change the parsing rules; it just changes the internal machinery for how fonts get
+        selected. Therefore, this patch simply makes zero-length ranges from the discrete values the parser emits, and passes
+        those zero-length ranges to the range-based font selection routine. This means that this patch doesn't actually
+        change the results of the font selection algorithm.
+
+        One of the inputs to the new font selection algorithm is font-stretch, which previously was not parsed inside
+        @font-face blocks or the CSS Font Loading API. This patch therefore adds parsing support and modifies the existing
+        tests for these pieces to expect parsing to work. Because the font selection algorithm itself is shared between
+        installed fonts and webfonts, this patch doesn't add any additional tests for it (because it is already covered under
+        existing tests).
+
+        No new tests because there is no behavior change.
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj: Add new file for the font selection algorithm.
+        * css/CSSFontFace.cpp:
+        (WebCore::CSSFontFace::calculateStretch): Used on @font-face blocks and the CSS Font Loading API.
+        (WebCore::CSSFontFace::setStretch): Fill out the previously stubbed function.
+        * css/CSSFontFace.h: Add the range member variable to CSSFontFaces.
+        * css/CSSFontFaceSet.cpp:
+        (WebCore::CSSFontFaceSet::ensureLocalFontFacesForFamilyRegistered): Now that we care about font-stretch, we need to
+        look it up for local fonts too. This current approach of an extra FontSelectionValue hanging around with a
+        FontTraitsMask is very ugly and will be removed soon (https://bugs.webkit.org/show_bug.cgi?id=168889 and
+        https://bugs.webkit.org/show_bug.cgi?id=168890).
+        (WebCore::computeFontStretch): Used on @font-face blocks and the CSS Font Loading API.
+        (WebCore::CSSFontFaceSet::matchingFaces): Educate about font-stretch.
+        (WebCore::CSSFontFaceSet::fontFace): Migrate to the new font-selection algorithm.
+        (WebCore::fontFaceComparator): Deleted.
+        * css/CSSFontFaceSet.h:
+        * css/CSSFontSelector.cpp:
+        (WebCore::CSSFontSelector::addFontFaceRule): Educate about font-stretch.
+        (WebCore::CSSFontSelector::fontRangesForFamily): Ditto.
+        * css/FontFace.cpp:
+        (WebCore::FontFace::setStretch): Ditto.
+        (WebCore::FontFace::stretch): Ditto.
+        * css/parser/CSSPropertyParser.cpp:
+        (WebCore::CSSPropertyParser::parseFontFaceDescriptor): Ditto.
+        * platform/graphics/FontCache.h: Ditto.
+        * platform/graphics/FontDescription.h:
+        * platform/graphics/FontSelectionAlgorithm.cpp: Added.
+        (WebCore::FontSelectionAlgorithm::filterCapability):
+        (WebCore::FontSelectionAlgorithm::indexOfBestCapabilities):
+        (WebCore::fontSelectionRequestForTraitsMask):
+        (WebCore::initialFontSelectionCapabilitiesForTraitsMask):
+        (WebCore::fontSelectionCapabilitiesForTraitsMask):
+        * platform/graphics/FontSelectionAlgorithm.h: Added. Taken from FontCacheCoreText.cpp.
+        (WebCore::FontSelectionValue::FontSelectionValue):
+        (WebCore::FontSelectionValue::operator float):
+        (WebCore::FontSelectionValue::rawValue):
+        (WebCore::FontSelectionValue::maximumValue):
+        (WebCore::FontSelectionValue::minimumValue):
+        (WebCore::FontSelectionValue::operator+):
+        (WebCore::FontSelectionValue::operator-):
+        (WebCore::FontSelectionValue::operator*):
+        (WebCore::FontSelectionValue::operator/):
+        (WebCore::FontSelectionValue::operator==):
+        (WebCore::FontSelectionValue::operator!=):
+        (WebCore::FontSelectionValue::operator<):
+        (WebCore::FontSelectionValue::operator<=):
+        (WebCore::FontSelectionValue::operator>):
+        (WebCore::FontSelectionValue::operator>=):
+        (WebCore::FontSelectionRange::isValid):
+        (WebCore::FontSelectionRange::expand):
+        (WebCore::FontSelectionRange::includes):
+        (WebCore::FontSelectionRequest::operator==):
+        (WebCore::FontSelectionRequest::operator!=):
+        (WebCore::FontSelectionRequestKey::FontSelectionRequestKey):
+        (WebCore::FontSelectionRequestKey::isHashTableDeletedValue):
+        (WebCore::FontSelectionRequestKey::operator==):
+        (WebCore::FontSelectionRequestKeyHash::hash):
+        (WebCore::FontSelectionRequestKeyHash::equal):
+        (WebCore::FontSelectionCapabilities::expand):
+        (WebCore::FontSelectionAlgorithm::FontSelectionAlgorithm):
+        (WebCore::FontSelectionAlgorithm::iterateActiveCapabilitiesWithReturn):
+        (WebCore::FontSelectionAlgorithm::iterateActiveCapabilities):
+        * platform/graphics/cocoa/FontCacheCoreText.cpp: Moved to FontSelectionAlgorithm.
+        (WebCore::stretchFromCoreTextTraits):
+        (WebCore::FontDatabase::capabilitiesForFontDescriptor):
+        (WebCore::findClosestFont):
+        (WebCore::calculateFontSelectionRequest):
+        (WebCore::platformFontLookupWithFamily):
+        (WebCore::FontCache::getTraitsInFamily): Deleted.
+        (WebCore::iterateActiveFontsWithReturn): Deleted.
+        (WebCore::iterateActiveFonts): Deleted.
+        (WebCore::findClosestStretch): Deleted.
+        (WebCore::filterStretch): Deleted.
+        (WebCore::findClosestStyle): Deleted.
+        (WebCore::filterStyle): Deleted.
+        (WebCore::findClosestWeight): Deleted.
+        (WebCore::filterWeight): Deleted.
+        (WebCore::computeTargetWeight): Deleted.
+        * platform/text/TextFlags.h: Moved to FontSelectionAlgorithm.
+        (WebCore::FontSelectionValue::FontSelectionValue): Deleted.
+        (WebCore::FontSelectionValue::operator float): Deleted.
+        (WebCore::FontSelectionValue::operator+): Deleted.
+        (WebCore::FontSelectionValue::operator-): Deleted.
+        (WebCore::FontSelectionValue::operator*): Deleted.
+        (WebCore::FontSelectionValue::operator/): Deleted.
+        (WebCore::FontSelectionValue::operator==): Deleted.
+        (WebCore::FontSelectionValue::operator!=): Deleted.
+        (WebCore::FontSelectionValue::operator<): Deleted.
+        (WebCore::FontSelectionValue::operator<=): Deleted.
+        (WebCore::FontSelectionValue::operator>): Deleted.
+        (WebCore::FontSelectionValue::operator>=): Deleted.
+        (WebCore::FontSelectionValue::rawValue): Deleted.
+        (WebCore::FontSelectionValue::maximumValue): Deleted.
+        (WebCore::FontSelectionValue::minimumValue): Deleted.
+        (WebCore::FontSelectionRange::isValid): Deleted.
+        (WebCore::FontSelectionRange::expand): Deleted.
+        (WebCore::FontSelectionRange::includes): Deleted.
+        (WebCore::FontSelectionCapabilities::expand): Deleted.
+
 2017-03-05  Simon Fraser  <simon.fra...@apple.com>
 
         Make some RenderLayer tree traversal in RenderLayerBacking more generic

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (213435 => 213436)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-03-05 20:14:02 UTC (rev 213436)
@@ -5747,6 +5747,8 @@
 		C280833F1C6DC26F001451B6 /* JSFontFace.h in Headers */ = {isa = PBXBuildFile; fileRef = C280833E1C6DC22C001451B6 /* JSFontFace.h */; };
 		C28083401C6DC275001451B6 /* JSFontFace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C280833D1C6DC22C001451B6 /* JSFontFace.cpp */; };
 		C28083421C6DC96A001451B6 /* JSFontFaceCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */; };
+		C2AB0AF61E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */; };
+		C2AB0AF71E6B3C6C001348C5 /* FontSelectionAlgorithm.h in Headers */ = {isa = PBXBuildFile; fileRef = C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		C2E1F43F1D6254E10094625C /* BreakLines.h in Headers */ = {isa = PBXBuildFile; fileRef = BCEA4816097D93020094C9E4 /* BreakLines.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		C2F4E78A1E45BEA1006D7105 /* ComplexTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */; };
 		C2F4E78C1E45C3EF006D7105 /* ComplexTextController.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -13873,6 +13875,8 @@
 		C280833D1C6DC22C001451B6 /* JSFontFace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFace.cpp; sourceTree = "<group>"; };
 		C280833E1C6DC22C001451B6 /* JSFontFace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFontFace.h; sourceTree = "<group>"; };
 		C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFaceCustom.cpp; sourceTree = "<group>"; };
+		C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontSelectionAlgorithm.cpp; sourceTree = "<group>"; };
+		C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontSelectionAlgorithm.h; sourceTree = "<group>"; };
 		C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexTextController.cpp; sourceTree = "<group>"; };
 		C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexTextController.h; sourceTree = "<group>"; };
 		C330A22113EC196B0000B45B /* ColorChooser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorChooser.h; sourceTree = "<group>"; };
@@ -22369,6 +22373,8 @@
 				501BAAA813950E2C00F7ACEB /* WindRule.h */,
 				379919941200DDF400EA041C /* WOFFFileFormat.cpp */,
 				379919951200DDF400EA041C /* WOFFFileFormat.h */,
+				C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */,
+				C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */,
 			);
 			path = graphics;
 			sourceTree = "<group>";
@@ -27639,6 +27645,7 @@
 				854FE7350A2297BE0058D7AD /* NodeIterator.h in Headers */,
 				A818721B0977D3C0005826D9 /* NodeList.h in Headers */,
 				63189AE30E83A33300012E41 /* NodeRareData.h in Headers */,
+				C2AB0AF71E6B3C6C001348C5 /* FontSelectionAlgorithm.h in Headers */,
 				63D7B32D0E78CD3F00F7617C /* NodeRenderStyle.h in Headers */,
 				E43105BB16750F1600DB2FB8 /* NodeTraversal.h in Headers */,
 				9382AAB40D8C386100F357A6 /* NodeWithIndex.h in Headers */,
@@ -29506,6 +29513,7 @@
 				1A8A645B1D19FCFC00D0E00F /* ApplePayShippingContactSelectedEvent.cpp in Sources */,
 				1A8A645F1D19FCFC00D0E00F /* ApplePayShippingMethodSelectedEvent.cpp in Sources */,
 				1A8A64621D19FCFC00D0E00F /* ApplePayValidateMerchantEvent.cpp in Sources */,
+				C2AB0AF61E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp in Sources */,
 				1A8F6BBC0DB55CDC001DB794 /* ApplicationCache.cpp in Sources */,
 				1A8F6BBE0DB55CDC001DB794 /* ApplicationCacheGroup.cpp in Sources */,
 				24F54EAC101FE914000AE741 /* ApplicationCacheHost.cpp in Sources */,

Modified: trunk/Source/WebCore/css/CSSFontFace.cpp (213435 => 213436)


--- trunk/Source/WebCore/css/CSSFontFace.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFace.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -194,6 +194,46 @@
     return FontWeight400Mask;
 }
 
+std::optional<FontSelectionValue> CSSFontFace::calculateStretch(CSSValue& stretch)
+{
+    if (!is<CSSPrimitiveValue>(stretch))
+        return std::nullopt;
+
+    const auto& primitiveValue = downcast<CSSPrimitiveValue>(stretch);
+    if (primitiveValue.isPercentage() || primitiveValue.isNumber()) {
+        auto value = primitiveValue.floatValue();
+        if (value < static_cast<float>(FontSelectionValue::minimumValue()))
+            return FontSelectionValue::minimumValue();
+        if (value > static_cast<float>(FontSelectionValue::maximumValue()))
+            return FontSelectionValue::maximumValue();
+        return FontSelectionValue(value);
+    }
+
+    switch (primitiveValue.valueID()) {
+    case CSSValueUltraCondensed:
+        return FontSelectionValue(50);
+    case CSSValueExtraCondensed:
+        return FontSelectionValue(62.5f);
+    case CSSValueCondensed:
+        return FontSelectionValue(75);
+    case CSSValueSemiCondensed:
+        return FontSelectionValue(87.5f);
+    case CSSValueNormal:
+        return FontSelectionValue(100);
+    case CSSValueSemiExpanded:
+        return FontSelectionValue(112.5f);
+    case CSSValueExpanded:
+        return FontSelectionValue(125);
+    case CSSValueExtraExpanded:
+        return FontSelectionValue(150);
+    case CSSValueUltraExpanded:
+        return FontSelectionValue(200);
+    default:
+        ASSERT_NOT_REACHED();
+        return std::nullopt;
+    }
+}
+
 bool CSSFontFace::setWeight(CSSValue& weight)
 {
     if (auto mask = calculateWeightMask(weight)) {
@@ -212,6 +252,24 @@
     return false;
 }
 
+bool CSSFontFace::setStretch(CSSValue& stretch)
+{
+    if (auto parsedStretch = calculateStretch(stretch)) {
+        m_stretch = FontSelectionRange(parsedStretch.value(), parsedStretch.value());
+
+        if (m_cssConnection)
+            m_cssConnection->mutableProperties().setProperty(CSSPropertyFontStretch, &stretch);
+
+        iterateClients(m_clients, [&](Client& client) {
+            client.fontPropertyChanged(*this);
+        });
+
+        return true;
+    }
+
+    return false;
+}
+
 bool CSSFontFace::setUnicodeRange(CSSValue& unicodeRange)
 {
     if (!is<CSSValueList>(unicodeRange))

Modified: trunk/Source/WebCore/css/CSSFontFace.h (213435 => 213436)


--- trunk/Source/WebCore/css/CSSFontFace.h	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFace.h	2017-03-05 20:14:02 UTC (rev 213436)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "CSSFontFaceRule.h"
+#include "FontSelectionAlgorithm.h"
 #include "FontTaggedSettings.h"
 #include "TextFlags.h"
 #include "Timer.h"
@@ -65,6 +66,7 @@
     bool setFamilies(CSSValue&);
     bool setStyle(CSSValue&);
     bool setWeight(CSSValue&);
+    bool setStretch(CSSValue&);
     bool setUnicodeRange(CSSValue&);
     bool setVariantLigatures(CSSValue&);
     bool setVariantPosition(CSSValue&);
@@ -78,17 +80,21 @@
     struct UnicodeRange;
     const CSSValueList* families() const { return m_families.get(); }
     FontTraitsMask traitsMask() const { return m_traitsMask; }
+    FontSelectionRange stretch() const { return m_stretch; }
     const Vector<UnicodeRange>& ranges() const { return m_ranges; }
     const FontFeatureSettings& featureSettings() const { return m_featureSettings; }
     const FontVariantSettings& variantSettings() const { return m_variantSettings; }
     void setVariantSettings(const FontVariantSettings& variantSettings) { m_variantSettings = variantSettings; }
     void setTraitsMask(FontTraitsMask traitsMask) { m_traitsMask = traitsMask; }
+    void setStretch(FontSelectionRange stretch) { m_stretch = stretch; }
     bool isLocalFallback() const { return m_isLocalFallback; }
     Status status() const { return m_status; }
     StyleRuleFontFace* cssConnection() const { return m_cssConnection.get(); }
+    FontSelectionCapabilities fontSelectionCapabilities() const { return fontSelectionCapabilitiesForTraitsMask(m_traitsMask, m_stretch); }
 
     static std::optional<FontTraitsMask> calculateStyleMask(CSSValue& style);
     static std::optional<FontTraitsMask> calculateWeightMask(CSSValue& weight);
+    static std::optional<FontSelectionValue> calculateStretch(CSSValue& stretch);
 
     class Client;
     void addClient(Client&);
@@ -173,6 +179,7 @@
     HashSet<Client*> m_clients;
     WeakPtr<FontFace> m_wrapper;
     Status m_status { Status::Pending };
+    FontSelectionRange m_stretch { FontSelectionValue(100), FontSelectionValue(100) };
     bool m_isLocalFallback { false };
     bool m_sourcesPopulated { false };
     bool m_mayBePurged { true };

Modified: trunk/Source/WebCore/css/CSSFontFaceSet.cpp (213435 => 213436)


--- trunk/Source/WebCore/css/CSSFontFaceSet.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFaceSet.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -101,18 +101,19 @@
     if (m_locallyInstalledFacesLookupTable.contains(familyName))
         return;
 
-    Vector<FontTraitsMask> traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
-    if (traitsMasks.isEmpty())
+    Vector<FontCache::TraitsAndStretch> traitsAndStretch = FontCache::singleton().getTraitsAndStretchInFamily(familyName);
+    if (traitsAndStretch.isEmpty())
         return;
 
     Vector<Ref<CSSFontFace>> faces;
-    for (auto mask : traitsMasks) {
+    for (auto item : traitsAndStretch) {
         Ref<CSSFontFace> face = CSSFontFace::create(nullptr, nullptr, nullptr, true);
         
         Ref<CSSValueList> familyList = CSSValueList::createCommaSeparated();
         familyList->append(CSSValuePool::singleton().createFontFamilyValue(familyName));
         face->setFamilies(familyList.get());
-        face->setTraitsMask(mask);
+        face->setTraitsMask(item.traits);
+        face->setStretch(item.stretch);
         face->adoptSource(std::make_unique<CSSFontFaceSource>(face.get(), familyName));
         ASSERT(!face->allSourcesFailed());
         faces.append(WTFMove(face));
@@ -311,6 +312,17 @@
     return static_cast<FontTraitsMask>(static_cast<unsigned>(styleMask) | static_cast<unsigned>(weightMask));
 }
 
+static std::optional<FontSelectionValue> computeFontStretch(MutableStyleProperties& style)
+{
+    RefPtr<CSSValue> stretchValue = style.getPropertyCSSValue(CSSPropertyFontStretch).get();
+    if (!stretchValue)
+        stretchValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal).ptr();
+
+    if (auto stretchOptional = CSSFontFace::calculateStretch(*stretchValue))
+        return stretchOptional.value();
+    return std::nullopt;
+}
+
 static HashSet<UChar32> codePointsFromString(StringView stringView)
 {
     HashSet<UChar32> result;
@@ -340,6 +352,12 @@
     else
         return Exception { SYNTAX_ERR };
 
+    FontSelectionValue stretch;
+    if (auto stretchOptional = computeFontStretch(style.get()))
+        stretch = stretchOptional.value();
+    else
+        return Exception { SYNTAX_ERR };
+
     auto family = style->getPropertyCSSValue(CSSPropertyFontFamily);
     if (!is<CSSValueList>(family.get()))
         return Exception { SYNTAX_ERR };
@@ -359,7 +377,7 @@
     for (auto codePoint : codePointsFromString(string)) {
         bool found = false;
         for (auto& family : familyOrder) {
-            auto* faces = fontFace(fontTraitsMask, family);
+            auto* faces = fontFace(fontTraitsMask, stretch, family);
             if (!faces)
                 continue;
             for (auto& constituentFace : faces->constituentFaces()) {
@@ -394,67 +412,8 @@
     return true;
 }
 
-static bool fontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison, const CSSFontFace& first, const CSSFontFace& second)
+CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, FontSelectionValue stretch, const AtomicString& family)
 {
-    FontTraitsMask firstTraitsMask = first.traitsMask();
-    FontTraitsMask secondTraitsMask = second.traitsMask();
-
-    bool firstHasDesiredStyle = firstTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
-    bool secondHasDesiredStyle = secondTraitsMask & desiredTraitsMaskForComparison & FontStyleMask;
-
-    if (firstHasDesiredStyle != secondHasDesiredStyle)
-        return firstHasDesiredStyle;
-
-    if ((desiredTraitsMaskForComparison & FontStyleItalicMask) && !first.isLocalFallback() && !second.isLocalFallback()) {
-        // Prefer a font that has indicated that it can only support italics to a font that claims to support
-        // all styles. The specialized font is more likely to be the one the author wants used.
-        bool firstRequiresItalics = (firstTraitsMask & FontStyleItalicMask) && !(firstTraitsMask & FontStyleNormalMask);
-        bool secondRequiresItalics = (secondTraitsMask & FontStyleItalicMask) && !(secondTraitsMask & FontStyleNormalMask);
-        if (firstRequiresItalics != secondRequiresItalics)
-            return firstRequiresItalics;
-    }
-
-    if (secondTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
-        return false;
-    if (firstTraitsMask & desiredTraitsMaskForComparison & FontWeightMask)
-        return true;
-
-    // http://www.w3.org/TR/2011/WD-css3-fonts-20111004/#font-matching-algorithm says :
-    //   - If the desired weight is less than 400, weights below the desired weight are checked in descending order followed by weights above the desired weight in ascending order until a match is found.
-    //   - If the desired weight is greater than 500, weights above the desired weight are checked in ascending order followed by weights below the desired weight in descending order until a match is found.
-    //   - If the desired weight is 400, 500 is checked first and then the rule for desired weights less than 400 is used.
-    //   - If the desired weight is 500, 400 is checked first and then the rule for desired weights less than 400 is used.
-
-    static const unsigned fallbackRuleSets = 9;
-    static const unsigned rulesPerSet = 8;
-    static const FontTraitsMask weightFallbackRuleSets[fallbackRuleSets][rulesPerSet] = {
-        { FontWeight200Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
-        { FontWeight100Mask, FontWeight300Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
-        { FontWeight200Mask, FontWeight100Mask, FontWeight400Mask, FontWeight500Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
-        { FontWeight500Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
-        { FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask, FontWeight600Mask, FontWeight700Mask, FontWeight800Mask, FontWeight900Mask },
-        { FontWeight700Mask, FontWeight800Mask, FontWeight900Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
-        { FontWeight800Mask, FontWeight900Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
-        { FontWeight900Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask },
-        { FontWeight800Mask, FontWeight700Mask, FontWeight600Mask, FontWeight500Mask, FontWeight400Mask, FontWeight300Mask, FontWeight200Mask, FontWeight100Mask }
-    };
-
-    unsigned ruleSetIndex = 0;
-    for (; !(desiredTraitsMaskForComparison & (1 << (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
-
-    const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
-    for (unsigned i = 0; i < rulesPerSet; ++i) {
-        if (secondTraitsMask & weightFallbackRule[i])
-            return false;
-        if (firstTraitsMask & weightFallbackRule[i])
-            return true;
-    }
-
-    return false;
-}
-
-CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, const AtomicString& family)
-{
     auto iterator = m_facesLookupTable.find(family);
     if (iterator == m_facesLookupTable.end())
         return nullptr;
@@ -487,12 +446,40 @@
         }
     }
 
-    std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [traitsMask](const CSSFontFace& first, const CSSFontFace& second) {
-        return fontFaceComparator(traitsMask, first, second);
-    });
-    for (auto& candidate : candidateFontFaces)
-        face->appendFontFace(candidate.get());
+    if (!candidateFontFaces.isEmpty()) {
+        Vector<FontSelectionCapabilities> capabilities;
+        capabilities.reserveInitialCapacity(candidateFontFaces.size());
+        for (auto& face : candidateFontFaces)
+            capabilities.uncheckedAppend(face.get().fontSelectionCapabilities());
+        FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequestForTraitsMask(traitsMask, stretch), capabilities);
+        std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [&fontSelectionAlgorithm](const CSSFontFace& first, const CSSFontFace& second) {
+            auto firstCapabilities = first.fontSelectionCapabilities();
+            auto secondCapabilities = second.fontSelectionCapabilities();
 
+            auto stretchDistanceFirst = fontSelectionAlgorithm.stretchDistance(firstCapabilities).distance;
+            auto stretchDistanceSecond = fontSelectionAlgorithm.stretchDistance(secondCapabilities).distance;
+            if (stretchDistanceFirst < stretchDistanceSecond)
+                return true;
+            if (stretchDistanceFirst > stretchDistanceSecond)
+                return false;
+
+            auto styleDistanceFirst = fontSelectionAlgorithm.styleDistance(firstCapabilities).distance;
+            auto styleDistanceSecond = fontSelectionAlgorithm.styleDistance(secondCapabilities).distance;
+            if (styleDistanceFirst < styleDistanceSecond)
+                return true;
+            if (styleDistanceFirst > styleDistanceSecond)
+                return false;
+
+            auto weightDistanceFirst = fontSelectionAlgorithm.weightDistance(firstCapabilities).distance;
+            auto weightDistanceSecond = fontSelectionAlgorithm.weightDistance(secondCapabilities).distance;
+            if (weightDistanceFirst < weightDistanceSecond)
+                return true;
+            return false;
+        });
+        for (auto& candidate : candidateFontFaces)
+            face->appendFontFace(candidate.get());
+    }
+
     return face.get();
 }
 

Modified: trunk/Source/WebCore/css/CSSFontFaceSet.h (213435 => 213436)


--- trunk/Source/WebCore/css/CSSFontFaceSet.h	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontFaceSet.h	2017-03-05 20:14:02 UTC (rev 213436)
@@ -67,7 +67,7 @@
 
     ExceptionOr<bool> check(const String& font, const String& text);
 
-    CSSSegmentedFontFace* fontFace(FontTraitsMask, const AtomicString& family);
+    CSSSegmentedFontFace* fontFace(FontTraitsMask, FontSelectionValue stretch, const AtomicString& family);
 
     enum class Status { Loading, Loaded };
     Status status() const { return m_status; }

Modified: trunk/Source/WebCore/css/CSSFontSelector.cpp (213435 => 213436)


--- trunk/Source/WebCore/css/CSSFontSelector.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/CSSFontSelector.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -145,6 +145,7 @@
     RefPtr<CSSValue> fontFamily = style.getPropertyCSSValue(CSSPropertyFontFamily);
     RefPtr<CSSValue> fontStyle = style.getPropertyCSSValue(CSSPropertyFontStyle);
     RefPtr<CSSValue> fontWeight = style.getPropertyCSSValue(CSSPropertyFontWeight);
+    RefPtr<CSSValue> fontStretch = style.getPropertyCSSValue(CSSPropertyFontStretch);
     RefPtr<CSSValue> src = ""
     RefPtr<CSSValue> unicodeRange = style.getPropertyCSSValue(CSSPropertyUnicodeRange);
     RefPtr<CSSValue> featureSettings = style.getPropertyCSSValue(CSSPropertyFontFeatureSettings);
@@ -167,6 +168,9 @@
     if (!fontWeight)
         fontWeight = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
 
+    if (!fontStretch)
+        fontStretch = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
+
     CSSValueList* rangeList = downcast<CSSValueList>(unicodeRange.get());
 
     CSSValueList& srcList = downcast<CSSValueList>(*src);
@@ -182,6 +186,8 @@
         return;
     if (!fontFace->setWeight(*fontWeight))
         return;
+    if (!fontFace->setStretch(*fontStretch))
+        return;
     if (rangeList && !fontFace->setUnicodeRange(*rangeList))
         return;
     if (variantLigatures && !fontFace->setVariantLigatures(*variantLigatures))
@@ -295,7 +301,7 @@
     bool resolveGenericFamilyFirst = familyName == standardFamily;
 
     AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document, fontDescription, familyName) : familyName;
-    auto* face = m_cssFontFaceSet->fontFace(fontDescription.traitsMask(), familyForLookup);
+    auto* face = m_cssFontFaceSet->fontFace(fontDescription.traitsMask(), fontDescription.stretch(), familyForLookup);
     if (!face) {
         if (!resolveGenericFamilyFirst)
             familyForLookup = resolveGenericFamily(m_document, fontDescription, familyName);

Modified: trunk/Source/WebCore/css/FontFace.cpp (213435 => 213436)


--- trunk/Source/WebCore/css/FontFace.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/FontFace.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -184,9 +184,16 @@
     return { };
 }
 
-ExceptionOr<void> FontFace::setStretch(const String&)
+ExceptionOr<void> FontFace::setStretch(const String& stretch)
 {
-    // We don't support font-stretch. Swallow the call.
+    if (stretch.isEmpty())
+        return Exception { SYNTAX_ERR };
+
+    bool success = false;
+    if (auto value = parseString(stretch, CSSPropertyFontStretch))
+        success = m_backing->setStretch(*value);
+    if (!success)
+        return Exception { SYNTAX_ERR };
     return { };
 }
 
@@ -318,7 +325,33 @@
 
 String FontFace::stretch() const
 {
-    return ASCIILiteral("normal");
+    m_backing->updateStyleIfNeeded();
+    auto stretch = m_backing->stretch();
+
+    auto rangeIsSingleValue = [](FontSelectionRange range, FontSelectionValue value) -> bool {
+        return range.minimum == value && range.maximum == value;
+    };
+
+    if (rangeIsSingleValue(stretch, FontSelectionValue(50)))
+        return ASCIILiteral("ultra-condensed");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(62.5f)))
+        return ASCIILiteral("extra-condensed");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(75)))
+        return ASCIILiteral("condensed");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(87.5f)))
+        return ASCIILiteral("semi-condensed");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(100)))
+        return ASCIILiteral("normal");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(112.5f)))
+        return ASCIILiteral("semi-expanded");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(125)))
+        return ASCIILiteral("expanded");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(150)))
+        return ASCIILiteral("extra-expanded");
+    if (rangeIsSingleValue(stretch, FontSelectionValue(200)))
+        return ASCIILiteral("ultra-expanded");
+
+    return String::format("%f-%f", static_cast<float>(stretch.minimum), static_cast<float>(stretch.maximum));
 }
 
 String FontFace::unicodeRange() const

Modified: trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp (213435 => 213436)


--- trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -4185,9 +4185,7 @@
         parsedValue = consumeFontFaceUnicodeRange(m_range);
         break;
     case CSSPropertyFontStretch:
-        // FIXME: Implement this.
-        m_range.consumeIncludingWhitespace();
-        parsedValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
+        parsedValue = consumeFontStretch(m_range);
         break;
     case CSSPropertyFontStyle: {
         CSSValueID id = m_range.consumeIncludingWhitespace().id();

Modified: trunk/Source/WebCore/platform/graphics/FontCache.h (213435 => 213436)


--- trunk/Source/WebCore/platform/graphics/FontCache.h	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/FontCache.h	2017-03-05 20:14:02 UTC (rev 213436)
@@ -203,7 +203,11 @@
 
     // This function exists so CSSFontSelector can have a unified notion of preinstalled fonts and @font-face.
     // It comes into play when you create an @font-face which shares a family name as a preinstalled font.
-    Vector<FontTraitsMask> getTraitsInFamily(const AtomicString&);
+    struct TraitsAndStretch {
+        FontTraitsMask traits;
+        FontSelectionRange stretch;
+    };
+    Vector<TraitsAndStretch> getTraitsAndStretchInFamily(const AtomicString&);
 
     WEBCORE_EXPORT RefPtr<Font> fontForFamily(const FontDescription&, const AtomicString&, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false);
     WEBCORE_EXPORT Ref<Font> lastResortFallbackFont(const FontDescription&);

Modified: trunk/Source/WebCore/platform/graphics/FontDescription.h (213435 => 213436)


--- trunk/Source/WebCore/platform/graphics/FontDescription.h	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/FontDescription.h	2017-03-05 20:14:02 UTC (rev 213436)
@@ -26,6 +26,7 @@
 #define FontDescription_h
 
 #include "CSSValueKeywords.h"
+#include "FontSelectionAlgorithm.h"
 #include "FontTaggedSettings.h"
 #include "TextFlags.h"
 #include "WebKitFontFamilyNames.h"

Added: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp (0 => 213436)


--- trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#include "config.h"
+#include "FontSelectionAlgorithm.h"
+
+namespace WebCore {
+
+auto FontSelectionAlgorithm::stretchDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+{
+    auto width = capabilities.width;
+    ASSERT(width.isValid());
+    if (width.includes(m_request.width))
+        return { FontSelectionValue(), m_request.width };
+
+    if (m_request.width >= FontSelectionValue(100)) {
+        if (width.minimum > m_request.width)
+            return { width.minimum - m_request.width, width.minimum };
+        ASSERT(width.maximum < m_request.width);
+        auto threshold = std::max(m_request.width, m_capabilitiesBounds.width.maximum);
+        return { threshold - width.maximum, width.maximum };
+    }
+
+    if (width.maximum < m_request.width)
+        return { m_request.width - width.maximum, width.maximum };
+    ASSERT(width.minimum > m_request.width);
+    auto threshold = std::min(m_request.width, m_capabilitiesBounds.width.minimum);
+    return { width.minimum - threshold, width.minimum };
+}
+
+auto FontSelectionAlgorithm::styleDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+{
+    auto slope = capabilities.slope;
+    ASSERT(slope.isValid());
+    if (slope.includes(m_request.slope))
+        return { FontSelectionValue(), m_request.slope };
+
+    if (m_request.slope >= italicThreshold()) {
+        if (slope.minimum > m_request.slope)
+            return { slope.minimum - m_request.slope, slope.minimum };
+        ASSERT(m_request.slope > slope.maximum);
+        auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
+        return { threshold - slope.maximum, slope.maximum };
+    }
+
+    if (m_request.slope >= FontSelectionValue()) {
+        if (slope.maximum >= FontSelectionValue() && slope.maximum < m_request.slope)
+            return { m_request.slope - slope.maximum, slope.maximum };
+        if (slope.minimum > m_request.slope)
+            return { slope.minimum, slope.minimum };
+        ASSERT(slope.maximum < FontSelectionValue());
+        auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
+        return { threshold - slope.maximum, slope.maximum };
+    }
+
+    if (m_request.slope > -italicThreshold()) {
+        if (slope.minimum > m_request.slope && slope.minimum <= FontSelectionValue())
+            return { slope.minimum - m_request.slope, slope.minimum };
+        if (slope.maximum < m_request.slope)
+            return { -slope.maximum, slope.maximum };
+        ASSERT(slope.minimum > FontSelectionValue());
+        auto threshold = std::min(m_request.slope, m_capabilitiesBounds.slope.minimum);
+        return { slope.minimum - threshold, slope.minimum };
+    }
+
+    if (slope.maximum < m_request.slope)
+        return { m_request.slope - slope.maximum, slope.maximum };
+    ASSERT(slope.minimum > m_request.slope);
+    auto threshold = std::min(m_request.slope, m_capabilitiesBounds.slope.minimum);
+    return { slope.minimum - threshold, slope.minimum };
+}
+
+auto FontSelectionAlgorithm::weightDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+{
+    auto weight = capabilities.weight;
+    ASSERT(weight.isValid());
+    if (weight.includes(m_request.weight))
+        return { FontSelectionValue(), m_request.weight };
+
+    // The spec states: "If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first"
+    FontSelectionValue offset(1);
+    if (m_request.weight == FontSelectionValue(400) && weight.includes(FontSelectionValue(500)))
+        return { offset, FontSelectionValue(500) };
+    if (m_request.weight == FontSelectionValue(500) && weight.includes(FontSelectionValue(400)))
+        return { offset, FontSelectionValue(400) };
+
+    if (m_request.weight <= weightSearchThreshold()) {
+        if (weight.maximum < m_request.weight)
+            return { m_request.weight - weight.maximum + offset, weight.maximum };
+        ASSERT(weight.minimum > m_request.weight);
+        auto threshold = std::min(m_request.weight, m_capabilitiesBounds.weight.minimum);
+        return { weight.minimum - threshold + offset, weight.minimum };
+    }
+
+    if (weight.minimum > m_request.weight)
+        return { weight.minimum - m_request.weight + offset, weight.minimum };
+    ASSERT(weight.maximum < m_request.weight);
+    auto threshold = std::max(m_request.weight, m_capabilitiesBounds.weight.maximum);
+    return { threshold - weight.maximum + offset, weight.maximum };
+}
+
+void FontSelectionAlgorithm::filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange)
+{
+    std::optional<FontSelectionValue> smallestDistance;
+    FontSelectionValue closestValue;
+    iterateActiveCapabilities([&](FontSelectionCapabilities capabilities, size_t) {
+        auto distanceResult = (this->*computeDistance)(capabilities);
+        if (!smallestDistance || distanceResult.distance < smallestDistance.value()) {
+            smallestDistance = distanceResult.distance;
+            closestValue = distanceResult.value;
+        }
+    });
+    ASSERT(smallestDistance);
+    iterateActiveCapabilities([&](auto& capabilities, size_t i) {
+        if (!(capabilities.*inclusionRange).includes(closestValue))
+            m_filter[i] = false;
+    });
+}
+
+size_t FontSelectionAlgorithm::indexOfBestCapabilities()
+{
+    filterCapability(&FontSelectionAlgorithm::stretchDistance, &FontSelectionCapabilities::width);
+    filterCapability(&FontSelectionAlgorithm::styleDistance, &FontSelectionCapabilities::slope);
+    filterCapability(&FontSelectionAlgorithm::weightDistance, &FontSelectionCapabilities::weight);
+
+    auto result = iterateActiveCapabilitiesWithReturn<size_t>([](FontSelectionCapabilities, size_t i) {
+        return i;
+    });
+    ASSERT(result);
+    return result.value_or(0);
+}
+
+FontSelectionRequest fontSelectionRequestForTraitsMask(FontTraitsMask traitsMask, FontSelectionValue stretch)
+{
+    FontSelectionRequest result;
+    if (traitsMask & FontWeight100Mask)
+        result.weight = FontSelectionValue(100);
+    else if (traitsMask & FontWeight200Mask)
+        result.weight = FontSelectionValue(200);
+    else if (traitsMask & FontWeight300Mask)
+        result.weight = FontSelectionValue(300);
+    else if (traitsMask & FontWeight400Mask)
+        result.weight = FontSelectionValue(400);
+    else if (traitsMask & FontWeight500Mask)
+        result.weight = FontSelectionValue(500);
+    else if (traitsMask & FontWeight600Mask)
+        result.weight = FontSelectionValue(600);
+    else if (traitsMask & FontWeight700Mask)
+        result.weight = FontSelectionValue(700);
+    else if (traitsMask & FontWeight800Mask)
+        result.weight = FontSelectionValue(800);
+    else {
+        ASSERT(traitsMask & FontWeight900Mask);
+        result.weight = FontSelectionValue(900);
+    }
+
+    result.width = stretch;
+
+    if (traitsMask & FontStyleNormalMask)
+        result.slope = FontSelectionValue();
+    else {
+        ASSERT(traitsMask & FontStyleItalicMask);
+        result.slope = italicThreshold();
+    }
+
+    return result;
+}
+
+static FontSelectionCapabilities initialFontSelectionCapabilitiesForTraitsMask(FontTraitsMask traitsMask)
+{
+    FontSelectionCapabilities result;
+    if (traitsMask & FontWeight100Mask)
+        result.weight = { FontSelectionValue(100), FontSelectionValue(100) };
+    else if (traitsMask & FontWeight200Mask)
+        result.weight = { FontSelectionValue(200), FontSelectionValue(200) };
+    else if (traitsMask & FontWeight300Mask)
+        result.weight = { FontSelectionValue(300), FontSelectionValue(300) };
+    else if (traitsMask & FontWeight400Mask)
+        result.weight = { FontSelectionValue(400), FontSelectionValue(400) };
+    else if (traitsMask & FontWeight500Mask)
+        result.weight = { FontSelectionValue(500), FontSelectionValue(500) };
+    else if (traitsMask & FontWeight600Mask)
+        result.weight = { FontSelectionValue(600), FontSelectionValue(600) };
+    else if (traitsMask & FontWeight700Mask)
+        result.weight = { FontSelectionValue(700), FontSelectionValue(700) };
+    else if (traitsMask & FontWeight800Mask)
+        result.weight = { FontSelectionValue(800), FontSelectionValue(800) };
+    else {
+        ASSERT(traitsMask & FontWeight900Mask);
+        result.weight = { FontSelectionValue(900), FontSelectionValue(900) };
+    }
+
+    if (traitsMask & FontStyleNormalMask)
+        result.slope = { FontSelectionValue(), FontSelectionValue() };
+    else {
+        ASSERT(traitsMask & FontStyleItalicMask);
+        result.slope = { italicThreshold(), italicThreshold() };
+    }
+
+    return result;
+}
+
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask traitsMask, FontSelectionValue stretch)
+{
+    FontSelectionCapabilities result = initialFontSelectionCapabilitiesForTraitsMask(traitsMask);
+    result.width = { stretch, stretch };
+    return result;
+}
+
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask traitsMask, FontSelectionRange stretch)
+{
+    FontSelectionCapabilities result = initialFontSelectionCapabilitiesForTraitsMask(traitsMask);
+    result.width = stretch;
+    return result;
+}
+
+}
Property changes on: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp
___________________________________________________________________

Added: svn:eol-style

+native \ No newline at end of property

Added: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h (0 => 213436)


--- trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h	2017-03-05 20:14:02 UTC (rev 213436)
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#pragma once
+
+#include "TextFlags.h"
+#include <wtf/GetPtr.h>
+#include <wtf/Hasher.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Optional.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+// Unclamped, unchecked, signed fixed-point number representing a value used for font variations.
+// Sixteen bits in total, one sign bit, two fractional bits, means the smallest positive representable value is 0.25,
+// the maximum representable value is 8191.75, and the minimum representable value is -8192.
+class FontSelectionValue {
+public:
+    FontSelectionValue() = default;
+
+    // Explicit because it is lossy.
+    explicit FontSelectionValue(int x)
+        : m_backing(x * fractionalEntropy)
+    {
+    }
+
+    // Explicit because it is lossy.
+    explicit FontSelectionValue(float x)
+        : m_backing(x * fractionalEntropy)
+    {
+    }
+
+    operator float() const
+    {
+        // floats have 23 fractional bits, but only 14 fractional bits are necessary, so every value can be represented losslessly.
+        return m_backing / static_cast<float>(fractionalEntropy);
+    }
+
+    FontSelectionValue operator+(const FontSelectionValue other) const;
+    FontSelectionValue operator-(const FontSelectionValue other) const;
+    FontSelectionValue operator*(const FontSelectionValue other) const;
+    FontSelectionValue operator/(const FontSelectionValue other) const;
+    FontSelectionValue operator-() const;
+    bool operator==(const FontSelectionValue other) const;
+    bool operator!=(const FontSelectionValue other) const;
+    bool operator<(const FontSelectionValue other) const;
+    bool operator<=(const FontSelectionValue other) const;
+    bool operator>(const FontSelectionValue other) const;
+    bool operator>=(const FontSelectionValue other) const;
+
+    int16_t rawValue() const
+    {
+        return m_backing;
+    }
+
+    static FontSelectionValue maximumValue()
+    {
+        static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::max(), RawTag::RawTag);
+        return result.get();
+    }
+
+    static FontSelectionValue minimumValue()
+    {
+        static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::min(), RawTag::RawTag);
+        return result.get();
+    }
+
+private:
+    enum class RawTag { RawTag };
+
+    FontSelectionValue(int16_t rawValue, RawTag)
+        : m_backing(rawValue)
+    {
+    }
+
+    static constexpr int fractionalEntropy = 4;
+    int16_t m_backing { 0 };
+};
+
+inline FontSelectionValue FontSelectionValue::operator+(const FontSelectionValue other) const
+{
+    return FontSelectionValue(m_backing + other.m_backing, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator-(const FontSelectionValue other) const
+{
+    return FontSelectionValue(m_backing - other.m_backing, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator*(const FontSelectionValue other) const
+{
+    return FontSelectionValue(static_cast<int32_t>(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator/(const FontSelectionValue other) const
+{
+    return FontSelectionValue(static_cast<int32_t>(m_backing) / other.m_backing * fractionalEntropy, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator-() const
+{
+    return FontSelectionValue(-m_backing, RawTag::RawTag);
+}
+
+inline bool FontSelectionValue::operator==(const FontSelectionValue other) const
+{
+    return m_backing == other.m_backing;
+}
+
+inline bool FontSelectionValue::operator!=(const FontSelectionValue other) const
+{
+    return !operator==(other);
+}
+
+inline bool FontSelectionValue::operator<(const FontSelectionValue other) const
+{
+    return m_backing < other.m_backing;
+}
+
+inline bool FontSelectionValue::operator<=(const FontSelectionValue other) const
+{
+    return m_backing <= other.m_backing;
+}
+
+inline bool FontSelectionValue::operator>(const FontSelectionValue other) const
+{
+    return m_backing > other.m_backing;
+}
+
+inline bool FontSelectionValue::operator>=(const FontSelectionValue other) const
+{
+    return m_backing >= other.m_backing;
+}
+
+static inline FontSelectionValue italicThreshold()
+{
+    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(20);
+    return result.get();
+}
+
+static inline FontSelectionValue boldThreshold()
+{
+    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(600);
+    return result.get();
+}
+
+static inline FontSelectionValue weightSearchThreshold()
+{
+    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(500);
+    return result.get();
+}
+
+// [Inclusive, Inclusive]
+struct FontSelectionRange {
+    FontSelectionRange(FontSelectionValue minimum, FontSelectionValue maximum)
+        : minimum(minimum)
+        , maximum(maximum)
+    {
+    }
+
+    bool operator==(const FontSelectionRange& other) const
+    {
+        return minimum == other.minimum
+            && maximum == other.maximum;
+    }
+
+    bool isValid() const
+    {
+        return minimum <= maximum;
+    }
+
+    void expand(const FontSelectionRange& other)
+    {
+        ASSERT(other.isValid());
+        if (!isValid())
+            *this = other;
+        else {
+            minimum = std::min(minimum, other.minimum);
+            maximum = std::max(maximum, other.maximum);
+        }
+        ASSERT(isValid());
+    }
+
+    bool includes(FontSelectionValue target) const
+    {
+        return target >= minimum && target <= maximum;
+    }
+
+    FontSelectionValue minimum { FontSelectionValue(1) };
+    FontSelectionValue maximum { FontSelectionValue(0) };
+};
+
+struct FontSelectionRequest {
+    bool operator==(const FontSelectionRequest& other) const
+    {
+        return weight == other.weight
+            && width == other.width
+            && slope == other.slope;
+    }
+
+    bool operator!=(const FontSelectionRequest& other) const
+    {
+        return !operator==(other);
+    }
+
+    FontSelectionValue weight;
+    FontSelectionValue width;
+    FontSelectionValue slope;
+};
+
+// Only used for HashMaps. We don't want to put the bool into FontSelectionRequest
+// because FontSelectionRequest needs to be as small as possible because it's inside
+// every FontDescription.
+struct FontSelectionRequestKey {
+    FontSelectionRequestKey() = default;
+
+    FontSelectionRequestKey(FontSelectionRequest request)
+        : request(request)
+    {
+    }
+
+    explicit FontSelectionRequestKey(WTF::HashTableDeletedValueType)
+        : isDeletedValue(true)
+    {
+    }
+
+    bool isHashTableDeletedValue() const
+    {
+        return isDeletedValue;
+    }
+
+    bool operator==(const FontSelectionRequestKey& other) const
+    {
+        return request == other.request
+            && isDeletedValue == other.isDeletedValue;
+    }
+
+    FontSelectionRequest request;
+    bool isDeletedValue { false };
+};
+
+struct FontSelectionRequestKeyHash {
+    static unsigned hash(const FontSelectionRequestKey& key)
+    {
+        IntegerHasher hasher;
+        hasher.add(key.request.weight.rawValue());
+        hasher.add(key.request.width.rawValue());
+        hasher.add(key.request.slope.rawValue());
+        hasher.add(key.isDeletedValue);
+        return hasher.hash();
+    }
+
+    static bool equal(const FontSelectionRequestKey& a, const FontSelectionRequestKey& b)
+    {
+        return a == b;
+    }
+
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct FontSelectionCapabilities {
+    void expand(const FontSelectionCapabilities& capabilities)
+    {
+        weight.expand(capabilities.weight);
+        width.expand(capabilities.width);
+        slope.expand(capabilities.slope);
+    }
+
+    FontSelectionRange weight { FontSelectionValue(400), FontSelectionValue(400) };
+    FontSelectionRange width { FontSelectionValue(100), FontSelectionValue(100) };
+    FontSelectionRange slope { FontSelectionValue(), FontSelectionValue() };
+};
+
+class FontSelectionAlgorithm {
+public:
+    FontSelectionAlgorithm() = delete;
+
+    FontSelectionAlgorithm(FontSelectionRequest request, const Vector<FontSelectionCapabilities>& capabilities, std::optional<FontSelectionCapabilities> capabilitiesBounds = std::nullopt)
+        : m_request(request)
+        , m_capabilities(capabilities)
+        , m_filter(new bool[m_capabilities.size()])
+    {
+        ASSERT(!m_capabilities.isEmpty());
+        if (capabilitiesBounds)
+            m_capabilitiesBounds = capabilitiesBounds.value();
+        else {
+            for (auto capabilities : m_capabilities)
+                m_capabilitiesBounds.expand(capabilities);
+        }
+        for (size_t i = 0; i < m_capabilities.size(); ++i)
+            m_filter[i] = true;
+    }
+
+    struct DistanceResult {
+        FontSelectionValue distance;
+        FontSelectionValue value;
+    };
+
+    DistanceResult stretchDistance(FontSelectionCapabilities) const;
+    DistanceResult styleDistance(FontSelectionCapabilities) const;
+    DistanceResult weightDistance(FontSelectionCapabilities) const;
+
+    size_t indexOfBestCapabilities();
+
+private:
+    template <typename T>
+    using IterateActiveCapabilitiesWithReturnCallback = std::function<std::optional<T>(FontSelectionCapabilities, size_t)>;
+
+    template <typename T>
+    inline std::optional<T> iterateActiveCapabilitiesWithReturn(IterateActiveCapabilitiesWithReturnCallback<T> callback)
+    {
+        for (size_t i = 0; i < m_capabilities.size(); ++i) {
+            if (!m_filter[i])
+                continue;
+            if (auto result = callback(m_capabilities[i], i))
+                return result;
+        }
+        return std::nullopt;
+    }
+
+    template <typename T>
+    inline void iterateActiveCapabilities(T callback)
+    {
+        iterateActiveCapabilitiesWithReturn<int>([&](FontSelectionCapabilities capabilities, size_t i) -> std::optional<int> {
+            callback(capabilities, i);
+            return std::nullopt;
+        });
+    }
+
+    void filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange);
+
+    FontSelectionRequest m_request;
+    FontSelectionCapabilities m_capabilitiesBounds;
+    const Vector<FontSelectionCapabilities>& m_capabilities;
+    std::unique_ptr<bool[]> m_filter;
+};
+
+FontSelectionRequest fontSelectionRequestForTraitsMask(FontTraitsMask, FontSelectionValue stretch);
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask, FontSelectionValue stretch);
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask, FontSelectionRange stretch);
+
+}
Property changes on: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h
___________________________________________________________________

Added: svn:eol-style

+native \ No newline at end of property

Added: svn:keywords

+Author Date Id Rev URL \ No newline at end of property

Modified: trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp (213435 => 213436)


--- trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -662,8 +662,21 @@
     return nullptr;
 }
 
-Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
+static FontSelectionValue stretchFromCoreTextTraits(CFDictionaryRef traits)
 {
+    auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits, kCTFontWidthTrait));
+    if (widthNumber) {
+        // FIXME: The normalization from Core Text's [-1, 1] range to CSS's [50%, 200%] range isn't perfect.
+        float ctWidth;
+        auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
+        ASSERT_UNUSED(success, success);
+        return FontSelectionValue(ctWidth < 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
+    }
+    return FontSelectionValue(100);
+}
+
+auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
+{
     auto familyNameStr = familyName.string().createCFString();
     CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
     CFTypeRef values[] = { familyNameStr.get() };
@@ -677,8 +690,8 @@
     if (!numMatches)
         return { };
 
-    Vector<FontTraitsMask> traitsMasks;
-    traitsMasks.reserveInitialCapacity(numMatches);
+    Vector<TraitsAndStretch> result;
+    result.reserveInitialCapacity(numMatches);
     for (CFIndex i = 0; i < numMatches; ++i) {
         auto traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)CFArrayGetValueAtIndex(matchedDescriptors.get(), i), kCTFontTraitsAttribute));
         CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
@@ -688,10 +701,11 @@
             CFNumberGetValue(resultRef, kCFNumberIntType, &symbolicTraits);
             CGFloat weight = 0;
             CFNumberGetValue(weightRef, kCFNumberCGFloatType, &weight);
-            traitsMasks.uncheckedAppend(toTraitsMask(symbolicTraits, weight));
+            auto stretch = stretchFromCoreTextTraits(traits.get());
+            result.uncheckedAppend({ toTraitsMask(symbolicTraits, weight), { stretch, stretch } });
         }
     }
-    return traitsMasks;
+    return result;
 }
 
 static void invalidateFontCache();
@@ -895,14 +909,7 @@
         FontSelectionValue slant;
         FontSelectionValue weight;
         if (traits) {
-            auto widthNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontWidthTrait));
-            if (widthNumber) {
-                // FIXME: The normalization from Core Text's [-1, 1] range to CSS's [50%, 200%] range isn't perfect.
-                float ctWidth;
-                auto success = CFNumberGetValue(widthNumber, kCFNumberFloatType, &ctWidth);
-                ASSERT_UNUSED(success, success);
-                width = FontSelectionValue(ctWidth < 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
-            }
+            width = stretchFromCoreTextTraits(traits.get());
 
             auto symbolicTraitsNumber = static_cast<CFNumberRef>(CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait));
             if (symbolicTraitsNumber) {
@@ -926,24 +933,6 @@
         return { { weight, weight }, { width, width }, { slant, slant } };
     }
 
-    static const FontSelectionValue stretchThreshold()
-    {
-        static NeverDestroyed<FontSelectionValue> threshold(100);
-        return threshold.get();
-    }
-
-    static const FontSelectionValue italicThreshold()
-    {
-        static NeverDestroyed<FontSelectionValue> threshold(20);
-        return threshold.get();
-    }
-
-    static const FontSelectionValue weightThreshold()
-    {
-        static NeverDestroyed<FontSelectionValue> threshold(500);
-        return threshold.get();
-    }
-
 private:
     friend class NeverDestroyed<FontDatabase>;
 
@@ -953,326 +942,61 @@
     HashMap<String, InstalledFont> m_postScriptNameToFontDescriptors;
 };
 
-template <typename T>
-using IterateActiveFontsWithReturnCallback = std::function<std::optional<T>(const FontDatabase::InstalledFont&, size_t)>;
-
-template <typename T>
-inline std::optional<T> iterateActiveFontsWithReturn(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, IterateActiveFontsWithReturnCallback<T> callback)
+static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, FontSelectionRequest fontSelectionRequest)
 {
-    for (size_t i = 0; i < installedFonts.size(); ++i) {
-        if (!filter[i])
-            continue;
-        if (auto result = callback(installedFonts.installedFonts[i], i))
-            return result;
-    }
-    return std::nullopt;
+    Vector<FontSelectionCapabilities> capabilities;
+    capabilities.reserveInitialCapacity(familyFonts.size());
+    for (auto& font : familyFonts.installedFonts)
+        capabilities.uncheckedAppend(font.capabilities);
+    FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequest, capabilities, familyFonts.capabilities);
+    return &familyFonts.installedFonts[fontSelectionAlgorithm.indexOfBestCapabilities()];
 }
 
-template <typename T>
-inline void iterateActiveFonts(const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter, T callback)
+static FontSelectionRequest calculateFontSelectionRequest(CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
 {
-    iterateActiveFontsWithReturn<int>(installedFonts, filter, [&](const FontDatabase::InstalledFont& font, size_t i) -> std::optional<int> {
-        callback(font, i);
-        return std::nullopt;
-    });
-}
-
-static inline std::optional<FontSelectionValue> findClosestStretch(FontSelectionValue targetStretch, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
-{
-    std::function<float(const FontDatabase::InstalledFont&)> computeScore;
-
-    if (targetStretch >= FontDatabase::stretchThreshold()) {
-        auto threshold = std::max(targetStretch, installedFonts.capabilities.width.maximum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
-            auto width = font.capabilities.width;
-            ASSERT(width.isValid());
-            if (width.includes(targetStretch))
-                return 0;
-            ASSERT(width.minimum > targetStretch || width.maximum < targetStretch);
-            if (width.minimum > targetStretch)
-                return width.minimum - targetStretch;
-            ASSERT(width.maximum < targetStretch);
-            return threshold - width.maximum;
-        };
-    } else {
-        ASSERT(targetStretch < FontDatabase::stretchThreshold());
-        auto threshold = std::min(targetStretch, installedFonts.capabilities.width.minimum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
-            auto width = font.capabilities.width;
-            if (width.includes(targetStretch))
-                return 0;
-            ASSERT(width.minimum > targetStretch || width.maximum < targetStretch);
-            if (width.maximum < targetStretch)
-                return targetStretch - width.maximum;
-            ASSERT(width.minimum > targetStretch);
-            return width.minimum - threshold;
-        };
-    }
-
-    size_t closestIndex = 0;
-    std::optional<float> minimumScore;
-    iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
-        auto score = computeScore(installedFont);
-        if (!minimumScore || score < minimumScore.value()) {
-            minimumScore = score;
-            closestIndex = i;
-        }
-    });
-
-    if (!minimumScore)
-        return std::nullopt;
-    auto& winner = installedFonts.installedFonts[closestIndex];
-    auto width = winner.capabilities.width;
-    if (width.includes(targetStretch))
-        return targetStretch;
-    if (width.minimum > targetStretch)
-        return width.minimum;
-    ASSERT(width.maximum < targetStretch);
-    return width.maximum;
-}
-
-static inline void filterStretch(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
-{
-    iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
-        if (!installedFont.capabilities.width.includes(target))
-            filter[i] = false;
-    });
-}
-
-static inline std::optional<FontSelectionValue> findClosestStyle(FontSelectionValue targetStyle, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
-{
-    std::function<float(const FontDatabase::InstalledFont&)> computeScore;
-
-    if (targetStyle >= FontDatabase::italicThreshold()) {
-        auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
-            if (slope.minimum > targetStyle)
-                return slope.minimum - targetStyle;
-            ASSERT(targetStyle > slope.maximum);
-            return threshold - slope.maximum;
-        };
-    } else if (targetStyle >= FontSelectionValue()) {
-        auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
-            if (slope.maximum >= FontSelectionValue() && slope.maximum < targetStyle)
-                return targetStyle - slope.maximum;
-            if (slope.minimum > targetStyle)
-                return slope.minimum;
-            ASSERT(slope.maximum < FontSelectionValue());
-            return threshold - slope.maximum;
-        };
-    } else if (targetStyle > -FontDatabase::italicThreshold()) {
-        auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
-            if (slope.minimum > targetStyle && slope.minimum <= FontSelectionValue())
-                return slope.minimum - targetStyle;
-            if (slope.maximum < targetStyle)
-                return -slope.maximum;
-            ASSERT(slope.minimum > FontSelectionValue());
-            return slope.minimum - threshold;
-        };
-    } else {
-        ASSERT(targetStyle <= -FontDatabase::italicThreshold());
-        auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum > targetStyle || slope.maximum < targetStyle);
-            if (slope.maximum < targetStyle)
-                return targetStyle - slope.maximum;
-            ASSERT(slope.minimum > targetStyle);
-            return slope.minimum - threshold;
-        };
-    }
-
-    size_t closestIndex = 0;
-    std::optional<float> minimumScore;
-    iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
-        auto score = computeScore(installedFont);
-        if (!minimumScore || score < minimumScore.value()) {
-            minimumScore = score;
-            closestIndex = i;
-        }
-    });
-
-    if (!minimumScore)
-        return std::nullopt;
-    auto& winner = installedFonts.installedFonts[closestIndex];
-    auto slope = winner.capabilities.slope;
-    if (slope.includes(targetStyle))
-        return targetStyle;
-    if (slope.minimum > targetStyle)
-        return slope.minimum;
-    ASSERT(slope.maximum < targetStyle);
-    return slope.maximum;
-}
-
-static inline void filterStyle(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
-{
-    iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
-        if (!installedFont.capabilities.slope.includes(target))
-            filter[i] = false;
-    });
-}
-
-static inline std::optional<FontSelectionValue> findClosestWeight(FontSelectionValue targetWeight, const FontDatabase::InstalledFontFamily& installedFonts, const std::unique_ptr<bool[]>& filter)
-{
-    {
-        // The spec states: "If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first"
-        IterateActiveFontsWithReturnCallback<FontSelectionValue> searchFor400 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<FontSelectionValue> {
-            if (font.capabilities.weight.includes(FontSelectionValue(400)))
-                return FontSelectionValue(400);
-            return std::nullopt;
-        };
-        IterateActiveFontsWithReturnCallback<FontSelectionValue> searchFor500 = [&](const FontDatabase::InstalledFont& font, size_t) -> std::optional<FontSelectionValue> {
-            if (font.capabilities.weight.includes(FontSelectionValue(500)))
-                return FontSelectionValue(500);
-            return std::nullopt;
-        };
-        if (targetWeight == FontSelectionValue(400)) {
-            if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
-                return result;
-            if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
-                return result;
-        } else if (targetWeight == FontSelectionValue(500)) {
-            if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor500))
-                return result;
-            if (auto result = iterateActiveFontsWithReturn(installedFonts, filter, searchFor400))
-                return result;
-        }
-    }
-
-    std::function<float(const FontDatabase::InstalledFont&)> computeScore;
-    if (targetWeight <= FontDatabase::weightThreshold()) {
-        auto threshold = std::min(targetWeight, installedFonts.capabilities.weight.minimum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> FontSelectionValue {
-            auto weight = font.capabilities.weight;
-            if (weight.includes(targetWeight))
-                return FontSelectionValue();
-            ASSERT(weight.minimum > targetWeight || weight.maximum < targetWeight);
-            if (weight.maximum < targetWeight)
-                return targetWeight - weight.maximum;
-            ASSERT(weight.minimum > targetWeight);
-            return weight.minimum - threshold;
-        };
-    } else {
-        ASSERT(targetWeight > FontDatabase::weightThreshold());
-        auto threshold = std::max(targetWeight, installedFonts.capabilities.weight.maximum);
-        computeScore = [&, threshold](const FontDatabase::InstalledFont& font) -> FontSelectionValue {
-            auto weight = font.capabilities.weight;
-            if (weight.includes(targetWeight))
-                return FontSelectionValue();
-            ASSERT(weight.minimum > targetWeight || weight.maximum < targetWeight);
-            if (weight.minimum > targetWeight)
-                return weight.minimum - targetWeight;
-            ASSERT(weight.maximum < targetWeight);
-            return threshold - weight.maximum;
-        };
-    }
-
-    size_t closestIndex = 0;
-    std::optional<float> minimumScore;
-    iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
-        auto score = computeScore(installedFont);
-        if (!minimumScore || score < minimumScore.value()) {
-            minimumScore = score;
-            closestIndex = i;
-        }
-    });
-
-    if (!minimumScore)
-        return std::nullopt;
-    auto& winner = installedFonts.installedFonts[closestIndex];
-    auto weight = winner.capabilities.weight;
-    if (weight.includes(targetWeight))
-        return targetWeight;
-    if (weight.minimum > targetWeight)
-        return weight.minimum;
-    ASSERT(weight.maximum < targetWeight);
-    return weight.maximum;
-}
-
-static inline void filterWeight(FontSelectionValue target, const FontDatabase::InstalledFontFamily& installedFonts, std::unique_ptr<bool[]>& filter)
-{
-    iterateActiveFonts(installedFonts, filter, [&](auto& installedFont, size_t i) {
-        if (!installedFont.capabilities.weight.includes(target))
-            filter[i] = false;
-    });
-}
-
-static inline FontSelectionValue computeTargetWeight(FontWeight weight)
-{
+    FontSelectionRequest result;
     switch (weight) {
     case FontWeight100:
-        return FontSelectionValue(100);
+        result.weight = FontSelectionValue(100);
+        break;
     case FontWeight200:
-        return FontSelectionValue(200);
+        result.weight = FontSelectionValue(200);
+        break;
     case FontWeight300:
-        return FontSelectionValue(300);
+        result.weight = FontSelectionValue(300);
+        break;
     case FontWeight400:
-        return FontSelectionValue(400);
+        result.weight = FontSelectionValue(400);
+        break;
     case FontWeight500:
-        return FontSelectionValue(500);
+        result.weight = FontSelectionValue(500);
+        break;
     case FontWeight600:
-        return FontSelectionValue(600);
+        result.weight = FontSelectionValue(600);
+        break;
     case FontWeight700:
-        return FontSelectionValue(700);
+        result.weight = FontSelectionValue(700);
+        break;
     case FontWeight800:
-        return FontSelectionValue(800);
+        result.weight = FontSelectionValue(800);
+        break;
     case FontWeight900:
-        return FontSelectionValue(900);
+        result.weight = FontSelectionValue(900);
+        break;
     default:
         ASSERT_NOT_REACHED();
-        return FontSelectionValue(400);
+        result.weight = FontSelectionValue(400);
+        break;
     }
-}
 
-static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily& familyFonts, CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
-{
-    ASSERT(!familyFonts.isEmpty());
+    result.width = stretch;
 
-    // Parallel to familyFonts.
-    std::unique_ptr<bool[]> filter { new bool[familyFonts.size()] };
-    for (size_t i = 0; i < familyFonts.size(); ++i)
-        filter[i] = true;
-
-    if (auto closestStretch = findClosestStretch(stretch, familyFonts, filter))
-        filterStretch(closestStretch.value(), familyFonts, filter);
+    if (requestedTraits & kCTFontTraitItalic)
+        result.slope = italicThreshold();
     else
-        return nullptr;
+        result.slope = FontSelectionValue();
 
-    FontSelectionValue targetStyle = requestedTraits & kCTFontTraitItalic ? FontDatabase::italicThreshold() : FontSelectionValue();
-    if (auto closestStyle = findClosestStyle(targetStyle, familyFonts, filter))
-        filterStyle(closestStyle.value(), familyFonts, filter);
-    else
-        return nullptr;
-
-    FontSelectionValue targetWeight = computeTargetWeight(weight);
-    if (auto closestWeight = findClosestWeight(targetWeight, familyFonts, filter))
-        filterWeight(closestWeight.value(), familyFonts, filter);
-    else
-        return nullptr;
-
-    return iterateActiveFontsWithReturn<const FontDatabase::InstalledFont*>(familyFonts, filter, [](const FontDatabase::InstalledFont& font, size_t) {
-        return &font;
-    }).value_or(nullptr);
+    return result;
 }
 
 #endif // !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
@@ -1283,7 +1007,6 @@
     if (!isSystemFont(family) && whitelist.size() && !whitelist.contains(family))
         return nullptr;
 
-
 #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
     UNUSED_PARAM(stretch);
     return adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), toCoreTextFontWeight(weight), requestedTraits, size));
@@ -1300,8 +1023,8 @@
         const auto& postScriptFont = FontDatabase::singleton().fontForPostScriptName(family);
         if (!postScriptFont.fontDescriptor)
             return nullptr;
-        if (((requestedTraits & kCTFontTraitItalic) && postScriptFont.capabilities.slope.maximum < FontDatabase::italicThreshold())
-            || (weight >= FontWeight600 && postScriptFont.capabilities.weight.maximum < FontSelectionValue(600))) {
+        if (((requestedTraits & kCTFontTraitItalic) && postScriptFont.capabilities.slope.maximum < italicThreshold())
+            || (weight >= FontWeight600 && postScriptFont.capabilities.weight.maximum < boldThreshold())) {
             auto postScriptFamilyName = adoptCF(static_cast<CFStringRef>(CTFontDescriptorCopyAttribute(postScriptFont.fontDescriptor.get(), kCTFontFamilyNameAttribute)));
             if (!postScriptFamilyName)
                 return nullptr;
@@ -1308,7 +1031,7 @@
             const auto& familyFonts = FontDatabase::singleton().collectionForFamily(String(postScriptFamilyName.get()));
             if (familyFonts.isEmpty())
                 return nullptr;
-            if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch)) {
+            if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch))) {
                 if (!installedFont->fontDescriptor)
                     return nullptr;
                 return adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr));
@@ -1318,7 +1041,7 @@
         return adoptCF(CTFontCreateWithFontDescriptor(postScriptFont.fontDescriptor.get(), size, nullptr));
     }
 
-    if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch))
+    if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch)))
         return adoptCF(CTFontCreateWithFontDescriptor(installedFont->fontDescriptor.get(), size, nullptr));
 
     return nullptr;

Modified: trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp (213435 => 213436)


--- trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -138,7 +138,7 @@
     RELEASE_ASSERT_NOT_REACHED();
 }
 
-Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString&)
+auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
 {
     return { };
 }

Modified: trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp (213435 => 213436)


--- trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp	2017-03-05 20:14:02 UTC (rev 213436)
@@ -559,7 +559,7 @@
     return 1;
 }
 
-Vector<FontTraitsMask> FontCache::getTraitsInFamily(const AtomicString& familyName)
+auto FontCache::getTraitsAndStretchInFamily(const AtomicString& familyName) -> Vector<TraitsAndStretch>
 {
     HWndDC hdc(0);
 
@@ -572,10 +572,10 @@
 
     TraitsInFamilyProcData procData(familyName);
     EnumFontFamiliesEx(hdc, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0);
-    Vector<FontTraitsMask> result;
+    Vector<TraitsAndStretch> result;
     result.reserveInitialCapacity(procData.m_traitsMasks.size());
     for (unsigned mask : procData.m_traitsMasks)
-        result.uncheckedAppend(static_cast<FontTraitsMask>(mask));
+        result.uncheckedAppend({ static_cast<FontTraitsMask>(mask), FontSelectionRange(FontSelectionValue(), FontSelectionValue()) });
     return result;
 }
 

Modified: trunk/Source/WebCore/platform/text/TextFlags.h (213435 => 213436)


--- trunk/Source/WebCore/platform/text/TextFlags.h	2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/Source/WebCore/platform/text/TextFlags.h	2017-03-05 20:14:02 UTC (rev 213436)
@@ -25,8 +25,6 @@
 
 #pragma once
 
-#include <wtf/NeverDestroyed.h>
-
 namespace WebCore {
 
 enum TextRenderingMode { AutoTextRendering, OptimizeSpeed, OptimizeLegibility, GeometricPrecision };
@@ -398,162 +396,6 @@
     FontWeightMask = FontWeight100Mask | FontWeight200Mask | FontWeight300Mask | FontWeight400Mask | FontWeight500Mask | FontWeight600Mask | FontWeight700Mask | FontWeight800Mask | FontWeight900Mask
 };
 
-// Unclamped, unchecked, signed fixed-point number representing a value used for font variations.
-// Sixteen bits in total, one sign bit, two fractional bits, means the smallest positive representable value is 0.25,
-// the maximum representable value is 8191.75, and the minimum representable value is -8192.
-class FontSelectionValue {
-public:
-    FontSelectionValue() = default;
-
-    // Explicit because it is lossy.
-    explicit FontSelectionValue(int x)
-        : m_backing(x * fractionalEntropy)
-    {
-    }
-
-    // Explicit because it is lossy.
-    explicit FontSelectionValue(float x)
-        : m_backing(x * fractionalEntropy)
-    {
-    }
-
-    operator float() const
-    {
-        // floats have 23 fractional bits, but only 14 fractional bits are necessary, so every value can be represented losslessly.
-        return m_backing / static_cast<float>(fractionalEntropy);
-    }
-
-    FontSelectionValue operator+(const FontSelectionValue other) const
-    {
-        return FontSelectionValue(m_backing + other.m_backing, RawTag::RawTag);
-    }
-
-    FontSelectionValue operator-(const FontSelectionValue other) const
-    {
-        return FontSelectionValue(m_backing - other.m_backing, RawTag::RawTag);
-    }
-
-    FontSelectionValue operator*(const FontSelectionValue other) const
-    {
-        return FontSelectionValue(static_cast<int32_t>(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
-    }
-
-    FontSelectionValue operator/(const FontSelectionValue other) const
-    {
-        return FontSelectionValue(static_cast<int32_t>(m_backing) / other.m_backing * fractionalEntropy, RawTag::RawTag);
-    }
-
-    FontSelectionValue operator-() const
-    {
-        return FontSelectionValue(-m_backing, RawTag::RawTag);
-    }
-
-    bool operator==(const FontSelectionValue other) const
-    {
-        return m_backing == other.m_backing;
-    }
-
-    bool operator!=(const FontSelectionValue other) const
-    {
-        return !operator==(other);
-    }
-
-    bool operator<(const FontSelectionValue other) const
-    {
-        return m_backing < other.m_backing;
-    }
-
-    bool operator<=(const FontSelectionValue other) const
-    {
-        return m_backing <= other.m_backing;
-    }
-
-    bool operator>(const FontSelectionValue other) const
-    {
-        return m_backing > other.m_backing;
-    }
-
-    bool operator>=(const FontSelectionValue other) const
-    {
-        return m_backing >= other.m_backing;
-    }
-
-    int16_t rawValue() const
-    {
-        return m_backing;
-    }
-
-    static FontSelectionValue maximumValue()
-    {
-        static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::max(), RawTag::RawTag);
-        return result.get();
-    }
-
-    static FontSelectionValue minimumValue()
-    {
-        static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<int16_t>::min(), RawTag::RawTag);
-        return result.get();
-    }
-
-private:
-    enum class RawTag { RawTag };
-
-    FontSelectionValue(int16_t rawValue, RawTag)
-        : m_backing(rawValue)
-    {
-    }
-
-    static constexpr int fractionalEntropy = 4;
-    int16_t m_backing { 0 };
-};
-
-// [Inclusive, Inclusive]
-struct FontSelectionRange {
-    bool isValid() const
-    {
-        return minimum <= maximum;
-    }
-
-    void expand(const FontSelectionRange& other)
-    {
-        ASSERT(other.isValid());
-        if (!isValid())
-            *this = other;
-        else {
-            minimum = std::min(minimum, other.minimum);
-            maximum = std::max(maximum, other.maximum);
-        }
-        ASSERT(isValid());
-    }
-
-    bool includes(FontSelectionValue target) const
-    {
-        return target >= minimum && target <= maximum;
-    }
-
-    FontSelectionValue minimum { FontSelectionValue(1) };
-    FontSelectionValue maximum { FontSelectionValue(0) };
-};
-
-struct FontSelectionRequest {
-    FontSelectionValue weight;
-    FontSelectionValue width;
-    FontSelectionValue slope;
-};
-
-struct FontSelectionCapabilities {
-    void expand(const FontSelectionCapabilities& capabilities)
-    {
-        weight.expand(capabilities.weight);
-        width.expand(capabilities.width);
-        slope.expand(capabilities.slope);
-    }
-
-    FontSelectionRange weight;
-    FontSelectionRange width;
-    FontSelectionRange slope;
-};
-
 enum class Kerning {
     Auto,
     Normal,
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to