<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[213436] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/213436">213436</a></dd>
<dt>Author</dt> <dd>mmaxfield@apple.com</dd>
<dt>Date</dt> <dd>2017-03-05 12:14:02 -0800 (Sun, 05 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>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&lt;):
(WebCore::FontSelectionValue::operator&lt;=):
(WebCore::FontSelectionValue::operator&gt;):
(WebCore::FontSelectionValue::operator&gt;=):
(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&lt;): Deleted.
(WebCore::FontSelectionValue::operator&lt;=): Deleted.
(WebCore::FontSelectionValue::operator&gt;): Deleted.
(WebCore::FontSelectionValue::operator&gt;=): 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:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsfasttextfontfacejavascriptexpectedtxt">trunk/LayoutTests/fast/text/font-face-javascript-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfasttextfontfacejavascripthtml">trunk/LayoutTests/fast/text/font-face-javascript.html</a></li>
<li><a href="#trunkSourceWebCoreCMakeListstxt">trunk/Source/WebCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCorecssCSSFontFacecpp">trunk/Source/WebCore/css/CSSFontFace.cpp</a></li>
<li><a href="#trunkSourceWebCorecssCSSFontFaceh">trunk/Source/WebCore/css/CSSFontFace.h</a></li>
<li><a href="#trunkSourceWebCorecssCSSFontFaceSetcpp">trunk/Source/WebCore/css/CSSFontFaceSet.cpp</a></li>
<li><a href="#trunkSourceWebCorecssCSSFontFaceSeth">trunk/Source/WebCore/css/CSSFontFaceSet.h</a></li>
<li><a href="#trunkSourceWebCorecssCSSFontSelectorcpp">trunk/Source/WebCore/css/CSSFontSelector.cpp</a></li>
<li><a href="#trunkSourceWebCorecssFontFacecpp">trunk/Source/WebCore/css/FontFace.cpp</a></li>
<li><a href="#trunkSourceWebCorecssparserCSSPropertyParsercpp">trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsFontCacheh">trunk/Source/WebCore/platform/graphics/FontCache.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsFontDescriptionh">trunk/Source/WebCore/platform/graphics/FontDescription.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicscocoaFontCacheCoreTextcpp">trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsfreetypeFontCacheFreeTypecpp">trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicswinFontCacheWincpp">trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformtextTextFlagsh">trunk/Source/WebCore/platform/text/TextFlags.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreplatformgraphicsFontSelectionAlgorithmcpp">trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsFontSelectionAlgorithmh">trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2017-03-05 19:14:12 UTC (rev 213435)
+++ trunk/LayoutTests/ChangeLog        2017-03-05 20:14:02 UTC (rev 213436)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2017-03-04  Myles C. Maxfield  &lt;mmaxfield@apple.com&gt;
+
+        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:
+
</ins><span class="cx"> 2017-03-05  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] Two file reset tests are failing in the bots since they were added in r213042
</span></span></pre></div>
<a id="trunkLayoutTestsfasttextfontfacejavascriptexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/text/font-face-javascript-expected.txt (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -16,7 +16,7 @@
</span><span class="cx"> PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'bolder'}).weight is &quot;bold&quot;
</span><span class="cx"> PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'lighter'}).weight is &quot;200&quot;
</span><span class="cx"> PASS new FontFace('family_name', 'url(\'asdf\')', {'weight': 'inherit'}).weight threw exception SyntaxError (DOM Exception 12): The string did not match the expected pattern..
</span><del>-PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'stretch_name'}).stretch is &quot;normal&quot;
</del><ins>+PASS new FontFace('family_name', 'url(\'asdf\')', {'stretch': 'ultra-expanded'}).stretch is &quot;ultra-expanded&quot;
</ins><span class="cx"> PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+26'}).unicodeRange is &quot;U+26-26&quot;
</span><span class="cx"> PASS new FontFace('family_name', 'url(\'asdf\')', {'unicodeRange': 'U+0-7F'}).unicodeRange is &quot;U+0-7f&quot;
</span><span class="cx"> 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..
</span><span class="lines">@@ -25,7 +25,7 @@
</span><span class="cx"> PASS new FontFace('family_name', 'url(\'asdf\')', {'featureSettings': '\'titl\''}).featureSettings is &quot;'titl' 1&quot;
</span><span class="cx"> PASS everything.style is &quot;italic&quot;
</span><span class="cx"> PASS everything.weight is &quot;bold&quot;
</span><del>-PASS everything.stretch is &quot;normal&quot;
</del><ins>+PASS everything.stretch is &quot;extra-expanded&quot;
</ins><span class="cx"> PASS everything.unicodeRange is &quot;U+26-26&quot;
</span><span class="cx"> PASS everything.variant is &quot;small-caps&quot;
</span><span class="cx"> PASS everything.featureSettings is &quot;'titl' 1&quot;
</span><span class="lines">@@ -32,7 +32,7 @@
</span><span class="cx"> PASS everything.family is &quot;other_family_name&quot;
</span><span class="cx"> PASS everything.style is &quot;normal&quot;
</span><span class="cx"> PASS everything.weight is &quot;300&quot;
</span><del>-PASS everything.stretch is &quot;normal&quot;
</del><ins>+PASS everything.stretch is &quot;condensed&quot;
</ins><span class="cx"> PASS everything.unicodeRange is &quot;U+0-7f&quot;
</span><span class="cx"> PASS everything.variant is &quot;stacked-fractions&quot;
</span><span class="cx"> PASS everything.featureSettings is &quot;'smcp' 1&quot;
</span></span></pre></div>
<a id="trunkLayoutTestsfasttextfontfacejavascripthtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/fast/text/font-face-javascript.html (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -24,7 +24,7 @@
</span><span class="cx"> shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'bolder'}).weight&quot;, &quot;bold&quot;);
</span><span class="cx"> shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'lighter'}).weight&quot;, &quot;200&quot;);
</span><span class="cx"> shouldThrow(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'weight': 'inherit'}).weight&quot;);
</span><del>-shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'stretch_name'}).stretch&quot;, &quot;normal&quot;);
</del><ins>+shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'stretch': 'ultra-expanded'}).stretch&quot;, &quot;ultra-expanded&quot;);
</ins><span class="cx"> shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+26'}).unicodeRange&quot;, &quot;U+26-26&quot;);
</span><span class="cx"> shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'unicodeRange': 'U+0-7F'}).unicodeRange&quot;, &quot;U+0-7f&quot;);
</span><span class="cx"> shouldThrow(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'variant': 'variant_name'}).variant&quot;);
</span><span class="lines">@@ -32,10 +32,10 @@
</span><span class="cx"> shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'variant': 'small-caps common-ligatures'}).variant&quot;, &quot;common-ligatures small-caps&quot;);
</span><span class="cx"> shouldBeEqualToString(&quot;new FontFace('family_name', 'url(\\'asdf\\')', {'featureSettings': '\\'titl\\''}).featureSettings&quot;, &quot;'titl' 1&quot;);
</span><span class="cx"> 
</span><del>-var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'stretch_name', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
</del><ins>+var everything = new FontFace('family_name', 'url(\'asdf\')', {'style': 'italic', 'weight': 'bold', 'stretch': 'extra-expanded', 'unicodeRange': 'U+26', 'variant': 'small-caps', 'featureSettings': '\'titl\''});
</ins><span class="cx"> shouldBeEqualToString(&quot;everything.style&quot;, &quot;italic&quot;);
</span><span class="cx"> shouldBeEqualToString(&quot;everything.weight&quot;, &quot;bold&quot;);
</span><del>-shouldBeEqualToString(&quot;everything.stretch&quot;, &quot;normal&quot;);
</del><ins>+shouldBeEqualToString(&quot;everything.stretch&quot;, &quot;extra-expanded&quot;);
</ins><span class="cx"> shouldBeEqualToString(&quot;everything.unicodeRange&quot;, &quot;U+26-26&quot;);
</span><span class="cx"> shouldBeEqualToString(&quot;everything.variant&quot;, &quot;small-caps&quot;);
</span><span class="cx"> shouldBeEqualToString(&quot;everything.featureSettings&quot;, &quot;'titl' 1&quot;);
</span><span class="lines">@@ -46,8 +46,8 @@
</span><span class="cx"> shouldBeEqualToString(&quot;everything.style&quot;, &quot;normal&quot;);
</span><span class="cx"> everything.weight = &quot;300&quot;;
</span><span class="cx"> shouldBeEqualToString(&quot;everything.weight&quot;, &quot;300&quot;);
</span><del>-everything.stretch = &quot;other_stretch_name&quot;;
-shouldBeEqualToString(&quot;everything.stretch&quot;, &quot;normal&quot;);
</del><ins>+everything.stretch = &quot;condensed&quot;;
+shouldBeEqualToString(&quot;everything.stretch&quot;, &quot;condensed&quot;);
</ins><span class="cx"> everything.unicodeRange = &quot;U+0-7F&quot;;
</span><span class="cx"> shouldBeEqualToString(&quot;everything.unicodeRange&quot;, &quot;U+0-7f&quot;);
</span><span class="cx"> everything.variant = &quot;stacked-fractions&quot;;
</span></span></pre></div>
<a id="trunkSourceWebCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/CMakeLists.txt (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -2200,6 +2200,7 @@
</span><span class="cx">     platform/graphics/FontCascade.cpp
</span><span class="cx">     platform/graphics/FontCascadeFonts.cpp
</span><span class="cx">     platform/graphics/FontDescription.cpp
</span><ins>+    platform/graphics/FontSelectionAlgorithm.cpp
</ins><span class="cx">     platform/graphics/FontTaggedSettings.cpp
</span><span class="cx">     platform/graphics/FontGenericFamilies.cpp
</span><span class="cx">     platform/graphics/FontPlatformData.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -1,3 +1,125 @@
</span><ins>+2017-03-04  Myles C. Maxfield  &lt;mmaxfield@apple.com&gt;
+
+        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&lt;):
+        (WebCore::FontSelectionValue::operator&lt;=):
+        (WebCore::FontSelectionValue::operator&gt;):
+        (WebCore::FontSelectionValue::operator&gt;=):
+        (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&lt;): Deleted.
+        (WebCore::FontSelectionValue::operator&lt;=): Deleted.
+        (WebCore::FontSelectionValue::operator&gt;): Deleted.
+        (WebCore::FontSelectionValue::operator&gt;=): 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.
+
</ins><span class="cx"> 2017-03-05  Simon Fraser  &lt;simon.fraser@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Make some RenderLayer tree traversal in RenderLayerBacking more generic
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -5747,6 +5747,8 @@
</span><span class="cx">                 C280833F1C6DC26F001451B6 /* JSFontFace.h in Headers */ = {isa = PBXBuildFile; fileRef = C280833E1C6DC22C001451B6 /* JSFontFace.h */; };
</span><span class="cx">                 C28083401C6DC275001451B6 /* JSFontFace.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C280833D1C6DC22C001451B6 /* JSFontFace.cpp */; };
</span><span class="cx">                 C28083421C6DC96A001451B6 /* JSFontFaceCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */; };
</span><ins>+                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, ); }; };
</ins><span class="cx">                 C2E1F43F1D6254E10094625C /* BreakLines.h in Headers */ = {isa = PBXBuildFile; fileRef = BCEA4816097D93020094C9E4 /* BreakLines.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 C2F4E78A1E45BEA1006D7105 /* ComplexTextController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */; };
</span><span class="cx">                 C2F4E78C1E45C3EF006D7105 /* ComplexTextController.h in Headers */ = {isa = PBXBuildFile; fileRef = C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -13873,6 +13875,8 @@
</span><span class="cx">                 C280833D1C6DC22C001451B6 /* JSFontFace.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFace.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 C280833E1C6DC22C001451B6 /* JSFontFace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSFontFace.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 C28083411C6DC96A001451B6 /* JSFontFaceCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSFontFaceCustom.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontSelectionAlgorithm.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontSelectionAlgorithm.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 C2F4E7881E45AEDF006D7105 /* ComplexTextController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ComplexTextController.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 C2F4E7891E45AEDF006D7105 /* ComplexTextController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComplexTextController.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 C330A22113EC196B0000B45B /* ColorChooser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorChooser.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -22369,6 +22373,8 @@
</span><span class="cx">                                 501BAAA813950E2C00F7ACEB /* WindRule.h */,
</span><span class="cx">                                 379919941200DDF400EA041C /* WOFFFileFormat.cpp */,
</span><span class="cx">                                 379919951200DDF400EA041C /* WOFFFileFormat.h */,
</span><ins>+                                C2AB0AF41E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp */,
+                                C2AB0AF51E6B3C6C001348C5 /* FontSelectionAlgorithm.h */,
</ins><span class="cx">                         );
</span><span class="cx">                         path = graphics;
</span><span class="cx">                         sourceTree = &quot;&lt;group&gt;&quot;;
</span><span class="lines">@@ -27639,6 +27645,7 @@
</span><span class="cx">                                 854FE7350A2297BE0058D7AD /* NodeIterator.h in Headers */,
</span><span class="cx">                                 A818721B0977D3C0005826D9 /* NodeList.h in Headers */,
</span><span class="cx">                                 63189AE30E83A33300012E41 /* NodeRareData.h in Headers */,
</span><ins>+                                C2AB0AF71E6B3C6C001348C5 /* FontSelectionAlgorithm.h in Headers */,
</ins><span class="cx">                                 63D7B32D0E78CD3F00F7617C /* NodeRenderStyle.h in Headers */,
</span><span class="cx">                                 E43105BB16750F1600DB2FB8 /* NodeTraversal.h in Headers */,
</span><span class="cx">                                 9382AAB40D8C386100F357A6 /* NodeWithIndex.h in Headers */,
</span><span class="lines">@@ -29506,6 +29513,7 @@
</span><span class="cx">                                 1A8A645B1D19FCFC00D0E00F /* ApplePayShippingContactSelectedEvent.cpp in Sources */,
</span><span class="cx">                                 1A8A645F1D19FCFC00D0E00F /* ApplePayShippingMethodSelectedEvent.cpp in Sources */,
</span><span class="cx">                                 1A8A64621D19FCFC00D0E00F /* ApplePayValidateMerchantEvent.cpp in Sources */,
</span><ins>+                                C2AB0AF61E6B3C6C001348C5 /* FontSelectionAlgorithm.cpp in Sources */,
</ins><span class="cx">                                 1A8F6BBC0DB55CDC001DB794 /* ApplicationCache.cpp in Sources */,
</span><span class="cx">                                 1A8F6BBE0DB55CDC001DB794 /* ApplicationCacheGroup.cpp in Sources */,
</span><span class="cx">                                 24F54EAC101FE914000AE741 /* ApplicationCacheHost.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSFontFacecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSFontFace.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -194,6 +194,46 @@
</span><span class="cx">     return FontWeight400Mask;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+std::optional&lt;FontSelectionValue&gt; CSSFontFace::calculateStretch(CSSValue&amp; stretch)
+{
+    if (!is&lt;CSSPrimitiveValue&gt;(stretch))
+        return std::nullopt;
+
+    const auto&amp; primitiveValue = downcast&lt;CSSPrimitiveValue&gt;(stretch);
+    if (primitiveValue.isPercentage() || primitiveValue.isNumber()) {
+        auto value = primitiveValue.floatValue();
+        if (value &lt; static_cast&lt;float&gt;(FontSelectionValue::minimumValue()))
+            return FontSelectionValue::minimumValue();
+        if (value &gt; static_cast&lt;float&gt;(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;
+    }
+}
+
</ins><span class="cx"> bool CSSFontFace::setWeight(CSSValue&amp; weight)
</span><span class="cx"> {
</span><span class="cx">     if (auto mask = calculateWeightMask(weight)) {
</span><span class="lines">@@ -212,6 +252,24 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool CSSFontFace::setStretch(CSSValue&amp; stretch)
+{
+    if (auto parsedStretch = calculateStretch(stretch)) {
+        m_stretch = FontSelectionRange(parsedStretch.value(), parsedStretch.value());
+
+        if (m_cssConnection)
+            m_cssConnection-&gt;mutableProperties().setProperty(CSSPropertyFontStretch, &amp;stretch);
+
+        iterateClients(m_clients, [&amp;](Client&amp; client) {
+            client.fontPropertyChanged(*this);
+        });
+
+        return true;
+    }
+
+    return false;
+}
+
</ins><span class="cx"> bool CSSFontFace::setUnicodeRange(CSSValue&amp; unicodeRange)
</span><span class="cx"> {
</span><span class="cx">     if (!is&lt;CSSValueList&gt;(unicodeRange))
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSFontFaceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSFontFace.h (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><span class="cx"> #include &quot;CSSFontFaceRule.h&quot;
</span><ins>+#include &quot;FontSelectionAlgorithm.h&quot;
</ins><span class="cx"> #include &quot;FontTaggedSettings.h&quot;
</span><span class="cx"> #include &quot;TextFlags.h&quot;
</span><span class="cx"> #include &quot;Timer.h&quot;
</span><span class="lines">@@ -65,6 +66,7 @@
</span><span class="cx">     bool setFamilies(CSSValue&amp;);
</span><span class="cx">     bool setStyle(CSSValue&amp;);
</span><span class="cx">     bool setWeight(CSSValue&amp;);
</span><ins>+    bool setStretch(CSSValue&amp;);
</ins><span class="cx">     bool setUnicodeRange(CSSValue&amp;);
</span><span class="cx">     bool setVariantLigatures(CSSValue&amp;);
</span><span class="cx">     bool setVariantPosition(CSSValue&amp;);
</span><span class="lines">@@ -78,17 +80,21 @@
</span><span class="cx">     struct UnicodeRange;
</span><span class="cx">     const CSSValueList* families() const { return m_families.get(); }
</span><span class="cx">     FontTraitsMask traitsMask() const { return m_traitsMask; }
</span><ins>+    FontSelectionRange stretch() const { return m_stretch; }
</ins><span class="cx">     const Vector&lt;UnicodeRange&gt;&amp; ranges() const { return m_ranges; }
</span><span class="cx">     const FontFeatureSettings&amp; featureSettings() const { return m_featureSettings; }
</span><span class="cx">     const FontVariantSettings&amp; variantSettings() const { return m_variantSettings; }
</span><span class="cx">     void setVariantSettings(const FontVariantSettings&amp; variantSettings) { m_variantSettings = variantSettings; }
</span><span class="cx">     void setTraitsMask(FontTraitsMask traitsMask) { m_traitsMask = traitsMask; }
</span><ins>+    void setStretch(FontSelectionRange stretch) { m_stretch = stretch; }
</ins><span class="cx">     bool isLocalFallback() const { return m_isLocalFallback; }
</span><span class="cx">     Status status() const { return m_status; }
</span><span class="cx">     StyleRuleFontFace* cssConnection() const { return m_cssConnection.get(); }
</span><ins>+    FontSelectionCapabilities fontSelectionCapabilities() const { return fontSelectionCapabilitiesForTraitsMask(m_traitsMask, m_stretch); }
</ins><span class="cx"> 
</span><span class="cx">     static std::optional&lt;FontTraitsMask&gt; calculateStyleMask(CSSValue&amp; style);
</span><span class="cx">     static std::optional&lt;FontTraitsMask&gt; calculateWeightMask(CSSValue&amp; weight);
</span><ins>+    static std::optional&lt;FontSelectionValue&gt; calculateStretch(CSSValue&amp; stretch);
</ins><span class="cx"> 
</span><span class="cx">     class Client;
</span><span class="cx">     void addClient(Client&amp;);
</span><span class="lines">@@ -173,6 +179,7 @@
</span><span class="cx">     HashSet&lt;Client*&gt; m_clients;
</span><span class="cx">     WeakPtr&lt;FontFace&gt; m_wrapper;
</span><span class="cx">     Status m_status { Status::Pending };
</span><ins>+    FontSelectionRange m_stretch { FontSelectionValue(100), FontSelectionValue(100) };
</ins><span class="cx">     bool m_isLocalFallback { false };
</span><span class="cx">     bool m_sourcesPopulated { false };
</span><span class="cx">     bool m_mayBePurged { true };
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSFontFaceSetcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSFontFaceSet.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -101,18 +101,19 @@
</span><span class="cx">     if (m_locallyInstalledFacesLookupTable.contains(familyName))
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    Vector&lt;FontTraitsMask&gt; traitsMasks = FontCache::singleton().getTraitsInFamily(familyName);
-    if (traitsMasks.isEmpty())
</del><ins>+    Vector&lt;FontCache::TraitsAndStretch&gt; traitsAndStretch = FontCache::singleton().getTraitsAndStretchInFamily(familyName);
+    if (traitsAndStretch.isEmpty())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;Ref&lt;CSSFontFace&gt;&gt; faces;
</span><del>-    for (auto mask : traitsMasks) {
</del><ins>+    for (auto item : traitsAndStretch) {
</ins><span class="cx">         Ref&lt;CSSFontFace&gt; face = CSSFontFace::create(nullptr, nullptr, nullptr, true);
</span><span class="cx">         
</span><span class="cx">         Ref&lt;CSSValueList&gt; familyList = CSSValueList::createCommaSeparated();
</span><span class="cx">         familyList-&gt;append(CSSValuePool::singleton().createFontFamilyValue(familyName));
</span><span class="cx">         face-&gt;setFamilies(familyList.get());
</span><del>-        face-&gt;setTraitsMask(mask);
</del><ins>+        face-&gt;setTraitsMask(item.traits);
+        face-&gt;setStretch(item.stretch);
</ins><span class="cx">         face-&gt;adoptSource(std::make_unique&lt;CSSFontFaceSource&gt;(face.get(), familyName));
</span><span class="cx">         ASSERT(!face-&gt;allSourcesFailed());
</span><span class="cx">         faces.append(WTFMove(face));
</span><span class="lines">@@ -311,6 +312,17 @@
</span><span class="cx">     return static_cast&lt;FontTraitsMask&gt;(static_cast&lt;unsigned&gt;(styleMask) | static_cast&lt;unsigned&gt;(weightMask));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+static std::optional&lt;FontSelectionValue&gt; computeFontStretch(MutableStyleProperties&amp; style)
+{
+    RefPtr&lt;CSSValue&gt; 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;
+}
+
</ins><span class="cx"> static HashSet&lt;UChar32&gt; codePointsFromString(StringView stringView)
</span><span class="cx"> {
</span><span class="cx">     HashSet&lt;UChar32&gt; result;
</span><span class="lines">@@ -340,6 +352,12 @@
</span><span class="cx">     else
</span><span class="cx">         return Exception { SYNTAX_ERR };
</span><span class="cx"> 
</span><ins>+    FontSelectionValue stretch;
+    if (auto stretchOptional = computeFontStretch(style.get()))
+        stretch = stretchOptional.value();
+    else
+        return Exception { SYNTAX_ERR };
+
</ins><span class="cx">     auto family = style-&gt;getPropertyCSSValue(CSSPropertyFontFamily);
</span><span class="cx">     if (!is&lt;CSSValueList&gt;(family.get()))
</span><span class="cx">         return Exception { SYNTAX_ERR };
</span><span class="lines">@@ -359,7 +377,7 @@
</span><span class="cx">     for (auto codePoint : codePointsFromString(string)) {
</span><span class="cx">         bool found = false;
</span><span class="cx">         for (auto&amp; family : familyOrder) {
</span><del>-            auto* faces = fontFace(fontTraitsMask, family);
</del><ins>+            auto* faces = fontFace(fontTraitsMask, stretch, family);
</ins><span class="cx">             if (!faces)
</span><span class="cx">                 continue;
</span><span class="cx">             for (auto&amp; constituentFace : faces-&gt;constituentFaces()) {
</span><span class="lines">@@ -394,67 +412,8 @@
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool fontFaceComparator(FontTraitsMask desiredTraitsMaskForComparison, const CSSFontFace&amp; first, const CSSFontFace&amp; second)
</del><ins>+CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, FontSelectionValue stretch, const AtomicString&amp; family)
</ins><span class="cx"> {
</span><del>-    FontTraitsMask firstTraitsMask = first.traitsMask();
-    FontTraitsMask secondTraitsMask = second.traitsMask();
-
-    bool firstHasDesiredStyle = firstTraitsMask &amp; desiredTraitsMaskForComparison &amp; FontStyleMask;
-    bool secondHasDesiredStyle = secondTraitsMask &amp; desiredTraitsMaskForComparison &amp; FontStyleMask;
-
-    if (firstHasDesiredStyle != secondHasDesiredStyle)
-        return firstHasDesiredStyle;
-
-    if ((desiredTraitsMaskForComparison &amp; FontStyleItalicMask) &amp;&amp; !first.isLocalFallback() &amp;&amp; !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 &amp; FontStyleItalicMask) &amp;&amp; !(firstTraitsMask &amp; FontStyleNormalMask);
-        bool secondRequiresItalics = (secondTraitsMask &amp; FontStyleItalicMask) &amp;&amp; !(secondTraitsMask &amp; FontStyleNormalMask);
-        if (firstRequiresItalics != secondRequiresItalics)
-            return firstRequiresItalics;
-    }
-
-    if (secondTraitsMask &amp; desiredTraitsMaskForComparison &amp; FontWeightMask)
-        return false;
-    if (firstTraitsMask &amp; desiredTraitsMaskForComparison &amp; 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 &amp; (1 &lt;&lt; (FontWeight100Bit + ruleSetIndex))); ruleSetIndex++) { }
-
-    const FontTraitsMask* weightFallbackRule = weightFallbackRuleSets[ruleSetIndex];
-    for (unsigned i = 0; i &lt; rulesPerSet; ++i) {
-        if (secondTraitsMask &amp; weightFallbackRule[i])
-            return false;
-        if (firstTraitsMask &amp; weightFallbackRule[i])
-            return true;
-    }
-
-    return false;
-}
-
-CSSSegmentedFontFace* CSSFontFaceSet::fontFace(FontTraitsMask traitsMask, const AtomicString&amp; family)
-{
</del><span class="cx">     auto iterator = m_facesLookupTable.find(family);
</span><span class="cx">     if (iterator == m_facesLookupTable.end())
</span><span class="cx">         return nullptr;
</span><span class="lines">@@ -487,12 +446,40 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [traitsMask](const CSSFontFace&amp; first, const CSSFontFace&amp; second) {
-        return fontFaceComparator(traitsMask, first, second);
-    });
-    for (auto&amp; candidate : candidateFontFaces)
-        face-&gt;appendFontFace(candidate.get());
</del><ins>+    if (!candidateFontFaces.isEmpty()) {
+        Vector&lt;FontSelectionCapabilities&gt; capabilities;
+        capabilities.reserveInitialCapacity(candidateFontFaces.size());
+        for (auto&amp; face : candidateFontFaces)
+            capabilities.uncheckedAppend(face.get().fontSelectionCapabilities());
+        FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequestForTraitsMask(traitsMask, stretch), capabilities);
+        std::stable_sort(candidateFontFaces.begin(), candidateFontFaces.end(), [&amp;fontSelectionAlgorithm](const CSSFontFace&amp; first, const CSSFontFace&amp; second) {
+            auto firstCapabilities = first.fontSelectionCapabilities();
+            auto secondCapabilities = second.fontSelectionCapabilities();
</ins><span class="cx"> 
</span><ins>+            auto stretchDistanceFirst = fontSelectionAlgorithm.stretchDistance(firstCapabilities).distance;
+            auto stretchDistanceSecond = fontSelectionAlgorithm.stretchDistance(secondCapabilities).distance;
+            if (stretchDistanceFirst &lt; stretchDistanceSecond)
+                return true;
+            if (stretchDistanceFirst &gt; stretchDistanceSecond)
+                return false;
+
+            auto styleDistanceFirst = fontSelectionAlgorithm.styleDistance(firstCapabilities).distance;
+            auto styleDistanceSecond = fontSelectionAlgorithm.styleDistance(secondCapabilities).distance;
+            if (styleDistanceFirst &lt; styleDistanceSecond)
+                return true;
+            if (styleDistanceFirst &gt; styleDistanceSecond)
+                return false;
+
+            auto weightDistanceFirst = fontSelectionAlgorithm.weightDistance(firstCapabilities).distance;
+            auto weightDistanceSecond = fontSelectionAlgorithm.weightDistance(secondCapabilities).distance;
+            if (weightDistanceFirst &lt; weightDistanceSecond)
+                return true;
+            return false;
+        });
+        for (auto&amp; candidate : candidateFontFaces)
+            face-&gt;appendFontFace(candidate.get());
+    }
+
</ins><span class="cx">     return face.get();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSFontFaceSeth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSFontFaceSet.h (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -67,7 +67,7 @@
</span><span class="cx"> 
</span><span class="cx">     ExceptionOr&lt;bool&gt; check(const String&amp; font, const String&amp; text);
</span><span class="cx"> 
</span><del>-    CSSSegmentedFontFace* fontFace(FontTraitsMask, const AtomicString&amp; family);
</del><ins>+    CSSSegmentedFontFace* fontFace(FontTraitsMask, FontSelectionValue stretch, const AtomicString&amp; family);
</ins><span class="cx"> 
</span><span class="cx">     enum class Status { Loading, Loaded };
</span><span class="cx">     Status status() const { return m_status; }
</span></span></pre></div>
<a id="trunkSourceWebCorecssCSSFontSelectorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/CSSFontSelector.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -145,6 +145,7 @@
</span><span class="cx">     RefPtr&lt;CSSValue&gt; fontFamily = style.getPropertyCSSValue(CSSPropertyFontFamily);
</span><span class="cx">     RefPtr&lt;CSSValue&gt; fontStyle = style.getPropertyCSSValue(CSSPropertyFontStyle);
</span><span class="cx">     RefPtr&lt;CSSValue&gt; fontWeight = style.getPropertyCSSValue(CSSPropertyFontWeight);
</span><ins>+    RefPtr&lt;CSSValue&gt; fontStretch = style.getPropertyCSSValue(CSSPropertyFontStretch);
</ins><span class="cx">     RefPtr&lt;CSSValue&gt; src = style.getPropertyCSSValue(CSSPropertySrc);
</span><span class="cx">     RefPtr&lt;CSSValue&gt; unicodeRange = style.getPropertyCSSValue(CSSPropertyUnicodeRange);
</span><span class="cx">     RefPtr&lt;CSSValue&gt; featureSettings = style.getPropertyCSSValue(CSSPropertyFontFeatureSettings);
</span><span class="lines">@@ -167,6 +168,9 @@
</span><span class="cx">     if (!fontWeight)
</span><span class="cx">         fontWeight = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
</span><span class="cx"> 
</span><ins>+    if (!fontStretch)
+        fontStretch = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
+
</ins><span class="cx">     CSSValueList* rangeList = downcast&lt;CSSValueList&gt;(unicodeRange.get());
</span><span class="cx"> 
</span><span class="cx">     CSSValueList&amp; srcList = downcast&lt;CSSValueList&gt;(*src);
</span><span class="lines">@@ -182,6 +186,8 @@
</span><span class="cx">         return;
</span><span class="cx">     if (!fontFace-&gt;setWeight(*fontWeight))
</span><span class="cx">         return;
</span><ins>+    if (!fontFace-&gt;setStretch(*fontStretch))
+        return;
</ins><span class="cx">     if (rangeList &amp;&amp; !fontFace-&gt;setUnicodeRange(*rangeList))
</span><span class="cx">         return;
</span><span class="cx">     if (variantLigatures &amp;&amp; !fontFace-&gt;setVariantLigatures(*variantLigatures))
</span><span class="lines">@@ -295,7 +301,7 @@
</span><span class="cx">     bool resolveGenericFamilyFirst = familyName == standardFamily;
</span><span class="cx"> 
</span><span class="cx">     AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document, fontDescription, familyName) : familyName;
</span><del>-    auto* face = m_cssFontFaceSet-&gt;fontFace(fontDescription.traitsMask(), familyForLookup);
</del><ins>+    auto* face = m_cssFontFaceSet-&gt;fontFace(fontDescription.traitsMask(), fontDescription.stretch(), familyForLookup);
</ins><span class="cx">     if (!face) {
</span><span class="cx">         if (!resolveGenericFamilyFirst)
</span><span class="cx">             familyForLookup = resolveGenericFamily(m_document, fontDescription, familyName);
</span></span></pre></div>
<a id="trunkSourceWebCorecssFontFacecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/FontFace.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -184,9 +184,16 @@
</span><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-ExceptionOr&lt;void&gt; FontFace::setStretch(const String&amp;)
</del><ins>+ExceptionOr&lt;void&gt; FontFace::setStretch(const String&amp; stretch)
</ins><span class="cx"> {
</span><del>-    // We don't support font-stretch. Swallow the call.
</del><ins>+    if (stretch.isEmpty())
+        return Exception { SYNTAX_ERR };
+
+    bool success = false;
+    if (auto value = parseString(stretch, CSSPropertyFontStretch))
+        success = m_backing-&gt;setStretch(*value);
+    if (!success)
+        return Exception { SYNTAX_ERR };
</ins><span class="cx">     return { };
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -318,7 +325,33 @@
</span><span class="cx"> 
</span><span class="cx"> String FontFace::stretch() const
</span><span class="cx"> {
</span><del>-    return ASCIILiteral(&quot;normal&quot;);
</del><ins>+    m_backing-&gt;updateStyleIfNeeded();
+    auto stretch = m_backing-&gt;stretch();
+
+    auto rangeIsSingleValue = [](FontSelectionRange range, FontSelectionValue value) -&gt; bool {
+        return range.minimum == value &amp;&amp; range.maximum == value;
+    };
+
+    if (rangeIsSingleValue(stretch, FontSelectionValue(50)))
+        return ASCIILiteral(&quot;ultra-condensed&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(62.5f)))
+        return ASCIILiteral(&quot;extra-condensed&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(75)))
+        return ASCIILiteral(&quot;condensed&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(87.5f)))
+        return ASCIILiteral(&quot;semi-condensed&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(100)))
+        return ASCIILiteral(&quot;normal&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(112.5f)))
+        return ASCIILiteral(&quot;semi-expanded&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(125)))
+        return ASCIILiteral(&quot;expanded&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(150)))
+        return ASCIILiteral(&quot;extra-expanded&quot;);
+    if (rangeIsSingleValue(stretch, FontSelectionValue(200)))
+        return ASCIILiteral(&quot;ultra-expanded&quot;);
+
+    return String::format(&quot;%f-%f&quot;, static_cast&lt;float&gt;(stretch.minimum), static_cast&lt;float&gt;(stretch.maximum));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> String FontFace::unicodeRange() const
</span></span></pre></div>
<a id="trunkSourceWebCorecssparserCSSPropertyParsercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/css/parser/CSSPropertyParser.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -4185,9 +4185,7 @@
</span><span class="cx">         parsedValue = consumeFontFaceUnicodeRange(m_range);
</span><span class="cx">         break;
</span><span class="cx">     case CSSPropertyFontStretch:
</span><del>-        // FIXME: Implement this.
-        m_range.consumeIncludingWhitespace();
-        parsedValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal);
</del><ins>+        parsedValue = consumeFontStretch(m_range);
</ins><span class="cx">         break;
</span><span class="cx">     case CSSPropertyFontStyle: {
</span><span class="cx">         CSSValueID id = m_range.consumeIncludingWhitespace().id();
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsFontCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/FontCache.h (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -203,7 +203,11 @@
</span><span class="cx"> 
</span><span class="cx">     // This function exists so CSSFontSelector can have a unified notion of preinstalled fonts and @font-face.
</span><span class="cx">     // It comes into play when you create an @font-face which shares a family name as a preinstalled font.
</span><del>-    Vector&lt;FontTraitsMask&gt; getTraitsInFamily(const AtomicString&amp;);
</del><ins>+    struct TraitsAndStretch {
+        FontTraitsMask traits;
+        FontSelectionRange stretch;
+    };
+    Vector&lt;TraitsAndStretch&gt; getTraitsAndStretchInFamily(const AtomicString&amp;);
</ins><span class="cx"> 
</span><span class="cx">     WEBCORE_EXPORT RefPtr&lt;Font&gt; fontForFamily(const FontDescription&amp;, const AtomicString&amp;, const FontFeatureSettings* fontFaceFeatures = nullptr, const FontVariantSettings* fontFaceVariantSettings = nullptr, bool checkingAlternateName = false);
</span><span class="cx">     WEBCORE_EXPORT Ref&lt;Font&gt; lastResortFallbackFont(const FontDescription&amp;);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsFontDescriptionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/FontDescription.h (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #define FontDescription_h
</span><span class="cx"> 
</span><span class="cx"> #include &quot;CSSValueKeywords.h&quot;
</span><ins>+#include &quot;FontSelectionAlgorithm.h&quot;
</ins><span class="cx"> #include &quot;FontTaggedSettings.h&quot;
</span><span class="cx"> #include &quot;TextFlags.h&quot;
</span><span class="cx"> #include &quot;WebKitFontFamilyNames.h&quot;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsFontSelectionAlgorithmcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp (0 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,239 @@
</span><ins>+/*
+ * 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 &quot;config.h&quot;
+#include &quot;FontSelectionAlgorithm.h&quot;
+
+namespace WebCore {
+
+auto FontSelectionAlgorithm::stretchDistance(FontSelectionCapabilities capabilities) const -&gt; DistanceResult
+{
+    auto width = capabilities.width;
+    ASSERT(width.isValid());
+    if (width.includes(m_request.width))
+        return { FontSelectionValue(), m_request.width };
+
+    if (m_request.width &gt;= FontSelectionValue(100)) {
+        if (width.minimum &gt; m_request.width)
+            return { width.minimum - m_request.width, width.minimum };
+        ASSERT(width.maximum &lt; m_request.width);
+        auto threshold = std::max(m_request.width, m_capabilitiesBounds.width.maximum);
+        return { threshold - width.maximum, width.maximum };
+    }
+
+    if (width.maximum &lt; m_request.width)
+        return { m_request.width - width.maximum, width.maximum };
+    ASSERT(width.minimum &gt; 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 -&gt; DistanceResult
+{
+    auto slope = capabilities.slope;
+    ASSERT(slope.isValid());
+    if (slope.includes(m_request.slope))
+        return { FontSelectionValue(), m_request.slope };
+
+    if (m_request.slope &gt;= italicThreshold()) {
+        if (slope.minimum &gt; m_request.slope)
+            return { slope.minimum - m_request.slope, slope.minimum };
+        ASSERT(m_request.slope &gt; slope.maximum);
+        auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
+        return { threshold - slope.maximum, slope.maximum };
+    }
+
+    if (m_request.slope &gt;= FontSelectionValue()) {
+        if (slope.maximum &gt;= FontSelectionValue() &amp;&amp; slope.maximum &lt; m_request.slope)
+            return { m_request.slope - slope.maximum, slope.maximum };
+        if (slope.minimum &gt; m_request.slope)
+            return { slope.minimum, slope.minimum };
+        ASSERT(slope.maximum &lt; FontSelectionValue());
+        auto threshold = std::max(m_request.slope, m_capabilitiesBounds.slope.maximum);
+        return { threshold - slope.maximum, slope.maximum };
+    }
+
+    if (m_request.slope &gt; -italicThreshold()) {
+        if (slope.minimum &gt; m_request.slope &amp;&amp; slope.minimum &lt;= FontSelectionValue())
+            return { slope.minimum - m_request.slope, slope.minimum };
+        if (slope.maximum &lt; m_request.slope)
+            return { -slope.maximum, slope.maximum };
+        ASSERT(slope.minimum &gt; FontSelectionValue());
+        auto threshold = std::min(m_request.slope, m_capabilitiesBounds.slope.minimum);
+        return { slope.minimum - threshold, slope.minimum };
+    }
+
+    if (slope.maximum &lt; m_request.slope)
+        return { m_request.slope - slope.maximum, slope.maximum };
+    ASSERT(slope.minimum &gt; 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 -&gt; DistanceResult
+{
+    auto weight = capabilities.weight;
+    ASSERT(weight.isValid());
+    if (weight.includes(m_request.weight))
+        return { FontSelectionValue(), m_request.weight };
+
+    // The spec states: &quot;If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first&quot;
+    FontSelectionValue offset(1);
+    if (m_request.weight == FontSelectionValue(400) &amp;&amp; weight.includes(FontSelectionValue(500)))
+        return { offset, FontSelectionValue(500) };
+    if (m_request.weight == FontSelectionValue(500) &amp;&amp; weight.includes(FontSelectionValue(400)))
+        return { offset, FontSelectionValue(400) };
+
+    if (m_request.weight &lt;= weightSearchThreshold()) {
+        if (weight.maximum &lt; m_request.weight)
+            return { m_request.weight - weight.maximum + offset, weight.maximum };
+        ASSERT(weight.minimum &gt; m_request.weight);
+        auto threshold = std::min(m_request.weight, m_capabilitiesBounds.weight.minimum);
+        return { weight.minimum - threshold + offset, weight.minimum };
+    }
+
+    if (weight.minimum &gt; m_request.weight)
+        return { weight.minimum - m_request.weight + offset, weight.minimum };
+    ASSERT(weight.maximum &lt; 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&lt;FontSelectionValue&gt; smallestDistance;
+    FontSelectionValue closestValue;
+    iterateActiveCapabilities([&amp;](FontSelectionCapabilities capabilities, size_t) {
+        auto distanceResult = (this-&gt;*computeDistance)(capabilities);
+        if (!smallestDistance || distanceResult.distance &lt; smallestDistance.value()) {
+            smallestDistance = distanceResult.distance;
+            closestValue = distanceResult.value;
+        }
+    });
+    ASSERT(smallestDistance);
+    iterateActiveCapabilities([&amp;](auto&amp; capabilities, size_t i) {
+        if (!(capabilities.*inclusionRange).includes(closestValue))
+            m_filter[i] = false;
+    });
+}
+
+size_t FontSelectionAlgorithm::indexOfBestCapabilities()
+{
+    filterCapability(&amp;FontSelectionAlgorithm::stretchDistance, &amp;FontSelectionCapabilities::width);
+    filterCapability(&amp;FontSelectionAlgorithm::styleDistance, &amp;FontSelectionCapabilities::slope);
+    filterCapability(&amp;FontSelectionAlgorithm::weightDistance, &amp;FontSelectionCapabilities::weight);
+
+    auto result = iterateActiveCapabilitiesWithReturn&lt;size_t&gt;([](FontSelectionCapabilities, size_t i) {
+        return i;
+    });
+    ASSERT(result);
+    return result.value_or(0);
+}
+
+FontSelectionRequest fontSelectionRequestForTraitsMask(FontTraitsMask traitsMask, FontSelectionValue stretch)
+{
+    FontSelectionRequest result;
+    if (traitsMask &amp; FontWeight100Mask)
+        result.weight = FontSelectionValue(100);
+    else if (traitsMask &amp; FontWeight200Mask)
+        result.weight = FontSelectionValue(200);
+    else if (traitsMask &amp; FontWeight300Mask)
+        result.weight = FontSelectionValue(300);
+    else if (traitsMask &amp; FontWeight400Mask)
+        result.weight = FontSelectionValue(400);
+    else if (traitsMask &amp; FontWeight500Mask)
+        result.weight = FontSelectionValue(500);
+    else if (traitsMask &amp; FontWeight600Mask)
+        result.weight = FontSelectionValue(600);
+    else if (traitsMask &amp; FontWeight700Mask)
+        result.weight = FontSelectionValue(700);
+    else if (traitsMask &amp; FontWeight800Mask)
+        result.weight = FontSelectionValue(800);
+    else {
+        ASSERT(traitsMask &amp; FontWeight900Mask);
+        result.weight = FontSelectionValue(900);
+    }
+
+    result.width = stretch;
+
+    if (traitsMask &amp; FontStyleNormalMask)
+        result.slope = FontSelectionValue();
+    else {
+        ASSERT(traitsMask &amp; FontStyleItalicMask);
+        result.slope = italicThreshold();
+    }
+
+    return result;
+}
+
+static FontSelectionCapabilities initialFontSelectionCapabilitiesForTraitsMask(FontTraitsMask traitsMask)
+{
+    FontSelectionCapabilities result;
+    if (traitsMask &amp; FontWeight100Mask)
+        result.weight = { FontSelectionValue(100), FontSelectionValue(100) };
+    else if (traitsMask &amp; FontWeight200Mask)
+        result.weight = { FontSelectionValue(200), FontSelectionValue(200) };
+    else if (traitsMask &amp; FontWeight300Mask)
+        result.weight = { FontSelectionValue(300), FontSelectionValue(300) };
+    else if (traitsMask &amp; FontWeight400Mask)
+        result.weight = { FontSelectionValue(400), FontSelectionValue(400) };
+    else if (traitsMask &amp; FontWeight500Mask)
+        result.weight = { FontSelectionValue(500), FontSelectionValue(500) };
+    else if (traitsMask &amp; FontWeight600Mask)
+        result.weight = { FontSelectionValue(600), FontSelectionValue(600) };
+    else if (traitsMask &amp; FontWeight700Mask)
+        result.weight = { FontSelectionValue(700), FontSelectionValue(700) };
+    else if (traitsMask &amp; FontWeight800Mask)
+        result.weight = { FontSelectionValue(800), FontSelectionValue(800) };
+    else {
+        ASSERT(traitsMask &amp; FontWeight900Mask);
+        result.weight = { FontSelectionValue(900), FontSelectionValue(900) };
+    }
+
+    if (traitsMask &amp; FontStyleNormalMask)
+        result.slope = { FontSelectionValue(), FontSelectionValue() };
+    else {
+        ASSERT(traitsMask &amp; 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;
+}
+
+}
</ins><span class="cx">Property changes on: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<ins>+native
</ins><span class="cx">\ No newline at end of property
</span><a id="trunkSourceWebCoreplatformgraphicsFontSelectionAlgorithmh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h (0 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -0,0 +1,365 @@
</span><ins>+/*
+ * 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 &quot;TextFlags.h&quot;
+#include &lt;wtf/GetPtr.h&gt;
+#include &lt;wtf/Hasher.h&gt;
+#include &lt;wtf/NeverDestroyed.h&gt;
+#include &lt;wtf/Optional.h&gt;
+#include &lt;wtf/Vector.h&gt;
+
+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&lt;float&gt;(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&lt;(const FontSelectionValue other) const;
+    bool operator&lt;=(const FontSelectionValue other) const;
+    bool operator&gt;(const FontSelectionValue other) const;
+    bool operator&gt;=(const FontSelectionValue other) const;
+
+    int16_t rawValue() const
+    {
+        return m_backing;
+    }
+
+    static FontSelectionValue maximumValue()
+    {
+        static NeverDestroyed&lt;FontSelectionValue&gt; result = FontSelectionValue(std::numeric_limits&lt;int16_t&gt;::max(), RawTag::RawTag);
+        return result.get();
+    }
+
+    static FontSelectionValue minimumValue()
+    {
+        static NeverDestroyed&lt;FontSelectionValue&gt; result = FontSelectionValue(std::numeric_limits&lt;int16_t&gt;::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&lt;int32_t&gt;(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
+}
+
+inline FontSelectionValue FontSelectionValue::operator/(const FontSelectionValue other) const
+{
+    return FontSelectionValue(static_cast&lt;int32_t&gt;(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&lt;(const FontSelectionValue other) const
+{
+    return m_backing &lt; other.m_backing;
+}
+
+inline bool FontSelectionValue::operator&lt;=(const FontSelectionValue other) const
+{
+    return m_backing &lt;= other.m_backing;
+}
+
+inline bool FontSelectionValue::operator&gt;(const FontSelectionValue other) const
+{
+    return m_backing &gt; other.m_backing;
+}
+
+inline bool FontSelectionValue::operator&gt;=(const FontSelectionValue other) const
+{
+    return m_backing &gt;= other.m_backing;
+}
+
+static inline FontSelectionValue italicThreshold()
+{
+    static NeverDestroyed&lt;FontSelectionValue&gt; result = FontSelectionValue(20);
+    return result.get();
+}
+
+static inline FontSelectionValue boldThreshold()
+{
+    static NeverDestroyed&lt;FontSelectionValue&gt; result = FontSelectionValue(600);
+    return result.get();
+}
+
+static inline FontSelectionValue weightSearchThreshold()
+{
+    static NeverDestroyed&lt;FontSelectionValue&gt; result = FontSelectionValue(500);
+    return result.get();
+}
+
+// [Inclusive, Inclusive]
+struct FontSelectionRange {
+    FontSelectionRange(FontSelectionValue minimum, FontSelectionValue maximum)
+        : minimum(minimum)
+        , maximum(maximum)
+    {
+    }
+
+    bool operator==(const FontSelectionRange&amp; other) const
+    {
+        return minimum == other.minimum
+            &amp;&amp; maximum == other.maximum;
+    }
+
+    bool isValid() const
+    {
+        return minimum &lt;= maximum;
+    }
+
+    void expand(const FontSelectionRange&amp; 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 &gt;= minimum &amp;&amp; target &lt;= maximum;
+    }
+
+    FontSelectionValue minimum { FontSelectionValue(1) };
+    FontSelectionValue maximum { FontSelectionValue(0) };
+};
+
+struct FontSelectionRequest {
+    bool operator==(const FontSelectionRequest&amp; other) const
+    {
+        return weight == other.weight
+            &amp;&amp; width == other.width
+            &amp;&amp; slope == other.slope;
+    }
+
+    bool operator!=(const FontSelectionRequest&amp; 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&amp; other) const
+    {
+        return request == other.request
+            &amp;&amp; isDeletedValue == other.isDeletedValue;
+    }
+
+    FontSelectionRequest request;
+    bool isDeletedValue { false };
+};
+
+struct FontSelectionRequestKeyHash {
+    static unsigned hash(const FontSelectionRequestKey&amp; 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&amp; a, const FontSelectionRequestKey&amp; b)
+    {
+        return a == b;
+    }
+
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+struct FontSelectionCapabilities {
+    void expand(const FontSelectionCapabilities&amp; 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&lt;FontSelectionCapabilities&gt;&amp; capabilities, std::optional&lt;FontSelectionCapabilities&gt; 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 &lt; 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 &lt;typename T&gt;
+    using IterateActiveCapabilitiesWithReturnCallback = std::function&lt;std::optional&lt;T&gt;(FontSelectionCapabilities, size_t)&gt;;
+
+    template &lt;typename T&gt;
+    inline std::optional&lt;T&gt; iterateActiveCapabilitiesWithReturn(IterateActiveCapabilitiesWithReturnCallback&lt;T&gt; callback)
+    {
+        for (size_t i = 0; i &lt; m_capabilities.size(); ++i) {
+            if (!m_filter[i])
+                continue;
+            if (auto result = callback(m_capabilities[i], i))
+                return result;
+        }
+        return std::nullopt;
+    }
+
+    template &lt;typename T&gt;
+    inline void iterateActiveCapabilities(T callback)
+    {
+        iterateActiveCapabilitiesWithReturn&lt;int&gt;([&amp;](FontSelectionCapabilities capabilities, size_t i) -&gt; std::optional&lt;int&gt; {
+            callback(capabilities, i);
+            return std::nullopt;
+        });
+    }
+
+    void filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange);
+
+    FontSelectionRequest m_request;
+    FontSelectionCapabilities m_capabilitiesBounds;
+    const Vector&lt;FontSelectionCapabilities&gt;&amp; m_capabilities;
+    std::unique_ptr&lt;bool[]&gt; m_filter;
+};
+
+FontSelectionRequest fontSelectionRequestForTraitsMask(FontTraitsMask, FontSelectionValue stretch);
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask, FontSelectionValue stretch);
+FontSelectionCapabilities fontSelectionCapabilitiesForTraitsMask(FontTraitsMask, FontSelectionRange stretch);
+
+}
</ins><span class="cx">Property changes on: trunk/Source/WebCore/platform/graphics/FontSelectionAlgorithm.h
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<ins>+native
</ins><span class="cx">\ No newline at end of property
</span><a id="svnkeywords"></a>
<div class="addfile"><h4>Added: svn:keywords</h4></div>
<ins>+Author Date Id Rev URL
</ins><span class="cx">\ No newline at end of property
</span><a id="trunkSourceWebCoreplatformgraphicscocoaFontCacheCoreTextcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/cocoa/FontCacheCoreText.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -662,8 +662,21 @@
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-Vector&lt;FontTraitsMask&gt; FontCache::getTraitsInFamily(const AtomicString&amp; familyName)
</del><ins>+static FontSelectionValue stretchFromCoreTextTraits(CFDictionaryRef traits)
</ins><span class="cx"> {
</span><ins>+    auto widthNumber = static_cast&lt;CFNumberRef&gt;(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, &amp;ctWidth);
+        ASSERT_UNUSED(success, success);
+        return FontSelectionValue(ctWidth &lt; 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
+    }
+    return FontSelectionValue(100);
+}
+
+auto FontCache::getTraitsAndStretchInFamily(const AtomicString&amp; familyName) -&gt; Vector&lt;TraitsAndStretch&gt;
+{
</ins><span class="cx">     auto familyNameStr = familyName.string().createCFString();
</span><span class="cx">     CFTypeRef keys[] = { kCTFontFamilyNameAttribute };
</span><span class="cx">     CFTypeRef values[] = { familyNameStr.get() };
</span><span class="lines">@@ -677,8 +690,8 @@
</span><span class="cx">     if (!numMatches)
</span><span class="cx">         return { };
</span><span class="cx"> 
</span><del>-    Vector&lt;FontTraitsMask&gt; traitsMasks;
-    traitsMasks.reserveInitialCapacity(numMatches);
</del><ins>+    Vector&lt;TraitsAndStretch&gt; result;
+    result.reserveInitialCapacity(numMatches);
</ins><span class="cx">     for (CFIndex i = 0; i &lt; numMatches; ++i) {
</span><span class="cx">         auto traits = adoptCF((CFDictionaryRef)CTFontDescriptorCopyAttribute((CTFontDescriptorRef)CFArrayGetValueAtIndex(matchedDescriptors.get(), i), kCTFontTraitsAttribute));
</span><span class="cx">         CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait);
</span><span class="lines">@@ -688,10 +701,11 @@
</span><span class="cx">             CFNumberGetValue(resultRef, kCFNumberIntType, &amp;symbolicTraits);
</span><span class="cx">             CGFloat weight = 0;
</span><span class="cx">             CFNumberGetValue(weightRef, kCFNumberCGFloatType, &amp;weight);
</span><del>-            traitsMasks.uncheckedAppend(toTraitsMask(symbolicTraits, weight));
</del><ins>+            auto stretch = stretchFromCoreTextTraits(traits.get());
+            result.uncheckedAppend({ toTraitsMask(symbolicTraits, weight), { stretch, stretch } });
</ins><span class="cx">         }
</span><span class="cx">     }
</span><del>-    return traitsMasks;
</del><ins>+    return result;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void invalidateFontCache();
</span><span class="lines">@@ -895,14 +909,7 @@
</span><span class="cx">         FontSelectionValue slant;
</span><span class="cx">         FontSelectionValue weight;
</span><span class="cx">         if (traits) {
</span><del>-            auto widthNumber = static_cast&lt;CFNumberRef&gt;(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, &amp;ctWidth);
-                ASSERT_UNUSED(success, success);
-                width = FontSelectionValue(ctWidth &lt; 0.5 ? ctWidth * 50 + 100 : ctWidth * 150 + 50);
-            }
</del><ins>+            width = stretchFromCoreTextTraits(traits.get());
</ins><span class="cx"> 
</span><span class="cx">             auto symbolicTraitsNumber = static_cast&lt;CFNumberRef&gt;(CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait));
</span><span class="cx">             if (symbolicTraitsNumber) {
</span><span class="lines">@@ -926,24 +933,6 @@
</span><span class="cx">         return { { weight, weight }, { width, width }, { slant, slant } };
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static const FontSelectionValue stretchThreshold()
-    {
-        static NeverDestroyed&lt;FontSelectionValue&gt; threshold(100);
-        return threshold.get();
-    }
-
-    static const FontSelectionValue italicThreshold()
-    {
-        static NeverDestroyed&lt;FontSelectionValue&gt; threshold(20);
-        return threshold.get();
-    }
-
-    static const FontSelectionValue weightThreshold()
-    {
-        static NeverDestroyed&lt;FontSelectionValue&gt; threshold(500);
-        return threshold.get();
-    }
-
</del><span class="cx"> private:
</span><span class="cx">     friend class NeverDestroyed&lt;FontDatabase&gt;;
</span><span class="cx"> 
</span><span class="lines">@@ -953,326 +942,61 @@
</span><span class="cx">     HashMap&lt;String, InstalledFont&gt; m_postScriptNameToFontDescriptors;
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-template &lt;typename T&gt;
-using IterateActiveFontsWithReturnCallback = std::function&lt;std::optional&lt;T&gt;(const FontDatabase::InstalledFont&amp;, size_t)&gt;;
-
-template &lt;typename T&gt;
-inline std::optional&lt;T&gt; iterateActiveFontsWithReturn(const FontDatabase::InstalledFontFamily&amp; installedFonts, const std::unique_ptr&lt;bool[]&gt;&amp; filter, IterateActiveFontsWithReturnCallback&lt;T&gt; callback)
</del><ins>+static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily&amp; familyFonts, FontSelectionRequest fontSelectionRequest)
</ins><span class="cx"> {
</span><del>-    for (size_t i = 0; i &lt; installedFonts.size(); ++i) {
-        if (!filter[i])
-            continue;
-        if (auto result = callback(installedFonts.installedFonts[i], i))
-            return result;
-    }
-    return std::nullopt;
</del><ins>+    Vector&lt;FontSelectionCapabilities&gt; capabilities;
+    capabilities.reserveInitialCapacity(familyFonts.size());
+    for (auto&amp; font : familyFonts.installedFonts)
+        capabilities.uncheckedAppend(font.capabilities);
+    FontSelectionAlgorithm fontSelectionAlgorithm(fontSelectionRequest, capabilities, familyFonts.capabilities);
+    return &amp;familyFonts.installedFonts[fontSelectionAlgorithm.indexOfBestCapabilities()];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-template &lt;typename T&gt;
-inline void iterateActiveFonts(const FontDatabase::InstalledFontFamily&amp; installedFonts, const std::unique_ptr&lt;bool[]&gt;&amp; filter, T callback)
</del><ins>+static FontSelectionRequest calculateFontSelectionRequest(CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
</ins><span class="cx"> {
</span><del>-    iterateActiveFontsWithReturn&lt;int&gt;(installedFonts, filter, [&amp;](const FontDatabase::InstalledFont&amp; font, size_t i) -&gt; std::optional&lt;int&gt; {
-        callback(font, i);
-        return std::nullopt;
-    });
-}
-
-static inline std::optional&lt;FontSelectionValue&gt; findClosestStretch(FontSelectionValue targetStretch, const FontDatabase::InstalledFontFamily&amp; installedFonts, const std::unique_ptr&lt;bool[]&gt;&amp; filter)
-{
-    std::function&lt;float(const FontDatabase::InstalledFont&amp;)&gt; computeScore;
-
-    if (targetStretch &gt;= FontDatabase::stretchThreshold()) {
-        auto threshold = std::max(targetStretch, installedFonts.capabilities.width.maximum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; float {
-            auto width = font.capabilities.width;
-            ASSERT(width.isValid());
-            if (width.includes(targetStretch))
-                return 0;
-            ASSERT(width.minimum &gt; targetStretch || width.maximum &lt; targetStretch);
-            if (width.minimum &gt; targetStretch)
-                return width.minimum - targetStretch;
-            ASSERT(width.maximum &lt; targetStretch);
-            return threshold - width.maximum;
-        };
-    } else {
-        ASSERT(targetStretch &lt; FontDatabase::stretchThreshold());
-        auto threshold = std::min(targetStretch, installedFonts.capabilities.width.minimum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; float {
-            auto width = font.capabilities.width;
-            if (width.includes(targetStretch))
-                return 0;
-            ASSERT(width.minimum &gt; targetStretch || width.maximum &lt; targetStretch);
-            if (width.maximum &lt; targetStretch)
-                return targetStretch - width.maximum;
-            ASSERT(width.minimum &gt; targetStretch);
-            return width.minimum - threshold;
-        };
-    }
-
-    size_t closestIndex = 0;
-    std::optional&lt;float&gt; minimumScore;
-    iterateActiveFonts(installedFonts, filter, [&amp;](auto&amp; installedFont, size_t i) {
-        auto score = computeScore(installedFont);
-        if (!minimumScore || score &lt; minimumScore.value()) {
-            minimumScore = score;
-            closestIndex = i;
-        }
-    });
-
-    if (!minimumScore)
-        return std::nullopt;
-    auto&amp; winner = installedFonts.installedFonts[closestIndex];
-    auto width = winner.capabilities.width;
-    if (width.includes(targetStretch))
-        return targetStretch;
-    if (width.minimum &gt; targetStretch)
-        return width.minimum;
-    ASSERT(width.maximum &lt; targetStretch);
-    return width.maximum;
-}
-
-static inline void filterStretch(FontSelectionValue target, const FontDatabase::InstalledFontFamily&amp; installedFonts, std::unique_ptr&lt;bool[]&gt;&amp; filter)
-{
-    iterateActiveFonts(installedFonts, filter, [&amp;](auto&amp; installedFont, size_t i) {
-        if (!installedFont.capabilities.width.includes(target))
-            filter[i] = false;
-    });
-}
-
-static inline std::optional&lt;FontSelectionValue&gt; findClosestStyle(FontSelectionValue targetStyle, const FontDatabase::InstalledFontFamily&amp; installedFonts, const std::unique_ptr&lt;bool[]&gt;&amp; filter)
-{
-    std::function&lt;float(const FontDatabase::InstalledFont&amp;)&gt; computeScore;
-
-    if (targetStyle &gt;= FontDatabase::italicThreshold()) {
-        auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum &gt; targetStyle || slope.maximum &lt; targetStyle);
-            if (slope.minimum &gt; targetStyle)
-                return slope.minimum - targetStyle;
-            ASSERT(targetStyle &gt; slope.maximum);
-            return threshold - slope.maximum;
-        };
-    } else if (targetStyle &gt;= FontSelectionValue()) {
-        auto threshold = std::max(targetStyle, installedFonts.capabilities.slope.maximum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum &gt; targetStyle || slope.maximum &lt; targetStyle);
-            if (slope.maximum &gt;= FontSelectionValue() &amp;&amp; slope.maximum &lt; targetStyle)
-                return targetStyle - slope.maximum;
-            if (slope.minimum &gt; targetStyle)
-                return slope.minimum;
-            ASSERT(slope.maximum &lt; FontSelectionValue());
-            return threshold - slope.maximum;
-        };
-    } else if (targetStyle &gt; -FontDatabase::italicThreshold()) {
-        auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum &gt; targetStyle || slope.maximum &lt; targetStyle);
-            if (slope.minimum &gt; targetStyle &amp;&amp; slope.minimum &lt;= FontSelectionValue())
-                return slope.minimum - targetStyle;
-            if (slope.maximum &lt; targetStyle)
-                return -slope.maximum;
-            ASSERT(slope.minimum &gt; FontSelectionValue());
-            return slope.minimum - threshold;
-        };
-    } else {
-        ASSERT(targetStyle &lt;= -FontDatabase::italicThreshold());
-        auto threshold = std::min(targetStyle, installedFonts.capabilities.slope.minimum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; float {
-            auto slope = font.capabilities.slope;
-            ASSERT(slope.isValid());
-            if (slope.includes(targetStyle))
-                return 0;
-            ASSERT(slope.minimum &gt; targetStyle || slope.maximum &lt; targetStyle);
-            if (slope.maximum &lt; targetStyle)
-                return targetStyle - slope.maximum;
-            ASSERT(slope.minimum &gt; targetStyle);
-            return slope.minimum - threshold;
-        };
-    }
-
-    size_t closestIndex = 0;
-    std::optional&lt;float&gt; minimumScore;
-    iterateActiveFonts(installedFonts, filter, [&amp;](auto&amp; installedFont, size_t i) {
-        auto score = computeScore(installedFont);
-        if (!minimumScore || score &lt; minimumScore.value()) {
-            minimumScore = score;
-            closestIndex = i;
-        }
-    });
-
-    if (!minimumScore)
-        return std::nullopt;
-    auto&amp; winner = installedFonts.installedFonts[closestIndex];
-    auto slope = winner.capabilities.slope;
-    if (slope.includes(targetStyle))
-        return targetStyle;
-    if (slope.minimum &gt; targetStyle)
-        return slope.minimum;
-    ASSERT(slope.maximum &lt; targetStyle);
-    return slope.maximum;
-}
-
-static inline void filterStyle(FontSelectionValue target, const FontDatabase::InstalledFontFamily&amp; installedFonts, std::unique_ptr&lt;bool[]&gt;&amp; filter)
-{
-    iterateActiveFonts(installedFonts, filter, [&amp;](auto&amp; installedFont, size_t i) {
-        if (!installedFont.capabilities.slope.includes(target))
-            filter[i] = false;
-    });
-}
-
-static inline std::optional&lt;FontSelectionValue&gt; findClosestWeight(FontSelectionValue targetWeight, const FontDatabase::InstalledFontFamily&amp; installedFonts, const std::unique_ptr&lt;bool[]&gt;&amp; filter)
-{
-    {
-        // The spec states: &quot;If the desired weight is 400, 500 is checked first ... If the desired weight is 500, 400 is checked first&quot;
-        IterateActiveFontsWithReturnCallback&lt;FontSelectionValue&gt; searchFor400 = [&amp;](const FontDatabase::InstalledFont&amp; font, size_t) -&gt; std::optional&lt;FontSelectionValue&gt; {
-            if (font.capabilities.weight.includes(FontSelectionValue(400)))
-                return FontSelectionValue(400);
-            return std::nullopt;
-        };
-        IterateActiveFontsWithReturnCallback&lt;FontSelectionValue&gt; searchFor500 = [&amp;](const FontDatabase::InstalledFont&amp; font, size_t) -&gt; std::optional&lt;FontSelectionValue&gt; {
-            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&lt;float(const FontDatabase::InstalledFont&amp;)&gt; computeScore;
-    if (targetWeight &lt;= FontDatabase::weightThreshold()) {
-        auto threshold = std::min(targetWeight, installedFonts.capabilities.weight.minimum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; FontSelectionValue {
-            auto weight = font.capabilities.weight;
-            if (weight.includes(targetWeight))
-                return FontSelectionValue();
-            ASSERT(weight.minimum &gt; targetWeight || weight.maximum &lt; targetWeight);
-            if (weight.maximum &lt; targetWeight)
-                return targetWeight - weight.maximum;
-            ASSERT(weight.minimum &gt; targetWeight);
-            return weight.minimum - threshold;
-        };
-    } else {
-        ASSERT(targetWeight &gt; FontDatabase::weightThreshold());
-        auto threshold = std::max(targetWeight, installedFonts.capabilities.weight.maximum);
-        computeScore = [&amp;, threshold](const FontDatabase::InstalledFont&amp; font) -&gt; FontSelectionValue {
-            auto weight = font.capabilities.weight;
-            if (weight.includes(targetWeight))
-                return FontSelectionValue();
-            ASSERT(weight.minimum &gt; targetWeight || weight.maximum &lt; targetWeight);
-            if (weight.minimum &gt; targetWeight)
-                return weight.minimum - targetWeight;
-            ASSERT(weight.maximum &lt; targetWeight);
-            return threshold - weight.maximum;
-        };
-    }
-
-    size_t closestIndex = 0;
-    std::optional&lt;float&gt; minimumScore;
-    iterateActiveFonts(installedFonts, filter, [&amp;](auto&amp; installedFont, size_t i) {
-        auto score = computeScore(installedFont);
-        if (!minimumScore || score &lt; minimumScore.value()) {
-            minimumScore = score;
-            closestIndex = i;
-        }
-    });
-
-    if (!minimumScore)
-        return std::nullopt;
-    auto&amp; winner = installedFonts.installedFonts[closestIndex];
-    auto weight = winner.capabilities.weight;
-    if (weight.includes(targetWeight))
-        return targetWeight;
-    if (weight.minimum &gt; targetWeight)
-        return weight.minimum;
-    ASSERT(weight.maximum &lt; targetWeight);
-    return weight.maximum;
-}
-
-static inline void filterWeight(FontSelectionValue target, const FontDatabase::InstalledFontFamily&amp; installedFonts, std::unique_ptr&lt;bool[]&gt;&amp; filter)
-{
-    iterateActiveFonts(installedFonts, filter, [&amp;](auto&amp; installedFont, size_t i) {
-        if (!installedFont.capabilities.weight.includes(target))
-            filter[i] = false;
-    });
-}
-
-static inline FontSelectionValue computeTargetWeight(FontWeight weight)
-{
</del><ins>+    FontSelectionRequest result;
</ins><span class="cx">     switch (weight) {
</span><span class="cx">     case FontWeight100:
</span><del>-        return FontSelectionValue(100);
</del><ins>+        result.weight = FontSelectionValue(100);
+        break;
</ins><span class="cx">     case FontWeight200:
</span><del>-        return FontSelectionValue(200);
</del><ins>+        result.weight = FontSelectionValue(200);
+        break;
</ins><span class="cx">     case FontWeight300:
</span><del>-        return FontSelectionValue(300);
</del><ins>+        result.weight = FontSelectionValue(300);
+        break;
</ins><span class="cx">     case FontWeight400:
</span><del>-        return FontSelectionValue(400);
</del><ins>+        result.weight = FontSelectionValue(400);
+        break;
</ins><span class="cx">     case FontWeight500:
</span><del>-        return FontSelectionValue(500);
</del><ins>+        result.weight = FontSelectionValue(500);
+        break;
</ins><span class="cx">     case FontWeight600:
</span><del>-        return FontSelectionValue(600);
</del><ins>+        result.weight = FontSelectionValue(600);
+        break;
</ins><span class="cx">     case FontWeight700:
</span><del>-        return FontSelectionValue(700);
</del><ins>+        result.weight = FontSelectionValue(700);
+        break;
</ins><span class="cx">     case FontWeight800:
</span><del>-        return FontSelectionValue(800);
</del><ins>+        result.weight = FontSelectionValue(800);
+        break;
</ins><span class="cx">     case FontWeight900:
</span><del>-        return FontSelectionValue(900);
</del><ins>+        result.weight = FontSelectionValue(900);
+        break;
</ins><span class="cx">     default:
</span><span class="cx">         ASSERT_NOT_REACHED();
</span><del>-        return FontSelectionValue(400);
</del><ins>+        result.weight = FontSelectionValue(400);
+        break;
</ins><span class="cx">     }
</span><del>-}
</del><span class="cx"> 
</span><del>-static const FontDatabase::InstalledFont* findClosestFont(const FontDatabase::InstalledFontFamily&amp; familyFonts, CTFontSymbolicTraits requestedTraits, FontWeight weight, FontSelectionValue stretch)
-{
-    ASSERT(!familyFonts.isEmpty());
</del><ins>+    result.width = stretch;
</ins><span class="cx"> 
</span><del>-    // Parallel to familyFonts.
-    std::unique_ptr&lt;bool[]&gt; filter { new bool[familyFonts.size()] };
-    for (size_t i = 0; i &lt; familyFonts.size(); ++i)
-        filter[i] = true;
-
-    if (auto closestStretch = findClosestStretch(stretch, familyFonts, filter))
-        filterStretch(closestStretch.value(), familyFonts, filter);
</del><ins>+    if (requestedTraits &amp; kCTFontTraitItalic)
+        result.slope = italicThreshold();
</ins><span class="cx">     else
</span><del>-        return nullptr;
</del><ins>+        result.slope = FontSelectionValue();
</ins><span class="cx"> 
</span><del>-    FontSelectionValue targetStyle = requestedTraits &amp; 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&lt;const FontDatabase::InstalledFont*&gt;(familyFonts, filter, [](const FontDatabase::InstalledFont&amp; font, size_t) {
-        return &amp;font;
-    }).value_or(nullptr);
</del><ins>+    return result;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #endif // !SHOULD_USE_CORE_TEXT_FONT_LOOKUP
</span><span class="lines">@@ -1283,7 +1007,6 @@
</span><span class="cx">     if (!isSystemFont(family) &amp;&amp; whitelist.size() &amp;&amp; !whitelist.contains(family))
</span><span class="cx">         return nullptr;
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> #if SHOULD_USE_CORE_TEXT_FONT_LOOKUP
</span><span class="cx">     UNUSED_PARAM(stretch);
</span><span class="cx">     return adoptCF(CTFontCreateForCSS(family.string().createCFString().get(), toCoreTextFontWeight(weight), requestedTraits, size));
</span><span class="lines">@@ -1300,8 +1023,8 @@
</span><span class="cx">         const auto&amp; postScriptFont = FontDatabase::singleton().fontForPostScriptName(family);
</span><span class="cx">         if (!postScriptFont.fontDescriptor)
</span><span class="cx">             return nullptr;
</span><del>-        if (((requestedTraits &amp; kCTFontTraitItalic) &amp;&amp; postScriptFont.capabilities.slope.maximum &lt; FontDatabase::italicThreshold())
-            || (weight &gt;= FontWeight600 &amp;&amp; postScriptFont.capabilities.weight.maximum &lt; FontSelectionValue(600))) {
</del><ins>+        if (((requestedTraits &amp; kCTFontTraitItalic) &amp;&amp; postScriptFont.capabilities.slope.maximum &lt; italicThreshold())
+            || (weight &gt;= FontWeight600 &amp;&amp; postScriptFont.capabilities.weight.maximum &lt; boldThreshold())) {
</ins><span class="cx">             auto postScriptFamilyName = adoptCF(static_cast&lt;CFStringRef&gt;(CTFontDescriptorCopyAttribute(postScriptFont.fontDescriptor.get(), kCTFontFamilyNameAttribute)));
</span><span class="cx">             if (!postScriptFamilyName)
</span><span class="cx">                 return nullptr;
</span><span class="lines">@@ -1308,7 +1031,7 @@
</span><span class="cx">             const auto&amp; familyFonts = FontDatabase::singleton().collectionForFamily(String(postScriptFamilyName.get()));
</span><span class="cx">             if (familyFonts.isEmpty())
</span><span class="cx">                 return nullptr;
</span><del>-            if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch)) {
</del><ins>+            if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch))) {
</ins><span class="cx">                 if (!installedFont-&gt;fontDescriptor)
</span><span class="cx">                     return nullptr;
</span><span class="cx">                 return adoptCF(CTFontCreateWithFontDescriptor(installedFont-&gt;fontDescriptor.get(), size, nullptr));
</span><span class="lines">@@ -1318,7 +1041,7 @@
</span><span class="cx">         return adoptCF(CTFontCreateWithFontDescriptor(postScriptFont.fontDescriptor.get(), size, nullptr));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (const auto* installedFont = findClosestFont(familyFonts, requestedTraits, weight, stretch))
</del><ins>+    if (const auto* installedFont = findClosestFont(familyFonts, calculateFontSelectionRequest(requestedTraits, weight, stretch)))
</ins><span class="cx">         return adoptCF(CTFontCreateWithFontDescriptor(installedFont-&gt;fontDescriptor.get(), size, nullptr));
</span><span class="cx"> 
</span><span class="cx">     return nullptr;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsfreetypeFontCacheFreeTypecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/freetype/FontCacheFreeType.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -138,7 +138,7 @@
</span><span class="cx">     RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-Vector&lt;FontTraitsMask&gt; FontCache::getTraitsInFamily(const AtomicString&amp;)
</del><ins>+auto FontCache::getTraitsAndStretchInFamily(const AtomicString&amp; familyName) -&gt; Vector&lt;TraitsAndStretch&gt;
</ins><span class="cx"> {
</span><span class="cx">     return { };
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicswinFontCacheWincpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/win/FontCacheWin.cpp (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -559,7 +559,7 @@
</span><span class="cx">     return 1;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-Vector&lt;FontTraitsMask&gt; FontCache::getTraitsInFamily(const AtomicString&amp; familyName)
</del><ins>+auto FontCache::getTraitsAndStretchInFamily(const AtomicString&amp; familyName) -&gt; Vector&lt;TraitsAndStretch&gt;
</ins><span class="cx"> {
</span><span class="cx">     HWndDC hdc(0);
</span><span class="cx"> 
</span><span class="lines">@@ -572,10 +572,10 @@
</span><span class="cx"> 
</span><span class="cx">     TraitsInFamilyProcData procData(familyName);
</span><span class="cx">     EnumFontFamiliesEx(hdc, &amp;logFont, traitsInFamilyEnumProc, reinterpret_cast&lt;LPARAM&gt;(&amp;procData), 0);
</span><del>-    Vector&lt;FontTraitsMask&gt; result;
</del><ins>+    Vector&lt;TraitsAndStretch&gt; result;
</ins><span class="cx">     result.reserveInitialCapacity(procData.m_traitsMasks.size());
</span><span class="cx">     for (unsigned mask : procData.m_traitsMasks)
</span><del>-        result.uncheckedAppend(static_cast&lt;FontTraitsMask&gt;(mask));
</del><ins>+        result.uncheckedAppend({ static_cast&lt;FontTraitsMask&gt;(mask), FontSelectionRange(FontSelectionValue(), FontSelectionValue()) });
</ins><span class="cx">     return result;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformtextTextFlagsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/text/TextFlags.h (213435 => 213436)</h4>
<pre class="diff"><span>
<span class="info">--- 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)
</span><span class="lines">@@ -25,8 +25,6 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><del>-#include &lt;wtf/NeverDestroyed.h&gt;
-
</del><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> enum TextRenderingMode { AutoTextRendering, OptimizeSpeed, OptimizeLegibility, GeometricPrecision };
</span><span class="lines">@@ -398,162 +396,6 @@
</span><span class="cx">     FontWeightMask = FontWeight100Mask | FontWeight200Mask | FontWeight300Mask | FontWeight400Mask | FontWeight500Mask | FontWeight600Mask | FontWeight700Mask | FontWeight800Mask | FontWeight900Mask
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-// 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&lt;float&gt;(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&lt;int32_t&gt;(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
-    }
-
-    FontSelectionValue operator/(const FontSelectionValue other) const
-    {
-        return FontSelectionValue(static_cast&lt;int32_t&gt;(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&lt;(const FontSelectionValue other) const
-    {
-        return m_backing &lt; other.m_backing;
-    }
-
-    bool operator&lt;=(const FontSelectionValue other) const
-    {
-        return m_backing &lt;= other.m_backing;
-    }
-
-    bool operator&gt;(const FontSelectionValue other) const
-    {
-        return m_backing &gt; other.m_backing;
-    }
-
-    bool operator&gt;=(const FontSelectionValue other) const
-    {
-        return m_backing &gt;= other.m_backing;
-    }
-
-    int16_t rawValue() const
-    {
-        return m_backing;
-    }
-
-    static FontSelectionValue maximumValue()
-    {
-        static NeverDestroyed&lt;FontSelectionValue&gt; result = FontSelectionValue(std::numeric_limits&lt;int16_t&gt;::max(), RawTag::RawTag);
-        return result.get();
-    }
-
-    static FontSelectionValue minimumValue()
-    {
-        static NeverDestroyed&lt;FontSelectionValue&gt; result = FontSelectionValue(std::numeric_limits&lt;int16_t&gt;::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 &lt;= maximum;
-    }
-
-    void expand(const FontSelectionRange&amp; 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 &gt;= minimum &amp;&amp; target &lt;= maximum;
-    }
-
-    FontSelectionValue minimum { FontSelectionValue(1) };
-    FontSelectionValue maximum { FontSelectionValue(0) };
-};
-
-struct FontSelectionRequest {
-    FontSelectionValue weight;
-    FontSelectionValue width;
-    FontSelectionValue slope;
-};
-
-struct FontSelectionCapabilities {
-    void expand(const FontSelectionCapabilities&amp; capabilities)
-    {
-        weight.expand(capabilities.weight);
-        width.expand(capabilities.width);
-        slope.expand(capabilities.slope);
-    }
-
-    FontSelectionRange weight;
-    FontSelectionRange width;
-    FontSelectionRange slope;
-};
-
</del><span class="cx"> enum class Kerning {
</span><span class="cx">     Auto,
</span><span class="cx">     Normal,
</span></span></pre>
</div>
</div>

</body>
</html>