<!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>[205574] 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/205574">205574</a></dd>
<dt>Author</dt> <dd>eric.carlson@apple.com</dd>
<dt>Date</dt> <dd>2016-09-07 17:01:29 -0700 (Wed, 07 Sep 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>[MediaStream] applyConstraints pt. 2 - advanced constraints
https://bugs.webkit.org/show_bug.cgi?id=161715
&lt;rdar://problem/28195461&gt;

Reviewed by Dean Jackson.

Source/WebCore:

Test: fast/mediastream/apply-constraints-advanced.html

* platform/mediastream/MediaConstraints.cpp:
(WebCore::MediaConstraint::create): Return Ref&lt;&gt;, not RefPtr&lt;&gt;.
(WebCore::MediaConstraint::copy): New
(WebCore::IntConstraint::copy): Ditto.
(WebCore::DoubleConstraint::copy): Ditto.
(WebCore::BooleanConstraint::copy): Ditto.
(WebCore::StringConstraint::copy): Ditto.
(WebCore::StringConstraint::fitnessDistance): New, compute the fitness distance between the
  constraint and the specified value.
(WebCore::StringConstraint::merge): New, merge value into constraint.
(WebCore::FlattenedConstraint::set): New, add or replace a constraint.
(WebCore::FlattenedConstraint::merge): New, merge or add a constraint.
* platform/mediastream/MediaConstraints.h:
(WebCore::MediaConstraint::fitnessDistance):
(WebCore::MediaConstraint::merge):
(WebCore::NumericConstraint::nearlyEqual):
(WebCore::FlattenedConstraint::isEmpty):
(WebCore::FlattenedConstraint::begin):
(WebCore::FlattenedConstraint::end):
(WebCore::MediaConstraint::~MediaConstraint): Deleted.
(WebCore::MediaConstraint::find): Deleted.
(WebCore::MediaConstraint::getIdeal): Deleted.

* platform/mediastream/RealtimeMediaSource.cpp:
(WebCore::RealtimeMediaSource::fitnessDistance): Return the fitness distance between the source
  capabilities and a constraint.
(WebCore::applyNumericConstraint): New, apply a numeric constraint.
(WebCore::RealtimeMediaSource::applyConstraint): Use applyNumericConstraint.
(WebCore::RealtimeMediaSource::selectSettings): New, implement the SelectSettings algorithm
(WebCore::RealtimeMediaSource::applyConstraints):
(WebCore::RealtimeMediaSource::supportsConstraint): Deleted.
(WebCore::value): Deleted.
* platform/mediastream/RealtimeMediaSource.h:

LayoutTests:

* fast/mediastream/apply-constraints-advanced-expected.txt: Added.
* fast/mediastream/apply-constraints-advanced.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamMediaConstraintscpp">trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamMediaConstraintsh">trunk/Source/WebCore/platform/mediastream/MediaConstraints.h</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamRealtimeMediaSourcecpp">trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformmediastreamRealtimeMediaSourceh">trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfastmediastreamapplyconstraintsadvancedexpectedtxt">trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfastmediastreamapplyconstraintsadvancedhtml">trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/LayoutTests/ChangeLog        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2016-09-07  Eric Carlson  &lt;eric.carlson@apple.com&gt;
+
+        [MediaStream] applyConstraints pt. 2 - advanced constraints
+        https://bugs.webkit.org/show_bug.cgi?id=161715
+        &lt;rdar://problem/28195461&gt;
+
+        Reviewed by Dean Jackson.
+
+        * fast/mediastream/apply-constraints-advanced-expected.txt: Added.
+        * fast/mediastream/apply-constraints-advanced.html: Added.
+
</ins><span class="cx"> 2016-09-06  Dean Jackson  &lt;dino@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Expose Apple Pencil input to testing system
</span></span></pre></div>
<a id="trunkLayoutTestsfastmediastreamapplyconstraintsadvancedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt (0 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-advanced-expected.txt        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -0,0 +1,48 @@
</span><ins>+Tests applyConstraints on a video stream track.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS stream.getVideoTracks().length is 1
+PASS stream.getAudioTracks().length is 0
+PASS video.videoTracks.length is 1
+PASS video.audioTracks.length is 0
+
+** Constraint: {&quot;width&quot;:640,&quot;height&quot;:480} - setup width and height.
+PASS settings['width'] is 640
+PASS settings['height'] is 480
+
+** Constraint: {&quot;width&quot;:{&quot;min&quot;:320},&quot;height&quot;:{&quot;min&quot;:240},&quot;advanced&quot;:[{&quot;width&quot;:1920,&quot;height&quot;:1280}]} - advanced width and height are too big, minimums are less than current, nothing is changed.
+PASS settings['width'] is 640
+PASS settings['height'] is 480
+
+** Constraint: {&quot;width&quot;:{&quot;min&quot;:640},&quot;height&quot;:{&quot;min&quot;:480},&quot;advanced&quot;:[{&quot;width&quot;:1920,&quot;height&quot;:1280},{&quot;width&quot;:960,&quot;height&quot;:720}]} - first width and height in advanced are too big, second is used.
+PASS settings['width'] is 960
+PASS settings['height'] is 720
+
+** Constraint: {&quot;width&quot;:320,&quot;height&quot;:240} - reset width and height.
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+
+** Constraint: {&quot;width&quot;:{&quot;min&quot;:640},&quot;height&quot;:{&quot;min&quot;:480},&quot;advanced&quot;:[{&quot;width&quot;:1920,&quot;height&quot;:1280}]} - advanced width and height are too big, fall back to required minimums.
+PASS settings['width'] is 640
+PASS settings['height'] is 480
+
+** Constraint: {&quot;width&quot;:320,&quot;height&quot;:240} - reset width and height.
+PASS settings['width'] is 320
+PASS settings['height'] is 240
+
+** Constraint: {&quot;width&quot;:{&quot;min&quot;:640},&quot;height&quot;:{&quot;min&quot;:480},&quot;advanced&quot;:[{&quot;width&quot;:1920,&quot;height&quot;:1280},{&quot;aspectRatio&quot;:1.777777}]} - advanced width and height are too big, aspectRatio is used.
+PASS settings['width'] is 640
+PASS settings['height'] is 360
+
+** Constraint: {&quot;advanced&quot;:[{&quot;facingMode&quot;:&quot;left&quot;},{&quot;facingMode&quot;:&quot;right&quot;},{&quot;facingMode&quot;:&quot;environment&quot;},{&quot;facingMode&quot;:&quot;user&quot;}]} - no required constraints, advanced constraints are ignored.
+PASS settings['facingMode'] is &quot;user&quot;
+
+** Constraint: {&quot;width&quot;:{&quot;min&quot;:640},&quot;advanced&quot;:[{&quot;facingMode&quot;:&quot;left&quot;},{&quot;facingMode&quot;:&quot;right&quot;},{&quot;facingMode&quot;:&quot;environment&quot;},{&quot;facingMode&quot;:&quot;user&quot;}]} - first two advanced facingModes are not supported, third is used.
+PASS settings['facingMode'] is &quot;environment&quot;
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfastmediastreamapplyconstraintsadvancedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html (0 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html                                (rev 0)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-advanced.html        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -0,0 +1,121 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+    &lt;head&gt;
+        &lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+        &lt;script src=&quot;resources/apply-constraints-utils.js&quot;&gt;&lt;/script&gt;
+        &lt;script&gt;
+
+            let tests = [
+                {
+                    message: &quot;setup width and height.&quot;,
+                    constraint: { width: 640, height: 480 }, 
+                    expected: { width: 640, height: 480 }, 
+                },
+                {
+                    message: &quot;advanced width and height are too big, minimums are less than current, nothing is changed.&quot;,
+                    constraint: {
+                                    width: { min: 320 },
+                                    height: { min: 240 },
+                                    advanced: [
+                                        { width: 1920, height: 1280 },
+                                    ]
+                                },
+                    expected: { width: 640, height: 480 }, 
+                },
+                {
+                    message: &quot;first width and height in advanced are too big, second is used.&quot;,
+                    constraint: {
+                                    width: { min: 640 },
+                                    height: { min: 480 },
+                                    advanced: [
+                                        { width: 1920, height: 1280 },
+                                        { width: 960, height: 720 },
+                                    ]
+                                 },
+                    expected: { width: 960, height: 720 }, 
+                },
+                {
+                    message: &quot;reset width and height.&quot;,
+                    constraint: { width: 320, height: 240 }, 
+                    expected: { width: 320, height: 240 }, 
+                },
+                {
+                    message: &quot;advanced width and height are too big, fall back to required minimums.&quot;,
+                    constraint: {
+                                    width: { min: 640 },
+                                    height: {  min: 480 },
+                                    advanced: [
+                                        { width: 1920, height: 1280 },
+                                    ]
+                                },
+                    expected: { width: 640, height: 480 }, 
+                },
+                {
+                    message: &quot;reset width and height.&quot;,
+                    constraint: { width: 320, height: 240 }, 
+                    expected: { width: 320, height: 240 }, 
+                },
+                {
+                    message: &quot;advanced width and height are too big, aspectRatio is used.&quot;,
+                    constraint: {
+                                    width: { min: 640 },
+                                    height: { min: 480 },
+                                    advanced: [
+                                        { width: 1920, height: 1280 },
+                                        { aspectRatio: 1.777777 }
+                                    ]
+                                },
+                    expected: { width: 640, height: 360 }, 
+                },
+                {
+                    message: &quot;no required constraints, advanced constraints are ignored.&quot;,
+                    constraint: {
+                                    advanced: [
+                                        { facingMode: &quot;left&quot; },
+                                        { facingMode: &quot;right&quot; },
+                                        { facingMode: &quot;environment&quot; },
+                                        { facingMode: &quot;user&quot; },
+                                    ]
+                                },
+                    expected: { facingMode: &quot;user&quot; }, 
+                },
+                {
+                    message: &quot;first two advanced facingModes are not supported, third is used.&quot;,
+                    constraint: {
+                                    width: { min: 640 },
+                                    advanced: [
+                                        { facingMode: &quot;left&quot; },
+                                        { facingMode: &quot;right&quot; },
+                                        { facingMode: &quot;environment&quot; },
+                                        { facingMode: &quot;user&quot; },
+                                    ]
+                                },
+                    expected: { facingMode: &quot;environment&quot; }, 
+                },
+            ];
+
+            let tester = new ConstraintsTest({ video: true }, tests, &quot;Tests applyConstraints on a video stream track.&quot;)
+                .onStreamReady((s) =&gt; {
+                    stream = s;
+                    shouldBe('stream.getVideoTracks().length', '1');
+                    shouldBe('stream.getAudioTracks().length', '0');
+                    tester.setStreamTrack(stream.getVideoTracks()[0]);
+                })
+                .onVideoReady((v) =&gt; {
+                    video = v;
+                    shouldBe('video.videoTracks.length', '1');
+                    shouldBe('video.audioTracks.length', '0');
+                })
+                .start();
+
+        &lt;/script&gt;
+        &lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+    &lt;/head&gt;
+    &lt;body&gt;
+        &lt;video controls id=&quot;video&quot;&lt;/video&gt;
+        &lt;br&gt;
+        &lt;div id=&quot;div&quot;&gt;&lt;/div&gt;
+
+    &lt;/body&gt;
+&lt;/html&gt;
+
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/ChangeLog        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2016-09-07  Eric Carlson  &lt;eric.carlson@apple.com&gt;
+
+        [MediaStream] applyConstraints pt. 2 - advanced constraints
+        https://bugs.webkit.org/show_bug.cgi?id=161715
+        &lt;rdar://problem/28195461&gt;
+
+        Reviewed by Dean Jackson.
+
+        Test: fast/mediastream/apply-constraints-advanced.html
+
+        * platform/mediastream/MediaConstraints.cpp:
+        (WebCore::MediaConstraint::create): Return Ref&lt;&gt;, not RefPtr&lt;&gt;.
+        (WebCore::MediaConstraint::copy): New
+        (WebCore::IntConstraint::copy): Ditto.
+        (WebCore::DoubleConstraint::copy): Ditto.
+        (WebCore::BooleanConstraint::copy): Ditto.
+        (WebCore::StringConstraint::copy): Ditto.
+        (WebCore::StringConstraint::fitnessDistance): New, compute the fitness distance between the
+          constraint and the specified value.
+        (WebCore::StringConstraint::merge): New, merge value into constraint.
+        (WebCore::FlattenedConstraint::set): New, add or replace a constraint.
+        (WebCore::FlattenedConstraint::merge): New, merge or add a constraint.
+        * platform/mediastream/MediaConstraints.h:
+        (WebCore::MediaConstraint::fitnessDistance):
+        (WebCore::MediaConstraint::merge):
+        (WebCore::NumericConstraint::nearlyEqual):
+        (WebCore::FlattenedConstraint::isEmpty):
+        (WebCore::FlattenedConstraint::begin):
+        (WebCore::FlattenedConstraint::end):
+        (WebCore::MediaConstraint::~MediaConstraint): Deleted.
+        (WebCore::MediaConstraint::find): Deleted.
+        (WebCore::MediaConstraint::getIdeal): Deleted.
+
+        * platform/mediastream/RealtimeMediaSource.cpp:
+        (WebCore::RealtimeMediaSource::fitnessDistance): Return the fitness distance between the source
+          capabilities and a constraint.
+        (WebCore::applyNumericConstraint): New, apply a numeric constraint.
+        (WebCore::RealtimeMediaSource::applyConstraint): Use applyNumericConstraint.
+        (WebCore::RealtimeMediaSource::selectSettings): New, implement the SelectSettings algorithm
+        (WebCore::RealtimeMediaSource::applyConstraints):
+        (WebCore::RealtimeMediaSource::supportsConstraint): Deleted.
+        (WebCore::value): Deleted.
+        * platform/mediastream/RealtimeMediaSource.h:
+
</ins><span class="cx"> 2016-09-07  Mark Lam  &lt;mark.lam@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add CatchScope and force all exception checks to be via ThrowScope or CatchScope.
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamMediaConstraintscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/MediaConstraints.cpp        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -37,7 +37,7 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-RefPtr&lt;MediaConstraint&gt; MediaConstraint::create(const String&amp; name)
</del><ins>+Ref&lt;MediaConstraint&gt; MediaConstraint::create(const String&amp; name)
</ins><span class="cx"> {
</span><span class="cx">     MediaConstraintType constraintType = RealtimeMediaSourceSupportedConstraints::constraintFromName(name);
</span><span class="cx"> 
</span><span class="lines">@@ -62,6 +62,51 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Ref&lt;MediaConstraint&gt; MediaConstraint::copy() const
+{
+    return MediaConstraint::create(name());
+}
+
+Ref&lt;MediaConstraint&gt; IntConstraint::copy() const
+{
+    auto copy = IntConstraint::create(name(), type());
+    copy-&gt;m_min = m_min;
+    copy-&gt;m_max = m_max;
+    copy-&gt;m_exact = m_exact;
+    copy-&gt;m_ideal = m_ideal;
+
+    return copy.leakRef();
+}
+
+Ref&lt;MediaConstraint&gt; DoubleConstraint::copy() const
+{
+    auto copy = DoubleConstraint::create(name(), type());
+    copy-&gt;m_min = m_min;
+    copy-&gt;m_max = m_max;
+    copy-&gt;m_exact = m_exact;
+    copy-&gt;m_ideal = m_ideal;
+    
+    return copy.leakRef();
+}
+
+Ref&lt;MediaConstraint&gt; BooleanConstraint::copy() const
+{
+    auto copy = BooleanConstraint::create(name(), type());
+    copy-&gt;m_exact = m_exact;
+    copy-&gt;m_ideal = m_ideal;
+
+    return copy.leakRef();
+}
+
+Ref&lt;MediaConstraint&gt; StringConstraint::copy() const
+{
+    auto copy = StringConstraint::create(name(), type());
+    copy-&gt;m_exact = m_exact;
+    copy-&gt;m_ideal = m_ideal;
+
+    return copy.leakRef();
+}
+
</ins><span class="cx"> bool BooleanConstraint::getExact(bool&amp; exact) const
</span><span class="cx"> {
</span><span class="cx">     if (!m_exact)
</span><span class="lines">@@ -136,6 +181,95 @@
</span><span class="cx">     return emptyString();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+double StringConstraint::fitnessDistance(const String&amp; value) const
+{
+    // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
+
+    // 1. If the constraint is not supported by the browser, the fitness distance is 0.
+    if (isEmpty())
+        return 0;
+
+    // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
+    //    dictionary's value for the constraint does not satisfy the constraint, the
+    //    fitness distance is positive infinity.
+    if (!m_exact.isEmpty() &amp;&amp; m_exact.find(value) == notFound)
+        return std::numeric_limits&lt;double&gt;::infinity();
+
+    // 3. If no ideal value is specified, the fitness distance is 0.
+    if (m_exact.isEmpty())
+        return 0;
+
+    // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode,
+    // echoCancellation), the fitness distance is the result of the formula
+    //        (actual == ideal) ? 0 : 1
+    return m_ideal.find(value) != notFound ? 0 : 1;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+double StringConstraint::fitnessDistance(const Vector&lt;String&gt;&amp; values) const
+{
+    if (isEmpty())
+        return 0;
+
+    double minimumDistance = std::numeric_limits&lt;double&gt;::infinity();
+    for (auto&amp; value : values)
+        minimumDistance = std::min(minimumDistance, fitnessDistance(value));
+
+    return minimumDistance;
+}
+
+void StringConstraint::merge(const MediaConstraint&amp; other)
+{
+    if (other.isEmpty())
+        return;
+
+    Vector&lt;String&gt; values;
+    if (other.getExact(values)) {
+        if (m_exact.isEmpty())
+            m_exact = values;
+        else {
+            for (auto&amp; value : values) {
+                if (m_exact.find(value) == notFound)
+                    m_exact.append(value);
+            }
+        }
+    }
+
+    if (other.getIdeal(values)) {
+        if (m_ideal.isEmpty())
+            m_ideal = values;
+        else {
+            for (auto&amp; value : values) {
+                if (m_ideal.find(value) == notFound)
+                    m_ideal.append(value);
+            }
+        }
+    }
+}
+
+void FlattenedConstraint::set(const MediaConstraint&amp; constraint)
+{
+    for (auto existingConstraint : m_constraints) {
+        if (existingConstraint-&gt;type() == constraint.type()) {
+            existingConstraint = constraint.copy();
+            return;
+        }
+    }
+
+    m_constraints.append(constraint.copy());
+}
+
+void FlattenedConstraint::merge(const MediaConstraint&amp; constraint)
+{
+    for (auto existingConstraint : m_constraints) {
+        if (existingConstraint-&gt;type() == constraint.type()) {
+            existingConstraint-&gt;merge(constraint);
+            return;
+        }
+    }
+
+    m_constraints.append(constraint.copy());
+}
+
+}
+
</ins><span class="cx"> #endif // ENABLE(MEDIA_STREAM)
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamMediaConstraintsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/MediaConstraints.h (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/MediaConstraints.h        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/MediaConstraints.h        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -35,8 +35,10 @@
</span><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;RealtimeMediaSourceSupportedConstraints.h&quot;
</span><ins>+#include &lt;cstdlib&gt;
</ins><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><span class="cx"> #include &lt;wtf/RefCounted.h&gt;
</span><ins>+#include &lt;wtf/text/StringHash.h&gt;
</ins><span class="cx"> #include &lt;wtf/text/WTFString.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -43,11 +45,13 @@
</span><span class="cx"> 
</span><span class="cx"> class MediaConstraint : public RefCounted&lt;MediaConstraint&gt; {
</span><span class="cx"> public:
</span><del>-    static RefPtr&lt;MediaConstraint&gt; create(const String&amp; name);
</del><ins>+    static Ref&lt;MediaConstraint&gt; create(const String&amp;);
</ins><span class="cx"> 
</span><span class="cx">     enum class ConstraintType { ExactConstraint, IdealConstraint, MinConstraint, MaxConstraint };
</span><span class="cx"> 
</span><span class="cx">     virtual ~MediaConstraint() { };
</span><ins>+
+    virtual Ref&lt;MediaConstraint&gt; copy() const;
</ins><span class="cx">     virtual bool isEmpty() const = 0;
</span><span class="cx">     virtual bool isMandatory() const = 0;
</span><span class="cx"> 
</span><span class="lines">@@ -57,6 +61,7 @@
</span><span class="cx">     virtual bool getIdeal(int&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual bool validForRange(int, int) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual int find(std::function&lt;bool(ConstraintType, int)&gt;) const { ASSERT_NOT_REACHED(); return 0; }
</span><ins>+    virtual double fitnessDistance(int, int) const { ASSERT_NOT_REACHED(); return 0; }
</ins><span class="cx"> 
</span><span class="cx">     virtual bool getMin(double&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual bool getMax(double&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="lines">@@ -64,11 +69,13 @@
</span><span class="cx">     virtual bool getIdeal(double&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual bool validForRange(double, double) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual double find(std::function&lt;bool(ConstraintType, double)&gt;) const { ASSERT_NOT_REACHED(); return 0; }
</span><ins>+    virtual double fitnessDistance(double, double) const { ASSERT_NOT_REACHED(); return 0; }
</ins><span class="cx"> 
</span><span class="cx">     virtual bool getMin(bool&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual bool getMax(bool&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual bool getExact(bool&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual bool getIdeal(bool&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><ins>+    virtual double fitnessDistance(bool) const { ASSERT_NOT_REACHED(); return 0; }
</ins><span class="cx"> 
</span><span class="cx">     virtual bool getMin(Vector&lt;String&gt;&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual bool getMax(Vector&lt;String&gt;&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="lines">@@ -76,6 +83,11 @@
</span><span class="cx">     virtual bool getIdeal(Vector&lt;String&gt;&amp;) const { ASSERT_NOT_REACHED(); return false; }
</span><span class="cx">     virtual const String&amp; find(std::function&lt;bool(ConstraintType, const String&amp;)&gt;) const { ASSERT_NOT_REACHED(); return emptyString(); }
</span><span class="cx"> 
</span><ins>+    virtual double fitnessDistance(const String&amp;) const { ASSERT_NOT_REACHED(); return 0; }
+    virtual double fitnessDistance(const Vector&lt;String&gt;&amp;) const { ASSERT_NOT_REACHED(); return 0; }
+
+    virtual void merge(const MediaConstraint&amp;) { ASSERT_NOT_REACHED(); }
+
</ins><span class="cx">     MediaConstraintType type() const { return m_type; }
</span><span class="cx">     const String&amp; name() const { return m_name; }
</span><span class="cx"> 
</span><span class="lines">@@ -134,19 +146,99 @@
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    bool nearlyEqual(double a, double b) const
+    {
+        // Don't require strict equality when comparing constraints, or many floating point constraint values,
+        // e.g. &quot;aspectRatio: 1.333&quot;, will never match.
+        const double epsilon = 0.00001;
+        return std::abs(a - b) &lt;= epsilon;
+    }
+
+    double fitnessDistance(ValueType rangeMin, ValueType rangeMax) const final {
+        // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
+        // 1. If the constraint is not supported by the browser, the fitness distance is 0.
+        if (isEmpty())
+            return 0;
+
+        // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
+        //    dictionary's value for the constraint does not satisfy the constraint, the
+        //    fitness distance is positive infinity.
+        bool valid = validForRange(rangeMin, rangeMax);
+        if (m_exact &amp;&amp; !valid)
+            return std::numeric_limits&lt;double&gt;::infinity();
+
+        if (m_min &amp;&amp; !valid)
+            return std::numeric_limits&lt;double&gt;::infinity();
+
+        if (m_max &amp;&amp; !valid)
+            return std::numeric_limits&lt;double&gt;::infinity();
+
+        // 3. If no ideal value is specified, the fitness distance is 0.
+        if (!m_ideal)
+            return 0;
+
+        // 4. For all positive numeric non-required constraints (such as height, width, frameRate,
+        //    aspectRatio, sampleRate and sampleSize), the fitness distance is the result of the formula
+        //
+        //         (actual == ideal) ? 0 : |actual - ideal| / max(|actual|,|ideal|)
+        ValueType ideal = m_ideal.value();
+        if (ideal &gt;= rangeMin &amp;&amp; ideal &lt;= rangeMax)
+            return 0;
+
+        ideal = ideal &gt; std::max(rangeMin, rangeMax) ? rangeMax : rangeMin;
+        return static_cast&lt;double&gt;(std::abs(ideal - m_ideal.value())) / std::max(std::abs(ideal), std::abs(m_ideal.value()));
+    }
+
+    void merge(const MediaConstraint&amp; other) final {
+        if (other.isEmpty())
+            return;
+
+        ValueType value;
+        if (other.getExact(value))
+            m_exact = value;
+
+        if (other.getMin(value))
+            m_min = value;
+
+        if (other.getMax(value))
+            m_max = value;
+
+        // https://w3c.github.io/mediacapture-main/#constrainable-interface
+        // When processing advanced constraints:
+        //   ... the User Agent must attempt to apply, individually, any 'ideal' constraints or
+        //   a constraint given as a bare value for the property. Of these properties, it must
+        //   satisfy the largest number that it can, in any order.
+        if (other.getIdeal(value)) {
+            if (!m_ideal || value &gt; m_ideal.value())
+                m_ideal = value;
+        }
+    }
+
</ins><span class="cx">     bool validForRange(ValueType rangeMin, ValueType rangeMax) const final {
</span><span class="cx">         if (isEmpty())
</span><span class="cx">             return false;
</span><span class="cx"> 
</span><del>-        if (m_exact &amp;&amp; (m_exact.value() &lt; rangeMin || m_exact.value() &gt; rangeMax))
-            return false;
</del><ins>+        if (m_exact) {
+            const ValueType exact = m_exact.value();
+            if (exact &lt; rangeMin &amp;&amp; !nearlyEqual(exact, rangeMin))
+                return false;
+            if (exact &gt; rangeMax &amp;&amp; !nearlyEqual(exact, rangeMax))
+                return false;
+        }
</ins><span class="cx"> 
</span><del>-        if (m_min &amp;&amp; m_min.value() &gt; rangeMax)
-            return false;
</del><ins>+        if (m_min) {
+            const ValueType constraintMin = m_min.value();
+            if (constraintMin &gt; rangeMax &amp;&amp; !nearlyEqual(constraintMin, rangeMax))
+                return false;
+        }
</ins><span class="cx"> 
</span><del>-        if (m_max &amp;&amp; m_max.value() &lt; rangeMin)
-            return false;
</del><span class="cx"> 
</span><ins>+        if (m_max) {
+            const ValueType constraintMax = m_max.value();
+            if (constraintMax &lt; rangeMin &amp;&amp; !nearlyEqual(constraintMax, rangeMin))
+                return false;
+        }
+
</ins><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -173,7 +265,6 @@
</span><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-private:
</del><span class="cx">     Optional&lt;ValueType&gt; m_min;
</span><span class="cx">     Optional&lt;ValueType&gt; m_max;
</span><span class="cx">     Optional&lt;ValueType&gt; m_exact;
</span><span class="lines">@@ -184,6 +275,8 @@
</span><span class="cx"> public:
</span><span class="cx">     static Ref&lt;IntConstraint&gt; create(const String&amp; name, MediaConstraintType type) { return adoptRef(*new IntConstraint(name, type)); }
</span><span class="cx"> 
</span><ins>+    Ref&lt;MediaConstraint&gt; copy() const final;
+
</ins><span class="cx"> private:
</span><span class="cx">     explicit IntConstraint(const String&amp; name, MediaConstraintType type)
</span><span class="cx">         : NumericConstraint&lt;int&gt;(name, type)
</span><span class="lines">@@ -195,6 +288,8 @@
</span><span class="cx"> public:
</span><span class="cx">     static Ref&lt;DoubleConstraint&gt; create(const String&amp; name, MediaConstraintType type) { return adoptRef(*new DoubleConstraint(name, type)); }
</span><span class="cx"> 
</span><ins>+    Ref&lt;MediaConstraint&gt; copy() const final;
+
</ins><span class="cx"> private:
</span><span class="cx">     explicit DoubleConstraint(const String&amp; name, MediaConstraintType type)
</span><span class="cx">         : NumericConstraint&lt;double&gt;(name, type)
</span><span class="lines">@@ -206,6 +301,8 @@
</span><span class="cx"> public:
</span><span class="cx">     static Ref&lt;BooleanConstraint&gt; create(const String&amp; name, MediaConstraintType type) { return adoptRef(*new BooleanConstraint(name, type)); }
</span><span class="cx"> 
</span><ins>+    Ref&lt;MediaConstraint&gt; copy() const final;
+
</ins><span class="cx">     void setExact(bool value) { m_exact = value; }
</span><span class="cx">     void setIdeal(bool value) { m_ideal = value; }
</span><span class="cx"> 
</span><span class="lines">@@ -215,6 +312,42 @@
</span><span class="cx">     bool isEmpty() const final { return !m_exact &amp;&amp; !m_ideal; };
</span><span class="cx">     bool isMandatory() const final { return bool(m_exact); }
</span><span class="cx"> 
</span><ins>+    double fitnessDistance(bool value) const final {
+        // https://w3c.github.io/mediacapture-main/#dfn-applyconstraints
+        // 1. If the constraint is not supported by the browser, the fitness distance is 0.
+        if (isEmpty())
+            return 0;
+
+        // 2. If the constraint is required ('min', 'max', or 'exact'), and the settings
+        //    dictionary's value for the constraint does not satisfy the constraint, the
+        //    fitness distance is positive infinity.
+        if (m_exact &amp;&amp; value != m_exact.value())
+            return std::numeric_limits&lt;double&gt;::infinity();
+
+        // 3. If no ideal value is specified, the fitness distance is 0.
+        if (!m_ideal || m_ideal.value() == value)
+            return 0;
+
+        // 5. For all string and enum non-required constraints (deviceId, groupId, facingMode,
+        // echoCancellation), the fitness distance is the result of the formula
+        //        (actual == ideal) ? 0 : 1
+        return 1;
+    }
+
+    void merge(const MediaConstraint&amp; other) final {
+        if (other.isEmpty())
+            return;
+
+        bool value;
+        if (other.getExact(value))
+            m_exact = value;
+
+        if (other.getIdeal(value)) {
+            if (!m_ideal || (value &amp;&amp; !m_ideal.value()))
+                m_ideal = value;
+        }
+    }
+
</ins><span class="cx"> private:
</span><span class="cx">     explicit BooleanConstraint(const String&amp; name, MediaConstraintType type)
</span><span class="cx">         : MediaConstraint(name, type)
</span><span class="lines">@@ -229,6 +362,8 @@
</span><span class="cx"> public:
</span><span class="cx">     static Ref&lt;StringConstraint&gt; create(const String&amp; name, MediaConstraintType type) { return adoptRef(*new StringConstraint(name, type)); }
</span><span class="cx"> 
</span><ins>+    Ref&lt;MediaConstraint&gt; copy() const final;
+
</ins><span class="cx">     void setExact(const String&amp;);
</span><span class="cx">     void appendExact(const String&amp;);
</span><span class="cx">     void setIdeal(const String&amp;);
</span><span class="lines">@@ -240,8 +375,12 @@
</span><span class="cx">     bool isEmpty() const final { return m_exact.isEmpty() &amp;&amp; m_ideal.isEmpty(); }
</span><span class="cx">     bool isMandatory() const final { return !m_exact.isEmpty(); }
</span><span class="cx"> 
</span><del>-    const String&amp; find(std::function&lt;bool(ConstraintType, const String&amp;)&gt;) const override;
</del><ins>+    double fitnessDistance(const String&amp;) const final;
+    double fitnessDistance(const Vector&lt;String&gt;&amp;) const final;
</ins><span class="cx"> 
</span><ins>+    const String&amp; find(std::function&lt;bool(ConstraintType, const String&amp;)&gt;) const final;
+    void merge(const MediaConstraint&amp;) final;
+
</ins><span class="cx"> private:
</span><span class="cx">     explicit StringConstraint(const String&amp; name, MediaConstraintType type)
</span><span class="cx">         : MediaConstraint(name, type)
</span><span class="lines">@@ -268,6 +407,22 @@
</span><span class="cx"> 
</span><span class="cx"> using MediaTrackConstraintSetMap = HashMap&lt;String, RefPtr&lt;MediaConstraint&gt;&gt;;
</span><span class="cx"> 
</span><ins>+class FlattenedConstraint {
+public:
+    typedef Vector&lt;RefPtr&lt;MediaConstraint&gt;&gt;::const_iterator const_iterator;
+
+    void set(const MediaConstraint&amp;);
+    void merge(const MediaConstraint&amp;);
+    bool isEmpty() const { return m_constraints.isEmpty(); }
+
+    const_iterator begin() const { return m_constraints.begin(); }
+    const_iterator end() const { return m_constraints.end(); }
+
+private:
+
+    Vector&lt;RefPtr&lt;MediaConstraint&gt;&gt; m_constraints;
+};
+
</ins><span class="cx"> class MediaConstraints : public RefCounted&lt;MediaConstraints&gt; {
</span><span class="cx"> public:
</span><span class="cx">     virtual ~MediaConstraints() { }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamRealtimeMediaSourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -150,7 +150,7 @@
</span><span class="cx">     stop(callingObserver);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-RealtimeMediaSource::ConstraintSupport RealtimeMediaSource::supportsConstraint(const MediaConstraint&amp; constraint)
</del><ins>+double RealtimeMediaSource::fitnessDistance(const MediaConstraint&amp; constraint)
</ins><span class="cx"> {
</span><span class="cx">     RealtimeMediaSourceCapabilities&amp; capabilities = *this-&gt;capabilities();
</span><span class="cx"> 
</span><span class="lines">@@ -157,137 +157,102 @@
</span><span class="cx">     switch (constraint.type()) {
</span><span class="cx">     case MediaConstraintType::Width: {
</span><span class="cx">         if (!capabilities.supportsWidth())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><del>-        auto widthRange = capabilities.width();
-        return constraint.validForRange(widthRange.rangeMin().asInt, widthRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+        auto range = capabilities.width();
+        return constraint.fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::Height: {
</span><span class="cx">         if (!capabilities.supportsHeight())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><del>-        auto heightRange = capabilities.height();
-        return constraint.validForRange(heightRange.rangeMin().asInt, heightRange.rangeMax().asInt) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+        auto range = capabilities.height();
+        return constraint.fitnessDistance(range.rangeMin().asInt, range.rangeMax().asInt);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::FrameRate: {
</span><span class="cx">         if (!capabilities.supportsFrameRate())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><del>-        auto rateRange = capabilities.frameRate();
-        return constraint.validForRange(rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+        auto range = capabilities.frameRate();
+        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::AspectRatio: {
</span><span class="cx">         if (!capabilities.supportsAspectRatio())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><span class="cx">         auto range = capabilities.aspectRatio();
</span><del>-        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::Volume: {
</span><span class="cx">         if (!capabilities.supportsVolume())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><span class="cx">         auto range = capabilities.volume();
</span><del>-        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::SampleRate: {
</span><span class="cx">         if (!capabilities.supportsSampleRate())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><span class="cx">         auto range = capabilities.sampleRate();
</span><del>-        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::SampleSize: {
</span><span class="cx">         if (!capabilities.supportsSampleSize())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><span class="cx">         auto range = capabilities.sampleSize();
</span><del>-        return constraint.validForRange(range.rangeMin().asDouble, range.rangeMax().asDouble) ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
</del><ins>+        return constraint.fitnessDistance(range.rangeMin().asDouble, range.rangeMax().asDouble);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::FacingMode: {
</span><span class="cx">         if (!capabilities.supportsFacingMode())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><del>-        ConstraintSupport support = ConstraintSupport::Ignored;
-        auto&amp; supportedModes = capabilities.facingMode();
-        std::function&lt;bool(MediaConstraint::ConstraintType, const String&amp;)&gt; filter = [supportedModes, &amp;support](MediaConstraint::ConstraintType type, const String&amp; modeString) {
-            if (type == MediaConstraint::ConstraintType::ExactConstraint)
-                support = ConstraintSupport::Unsupported;
-
-            auto mode = RealtimeMediaSourceSettings::videoFacingModeEnum(modeString);
-            for (auto&amp; supportedMode : supportedModes) {
-                if (supportedMode == mode) {
-                    support = ConstraintSupport::Supported;
-                    break;
-                }
-            }
-
-            return type == MediaConstraint::ConstraintType::ExactConstraint ? true : false;
-        };
-
-        constraint.find(filter);
-        return support;
</del><ins>+        auto&amp; modes = capabilities.facingMode();
+        Vector&lt;String&gt; supportedModes;
+        supportedModes.reserveInitialCapacity(modes.size());
+        for (auto&amp; mode : modes)
+            supportedModes.append(RealtimeMediaSourceSettings::facingMode(mode));
+        return constraint.fitnessDistance(supportedModes);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    case MediaConstraintType::EchoCancellation:
</del><ins>+    case MediaConstraintType::EchoCancellation: {
</ins><span class="cx">         if (!capabilities.supportsEchoCancellation())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><del>-        if (capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadOnly)
-            return constraint.isMandatory() ? ConstraintSupport::Unsupported : ConstraintSupport::Ignored;
-
-        return ConstraintSupport::Supported;
</del><ins>+        bool echoCancellationReadWrite = capabilities.echoCancellation() == RealtimeMediaSourceCapabilities::EchoCancellation::ReadWrite;
+        return constraint.fitnessDistance(echoCancellationReadWrite);
</ins><span class="cx">         break;
</span><ins>+    }
</ins><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::DeviceId: {
</span><span class="cx">         if (!capabilities.supportsDeviceId())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><del>-        ConstraintSupport support = ConstraintSupport::Ignored;
-        std::function&lt;bool(MediaConstraint::ConstraintType, const String&amp;)&gt; filter = [this, &amp;support](MediaConstraint::ConstraintType type, const String&amp; idString) {
-            if (type != MediaConstraint::ConstraintType::ExactConstraint)
-                return false; // Keep looking.
-
-            support = idString == m_id ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
-            return false;
-        };
-
-        constraint.find(filter);
-        return support;
</del><ins>+        return constraint.fitnessDistance(m_id);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     case MediaConstraintType::GroupId: {
</span><span class="cx">         if (!capabilities.supportsDeviceId())
</span><del>-            return ConstraintSupport::Ignored;
</del><ins>+            return 0;
</ins><span class="cx"> 
</span><del>-        ConstraintSupport support = ConstraintSupport::Ignored;
-        String groupId = settings().groupId();
-        std::function&lt;bool(MediaConstraint::ConstraintType, const String&amp;)&gt; filter = [groupId, &amp;support](MediaConstraint::ConstraintType type, const String&amp; idString) {
-            if (type != MediaConstraint::ConstraintType::ExactConstraint)
-                return false; // Keep looking.
-
-            support = idString == groupId ? ConstraintSupport::Supported : ConstraintSupport::Unsupported;
-            return false;
-        };
-
-        constraint.find(filter);
-        return support;
</del><ins>+        return constraint.fitnessDistance(settings().groupId());
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -296,39 +261,50 @@
</span><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return ConstraintSupport::Ignored;
</del><ins>+    return 0;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-
-template &lt;typename T&gt;
-T value(const MediaConstraint&amp; constraint, T rangeMin, T rangeMax)
</del><ins>+template &lt;typename ValueType&gt;
+static void applyNumericConstraint(const MediaConstraint&amp; constraint, ValueType current, ValueType capabilityMin, ValueType capabilityMax, RealtimeMediaSource* source, void (RealtimeMediaSource::*function)(ValueType))
</ins><span class="cx"> {
</span><del>-    T result;
</del><ins>+    ValueType value;
+    ValueType min = capabilityMin;
+    ValueType max = capabilityMax;
</ins><span class="cx"> 
</span><del>-    if (constraint.getExact(result)) {
-        ASSERT(result &gt;= rangeMin &amp;&amp; result &lt;= rangeMax);
-        return result;
</del><ins>+    if (constraint.getExact(value)) {
+        ASSERT(constraint.validForRange(capabilityMin, capabilityMax));
+        (source-&gt;*function)(value);
+        return;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (constraint.getIdeal(result)) {
-        if (result &lt; rangeMin)
-            result = rangeMin;
-        else if (result &gt; rangeMax)
-            result = rangeMax;
</del><ins>+    if (constraint.getMin(value)) {
+        ASSERT(constraint.validForRange(value, capabilityMax));
+        if (value &gt; min)
+            min = value;
</ins><span class="cx"> 
</span><del>-        return result;
</del><ins>+        // If there is no ideal, don't change if minimum is smaller than current.
+        ValueType ideal;
+        if (!constraint.getIdeal(ideal) &amp;&amp; value &lt; current)
+            value = current;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (constraint.getMin(result) &amp;&amp; result &gt; rangeMax)
-        return false;
</del><ins>+    if (constraint.getMax(value)) {
+        ASSERT(constraint.validForRange(capabilityMin, value));
+        if (value &lt; max)
+            max = value;
+    }
</ins><span class="cx"> 
</span><del>-    if (constraint.getMax(result) &amp;&amp; result &lt; rangeMin)
-        return false;
-    
-    return result;
</del><ins>+    if (constraint.getIdeal(value)) {
+        if (value &lt; min)
+            value = min;
+        if (value &gt; max)
+            value = max;
+    }
+
+    if (value != current)
+        (source-&gt;*function)(value);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-
</del><span class="cx"> void RealtimeMediaSource::applyConstraint(const MediaConstraint&amp; constraint)
</span><span class="cx"> {
</span><span class="cx">     RealtimeMediaSourceCapabilities&amp; capabilities = *this-&gt;capabilities();
</span><span class="lines">@@ -337,8 +313,8 @@
</span><span class="cx">         if (!capabilities.supportsWidth())
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        auto widthRange = capabilities.width();
-        setWidth(value(constraint, widthRange.rangeMin().asInt, widthRange.rangeMax().asInt));
</del><ins>+        auto range = capabilities.width();
+        applyNumericConstraint(constraint, size().width(), range.rangeMin().asInt, range.rangeMax().asInt, this, &amp;RealtimeMediaSource::setWidth);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -346,8 +322,8 @@
</span><span class="cx">         if (!capabilities.supportsHeight())
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        auto heightRange = capabilities.height();
-        setHeight(value(constraint, heightRange.rangeMin().asInt, heightRange.rangeMax().asInt));
</del><ins>+        auto range = capabilities.height();
+        applyNumericConstraint(constraint, size().height(), range.rangeMin().asInt, range.rangeMax().asInt, this, &amp;RealtimeMediaSource::setHeight);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -355,8 +331,8 @@
</span><span class="cx">         if (!capabilities.supportsFrameRate())
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        auto rateRange = capabilities.frameRate();
-        setFrameRate(value(constraint, rateRange.rangeMin().asDouble, rateRange.rangeMax().asDouble));
</del><ins>+        auto range = capabilities.frameRate();
+        applyNumericConstraint(constraint, frameRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &amp;RealtimeMediaSource::setFrameRate);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -365,7 +341,7 @@
</span><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         auto range = capabilities.aspectRatio();
</span><del>-        setAspectRatio(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+        applyNumericConstraint(constraint, aspectRatio(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &amp;RealtimeMediaSource::setAspectRatio);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -374,8 +350,7 @@
</span><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         auto range = capabilities.volume();
</span><del>-        // std::pair&lt;T, T&gt; valuesForRange(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble)
-        setVolume(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+        applyNumericConstraint(constraint, volume(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &amp;RealtimeMediaSource::setVolume);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -384,7 +359,7 @@
</span><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         auto range = capabilities.sampleRate();
</span><del>-        setSampleRate(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+        applyNumericConstraint(constraint, sampleRate(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &amp;RealtimeMediaSource::setSampleRate);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -393,7 +368,7 @@
</span><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         auto range = capabilities.sampleSize();
</span><del>-        setSampleSize(value(constraint, range.rangeMin().asDouble, range.rangeMax().asDouble));
</del><ins>+        applyNumericConstraint(constraint, sampleSize(), range.rangeMin().asDouble, range.rangeMax().asDouble, this, &amp;RealtimeMediaSource::setSampleSize);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -436,22 +411,101 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void RealtimeMediaSource::applyConstraints(const MediaConstraints&amp; constraints, SuccessHandler successHandler, FailureHandler failureHandler)
</del><ins>+bool RealtimeMediaSource::selectSettings(const MediaConstraints&amp; constraints, FlattenedConstraint&amp; candidates, String&amp; failedConstraint)
</ins><span class="cx"> {
</span><del>-    ASSERT(constraints.isValid());
</del><ins>+    // https://w3c.github.io/mediacapture-main/#dfn-selectsettings
+    //
+    // 1. Each constraint specifies one or more values (or a range of values) for its property.
+    //    A property may appear more than once in the list of 'advanced' ConstraintSets. If an
+    //    empty object or list has been given as the value for a constraint, it must be interpreted
+    //    as if the constraint were not specified (in other words, an empty constraint == no constraint).
+    //
+    //    Note that unknown properties are discarded by WebIDL, which means that unknown/unsupported required
+    //    constraints will silently disappear. To avoid this being a surprise, application authors are
+    //    expected to first use the getSupportedConstraints() method as shown in the Examples below.
</ins><span class="cx"> 
</span><ins>+    // 2. Let object be the ConstrainablePattern object on which this algorithm is applied. Let copy be an
+    //    unconstrained copy of object (i.e., copy should behave as if it were object with all ConstraintSets
+    //    removed.)
+
+    // 3. For every possible settings dictionary of copy compute its fitness distance, treating bare values of
+    //    properties as ideal values. Let candidates be the set of settings dictionaries for which the fitness
+    //    distance is finite.
</ins><span class="cx">     auto&amp; mandatoryConstraints = constraints.mandatoryConstraints();
</span><span class="cx">     for (auto&amp; nameConstraintPair : mandatoryConstraints) {
</span><del>-        auto&amp; constraint = *nameConstraintPair.value;
-        if (supportsConstraint(constraint) == ConstraintSupport::Unsupported) {
-            failureHandler(constraint.name(), &quot;Constraint not supported&quot;);
-            return;
</del><ins>+        const auto&amp; constraint = nameConstraintPair.value;
+        if (fitnessDistance(*constraint) == std::numeric_limits&lt;double&gt;::infinity()) {
+            failedConstraint = constraint-&gt;name();
+            return false;
</ins><span class="cx">         }
</span><ins>+
+        candidates.set(*constraint);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    for (auto&amp; nameConstraintPair : mandatoryConstraints)
-        applyConstraint(*nameConstraintPair.value);
</del><ins>+    // 4. If candidates is empty, return undefined as the result of the SelectSettings() algorithm.
+    if (candidates.isEmpty())
+        return true;
</ins><span class="cx"> 
</span><ins>+    // 5. Iterate over the 'advanced' ConstraintSets in newConstraints in the order in which they were specified.
+    //    For each ConstraintSet:
+
+    // 5.1 compute the fitness distance between it and each settings dictionary in candidates, treating bare
+    //     values of properties as exact.
+    Vector&lt;std::pair&lt;double, MediaTrackConstraintSetMap&gt;&gt; supportedConstraints;
+    double minimumDistance = std::numeric_limits&lt;double&gt;::infinity();
+    for (const auto&amp; advancedConstraint : constraints.advancedConstraints()) {
+        double constraintDistance = 0;
+        bool supported = false;
+        for (auto&amp; nameConstraintPair : advancedConstraint) {
+            double distance = fitnessDistance(*nameConstraintPair.value);
+            constraintDistance += distance;
+            if (distance != std::numeric_limits&lt;double&gt;::infinity())
+                supported = true;
+        }
+
+        if (constraintDistance &lt; minimumDistance)
+            minimumDistance = constraintDistance;
+
+        // 5.2 If the fitness distance is finite for one or more settings dictionaries in candidates, keep those
+        //     settings dictionaries in candidates, discarding others.
+        //     If the fitness distance is infinite for all settings dictionaries in candidates, ignore this ConstraintSet.
+        if (supported)
+            supportedConstraints.append({constraintDistance, advancedConstraint});
+    }
+
+    // 6. Select one settings dictionary from candidates, and return it as the result of the SelectSettings() algorithm.
+    //    The UA should use the one with the smallest fitness distance, as calculated in step 3.
+    if (minimumDistance != std::numeric_limits&lt;double&gt;::infinity()) {
+        supportedConstraints.removeAllMatching( [&amp;] (std::pair&lt;double, MediaTrackConstraintSetMap&gt; pair) -&gt; bool {
+            return pair.first &gt; minimumDistance;
+        });
+
+        if (!supportedConstraints.isEmpty()) {
+            auto&amp; advancedConstraint = supportedConstraints[0].second;
+            for (auto&amp; nameConstraintPair : advancedConstraint)
+                candidates.merge(*nameConstraintPair.value);
+        }
+    }
+
+    return true;
+}
+
+
+void RealtimeMediaSource::applyConstraints(const MediaConstraints&amp; constraints, SuccessHandler successHandler, FailureHandler failureHandler)
+{
+    ASSERT(constraints.isValid());
+
+    FlattenedConstraint candidates;
+
+    String failedConstraint;
+    if (!selectSettings(constraints, candidates, failedConstraint)) {
+        failureHandler(failedConstraint, &quot;Constraint not supported&quot;);
+        return;
+    }
+
+    for (auto constraint : candidates)
+        applyConstraint(*constraint);
+
</ins><span class="cx">     successHandler();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformmediastreamRealtimeMediaSourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h (205573 => 205574)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h        2016-09-08 00:00:40 UTC (rev 205573)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h        2016-09-08 00:01:29 UTC (rev 205574)
</span><span class="lines">@@ -42,7 +42,6 @@
</span><span class="cx"> #include &quot;MediaSample.h&quot;
</span><span class="cx"> #include &quot;PlatformLayer.h&quot;
</span><span class="cx"> #include &quot;RealtimeMediaSourceCapabilities.h&quot;
</span><del>-#include &lt;wtf/Lock.h&gt;
</del><span class="cx"> #include &lt;wtf/RefCounted.h&gt;
</span><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> #include &lt;wtf/WeakPtr.h&gt;
</span><span class="lines">@@ -171,13 +170,12 @@
</span><span class="cx"> private:
</span><span class="cx">     WeakPtr&lt;RealtimeMediaSource&gt; createWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
</span><span class="cx"> 
</span><del>-    enum ConstraintSupport { Ignored, Supported, Unsupported };
-    ConstraintSupport supportsConstraint(const MediaConstraint&amp;);
</del><ins>+    bool supportsConstraints(const MediaTrackConstraintSetMap&amp;);
+    bool selectSettings(const MediaConstraints&amp;, FlattenedConstraint&amp;, String&amp;);
+    double fitnessDistance(const MediaConstraint&amp;);
</ins><span class="cx">     void applyConstraint(const MediaConstraint&amp;);
</span><span class="cx"> 
</span><span class="cx">     WeakPtrFactory&lt;RealtimeMediaSource&gt; m_weakPtrFactory;
</span><del>-    Lock m_lock;
-
</del><span class="cx">     String m_id;
</span><span class="cx">     String m_persistentID;
</span><span class="cx">     Type m_type;
</span></span></pre>
</div>
</div>

</body>
</html>