<!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>[164504] 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/164504">164504</a></dd>
<dt>Author</dt> <dd>dbates@webkit.org</dd>
<dt>Date</dt> <dd>2014-02-21 14:38:12 -0800 (Fri, 21 Feb 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>COL element in table has 0 for offsetWidth
https://bugs.webkit.org/show_bug.cgi?id=15277

Reviewed by David Hyatt.

Source/WebCore:

Implements offset{Left, Top, Width, Height} for table columns and column groups
per section Extensions to the HTMLElement Interface of the CSSOM View spec,
&lt;http://www.w3.org/TR/cssom-view/#extensions-to-the-htmlelement-interface&gt; (Draft 17 December 2013).

For now, we fail almost all of the offset{Height, Top} sub-tests in the included test
for the separate border model as we need to fix &lt;https://bugs.webkit.org/show_bug.cgi?id=128988&gt;.

Test: fast/table/col-and-colgroup-offsets.html

* rendering/RenderTable.cpp:
(WebCore::RenderTable::RenderTable): Initialize cached column offset top and offset height.
We cache these offsets since they are the same for all columns in the table.
(WebCore::RenderTable::invalidateCachedColumns): Clear cached effective column index map.
(WebCore::RenderTable::invalidateCachedColumnOffsets): Added.
(WebCore::RenderTable::layout): Invalidate cached column offsets as the location or height
of one or more sections may have changed.
(WebCore::RenderTable::updateColumnCache): Modified to build effective column index map.
(WebCore::RenderTable::effectiveIndexOfColumn): Added.
(WebCore::RenderTable::offsetTopForColumn): Added.
(WebCore::RenderTable::offsetLeftForColumn): Added.
(WebCore::RenderTable::offsetWidthForColumn): Added.
(WebCore::RenderTable::offsetHeightForColumn): Added.
* rendering/RenderTable.h: Make isTableColumnGroupWithColumnChildren() const.
* rendering/RenderTableCol.cpp:
(WebCore::RenderTableCol::offsetLeft): Added; turns around and calls RenderTable::offsetLeftForColumn().
(WebCore::RenderTableCol::offsetTop): Added; turns around and calls RenderTable::offsetTopForColumn().
(WebCore::RenderTableCol::offsetWidth): Added; turns around and calls RenderTable::offsetWidthForColumn().
(WebCore::RenderTableCol::offsetHeight): Added; turns around and calls RenderTable::offsetHeightForColumn().
* rendering/RenderTableCol.h:

LayoutTests:

Added test to ensure that offset{Left, Top, Width, Height} return correct results
for table columns and column groups.

For now, we fail almost all of the offset{Height, Top} sub-tests for the separate
border model as we need to fix &lt;https://bugs.webkit.org/show_bug.cgi?id=128988&gt;.

* fast/table/col-and-colgroup-offsets-expected.txt: Added.
* fast/table/col-and-colgroup-offsets.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="#trunkSourceWebCorerenderingRenderTablecpp">trunk/Source/WebCore/rendering/RenderTable.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderTableh">trunk/Source/WebCore/rendering/RenderTable.h</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderTableColcpp">trunk/Source/WebCore/rendering/RenderTableCol.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingRenderTableColh">trunk/Source/WebCore/rendering/RenderTableCol.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfasttablecolandcolgroupoffsetsexpectedtxt">trunk/LayoutTests/fast/table/col-and-colgroup-offsets-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfasttablecolandcolgroupoffsetshtml">trunk/LayoutTests/fast/table/col-and-colgroup-offsets.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (164503 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2014-02-21 22:37:29 UTC (rev 164503)
+++ trunk/LayoutTests/ChangeLog        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2014-02-21  Daniel Bates  &lt;dabates@apple.com&gt;
+
+        COL element in table has 0 for offsetWidth
+        https://bugs.webkit.org/show_bug.cgi?id=15277
+
+        Reviewed by David Hyatt.
+
+        Added test to ensure that offset{Left, Top, Width, Height} return correct results
+        for table columns and column groups.
+
+        For now, we fail almost all of the offset{Height, Top} sub-tests for the separate
+        border model as we need to fix &lt;https://bugs.webkit.org/show_bug.cgi?id=128988&gt;.
+
+        * fast/table/col-and-colgroup-offsets-expected.txt: Added.
+        * fast/table/col-and-colgroup-offsets.html: Added.
+
</ins><span class="cx"> 2014-02-21  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         'mouseenter' mouse compat event not fired when listeners for touch events
</span></span></pre></div>
<a id="trunkLayoutTestsfasttablecolandcolgroupoffsetsexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/table/col-and-colgroup-offsets-expected.txt (0 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/table/col-and-colgroup-offsets-expected.txt                                (rev 0)
+++ trunk/LayoutTests/fast/table/col-and-colgroup-offsets-expected.txt        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -0,0 +1,93 @@
</span><ins>+This test checks that offset{Left, Top, Width, Height} work for table columns and column groups.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+
+When borderCollapse == &quot;separate&quot;
+
+Tests for offsetLeft:
+PASS document.getElementById(&quot;productNo&quot;).parentNode.offsetLeft is document.getElementById(&quot;row1Cell1&quot;).offsetLeft
+PASS document.getElementById(&quot;productNo&quot;).offsetLeft is document.getElementById(&quot;row1Cell1&quot;).offsetLeft
+PASS document.getElementById(&quot;productName&quot;).offsetLeft is document.getElementById(&quot;row1Cell2&quot;).offsetLeft
+PASS document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetLeft is document.getElementById(&quot;row1Cell3&quot;).offsetLeft
+PASS document.getElementById(&quot;hasMAndHasN&quot;).offsetLeft is document.getElementById(&quot;row1Cell3&quot;).offsetLeft
+PASS document.getElementById(&quot;hasO&quot;).offsetLeft is document.getElementById(&quot;row1Cell5&quot;).offsetLeft
+PASS document.getElementById(&quot;hasP&quot;).offsetLeft is document.getElementById(&quot;row1Cell6&quot;).offsetLeft
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetLeft is 0
+
+Tests for offsetTop:
+FAIL document.getElementById(&quot;productNo&quot;).parentNode.offsetTop should be 24. Was 20.
+FAIL document.getElementById(&quot;productNo&quot;).offsetTop should be 24. Was 20.
+FAIL document.getElementById(&quot;productName&quot;).offsetTop should be 24. Was 20.
+FAIL document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetTop should be 24. Was 20.
+FAIL document.getElementById(&quot;hasMAndHasN&quot;).offsetTop should be 24. Was 20.
+FAIL document.getElementById(&quot;hasO&quot;).offsetTop should be 24. Was 20.
+FAIL document.getElementById(&quot;hasP&quot;).offsetTop should be 24. Was 20.
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetTop is 0
+
+Tests for offsetWidth:
+PASS document.getElementById(&quot;productNo&quot;).parentNode.offsetWidth is 184
+PASS document.getElementById(&quot;productNo&quot;).offsetWidth is 89
+PASS document.getElementById(&quot;productName&quot;).offsetWidth is 91
+PASS document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetWidth is 155
+PASS document.getElementById(&quot;hasMAndHasN&quot;).offsetWidth is 103
+PASS document.getElementById(&quot;hasO&quot;).offsetWidth is 48
+PASS document.getElementById(&quot;hasP&quot;).offsetWidth is 46
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetWidth is 0
+
+Tests for offsetHeight:
+FAIL document.getElementById(&quot;productNo&quot;).parentNode.offsetHeight should be 111. Was 119.
+FAIL document.getElementById(&quot;productNo&quot;).offsetHeight should be 111. Was 119.
+FAIL document.getElementById(&quot;productName&quot;).offsetHeight should be 111. Was 119.
+FAIL document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetHeight should be 111. Was 119.
+FAIL document.getElementById(&quot;hasMAndHasN&quot;).offsetHeight should be 111. Was 119.
+FAIL document.getElementById(&quot;hasO&quot;).offsetHeight should be 111. Was 119.
+FAIL document.getElementById(&quot;hasP&quot;).offsetHeight should be 111. Was 119.
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetHeight is 0
+
+When borderCollapse == &quot;collapse&quot;
+
+Tests for offsetLeft:
+PASS document.getElementById(&quot;productNo&quot;).parentNode.offsetLeft is document.getElementById(&quot;row1Cell1&quot;).offsetLeft
+PASS document.getElementById(&quot;productNo&quot;).offsetLeft is document.getElementById(&quot;row1Cell1&quot;).offsetLeft
+PASS document.getElementById(&quot;productName&quot;).offsetLeft is document.getElementById(&quot;row1Cell2&quot;).offsetLeft
+PASS document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetLeft is document.getElementById(&quot;row1Cell3&quot;).offsetLeft
+PASS document.getElementById(&quot;hasMAndHasN&quot;).offsetLeft is document.getElementById(&quot;row1Cell3&quot;).offsetLeft
+PASS document.getElementById(&quot;hasO&quot;).offsetLeft is document.getElementById(&quot;row1Cell5&quot;).offsetLeft
+PASS document.getElementById(&quot;hasP&quot;).offsetLeft is document.getElementById(&quot;row1Cell6&quot;).offsetLeft
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetLeft is 0
+
+Tests for offsetTop:
+PASS document.getElementById(&quot;productNo&quot;).parentNode.offsetTop is 19
+PASS document.getElementById(&quot;productNo&quot;).offsetTop is 19
+PASS document.getElementById(&quot;productName&quot;).offsetTop is 19
+PASS document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetTop is 19
+PASS document.getElementById(&quot;hasMAndHasN&quot;).offsetTop is 19
+PASS document.getElementById(&quot;hasO&quot;).offsetTop is 19
+PASS document.getElementById(&quot;hasP&quot;).offsetTop is 19
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetTop is 0
+
+Tests for offsetWidth:
+PASS document.getElementById(&quot;productNo&quot;).parentNode.offsetWidth is 176
+PASS document.getElementById(&quot;productNo&quot;).offsetWidth is 87
+PASS document.getElementById(&quot;productName&quot;).offsetWidth is 89
+PASS document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetWidth is 141
+PASS document.getElementById(&quot;hasMAndHasN&quot;).offsetWidth is 95
+PASS document.getElementById(&quot;hasO&quot;).offsetWidth is 46
+PASS document.getElementById(&quot;hasP&quot;).offsetWidth is 44
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetWidth is 0
+
+Tests for offsetHeight:
+PASS document.getElementById(&quot;productNo&quot;).parentNode.offsetHeight is 91
+PASS document.getElementById(&quot;productNo&quot;).offsetHeight is 91
+PASS document.getElementById(&quot;productName&quot;).offsetHeight is 91
+PASS document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetHeight is 91
+PASS document.getElementById(&quot;hasMAndHasN&quot;).offsetHeight is 91
+PASS document.getElementById(&quot;hasO&quot;).offsetHeight is 91
+PASS document.getElementById(&quot;hasP&quot;).offsetHeight is 91
+PASS document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetHeight is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsfasttablecolandcolgroupoffsetshtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/table/col-and-colgroup-offsets.html (0 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/table/col-and-colgroup-offsets.html                                (rev 0)
+++ trunk/LayoutTests/fast/table/col-and-colgroup-offsets.html        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -0,0 +1,162 @@
</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;style&gt;
+table, td, th { border: 2px solid blue; } /* To make it easier to visually inspect the test case. */
+&lt;/style&gt;
+&lt;script&gt;
+window.jsTestIsAsync = true;
+
+var table;
+var tableHorizontalBorderSpacing;
+var tableVerticalBorderSpacing;
+var borderCollapseMenu;
+
+window.onload = function() {
+    description(&quot;This test checks that offset{Left, Top, Width, Height} work for table columns and column groups.&quot;);
+
+    table = document.querySelector(&quot;table&quot;);
+    borderCollapseMenu = document.getElementById(&quot;borderCollapseMenu&quot;);
+    borderCollapseMenu.onchange = didChangeBorderCollapse;
+
+    runTests();
+}
+
+function didChangeBorderCollapse()
+{
+    var newBorderCollapse = borderCollapseMenu.options[borderCollapseMenu.selectedIndex].value;
+    table.style.borderCollapse = borderCollapseMenu.options[borderCollapseMenu.selectedIndex].value;
+    tableHorizontalBorderSpacing = newBorderCollapse === &quot;collapse&quot; ? 0 : parseInt(table.getAttribute(&quot;cellspacing&quot;), 10);
+    tableVerticalBorderSpacing = tableHorizontalBorderSpacing;
+}
+
+function runTests()
+{
+    var savedSelectedIndex;
+    if (!window.testRunner)
+        savedSelectedIndex = borderCollapseMenu.selectedIndex;
+
+    for (var i = 0; i &lt; borderCollapseMenu.options.length; ++i) {
+        borderCollapseMenu.options[i].selected = true;
+        didChangeBorderCollapse();
+
+        debug('&lt;br&gt;When borderCollapse == &quot;' + borderCollapseMenu.options[i].value + '&quot;');
+
+        debug(&quot;&lt;br&gt;Tests for offsetLeft:&quot;);
+        testOffsetLeft();
+
+        debug(&quot;&lt;br&gt;Tests for offsetTop:&quot;);
+        testOffsetTop();
+
+        debug(&quot;&lt;br&gt;Tests for offsetWidth:&quot;);
+        testOffsetWidth();
+
+        debug(&quot;&lt;br&gt;Tests for offsetHeight:&quot;);
+        testOffsetHeight();
+    }
+
+    if (window.testRunner)
+        document.body.removeChild(document.getElementById(&quot;test-container&quot;));
+    else {
+        borderCollapseMenu.options[savedSelectedIndex].selected = true;
+        didChangeBorderCollapse();
+    }
+    finishJSTest();
+}
+
+function testOffsetLeft()
+{
+    shouldEvaluateTo('document.getElementById(&quot;productNo&quot;).parentNode.offsetLeft', 'document.getElementById(&quot;row1Cell1&quot;).offsetLeft');
+    shouldBe('document.getElementById(&quot;productNo&quot;).offsetLeft', 'document.getElementById(&quot;row1Cell1&quot;).offsetLeft');
+    shouldBe('document.getElementById(&quot;productName&quot;).offsetLeft', 'document.getElementById(&quot;row1Cell2&quot;).offsetLeft');
+    shouldBe('document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetLeft', 'document.getElementById(&quot;row1Cell3&quot;).offsetLeft');
+    shouldBe('document.getElementById(&quot;hasMAndHasN&quot;).offsetLeft', 'document.getElementById(&quot;row1Cell3&quot;).offsetLeft');
+    shouldBe('document.getElementById(&quot;hasO&quot;).offsetLeft', 'document.getElementById(&quot;row1Cell5&quot;).offsetLeft');
+    shouldBe('document.getElementById(&quot;hasP&quot;).offsetLeft', 'document.getElementById(&quot;row1Cell6&quot;).offsetLeft');
+    shouldEvaluateTo('document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetLeft', 0);
+}
+
+function testOffsetTop()
+{
+    var top = document.getElementById(&quot;productNoHeading&quot;).offsetTop;
+    shouldEvaluateTo('document.getElementById(&quot;productNo&quot;).parentNode.offsetTop', top);
+    shouldEvaluateTo('document.getElementById(&quot;productNo&quot;).offsetTop', top);
+    shouldEvaluateTo('document.getElementById(&quot;productName&quot;).offsetTop', top);
+    shouldEvaluateTo('document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetTop', top);
+    shouldEvaluateTo('document.getElementById(&quot;hasMAndHasN&quot;).offsetTop', top);
+    shouldEvaluateTo('document.getElementById(&quot;hasO&quot;).offsetTop', top);
+    shouldEvaluateTo('document.getElementById(&quot;hasP&quot;).offsetTop', top);
+    shouldEvaluateTo('document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetTop', 0);
+}
+
+function testOffsetWidth()
+{
+    shouldEvaluateTo('document.getElementById(&quot;productNo&quot;).parentNode.offsetWidth', document.getElementById(&quot;row1Cell1&quot;).offsetWidth + tableHorizontalBorderSpacing + document.getElementById(&quot;row1Cell2&quot;).offsetWidth);
+    shouldEvaluateTo('document.getElementById(&quot;productNo&quot;).offsetWidth', document.getElementById(&quot;row1Cell1&quot;).offsetWidth);
+    shouldEvaluateTo('document.getElementById(&quot;productName&quot;).offsetWidth', document.getElementById(&quot;row1Cell2&quot;).offsetWidth);
+    shouldEvaluateTo('document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetWidth', document.getElementById(&quot;row1Cell3&quot;).offsetWidth + tableHorizontalBorderSpacing + document.getElementById(&quot;row1Cell4&quot;).offsetWidth + tableHorizontalBorderSpacing + document.getElementById(&quot;row1Cell5&quot;).offsetWidth);
+    shouldEvaluateTo('document.getElementById(&quot;hasMAndHasN&quot;).offsetWidth', document.getElementById(&quot;row1Cell3&quot;).offsetWidth + tableHorizontalBorderSpacing + document.getElementById(&quot;row1Cell4&quot;).offsetWidth);
+    shouldEvaluateTo('document.getElementById(&quot;hasO&quot;).offsetWidth', document.getElementById(&quot;row1Cell5&quot;).offsetWidth);
+    shouldEvaluateTo('document.getElementById(&quot;hasP&quot;).offsetWidth', document.getElementById(&quot;row1Cell6&quot;).offsetWidth);
+    shouldEvaluateTo('document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetWidth', 0);
+}
+
+function testOffsetHeight()
+{
+    var height = tableContentHeight();
+    shouldEvaluateTo('document.getElementById(&quot;productNo&quot;).parentNode.offsetHeight', height);
+    shouldEvaluateTo('document.getElementById(&quot;productNo&quot;).offsetHeight', height);
+    shouldEvaluateTo('document.getElementById(&quot;productName&quot;).offsetHeight', height);
+    shouldEvaluateTo('document.getElementById(&quot;hasMAndHasNAndHasO&quot;).offsetHeight', height);
+    shouldEvaluateTo('document.getElementById(&quot;hasMAndHasN&quot;).offsetHeight', height);
+    shouldEvaluateTo('document.getElementById(&quot;hasO&quot;).offsetHeight', height);
+    shouldEvaluateTo('document.getElementById(&quot;hasP&quot;).offsetHeight', height);
+    shouldEvaluateTo('document.getElementById(&quot;columnThatShouldNotBeRendered&quot;).offsetHeight', 0);
+}
+
+function tableContentHeight()
+{
+    var result = 0;
+    var rows = document.getElementsByTagName(&quot;tr&quot;);
+    var numberOfRows = rows.length;
+    if (numberOfRows)
+        result += rows[0].offsetHeight;
+    for (var i = 1; i &lt; numberOfRows; ++i)
+        result += tableVerticalBorderSpacing + rows[i].offsetHeight;
+    return result;
+}
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;p id=&quot;description&quot;&gt;&lt;/p&gt;
+&lt;div id=&quot;test-container&quot;&gt;
+    &lt;label for=&quot;borderCollapseMenu&quot;&gt;border-collapse&lt;/label&gt;
+    &lt;select id=&quot;borderCollapseMenu&quot;&gt;
+        &lt;option value=&quot;separate&quot;&gt;separate&lt;/option&gt;
+        &lt;option value=&quot;collapse&quot;&gt;collapse&lt;/option&gt;
+    &lt;/select&gt;
+    &lt;table cellspacing=&quot;4&quot;&gt;
+        &lt;caption&gt;Product Table&lt;/caption&gt;
+        &lt;col id=&quot;productNo&quot; style=&quot;background-color: lightblue&quot;&gt;
+        &lt;col id=&quot;productName&quot; style=&quot;background-color: lightgreen&quot;&gt;
+        &lt;colgroup id=&quot;hasMAndHasNAndHasO&quot; style=&quot;background-color: lightgray&quot;&gt;
+            &lt;col id=&quot;hasMAndHasN&quot; span=&quot;2&quot;&gt;
+            &lt;col id=&quot;hasO&quot;&gt;
+        &lt;/colgroup&gt;
+        &lt;colgroup id=&quot;hasP&quot; style=&quot;background-color: gray&quot;&gt;
+        &lt;colgroup id=&quot;columnThatShouldNotBeRendered&quot; style=&quot;background-color: red&quot; span=&quot;2&quot;&gt;
+        &lt;thead&gt;
+            &lt;tr&gt;&lt;th id=&quot;productNoHeading&quot;&gt;Product No.&lt;/th&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Has M&lt;/th&gt;&lt;th&gt;Has N&lt;/th&gt;&lt;th&gt;Has O&lt;/th&gt;&lt;th&gt;Has P&lt;/th&gt;&lt;/tr&gt;
+        &lt;/thead&gt;
+        &lt;tbody&gt;
+            &lt;tr&gt;&lt;td id=&quot;row1Cell1&quot;&gt;1&lt;/td&gt;&lt;td id=&quot;row1Cell2&quot;&gt;Gizmo A&lt;/td&gt;&lt;td id=&quot;row1Cell3&quot;&gt;&amp;#x2713;&lt;/td&gt;&lt;td id=&quot;row1Cell4&quot;&gt;&lt;/td&gt;&lt;td id=&quot;row1Cell5&quot;&gt;&amp;#x2713;&lt;/td&gt;&lt;td id=&quot;row1Cell6&quot;&gt;&amp;#x2713;&lt;/td&gt;&lt;/tr&gt;
+            &lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Gizmo A Pro&lt;/td&gt;&lt;td&gt;&amp;#x2713;&lt;/td&gt;&lt;td&gt;&amp;#x2713;&lt;/td&gt;&lt;td&gt;&amp;#x2713;&lt;/td&gt;&lt;td&gt;&amp;#x2713;&lt;/td&gt;&lt;/tr&gt;
+            &lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Gizmo B&lt;/td&gt;&lt;td&gt;&lt;td&gt;&lt;td colspan=&quot;2&quot; style=&quot;text-align: center&quot;&gt;&amp;#x2713;&lt;/td&gt;&lt;/tr&gt;
+        &lt;/tbody&gt;
+    &lt;/table&gt;
+&lt;/div&gt;
+&lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (164503 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-02-21 22:37:29 UTC (rev 164503)
+++ trunk/Source/WebCore/ChangeLog        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -1,3 +1,40 @@
</span><ins>+2014-02-21  Daniel Bates  &lt;dabates@apple.com&gt;
+
+        COL element in table has 0 for offsetWidth
+        https://bugs.webkit.org/show_bug.cgi?id=15277
+
+        Reviewed by David Hyatt.
+
+        Implements offset{Left, Top, Width, Height} for table columns and column groups
+        per section Extensions to the HTMLElement Interface of the CSSOM View spec,
+        &lt;http://www.w3.org/TR/cssom-view/#extensions-to-the-htmlelement-interface&gt; (Draft 17 December 2013).
+
+        For now, we fail almost all of the offset{Height, Top} sub-tests in the included test
+        for the separate border model as we need to fix &lt;https://bugs.webkit.org/show_bug.cgi?id=128988&gt;.
+
+        Test: fast/table/col-and-colgroup-offsets.html
+
+        * rendering/RenderTable.cpp:
+        (WebCore::RenderTable::RenderTable): Initialize cached column offset top and offset height.
+        We cache these offsets since they are the same for all columns in the table.
+        (WebCore::RenderTable::invalidateCachedColumns): Clear cached effective column index map.
+        (WebCore::RenderTable::invalidateCachedColumnOffsets): Added.
+        (WebCore::RenderTable::layout): Invalidate cached column offsets as the location or height
+        of one or more sections may have changed.
+        (WebCore::RenderTable::updateColumnCache): Modified to build effective column index map.
+        (WebCore::RenderTable::effectiveIndexOfColumn): Added.
+        (WebCore::RenderTable::offsetTopForColumn): Added.
+        (WebCore::RenderTable::offsetLeftForColumn): Added.
+        (WebCore::RenderTable::offsetWidthForColumn): Added.
+        (WebCore::RenderTable::offsetHeightForColumn): Added.
+        * rendering/RenderTable.h: Make isTableColumnGroupWithColumnChildren() const.
+        * rendering/RenderTableCol.cpp:
+        (WebCore::RenderTableCol::offsetLeft): Added; turns around and calls RenderTable::offsetLeftForColumn().
+        (WebCore::RenderTableCol::offsetTop): Added; turns around and calls RenderTable::offsetTopForColumn().
+        (WebCore::RenderTableCol::offsetWidth): Added; turns around and calls RenderTable::offsetWidthForColumn().
+        (WebCore::RenderTableCol::offsetHeight): Added; turns around and calls RenderTable::offsetHeightForColumn().
+        * rendering/RenderTableCol.h:
+
</ins><span class="cx"> 2014-02-21  Enrica Casucci  &lt;enrica@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Build fix for iOS after r164498.
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderTablecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderTable.cpp (164503 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderTable.cpp        2014-02-21 22:37:29 UTC (rev 164503)
+++ trunk/Source/WebCore/rendering/RenderTable.cpp        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -4,7 +4,7 @@
</span><span class="cx">  *           (C) 1998 Waldo Bastian (bastian@kde.org)
</span><span class="cx">  *           (C) 1999 Lars Knoll (knoll@kde.org)
</span><span class="cx">  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
</span><del>- * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014 Apple Inc. All rights reserved.
</ins><span class="cx">  * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
</span><span class="cx">  *
</span><span class="cx">  * This library is free software; you can redistribute it and/or
</span><span class="lines">@@ -64,6 +64,8 @@
</span><span class="cx">     , m_vSpacing(0)
</span><span class="cx">     , m_borderStart(0)
</span><span class="cx">     , m_borderEnd(0)
</span><ins>+    , m_columnOffsetTop(-1)
+    , m_columnOffsetHeight(-1)
</ins><span class="cx"> {
</span><span class="cx">     setChildrenInline(false);
</span><span class="cx">     m_columnPos.fill(0, 1);
</span><span class="lines">@@ -237,8 +239,15 @@
</span><span class="cx"> {
</span><span class="cx">     m_columnRenderersValid = false;
</span><span class="cx">     m_columnRenderers.resize(0);
</span><ins>+    m_effectiveColumnIndexMap.clear();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void RenderTable::invalidateCachedColumnOffsets()
+{
+    m_columnOffsetTop = -1;
+    m_columnOffsetHeight = -1;
+}
+
</ins><span class="cx"> void RenderTable::addColumn(const RenderTableCol*)
</span><span class="cx"> {
</span><span class="cx">     invalidateCachedColumns();
</span><span class="lines">@@ -557,6 +566,9 @@
</span><span class="cx">     // Layout was changed, so probably borders too.
</span><span class="cx">     invalidateCollapsedBorders();
</span><span class="cx"> 
</span><ins>+    // The location or height of one or more sections may have changed.
+    invalidateCachedColumnOffsets();
+
</ins><span class="cx">     computeOverflow(clientLogicalBottom());
</span><span class="cx"> 
</span><span class="cx">     statePusher.pop();
</span><span class="lines">@@ -849,16 +861,100 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_hasColElements);
</span><span class="cx">     ASSERT(m_columnRenderers.isEmpty());
</span><ins>+    ASSERT(m_effectiveColumnIndexMap.isEmpty());
</ins><span class="cx">     ASSERT(!m_columnRenderersValid);
</span><span class="cx"> 
</span><ins>+    unsigned columnIndex = 0;
</ins><span class="cx">     for (RenderTableCol* columnRenderer = firstColumn(); columnRenderer; columnRenderer = columnRenderer-&gt;nextColumn()) {
</span><span class="cx">         if (columnRenderer-&gt;isTableColumnGroupWithColumnChildren())
</span><span class="cx">             continue;
</span><span class="cx">         m_columnRenderers.append(columnRenderer);
</span><ins>+        // FIXME: We should look to compute the effective column index successively from previous values instead of
+        // calling colToEffCol(), which is in O(numEffCols()). Although it's unlikely that this is a hot function.
+        m_effectiveColumnIndexMap.add(columnRenderer, colToEffCol(columnIndex));
+        columnIndex += columnRenderer-&gt;span();
</ins><span class="cx">     }
</span><span class="cx">     m_columnRenderersValid = true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+unsigned RenderTable::effectiveIndexOfColumn(const RenderTableCol&amp; column) const
+{
+    if (!m_columnRenderersValid)
+        updateColumnCache();
+    const RenderTableCol* columnToUse = &amp;column;
+    if (columnToUse-&gt;isTableColumnGroupWithColumnChildren())
+        columnToUse = columnToUse-&gt;nextColumn(); // First column in column-group
+    auto it = m_effectiveColumnIndexMap.find(columnToUse);
+    ASSERT(it != m_effectiveColumnIndexMap.end());
+    if (it == m_effectiveColumnIndexMap.end())
+        return std::numeric_limits&lt;unsigned&gt;::max();
+    return it-&gt;value;
+}
+
+LayoutUnit RenderTable::offsetTopForColumn(const RenderTableCol&amp; column) const
+{
+    if (effectiveIndexOfColumn(column) &gt;= numEffCols())
+        return 0;
+    if (m_columnOffsetTop &gt;= 0) {
+        ASSERT(!needsLayout());
+        return m_columnOffsetTop;
+    }
+    RenderTableSection* section = topNonEmptySection();
+    return m_columnOffsetTop = section ? section-&gt;offsetTop() : LayoutUnit(0);
+}
+
+LayoutUnit RenderTable::offsetLeftForColumn(const RenderTableCol&amp; column) const
+{
+    unsigned columnIndex = effectiveIndexOfColumn(column);
+    if (columnIndex &gt;= numEffCols())
+        return 0;
+    return m_columnPos[columnIndex] + m_hSpacing + borderLeft();
+}
+
+LayoutUnit RenderTable::offsetWidthForColumn(const RenderTableCol&amp; column) const
+{
+    const RenderTableCol* currentColumn = &amp;column;
+    bool hasColumnChildren;
+    if ((hasColumnChildren = currentColumn-&gt;isTableColumnGroupWithColumnChildren()))
+        currentColumn = currentColumn-&gt;nextColumn(); // First column in column-group
+    unsigned numberOfEffectiveColumns = numEffCols();
+    ASSERT_WITH_SECURITY_IMPLICATION(m_columnPos.size() &gt;= numberOfEffectiveColumns + 1);
+    unsigned width = 0;
+    while (currentColumn) {
+        unsigned columnIndex = effectiveIndexOfColumn(*currentColumn);
+        unsigned span = currentColumn-&gt;span();
+        while (span &amp;&amp; columnIndex &lt; numberOfEffectiveColumns) {
+            width += m_columnPos[columnIndex + 1] - m_columnPos[columnIndex] - m_hSpacing;
+            span -= m_columns[columnIndex].span;
+            ++columnIndex;
+            if (span)
+                width += m_hSpacing;
+        }
+        if (!hasColumnChildren)
+            break;
+        currentColumn = currentColumn-&gt;nextColumn();
+        if (!currentColumn || currentColumn-&gt;isTableColumnGroup())
+            break;
+        width += m_hSpacing;
+    }
+    return width;
+}
+
+LayoutUnit RenderTable::offsetHeightForColumn(const RenderTableCol&amp; column) const
+{
+    if (effectiveIndexOfColumn(column) &gt;= numEffCols())
+        return 0;
+    if (m_columnOffsetHeight &gt;= 0) {
+        ASSERT(!needsLayout());
+        return m_columnOffsetHeight;
+    }
+    LayoutUnit height = 0;
+    for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
+        height += section-&gt;offsetHeight();
+    m_columnOffsetHeight = height;
+    return m_columnOffsetHeight;
+}
+
</ins><span class="cx"> RenderTableCol* RenderTable::slowColElement(unsigned col, bool* startEdge, bool* endEdge) const
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_hasColElements);
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderTableh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderTable.h (164503 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderTable.h        2014-02-21 22:37:29 UTC (rev 164503)
+++ trunk/Source/WebCore/rendering/RenderTable.h        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -4,7 +4,7 @@
</span><span class="cx">  *           (C) 1998 Waldo Bastian (bastian@kde.org)
</span><span class="cx">  *           (C) 1999 Lars Knoll (knoll@kde.org)
</span><span class="cx">  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
</span><del>- * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2010 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2010, 2014 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * This library is free software; you can redistribute it and/or
</span><span class="cx">  * modify it under the terms of the GNU Library General Public
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> #include &quot;CSSPropertyNames.h&quot;
</span><span class="cx"> #include &quot;CollapsedBorderValue.h&quot;
</span><span class="cx"> #include &quot;RenderBlock.h&quot;
</span><ins>+#include &lt;wtf/HashMap.h&gt;
</ins><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="lines">@@ -263,6 +264,11 @@
</span><span class="cx">     void addColumn(const RenderTableCol*);
</span><span class="cx">     void removeColumn(const RenderTableCol*);
</span><span class="cx"> 
</span><ins>+    LayoutUnit offsetTopForColumn(const RenderTableCol&amp;) const;
+    LayoutUnit offsetLeftForColumn(const RenderTableCol&amp;) const;
+    LayoutUnit offsetWidthForColumn(const RenderTableCol&amp;) const;
+    LayoutUnit offsetHeightForColumn(const RenderTableCol&amp;) const;
+
</ins><span class="cx"> protected:
</span><span class="cx">     virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle) override final;
</span><span class="cx">     virtual void simplifiedNormalFlowLayout() override final;
</span><span class="lines">@@ -292,6 +298,8 @@
</span><span class="cx">     void updateColumnCache() const;
</span><span class="cx">     void invalidateCachedColumns();
</span><span class="cx"> 
</span><ins>+    void invalidateCachedColumnOffsets();
+
</ins><span class="cx">     virtual RenderBlock* firstLineBlock() const override final;
</span><span class="cx">     virtual void updateFirstLetter() override final;
</span><span class="cx">     
</span><span class="lines">@@ -318,6 +326,10 @@
</span><span class="cx">     mutable Vector&lt;RenderTableCaption*&gt; m_captions;
</span><span class="cx">     mutable Vector&lt;RenderTableCol*&gt; m_columnRenderers;
</span><span class="cx"> 
</span><ins>+    unsigned effectiveIndexOfColumn(const RenderTableCol&amp;) const;
+    typedef HashMap&lt;const RenderTableCol*, unsigned&gt; EffectiveColumnIndexMap;
+    mutable EffectiveColumnIndexMap m_effectiveColumnIndexMap;
+
</ins><span class="cx">     mutable RenderTableSection* m_head;
</span><span class="cx">     mutable RenderTableSection* m_foot;
</span><span class="cx">     mutable RenderTableSection* m_firstBody;
</span><span class="lines">@@ -338,6 +350,8 @@
</span><span class="cx">     short m_vSpacing;
</span><span class="cx">     int m_borderStart;
</span><span class="cx">     int m_borderEnd;
</span><ins>+    mutable LayoutUnit m_columnOffsetTop;
+    mutable LayoutUnit m_columnOffsetHeight;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> inline RenderTableSection* RenderTable::topSection() const
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderTableColcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderTableCol.cpp (164503 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderTableCol.cpp        2014-02-21 22:37:29 UTC (rev 164503)
+++ trunk/Source/WebCore/rendering/RenderTableCol.cpp        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -200,4 +200,24 @@
</span><span class="cx">     return style().borderEnd();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+LayoutUnit RenderTableCol::offsetLeft() const
+{
+    return table()-&gt;offsetLeftForColumn(*this);
</ins><span class="cx"> }
</span><ins>+
+LayoutUnit RenderTableCol::offsetTop() const
+{
+    return table()-&gt;offsetTopForColumn(*this);
+}
+
+LayoutUnit RenderTableCol::offsetWidth() const
+{
+    return table()-&gt;offsetWidthForColumn(*this);
+}
+
+LayoutUnit RenderTableCol::offsetHeight() const
+{
+    return table()-&gt;offsetHeightForColumn(*this);
+}
+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCorerenderingRenderTableColh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/RenderTableCol.h (164503 => 164504)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/RenderTableCol.h        2014-02-21 22:37:29 UTC (rev 164503)
+++ trunk/Source/WebCore/rendering/RenderTableCol.h        2014-02-21 22:38:12 UTC (rev 164504)
</span><span class="lines">@@ -43,7 +43,7 @@
</span><span class="cx">     unsigned span() const { return m_span; }
</span><span class="cx">     void setSpan(unsigned span) { m_span = span; }
</span><span class="cx"> 
</span><del>-    bool isTableColumnGroupWithColumnChildren() { return firstChild(); }
</del><ins>+    bool isTableColumnGroupWithColumnChildren() const { return firstChild(); }
</ins><span class="cx">     bool isTableColumn() const { return style().display() == TABLE_COLUMN; }
</span><span class="cx">     bool isTableColumnGroup() const { return style().display() == TABLE_COLUMN_GROUP; }
</span><span class="cx"> 
</span><span class="lines">@@ -71,6 +71,11 @@
</span><span class="cx">     const BorderValue&amp; borderAdjoiningCellBefore(const RenderTableCell*) const;
</span><span class="cx">     const BorderValue&amp; borderAdjoiningCellAfter(const RenderTableCell*) const;
</span><span class="cx"> 
</span><ins>+    virtual LayoutUnit offsetLeft() const override;
+    virtual LayoutUnit offsetTop() const override;
+    virtual LayoutUnit offsetWidth() const override;
+    virtual LayoutUnit offsetHeight() const override;
+
</ins><span class="cx"> private:
</span><span class="cx">     virtual const char* renderName() const override { return &quot;RenderTableCol&quot;; }
</span><span class="cx">     virtual bool isRenderTableCol() const override { return true; }
</span></span></pre>
</div>
</div>

</body>
</html>