<!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>[161120] trunk/Tools</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/161120">161120</a></dd>
<dt>Author</dt> <dd>ap@apple.com</dd>
<dt>Date</dt> <dd>2013-12-29 09:24:57 -0800 (Sun, 29 Dec 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Please display information about pending runs in build.webkit.org/dashboard
https://bugs.webkit.org/show_bug.cgi?id=122180

Reviewed by Timothy Hatcher.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotBuilderQueueView.js:
(BuildbotBuilderQueueView.prototype.update.appendBuilderQueueStatus): Added a semicolon at the end of a line.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js:
(BuildbotQueueView.prototype._latestFinishedIteration): Factored out of _appendPendingRevisionCount.
(BuildbotQueueView.prototype._appendPendingRevisionCount): Install a popover tracker over the element.
(BuildbotQueueView.prototype.lineForCommit): Build an element for a particular commit ot be shown in popover.
(BuildbotQueueView.prototype.presentPopoverForElement): Build and show popover content when PopoverTracker
asks us to.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Geometry.js: Added.
Taken from WebInspector with minimal changes:
- Changed root name from WebInspector to Dashboard.
- Removed some unused functionality.
- Added Rect.containsPoint.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Popover.js: Added.
Popover has extensive changes compared to WebInspector version, only drawing code is the same:
- Fixed to work in scrollable pages - WebInspector version assumes that window
and document coordinates are the same, and also erroneously dismisses a scrollable
popover if scrolling cascades out of it after reaching a bound.
- Simplified API and implementation to Dashboard needs, it is no longer possible to
change content of an existing popover.
- Rewrote visibility tracking to be more complete, and not rely on external tracker
object so much.
- Added code to flash scroll bars when showing a scrollable popover.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/PopoverTracker.js: Added.
Objects of this class show and hide popovers as appropriate for registered active elements.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/StatusLineView.js:
(StatusLineView.prototype.get messageElement): Added an accessor, so that we could
install a popover on message element. It's the only visible element in pending commit
line, but the line has different bounds, so we can't install a popover on it (it
would be incorrectly positioned if we did).

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Trac.js:
(Trac.prototype._convertCommitInfoElementToObject):
- Some trac installations report author in a different element, updated to support that.
- Changed to parse title out of description, because trac titles are ugly. Also,
we get a nice HTML with links from the description.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Utilities.js:
(Node.prototype.isAncestor): Copied from Web Inspector, only changing the form for
consistency with the rest of this file (add a property on prototype with assignment
instead of using Object.defineProperty).
(Node.prototype.isDescendant): Ditto.
(Node.prototype.isSelfOrAncestor): Ditto.
(Node.prototype.isSelfOrDescendant): Ditto.
(DOMTokenList.prototype.contains): Ditto.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Popover.css: Added.
Like JS counterpart, mostly lifted from Web Inspector.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css:
Added style rules for pending commits popover.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/StatusLineView.css:
(.status-line.no-bubble .message): Changed to display:inline-block, so that it fits
to content, and we can show the popover in a correct place.

* BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html: Added new files.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotBuilderQueueViewjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotBuilderQueueView.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotQueueViewjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsStatusLineViewjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/StatusLineView.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsTracjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Trac.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsUtilitiesjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Utilities.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesQueueViewcss">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesStatusLineViewcss">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/StatusLineView.css</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardindexhtml">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsGeometryjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Geometry.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsPopoverjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Popover.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsPopoverTrackerjs">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/PopoverTracker.js</a></li>
<li><a href="#trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesPopovercss">trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Popover.css</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotBuilderQueueViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotBuilderQueueView.js (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotBuilderQueueView.js        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotBuilderQueueView.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -60,7 +60,7 @@
</span><span class="cx"> 
</span><span class="cx">         function appendBuilderQueueStatus(queue)
</span><span class="cx">         {
</span><del>-            this._appendPendingRevisionCount(queue)
</del><ins>+            this._appendPendingRevisionCount(queue);
</ins><span class="cx"> 
</span><span class="cx">             var firstRecentFailedIteration = queue.firstRecentFailedIteration;
</span><span class="cx">             if (firstRecentFailedIteration &amp;&amp; firstRecentFailedIteration.loaded) {
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsBuildbotQueueViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -60,42 +60,116 @@
</span><span class="cx">     constructor: BuildbotQueueView,
</span><span class="cx">     __proto__: QueueView.prototype,
</span><span class="cx"> 
</span><del>-    _appendPendingRevisionCount: function(queue)
</del><ins>+    _latestFinishedIteration: function(queue)
</ins><span class="cx">     {
</span><span class="cx">         for (var i = 0; i &lt; queue.iterations.length; ++i) {
</span><span class="cx">             var iteration = queue.iterations[i];
</span><span class="cx">             if (!iteration.loaded || !iteration.finished)
</span><span class="cx">                 continue;
</span><ins>+            return iteration;
+        }
+        return null;
+    },
</ins><span class="cx"> 
</span><del>-            var latestRecordedOpenSourceRevisionNumber = webkitTrac.latestRecordedRevisionNumber;
-            if (!latestRecordedOpenSourceRevisionNumber)
-                return;
</del><ins>+    _appendPendingRevisionCount: function(queue)
+    {
+        var latestFinishedIteration = this._latestFinishedIteration(queue);
+        if (!latestFinishedIteration)
+            return;
</ins><span class="cx"> 
</span><del>-            var openSourceRevisionsBehind = latestRecordedOpenSourceRevisionNumber - iteration.openSourceRevision;
-            if (openSourceRevisionsBehind &lt; 0)
-                openSourceRevisionsBehind = 0;
</del><ins>+        var latestRecordedOpenSourceRevisionNumber = webkitTrac.latestRecordedRevisionNumber;
+        if (!latestRecordedOpenSourceRevisionNumber)
+            return;
</ins><span class="cx"> 
</span><del>-            if (iteration.internalRevision) {
-                var latestRecordedInternalRevisionNumber = internalTrac.latestRecordedRevisionNumber;
-                if (!latestRecordedInternalRevisionNumber)
-                    return;
</del><ins>+        var openSourceRevisionsBehind = latestRecordedOpenSourceRevisionNumber - latestFinishedIteration.openSourceRevision;
+        if (openSourceRevisionsBehind &lt; 0)
+            openSourceRevisionsBehind = 0;
</ins><span class="cx"> 
</span><del>-                var internalRevisionsBehind = latestRecordedInternalRevisionNumber - iteration.internalRevision;
-                if (internalRevisionsBehind &lt; 0)
-                    internalRevisionsBehind = 0;
-                if (openSourceRevisionsBehind || internalRevisionsBehind) {
-                    var message = openSourceRevisionsBehind + &quot; \uff0b &quot; + internalRevisionsBehind + &quot; revisions behind&quot;;
-                    var status = new StatusLineView(message, StatusLineView.Status.NoBubble);
-                    this.element.appendChild(status.element);
-                }
-            } else if (openSourceRevisionsBehind) {
-                var message = openSourceRevisionsBehind + &quot; &quot; + (openSourceRevisionsBehind === 1 ? &quot;revision behind&quot; : &quot;revisions behind&quot;);
</del><ins>+        if (latestFinishedIteration.internalRevision) {
+            var latestRecordedInternalRevisionNumber = internalTrac.latestRecordedRevisionNumber;
+            if (!latestRecordedInternalRevisionNumber)
+                return;
+
+            var internalRevisionsBehind = latestRecordedInternalRevisionNumber - latestFinishedIteration.internalRevision;
+            if (internalRevisionsBehind &lt; 0)
+                internalRevisionsBehind = 0;
+            if (openSourceRevisionsBehind || internalRevisionsBehind) {
+                var message = openSourceRevisionsBehind + &quot; \uff0b &quot; + internalRevisionsBehind + &quot; revisions behind&quot;;
</ins><span class="cx">                 var status = new StatusLineView(message, StatusLineView.Status.NoBubble);
</span><span class="cx">                 this.element.appendChild(status.element);
</span><span class="cx">             }
</span><ins>+        } else if (openSourceRevisionsBehind) {
+            var message = openSourceRevisionsBehind + &quot; &quot; + (openSourceRevisionsBehind === 1 ? &quot;revision behind&quot; : &quot;revisions behind&quot;);
+            var status = new StatusLineView(message, StatusLineView.Status.NoBubble);
+            this.element.appendChild(status.element);
+        }
</ins><span class="cx"> 
</span><del>-            return;
</del><ins>+        if (status)
+            new PopoverTracker(status.messageElement, this, queue);
+    },
+
+    presentPopoverForElement: function(element, popover, queue)
+    {
+        var latestFinishedIteration = this._latestFinishedIteration(queue);
+        if (!latestFinishedIteration)
+            return false;
+
+        function lineForCommit(trac, commit)
+        {
+            var result = document.createElement(&quot;div&quot;);
+            result.className = &quot;pending-commit&quot;;
+
+            var linkElement = document.createElement(&quot;a&quot;);
+            linkElement.className = &quot;revision&quot;;
+            linkElement.href = trac.revisionURL(commit.revisionNumber);
+            linkElement.target = &quot;_blank&quot;;
+            linkElement.textContent = &quot;r&quot; + commit.revisionNumber;
+            result.appendChild(linkElement);
+
+            var authorElement = document.createElement(&quot;span&quot;);
+            authorElement.className = &quot;author&quot;;
+            authorElement.textContent = commit.author;
+            result.appendChild(authorElement);
+
+            var titleElement = document.createElement(&quot;span&quot;);
+            titleElement.className = &quot;title&quot;;
+            titleElement.innerHTML = commit.title.innerHTML;
+            result.appendChild(titleElement);
+
+            return result;
</ins><span class="cx">         }
</span><ins>+
+        var content = document.createElement(&quot;div&quot;);
+        content.className = &quot;pending-commits-popover&quot;;
+
+        for (var i = webkitTrac.recordedCommits.length - 1; i &gt;= 0; --i) {
+            var commit = webkitTrac.recordedCommits[i];
+            if (commit.revisionNumber &lt;= latestFinishedIteration.openSourceRevision)
+                break;
+
+            content.appendChild(lineForCommit(webkitTrac, commit));
+        }
+
+        if (latestFinishedIteration.internalRevision &amp;&amp; internalTrac.latestRecordedRevisionNumber) {
+            if (latestFinishedIteration.internalRevision &lt; internalTrac.latestRecordedRevisionNumber &amp;&amp; content.hasChildNodes()) {
+                var divider = document.createElement(&quot;div&quot;);
+                divider.className = &quot;divider&quot;;
+                content.appendChild(divider);
+            }
+
+            for (var i = internalTrac.recordedCommits.length - 1; i &gt;= 0; --i) {
+                var commit = internalTrac.recordedCommits[i];
+                if (commit.revisionNumber &lt;= latestFinishedIteration.internalRevision)
+                    break;
+
+                content.appendChild(lineForCommit(internalTrac, commit));
+            }
+        }
+
+        var rect = Dashboard.Rect.rectFromClientRect(element.getBoundingClientRect());
+        popover.present(rect, content, [Dashboard.RectEdge.MIN_Y, Dashboard.RectEdge.MAX_Y, Dashboard.RectEdge.MAX_X, Dashboard.RectEdge.MIN_X]);
+
+        return true;
</ins><span class="cx">     },
</span><span class="cx"> 
</span><span class="cx">     revisionLinksForIteration: function(iteration)
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsGeometryjs"></a>
<div class="addfile"><h4>Added: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Geometry.js (0 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Geometry.js                                (rev 0)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Geometry.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -0,0 +1,223 @@
</span><ins>+/*
+ * Copyright (C) 2013 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.
+ */
+
+Dashboard.Point = function(x, y)
+{
+    this.x = x || 0;
+    this.y = y || 0;
+};
+
+Dashboard.Point.prototype = {
+    constructor: Dashboard.Point,
+
+    toString : function()
+    {
+        return &quot;Dashboard.Point[&quot; + this.x + &quot;,&quot; + this.y + &quot;]&quot;;
+    },
+
+    copy: function()
+    {
+        return new Dashboard.Point(this.x, this.y);
+    },
+
+    equals: function(anotherPoint)
+    {
+        return (this.x === anotherPoint.x &amp;&amp; this.y === anotherPoint.y);
+    }
+};
+
+Dashboard.Size = function(width, height)
+{
+    this.width = width || 0;
+    this.height = height || 0;
+};
+
+Dashboard.Size.prototype = {
+    constructor: Dashboard.Size,
+
+    toString: function()
+    {
+        return &quot;Dashboard.Size[&quot; + this.width + &quot;,&quot; + this.height + &quot;]&quot;;
+    },
+
+    copy: function()
+    {
+        return new Dashboard.Size(this.width, this.height);
+    },
+
+    equals: function(anotherSize)
+    {
+        return (this.width === anotherSize.width &amp;&amp; this.height === anotherSize.height);
+    }
+};
+
+Dashboard.Size.ZERO_SIZE = new Dashboard.Size(0, 0);
+
+
+Dashboard.Rect = function(x, y, width, height)
+{
+    this.origin = new Dashboard.Point(x || 0, y || 0);
+    this.size = new Dashboard.Size(width || 0, height || 0);
+};
+
+Dashboard.Rect.rectFromClientRect = function(clientRect)
+{
+    return new Dashboard.Rect(clientRect.left, clientRect.top, clientRect.width, clientRect.height);
+};
+
+Dashboard.Rect.prototype = {
+    constructor: Dashboard.Rect,
+
+    toString: function()
+    {
+        return &quot;Dashboard.Rect[&quot; + [this.origin.x, this.origin.y, this.size.width, this.size.height].join(&quot;, &quot;) + &quot;]&quot;;
+    },
+
+    copy: function()
+    {
+        return new Dashboard.Rect(this.origin.x, this.origin.y, this.size.width, this.size.height);
+    },
+
+    equals: function(anotherRect)
+    {
+        return (this.origin.equals(anotherRect.origin) &amp;&amp; this.size.equals(anotherRect.size));
+    },
+
+    inset: function(insets)
+    {
+        return new Dashboard.Rect(
+            this.origin.x + insets.left,
+            this.origin.y + insets.top,
+            this.size.width - insets.left - insets.right,
+            this.size.height - insets.top - insets.bottom
+        );
+    },
+
+    pad: function(padding)
+    {
+        return new Dashboard.Rect(
+            this.origin.x - padding,
+            this.origin.y - padding,
+            this.size.width + padding * 2,
+            this.size.height + padding * 2
+        );
+    },
+
+    minX: function()
+    {
+        return this.origin.x;
+    },
+
+    minY: function()
+    {
+        return this.origin.y;
+    },
+
+    midX: function()
+    {
+        return this.origin.x + (this.size.width / 2);
+    },
+
+    midY: function()
+    {
+        return this.origin.y + (this.size.height / 2);
+    },
+
+    maxX: function()
+    {
+        return this.origin.x + this.size.width;
+    },
+
+    maxY: function()
+    {
+        return this.origin.y + this.size.height;
+    },
+
+    intersectionWithRect: function(rect)
+    {
+        var x1 = Math.max(this.minX(), rect.minX());
+        var x2 = Math.min(this.maxX(), rect.maxX());
+        if (x1 &gt; x2)
+            return Dashboard.Rect.ZERO_RECT;
+        var intersection = new Dashboard.Rect;
+        intersection.origin.x = x1;
+        intersection.size.width = x2 - x1;
+        var y1 = Math.max(this.minY(), rect.minY());
+        var y2 = Math.min(this.maxY(), rect.maxY());
+        if (y1 &gt; y2)
+            return Dashboard.Rect.ZERO_RECT;
+        intersection.origin.y = y1;
+        intersection.size.height = y2 - y1;
+        return intersection;
+    },
+
+    containsPoint: function(point)
+    {
+        return point.x &gt;= this.minX() &amp;&amp; point.x &lt;= this.maxX()
+            &amp;&amp; point.y &gt;= this.minY() &amp;&amp; point.y &lt;= this.maxY();
+    }
+};
+
+Dashboard.Rect.ZERO_RECT = new Dashboard.Rect(0, 0, 0, 0);
+
+
+Dashboard.EdgeInsets = function(top, right, bottom, left)
+{
+    console.assert(arguments.length === 1 || arguments.length === 4);
+
+    if (arguments.length === 1) {
+        this.top = top;
+        this.right = top;
+        this.bottom = top;
+        this.left = top;
+    } else if (arguments.length === 4) {
+        this.top = top;
+        this.right = right;
+        this.bottom = bottom;
+        this.left = left;
+    }
+};
+
+Dashboard.EdgeInsets.prototype = {
+    constructor: Dashboard.EdgeInsets,
+
+    equals: function(anotherInset)
+    {
+        return (this.top === anotherInset.top &amp;&amp; this.right === anotherInset.right &amp;&amp;
+                this.bottom === anotherInset.bottom &amp;&amp; this.left === anotherInset.left);
+    },
+
+    copy: function()
+    {
+        return new Dashboard.EdgeInsets(this.top, this.right, this.bottom, this.left);
+    }
+};
+
+Dashboard.RectEdge = {
+    MIN_X : 0,
+    MIN_Y : 1,
+    MAX_X : 2,
+    MAX_Y : 3
+};
</ins><span class="cx">Property changes on: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Geometry.js
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsPopoverjs"></a>
<div class="addfile"><h4>Added: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Popover.js (0 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Popover.js                                (rev 0)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Popover.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -0,0 +1,506 @@
</span><ins>+/*
+ * Copyright (C) 2013 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.
+ */
+
+Dashboard.Popover = function(delegate)
+{
+    BaseObject.call(this);
+
+    this.delegate = delegate;
+    this._edge = null;
+    this._frame = new Dashboard.Rect;
+    this._content = null;
+    this._targetFrame = new Dashboard.Rect;
+    this._preferredEdges = null;
+
+    this._element = document.createElement(&quot;div&quot;);
+    this._element.className = Dashboard.Popover.StyleClassName;
+    this._canvasId = &quot;popover-&quot; + (Dashboard.Popover.canvasId++);
+    this._element.style.backgroundImage = &quot;-webkit-canvas(&quot; + this._canvasId + &quot;)&quot;;
+    this._element.addEventListener(&quot;transitionend&quot;, this, true);
+    
+    this._container = this._element.appendChild(document.createElement(&quot;div&quot;));
+    this._container.className = &quot;container&quot;;
+};
+
+Dashboard.Popover.StyleClassName = &quot;popover&quot;;
+Dashboard.Popover.VisibleClassName = &quot;visible&quot;;
+Dashboard.Popover.StepInClassName = &quot;step-in&quot;;
+Dashboard.Popover.FadeOutClassName = &quot;fade-out&quot;;
+Dashboard.Popover.PreventDocumentScrollingClassName = &quot;popover-prevent-document-scrolling&quot;;
+Dashboard.Popover.canvasId = 0;
+Dashboard.Popover.CornerRadius = 5;
+Dashboard.Popover.MinWidth = 40;
+Dashboard.Popover.MinHeight = 40;
+Dashboard.Popover.ShadowPadding = 5;
+Dashboard.Popover.ContentPadding = 5;
+Dashboard.Popover.AnchorSize = new Dashboard.Size(22, 11);
+Dashboard.Popover.ShadowEdgeInsets = new Dashboard.EdgeInsets(Dashboard.Popover.ShadowPadding);
+
+BaseObject.addConstructorFunctions(Dashboard.Popover);
+
+Dashboard.Popover.prototype = {
+    constructor: Dashboard.Popover,
+    __proto__: BaseObject.prototype,
+
+    // Public
+
+    get element()
+    {
+        return this._element;
+    },
+
+    get frame()
+    {
+        return this._frame;
+    },
+
+    set frame(frame)
+    {
+        this._element.style.left = window.scrollX + frame.origin.x + &quot;px&quot;;
+        this._element.style.top = window.scrollY + frame.origin.y + &quot;px&quot;;
+        this._element.style.width = frame.size.width + &quot;px&quot;;
+        this._element.style.height = frame.size.height + &quot;px&quot;;
+        this._element.style.backgroundSize = frame.size.width + &quot;px &quot; + frame.size.height + &quot;px&quot;;
+        this._frame = frame;
+    },
+
+    get visible()
+    {
+        return this._element.parentNode === document.body
+            &amp;&amp; !this._element.classList.contains(Dashboard.Popover.StepInClassName);
+    },
+
+    /**
+     * @param {Dashboard.Rect} targetFrame
+     * @param {Element} content
+     * @param {Dashboard.RectEdge}[] preferredEdges
+     */
+    present: function(targetFrame, content, preferredEdges)
+    {
+        console.assert(!this._content)
+
+        this._targetFrame = targetFrame;
+        this._content = content;
+        this._preferredEdges = preferredEdges;
+
+        window.addEventListener(&quot;mousedown&quot;, this, true);
+        window.addEventListener(&quot;scroll&quot;, this, true);
+
+        this._update();
+
+        this._element.classList.add(Dashboard.Popover.StepInClassName);
+
+        // Scrolling inside a popover should not cascade to document when reaching a bound, because that would make it disappear unexpectedly.
+        if (this._container.offsetHeight &lt; this._container.scrollHeight) {
+            this._element.addEventListener(&quot;mouseenter&quot;, this, true);
+            this._element.addEventListener(&quot;mouseleave&quot;, this, true);
+        }
+    },
+
+    makeVisibleImmediately: function()
+    {
+        console.assert(this._content);
+
+        this._finalizePresentation();
+    },
+    
+    dismiss: function()
+    {
+        if (this._element.parentNode !== document.body)
+            return;
+
+        this._element.classList.add(Dashboard.Popover.FadeOutClassName);
+    },
+
+    dismissImmediately: function()
+    {
+        if (this._element.parentNode !== document.body)
+            return;
+
+        this._finalizeDismissal();
+    },
+
+    handleEvent: function(event)
+    {
+        switch (event.type) {
+        case &quot;mousedown&quot;:
+        case &quot;scroll&quot;:
+            if (!this._element.contains(event.target))
+                this.dismissImmediately();
+            break;
+        case &quot;transitionend&quot;:
+            if (this._element.classList.contains(Dashboard.Popover.StepInClassName))
+                this._finalizePresentation();
+            else if (this._element.classList.contains(Dashboard.Popover.FadeOutClassName))
+                this._finalizeDismissal();
+            break;
+        case &quot;mouseenter&quot;:
+            document.body.classList.add(Dashboard.Popover.PreventDocumentScrollingClassName);
+            break;
+        case &quot;mouseleave&quot;:
+            if (!this._element.isSelfOrAncestor(event.toElement))
+                document.body.classList.remove(Dashboard.Popover.PreventDocumentScrollingClassName);
+            break;
+        }
+    },
+
+    // Private
+
+    _update: function()
+    {
+        var targetFrame = this._targetFrame;
+        var preferredEdges = this._preferredEdges;
+
+        // Ensure our element is on display so that its metrics can be resolved
+        // or interrupt any pending transition to remove it from display.
+        if (this._element.parentNode !== document.body)
+            document.body.appendChild(this._element);
+        else
+            this._element.classList.remove(Dashboard.Popover.FadeOutClassName);
+
+        // Reset CSS properties on element so that the element may be sized to fit its content.
+        this._element.style.removeProperty(&quot;left&quot;);
+        this._element.style.removeProperty(&quot;top&quot;);
+        this._element.style.removeProperty(&quot;width&quot;);
+        this._element.style.removeProperty(&quot;height&quot;);
+        if (this._edge !== null)
+            this._element.classList.remove(this._cssClassNameForEdge());
+
+        // Add the content in place of the wrapper to get the raw metrics.
+        this._element.replaceChild(this._content, this._container);
+
+        // Get the ideal size for the popover to fit its content.
+        var popoverBounds = this._element.getBoundingClientRect();
+        this._preferredSize = new Dashboard.Size(Math.ceil(popoverBounds.width), Math.ceil(popoverBounds.height));
+
+        // The frame of the window with a little inset to make sure we have room for shadows.
+        var containerFrame = new Dashboard.Rect(0, 0, window.innerWidth, window.innerHeight);
+        containerFrame = containerFrame.inset(Dashboard.Popover.ShadowEdgeInsets);
+
+        // Work out the metrics for all edges.
+        var metrics = new Array(preferredEdges.length);
+        for (var edgeName in Dashboard.RectEdge) {
+            var edge = Dashboard.RectEdge[edgeName];
+            var item = {
+                edge: edge,
+                metrics: this._bestMetricsForEdge(this._preferredSize, targetFrame, containerFrame, edge)
+            };
+            var preferredIndex = preferredEdges.indexOf(edge);
+            if (preferredIndex !== -1)
+                metrics[preferredIndex] = item;
+            else
+                metrics.push(item);
+        }
+
+        function area(size)
+        {
+            return size.width * size.height;
+        }
+
+        // Find if any of those fit better than the frame for the preferred edge.
+        var bestEdge = metrics[0].edge;
+        var bestMetrics = metrics[0].metrics;
+        for (var i = 1; i &lt; metrics.length; i++) {
+            var itemMetrics = metrics[i].metrics;
+            if (area(itemMetrics.contentSize) &gt; area(bestMetrics.contentSize)) {
+                bestEdge = metrics[i].edge;
+                bestMetrics = itemMetrics;
+            }
+        }
+
+        var anchorPoint;
+        var bestFrame = bestMetrics.frame;
+
+        this.frame = bestFrame;
+        this._edge = bestEdge;
+
+        if (this.frame === Dashboard.Rect.ZERO_RECT) {
+            // The target for the popover is offscreen.
+            this.dismiss();
+        } else {
+            switch (bestEdge) {
+            case Dashboard.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
+                anchorPoint = new Dashboard.Point(bestFrame.size.width - Dashboard.Popover.ShadowPadding, targetFrame.midY() - bestFrame.minY());
+                break;
+            case Dashboard.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
+                anchorPoint = new Dashboard.Point(Dashboard.Popover.ShadowPadding, targetFrame.midY() - bestFrame.minY());
+                break;
+            case Dashboard.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
+                anchorPoint = new Dashboard.Point(targetFrame.midX() - bestFrame.minX(), bestFrame.size.height - Dashboard.Popover.ShadowPadding);
+                break;
+            case Dashboard.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
+                anchorPoint = new Dashboard.Point(targetFrame.midX() - bestFrame.minX(), Dashboard.Popover.ShadowPadding);
+                break;
+            }
+
+            this._element.classList.add(this._cssClassNameForEdge());
+
+            this._drawBackground(bestEdge, anchorPoint);
+
+            // Make sure content is centered in case either of the dimension is smaller than the minimal bounds.
+            if (this._preferredSize.width &lt; Dashboard.Popover.MinWidth || this._preferredSize.height &lt; Dashboard.Popover.MinHeight)
+                this._container.classList.add(&quot;center&quot;);
+            else
+                this._container.classList.remove(&quot;center&quot;);
+        }
+
+        // Wrap the content in the container so that it's located correctly.
+        this._container.textContent = &quot;&quot;;
+        this._element.replaceChild(this._container, this._content);
+        this._container.appendChild(this._content);
+    },
+
+    _cssClassNameForEdge: function()
+    {
+        switch (this._edge) {
+        case Dashboard.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
+            return &quot;arrow-right&quot;;
+        case Dashboard.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
+            return &quot;arrow-left&quot;;
+        case Dashboard.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
+            return &quot;arrow-down&quot;;
+        case Dashboard.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
+            return &quot;arrow-up&quot;;
+        }
+        console.error(&quot;Unknown edge.&quot;);
+        return &quot;arrow-up&quot;;
+    },
+
+    _drawBackground: function(edge, anchorPoint)
+    {
+        var scaleFactor = window.devicePixelRatio;
+
+        var width = this._frame.size.width;
+        var height = this._frame.size.height;
+        var scaledWidth = width * scaleFactor;
+        var scaledHeight = height * scaleFactor;
+
+        // Create a scratch canvas so we can draw the popover that will later be drawn into
+        // the final context with a shadow.
+        var scratchCanvas = document.createElement(&quot;canvas&quot;);
+        scratchCanvas.width = scaledWidth;
+        scratchCanvas.height = scaledHeight;
+
+        var ctx = scratchCanvas.getContext(&quot;2d&quot;);
+        ctx.scale(scaleFactor, scaleFactor);
+
+        // Bounds of the path don't take into account the arrow, but really only the tight bounding box
+        // of the content contained within the frame.
+        var bounds;
+        var arrowHeight = Dashboard.Popover.AnchorSize.height;
+        switch (edge) {
+        case Dashboard.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
+            bounds = new Dashboard.Rect(0, 0, width - arrowHeight, height);
+            break;
+        case Dashboard.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
+            bounds = new Dashboard.Rect(arrowHeight, 0, width - arrowHeight, height);
+            break;
+        case Dashboard.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
+            bounds = new Dashboard.Rect(0, 0, width, height - arrowHeight);
+            break;
+        case Dashboard.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
+            bounds = new Dashboard.Rect(0, arrowHeight, width, height - arrowHeight);
+            break;
+        }
+
+        bounds = bounds.inset(Dashboard.Popover.ShadowEdgeInsets);
+
+        // Clip the frame.
+        ctx.fillStyle = &quot;black&quot;;
+        this._drawFrame(ctx, bounds, edge, anchorPoint);
+        ctx.clip();
+
+        // Gradient fill, top-to-bottom.
+        var fillGradient = ctx.createLinearGradient(0, 0, 0, height);
+        fillGradient.addColorStop(0, &quot;rgba(255, 255, 255, 0.95)&quot;);
+        fillGradient.addColorStop(1, &quot;rgba(235, 235, 235, 0.95)&quot;);
+        ctx.fillStyle = fillGradient;
+        ctx.fillRect(0, 0, width, height);
+
+        // Stroke.
+        ctx.strokeStyle = &quot;rgba(0, 0, 0, 0.25)&quot;;
+        ctx.lineWidth = 2;
+        this._drawFrame(ctx, bounds, edge, anchorPoint);
+        ctx.stroke();
+
+        // Draw the popover into the final context with a drop shadow.
+        var finalContext = document.getCSSCanvasContext(&quot;2d&quot;, this._canvasId, scaledWidth, scaledHeight);
+
+        finalContext.clearRect(0, 0, scaledWidth, scaledHeight);
+
+        finalContext.shadowOffsetX = 1;
+        finalContext.shadowOffsetY = 1;
+        finalContext.shadowBlur = 5;
+        finalContext.shadowColor = &quot;rgba(0, 0, 0, 0.5)&quot;;
+
+        finalContext.drawImage(scratchCanvas, 0, 0, scaledWidth, scaledHeight);
+    },
+    
+    _bestMetricsForEdge: function(preferredSize, targetFrame, containerFrame, edge)
+    {
+        var x, y;
+        var width = preferredSize.width + (Dashboard.Popover.ShadowPadding * 2) + (Dashboard.Popover.ContentPadding * 2);
+        var height = preferredSize.height + (Dashboard.Popover.ShadowPadding * 2) + (Dashboard.Popover.ContentPadding * 2);
+        var arrowLength = Dashboard.Popover.AnchorSize.height;
+
+        switch (edge) {
+        case Dashboard.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
+            width += arrowLength;
+            x = targetFrame.origin.x - width + Dashboard.Popover.ShadowPadding;
+            y = targetFrame.origin.y - (height - targetFrame.size.height) / 2;
+            break;
+        case Dashboard.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
+            width += arrowLength;
+            x = targetFrame.origin.x + targetFrame.size.width - Dashboard.Popover.ShadowPadding;
+            y = targetFrame.origin.y - (height - targetFrame.size.height) / 2;
+            break;
+        case Dashboard.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
+            height += arrowLength;
+            x = targetFrame.origin.x - (width - targetFrame.size.width) / 2;
+            y = targetFrame.origin.y - height + Dashboard.Popover.ShadowPadding;
+            break;
+        case Dashboard.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
+            height += arrowLength;
+            x = targetFrame.origin.x - (width - targetFrame.size.width) / 2;
+            y = targetFrame.origin.y + targetFrame.size.height - Dashboard.Popover.ShadowPadding;
+            break;
+        }
+
+        if (edge === Dashboard.RectEdge.MIN_X || edge === Dashboard.RectEdge.MAX_X) {
+            if (y &lt; containerFrame.minY())
+                y = containerFrame.minY();
+            if (y + height &gt; containerFrame.maxY())
+                y = containerFrame.maxY() - height;
+        } else {
+            if (x &lt; containerFrame.minX())
+                x = containerFrame.minX(); 
+            if (x + width &gt; containerFrame.maxX())
+                x = containerFrame.maxX() - width;
+        }
+
+        var preferredFrame = new Dashboard.Rect(x, y, width, height);
+        var bestFrame = preferredFrame.intersectionWithRect(containerFrame);
+
+        width = bestFrame.size.width - (Dashboard.Popover.ShadowPadding * 2);
+        height = bestFrame.size.height - (Dashboard.Popover.ShadowPadding * 2);
+
+        switch (edge) {
+        case Dashboard.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
+        case Dashboard.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
+            width -= arrowLength;
+            break;
+        case Dashboard.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
+        case Dashboard.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
+            height -= arrowLength;
+            break;
+        }
+
+        return {
+            frame: bestFrame,
+            contentSize: new Dashboard.Size(width, height)
+        };
+    },
+
+    _drawFrame: function(ctx, bounds, anchorEdge, anchorPoint)
+    {
+        var r = Dashboard.Popover.CornerRadius;
+        var arrowHalfLength = Dashboard.Popover.AnchorSize.width / 2;
+
+        ctx.beginPath();
+        switch (anchorEdge) {
+        case Dashboard.RectEdge.MIN_X: // Displayed on the left of the target, arrow points right.
+            ctx.moveTo(bounds.maxX(), bounds.minY() + r);
+            ctx.lineTo(bounds.maxX(), anchorPoint.y - arrowHalfLength);
+            ctx.lineTo(anchorPoint.x, anchorPoint.y);
+            ctx.lineTo(bounds.maxX(), anchorPoint.y + arrowHalfLength);
+            ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), r);
+            ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), r);
+            ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), r);
+            ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), r);
+            break;
+        case Dashboard.RectEdge.MAX_X: // Displayed on the right of the target, arrow points left.
+            ctx.moveTo(bounds.minX(), bounds.maxY() - r);
+            ctx.lineTo(bounds.minX(), anchorPoint.y + arrowHalfLength);
+            ctx.lineTo(anchorPoint.x, anchorPoint.y);
+            ctx.lineTo(bounds.minX(), anchorPoint.y - arrowHalfLength);
+            ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), r);
+            ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), r);
+            ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), r);
+            ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), r);
+            break;
+        case Dashboard.RectEdge.MIN_Y: // Displayed above the target, arrow points down.
+            ctx.moveTo(bounds.maxX() - r, bounds.maxY());
+            ctx.lineTo(anchorPoint.x + arrowHalfLength, bounds.maxY());
+            ctx.lineTo(anchorPoint.x, anchorPoint.y);
+            ctx.lineTo(anchorPoint.x - arrowHalfLength, bounds.maxY());
+            ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), r);
+            ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), r);
+            ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), r);
+            ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), r);
+            break;
+        case Dashboard.RectEdge.MAX_Y: // Displayed below the target, arrow points up.
+            ctx.moveTo(bounds.minX() + r, bounds.minY());
+            ctx.lineTo(anchorPoint.x - arrowHalfLength, bounds.minY());
+            ctx.lineTo(anchorPoint.x, anchorPoint.y);
+            ctx.lineTo(anchorPoint.x + arrowHalfLength, bounds.minY());
+            ctx.arcTo(bounds.maxX(), bounds.minY(), bounds.maxX(), bounds.maxY(), r);
+            ctx.arcTo(bounds.maxX(), bounds.maxY(), bounds.minX(), bounds.maxY(), r);
+            ctx.arcTo(bounds.minX(), bounds.maxY(), bounds.minX(), bounds.minY(), r);
+            ctx.arcTo(bounds.minX(), bounds.minY(), bounds.maxX(), bounds.minY(), r);
+            break;
+        }
+        ctx.closePath();
+    },
+
+    _finalizePresentation: function()
+    {
+        var wasVisible = this.visible;
+        this._element.classList.remove(Dashboard.Popover.StepInClassName);
+        this._element.classList.remove(Dashboard.Popover.FadeOutClassName);
+        this._element.classList.add(Dashboard.Popover.VisibleClassName);
+
+        // Make scroll bar flash if present, so that the user sees that scrolling is possible.
+        // FIXME: Is there a better way?
+        if (!wasVisible) {
+            this._container.scrollTop = 1;
+            this._container.scrollTop = 0;
+        }
+    },
+
+    _finalizeDismissal: function()
+    {
+        window.removeEventListener(&quot;mousedown&quot;, this, true);
+        window.removeEventListener(&quot;scroll&quot;, this, true);
+
+        document.body.removeChild(this._element);
+        document.body.classList.remove(Dashboard.Popover.PreventDocumentScrollingClassName);
+        this._element.classList.remove(Dashboard.Popover.VisibleClassName);
+        this._element.classList.remove(Dashboard.Popover.StepInClassName);
+        this._element.classList.remove(Dashboard.Popover.FadeOutClassName);
+        this._container.textContent = &quot;&quot;;
+        if (this.delegate &amp;&amp; typeof this.delegate.didDismissPopover === &quot;function&quot;)
+            this.delegate.didDismissPopover(this);
+    }
+};
</ins><span class="cx">Property changes on: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Popover.js
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsPopoverTrackerjs"></a>
<div class="addfile"><h4>Added: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/PopoverTracker.js (0 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/PopoverTracker.js                                (rev 0)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/PopoverTracker.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -0,0 +1,102 @@
</span><ins>+/*
+ * Copyright (C) 2013 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.
+ */
+
+PopoverTracker = function(element, delegate, context)
+{
+    BaseObject.call(this);
+
+    this._element = element;
+    this._delegate = delegate;
+    this._context = context;
+    this._active = false;
+
+    element.addEventListener(&quot;mouseenter&quot;, this._mouseEnteredPopoverOrElement.bind(this), true);
+    element.addEventListener(&quot;mouseleave&quot;, this._mouseExitedPopoverOrElement.bind(this), true);
+};
+
+PopoverTracker._popover = null; // Only one popover may be active at any time.
+
+BaseObject.addConstructorFunctions(PopoverTracker);
+
+PopoverTracker.prototype = {
+    constructor: PopoverTracker,
+    __proto__: BaseObject.prototype,
+
+    _mouseEnteredPopoverOrElement: function(event)
+    {
+        var popover = PopoverTracker._popover;
+        var popoverWasVisible = popover &amp;&amp; popover.visible;
+        if (popover) {
+            // Abort fade-out when re-entering the same element or an existing popover.
+            if ((this._active &amp;&amp; this._element.isSelfOrAncestor(event.toElement))
+                || popover.element.isSelfOrAncestor(event.toElement)) {
+                popover.makeVisibleImmediately();
+                return;
+            }
+
+            // We entered a different element, dismiss the old popover.
+            popover.dismissImmediately();
+        }
+        console.assert(!PopoverTracker._popover);
+
+        if (!this._delegate || !this._delegate.presentPopoverForElement || typeof this._delegate.presentPopoverForElement != &quot;function&quot;)
+            return;
+
+        var popover = new Dashboard.Popover(this);
+        if (!this._delegate.presentPopoverForElement(event.target, popover, this._context))
+            return;
+
+        if (popoverWasVisible)
+            popover.makeVisibleImmediately();
+
+        this._active = true;
+        PopoverTracker._popover = popover;
+        popover.element.addEventListener(&quot;mouseenter&quot;, this._mouseEnteredPopoverOrElement.bind(this), true);
+        popover.element.addEventListener(&quot;mouseleave&quot;, this._mouseExitedPopoverOrElement.bind(this), true);
+    },
+
+    _mouseExitedPopoverOrElement: function(event)
+    {
+        var popover = PopoverTracker._popover;
+
+        if (!popover)
+            return;
+
+        if (this._element.isSelfOrAncestor(event.toElement) || popover.element.isSelfOrAncestor(event.toElement))
+            return;
+
+        if (popover.visible)
+            popover.dismiss();
+        else
+            popover.dismissImmediately();
+    },
+
+    didDismissPopover: function(popover)
+    {
+        console.assert(popover === PopoverTracker._popover);
+        this._active = false;
+        PopoverTracker._popover = null;
+    }
+};
</ins><span class="cx">Property changes on: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/PopoverTracker.js
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4>Added: svn:eol-style</h4></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsStatusLineViewjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/StatusLineView.js (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/StatusLineView.js        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/StatusLineView.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -160,5 +160,10 @@
</span><span class="cx">         } else {
</span><span class="cx">             this._messageElement.textContent = x;
</span><span class="cx">         }
</span><ins>+    },
+
+    get messageElement()
+    {
+        return this._messageElement;
</ins><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsTracjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Trac.js (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Trac.js        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Trac.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -70,13 +70,32 @@
</span><span class="cx">         var link = doc.evaluate(&quot;./link&quot;, commitElement, null, XPathResult.STRING_TYPE).stringValue;
</span><span class="cx">         var revisionNumber = parseInt(/\d+$/.exec(link))
</span><span class="cx"> 
</span><del>-        var title = doc.evaluate(&quot;./title&quot;, commitElement, null, XPathResult.STRING_TYPE).stringValue;
-        title = title.replace(/^Changeset \[\d+\]: /, &quot;&quot;);
-        var author = doc.evaluate(&quot;./author&quot;, commitElement, null, XPathResult.STRING_TYPE).stringValue;
</del><ins>+        function tracNSResolver(prefix)
+        {
+            if (prefix == &quot;dc&quot;)
+                return &quot;http://purl.org/dc/elements/1.1/&quot;;
+            return null;
+        }
+
+        var author = doc.evaluate(&quot;./author|dc:creator&quot;, commitElement, tracNSResolver, XPathResult.STRING_TYPE).stringValue;
</ins><span class="cx">         var date = doc.evaluate(&quot;./pubDate&quot;, commitElement, null, XPathResult.STRING_TYPE).stringValue;
</span><span class="cx">         date = new Date(Date.parse(date));
</span><span class="cx">         var description = doc.evaluate(&quot;./description&quot;, commitElement, null, XPathResult.STRING_TYPE).stringValue;
</span><span class="cx"> 
</span><ins>+        // The feed contains a &lt;title&gt;, but it's not parsed as well as what we are getting from description.
+        var parsedDescription = document.createElement(&quot;div&quot;);
+        parsedDescription.innerHTML = description;
+        var title = document.createElement(&quot;div&quot;);
+        var node = parsedDescription.firstChild.firstChild;
+        while (node &amp;&amp; node.tagName != &quot;BR&quot;) {
+            title.appendChild(node.cloneNode(true));
+            node = node.nextSibling;
+        }
+
+        // For some reason, trac titles start with a newline. Delete it.
+        if (title.firstChild &amp;&amp; title.firstChild.nodeType == Node.TEXT_NODE &amp;&amp; title.firstChild.textContent.length &gt; 0 &amp;&amp; title.firstChild.textContent[0] == &quot;\n&quot;)
+            title.firstChild.textContent = title.firstChild.textContent.substring(1);
+
</ins><span class="cx">         return {
</span><span class="cx">             revisionNumber: revisionNumber,
</span><span class="cx">             link: link,
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardScriptsUtilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Utilities.js (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Utilities.js        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Utilities.js        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -70,6 +70,36 @@
</span><span class="cx">     request.send();
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+Node.prototype.isAncestor = function(node)
+{
+    if (!node)
+        return false;
+
+    var currentNode = node.parentNode;
+    while (currentNode) {
+        if (this === currentNode)
+            return true;
+        currentNode = currentNode.parentNode;
+    }
+
+    return false;
+}
+
+Node.prototype.isDescendant = function(descendant)
+{
+    return !!descendant &amp;&amp; descendant.isAncestor(this);
+}
+
+Node.prototype.isSelfOrAncestor = function(node)
+{
+    return !!node &amp;&amp; (node === this || this.isAncestor(node));
+}
+
+Node.prototype.isSelfOrDescendant = function(node)
+{
+    return !!node &amp;&amp; (node === this || this.isDescendant(node));
+}
+
</ins><span class="cx"> Element.prototype.removeChildren = function()
</span><span class="cx"> {
</span><span class="cx">     // This has been tested to be the fastest removal method.
</span><span class="lines">@@ -77,6 +107,15 @@
</span><span class="cx">         this.textContent = &quot;&quot;;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+DOMTokenList.prototype.contains = function(string)
+{
+    for (var i = 0, end = this.length; i &lt; end; ++i) {
+        if (this.item(i) === string)
+            return true;
+    }
+    return false;
+}
+
</ins><span class="cx"> Array.prototype.contains = function(value)
</span><span class="cx"> {
</span><span class="cx">     return this.indexOf(value) &gt;= 0;
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesPopovercss"></a>
<div class="addfile"><h4>Added: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Popover.css (0 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Popover.css                                (rev 0)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Popover.css        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -0,0 +1,107 @@
</span><ins>+/*
+ * Copyright (C) 2013 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.
+ */
+
+.popover {
+    position: absolute;
+    min-width: 20px;
+    min-height: 20px;
+    box-sizing: border-box;
+    pointer-events: none;
+    z-index: 1000;
+    opacity: 0;
+}
+
+.popover.arrow-up {
+    padding: 16px 5px 5px 5px;
+}
+
+.popover.arrow-right {
+    padding: 5px 16px 5px 5px;
+}
+
+.popover.arrow-down {
+    padding: 5px 5px 16px 5px;
+}
+
+.popover.arrow-left {
+    padding: 5px 5px 5px 16px;
+}
+
+.popover.visible {
+    opacity: 1;
+}
+
+.popover.step-in {
+    transition: opacity 0ms;
+    transition-delay: 1s;
+    opacity: 1;
+}
+
+.popover.fade-out {
+    transition: opacity 350ms;
+    transition-delay: 1s;
+    opacity: 0;
+}
+
+.popover &gt; .container {
+    position: absolute;
+    left: 5px;
+    top: 5px;
+    right: 5px;
+    bottom: 5px;
+
+    padding: 5px;
+
+    overflow-y: auto;
+    overflow-x: hidden;
+
+    pointer-events: auto;
+}
+
+.popover &gt; .container.center {
+    display: -webkit-flex;
+    -webkit-justify-content: center;
+    -webkit-align-items: center;
+}
+
+.popover.arrow-up &gt; .container {
+    top: 16px;
+}
+
+.popover.arrow-right &gt; .container {
+    right: 16px;
+}
+
+.popover.arrow-down &gt; .container {
+    bottom: 16px;
+}
+
+.popover.arrow-left &gt; .container {
+    left: 16px;
+}
+
+body.popover-prevent-document-scrolling {
+    overflow: hidden;
+}
</ins><span class="cx">Property changes on: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Popover.css
</span><span class="cx">___________________________________________________________________
</span></span></pre></div>
<a id="svnmimetype"></a>
<div class="addfile"><h4>Added: svn:mime-type</h4></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesQueueViewcss"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -39,3 +39,25 @@
</span><span class="cx"> .queue-view .queueLabel:not(:first-child) {
</span><span class="cx">     margin-top: 10px;
</span><span class="cx"> }
</span><ins>+
+.pending-commits-popover &gt; .pending-commit {
+    font-family: &quot;HelveticaNeue-Light&quot;, &quot;Helvetica Neue&quot;, sans-serif;
+    color: rgb(145, 135, 95);
+    font-size: 12px;
+    text-align: left;
+    padding: 1px 6px 1px 6px;
+    user-select: auto;
+}
+
+.pending-commits-popover &gt; .pending-commit &gt; .author {
+    padding-left: 5px;
+    padding-right: 5px;
+}
+
+.pending-commits-popover &gt; .pending-commit &gt; .title {
+    color: black;
+}
+
+.pending-commits-popover &gt; .divider {
+    height: 7px;
+}
</ins></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardStylesStatusLineViewcss"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/StatusLineView.css (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/StatusLineView.css        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/StatusLineView.css        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -109,6 +109,10 @@
</span><span class="cx">     line-height: 13px;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.status-line.no-bubble .message {
+    display: inline-block; /* Fit block to content, so that popovers are positioned correctly. */
+}
+
</ins><span class="cx"> .status-line.neutral .label,
</span><span class="cx"> .status-line.neutral .message,
</span><span class="cx"> .status-line.no-bubble .label,
</span></span></pre></div>
<a id="trunkToolsBuildSlaveSupportbuildwebkitorgconfigpublic_htmldashboardindexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx">     &lt;title&gt;WebKit Bot Watcher's Dashboard&lt;/title&gt;
</span><span class="cx"> 
</span><span class="cx">     &lt;link rel=&quot;stylesheet&quot; href=&quot;Styles/Main.css&quot;&gt;&lt;/link&gt;
</span><ins>+    &lt;link rel=&quot;stylesheet&quot; href=&quot;Styles/Popover.css&quot;&gt;&lt;/link&gt;
</ins><span class="cx">     &lt;link rel=&quot;stylesheet&quot; href=&quot;Styles/QueueView.css&quot;&gt;&lt;/link&gt;
</span><span class="cx">     &lt;link rel=&quot;stylesheet&quot; href=&quot;Styles/StatusLineView.css&quot;&gt;&lt;/link&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -41,6 +42,9 @@
</span><span class="cx">     &lt;script src=&quot;Scripts/EWSQueue.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/BuildbotIteration.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/BuildbotTestResults.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;Scripts/Geometry.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Scripts/Popover.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;Scripts/PopoverTracker.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;Scripts/QueueView.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/BuildbotQueueView.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;Scripts/BuildbotBuilderQueueView.js&quot;&gt;&lt;/script&gt;
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (161119 => 161120)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2013-12-29 16:44:14 UTC (rev 161119)
+++ trunk/Tools/ChangeLog        2013-12-29 17:24:57 UTC (rev 161120)
</span><span class="lines">@@ -1,3 +1,73 @@
</span><ins>+2013-12-29  Alexey Proskuryakov  &lt;ap@apple.com&gt;
+
+        Please display information about pending runs in build.webkit.org/dashboard
+        https://bugs.webkit.org/show_bug.cgi?id=122180
+
+        Reviewed by Timothy Hatcher.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotBuilderQueueView.js:
+        (BuildbotBuilderQueueView.prototype.update.appendBuilderQueueStatus): Added a semicolon at the end of a line.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/BuildbotQueueView.js:
+        (BuildbotQueueView.prototype._latestFinishedIteration): Factored out of _appendPendingRevisionCount.
+        (BuildbotQueueView.prototype._appendPendingRevisionCount): Install a popover tracker over the element.
+        (BuildbotQueueView.prototype.lineForCommit): Build an element for a particular commit ot be shown in popover.
+        (BuildbotQueueView.prototype.presentPopoverForElement): Build and show popover content when PopoverTracker
+        asks us to.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Geometry.js: Added.
+        Taken from WebInspector with minimal changes:
+        - Changed root name from WebInspector to Dashboard.
+        - Removed some unused functionality.
+        - Added Rect.containsPoint.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Popover.js: Added.
+        Popover has extensive changes compared to WebInspector version, only drawing code is the same:
+        - Fixed to work in scrollable pages - WebInspector version assumes that window
+        and document coordinates are the same, and also erroneously dismisses a scrollable
+        popover if scrolling cascades out of it after reaching a bound.
+        - Simplified API and implementation to Dashboard needs, it is no longer possible to
+        change content of an existing popover.
+        - Rewrote visibility tracking to be more complete, and not rely on external tracker
+        object so much.
+        - Added code to flash scroll bars when showing a scrollable popover.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/PopoverTracker.js: Added.
+        Objects of this class show and hide popovers as appropriate for registered active elements.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/StatusLineView.js:
+        (StatusLineView.prototype.get messageElement): Added an accessor, so that we could
+        install a popover on message element. It's the only visible element in pending commit
+        line, but the line has different bounds, so we can't install a popover on it (it
+        would be incorrectly positioned if we did).
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Trac.js:
+        (Trac.prototype._convertCommitInfoElementToObject):
+        - Some trac installations report author in a different element, updated to support that.
+        - Changed to parse title out of description, because trac titles are ugly. Also,
+        we get a nice HTML with links from the description.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Scripts/Utilities.js:
+        (Node.prototype.isAncestor): Copied from Web Inspector, only changing the form for
+        consistency with the rest of this file (add a property on prototype with assignment
+        instead of using Object.defineProperty).
+        (Node.prototype.isDescendant): Ditto.
+        (Node.prototype.isSelfOrAncestor): Ditto.
+        (Node.prototype.isSelfOrDescendant): Ditto.
+        (DOMTokenList.prototype.contains): Ditto.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/Popover.css: Added.
+        Like JS counterpart, mostly lifted from Web Inspector.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/QueueView.css:
+        Added style rules for pending commits popover.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/Styles/StatusLineView.css:
+        (.status-line.no-bubble .message): Changed to display:inline-block, so that it fits
+        to content, and we can show the popover in a correct place.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/dashboard/index.html: Added new files.
+
</ins><span class="cx"> 2013-12-27  Gavin Barraclough  &lt;barraclough@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Merge PageVisibilityState &amp; ViewState::IsVisible in WebKit2
</span></span></pre>
</div>
</div>

</body>
</html>