HTML: Tests for button layout

Follows https://github.com/whatwg/html/pull/4143.
diff --git a/html/rendering/non-replaced-elements/form-controls/button-style.html b/html/rendering/non-replaced-elements/form-controls/button-style.html
new file mode 100644
index 0000000..2d23b4a
--- /dev/null
+++ b/html/rendering/non-replaced-elements/form-controls/button-style.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>default style on buttons</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div class="tests">
+ <input type="reset">
+ <input type="button">
+ <input type="submit">
+ <input type="color">
+ <button></button>
+</div>
+<script>
+[].slice.call(document.querySelectorAll('.tests > *')).forEach(el => {
+  test(() => {
+    assert_equals(getComputedStyle(el).display, 'inline-block');
+  }, `default display on ${el.outerHTML}`);
+  test(() => {
+    assert_equals(getComputedStyle(el).boxSizing, 'border-box');
+  }, `default box-sizing on ${el.outerHTML}`);})
+</script>
\ No newline at end of file
diff --git a/html/rendering/widgets/button-layout/abspos.html b/html/rendering/widgets/button-layout/abspos.html
new file mode 100644
index 0000000..c3089b1
--- /dev/null
+++ b/html/rendering/widgets/button-layout/abspos.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>abspos button with auto width, non-auto left/right</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<link rel=help href=https://html.spec.whatwg.org/multipage/rendering.html#button-layout>
+<!--
+If the element is absolutely positioned, then for the purpose of the CSS visual formatting model, act as if the element is a replaced element.  [CSS]
+-->
+<link rel=help href=https://drafts.csswg.org/css2/visudet.html#abs-replaced-width>
+<!--
+If at this point the values are over-constrained, ignore the value for either 'left' (in case the 'direction' property of the containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') and solve for that value.
+-->
+<style>
+#overconstrained { position: absolute; left: 100px; right: 100px; }
+#ltr { position: absolute; left: 100px; margin-top: 2em }
+#rtl { position: absolute; right: 100px; margin-top: 2em }
+</style>
+<button id=overconstrained>test</button>
+<button id=ltr>test</button>
+<button id=rtl>test</button>
+<script>
+for (const dir of ['rtl', 'ltr']) {
+  test(() => {
+    document.documentElement.dir = dir;
+    const overconstrained = document.getElementById('overconstrained');
+    const ref = document.getElementById(dir);
+    assert_equals(overconstrained.offsetLeft, ref.offsetLeft, 'offsetLeft');
+    assert_equals(overconstrained.clientWidth, ref.clientWidth, 'clientWidth');
+  }, `${document.title} (${dir})`);
+}
+</script>
diff --git a/html/rendering/widgets/button-layout/anonymous-button-content-box-ref.html b/html/rendering/widgets/button-layout/anonymous-button-content-box-ref.html
new file mode 100644
index 0000000..243f0ad
--- /dev/null
+++ b/html/rendering/widgets/button-layout/anonymous-button-content-box-ref.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Reference for Anonymous button content box</title>
+<style>
+body { margin: 0 }
+.button { width: 124px; height: 124px; background: papayawhip; border: 2px solid; display: table-cell; vertical-align: middle; text-align: center; box-sizing: border-box; }
+.top { vertical-align: top }
+.left { text-align: left }
+.float { display: block; float: left; }
+body > div { clear: left; }
+.button > div { width: 50px; height: 50px; border: solid }
+.button > .tall { height: 150px }
+.button > .wide { width: 150px; margin-top: 32px }
+</style>
+<div>
+ <div class=button>input</div>
+ <div class="button">input grid</div>
+ <div class="button">input flex</div>
+ <div class=button>button</div>
+</div>
+<div>
+ <div class=button style="text-align: left">button left</div>
+ <div class=button><div>div</div></div>
+ <div class="button top">grid</div>
+ <div class="button top"><div>grid</div></div>
+</div>
+<div>
+ <div class="button top left float">flex</div>
+ <div class="button top float"><div>flex</div></div>
+ <div class="button float"><div class=tall>tall</div></div>
+ <div class="button float"><div class=wide>wide</div></div>
+</div>
diff --git a/html/rendering/widgets/button-layout/anonymous-button-content-box.html b/html/rendering/widgets/button-layout/anonymous-button-content-box.html
new file mode 100644
index 0000000..07c7b8e
--- /dev/null
+++ b/html/rendering/widgets/button-layout/anonymous-button-content-box.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>Anonymous button content box</title>
+<link rel=match href=anonymous-button-content-box-ref.html>
+<style>
+body { margin: 0 }
+button, input { width: 124px; height: 124px; background: papayawhip; border: 2px solid; margin: 0; padding: 0; float: left; font: inherit }
+body > div { clear: left; }
+button > div { width: 50px; height: 50px; border: solid }
+.grid { display: grid }
+.flex { display: flex }
+.tall { height: 150px }
+.wide { width: 150px }
+</style>
+<div>
+ <input type=button value=input>
+ <input type=button value="input grid" class=grid>
+ <input type=button value="input flex" class=flex>
+ <button>button</button>
+</div>
+<div>
+ <button style="text-align: left">button left</button>
+ <button><div>div</div></button>
+ <button class=grid>grid</button>
+ <button class=grid><div>grid</div></button>
+</div>
+<div>
+ <button class=flex>flex</button>
+ <button class=flex><div>flex</div></button>
+ <button><div class=tall>tall</div></button>
+ <button><div class=wide>wide</div></button>
+</div>
diff --git a/html/rendering/widgets/button-layout/computed-style.html b/html/rendering/widgets/button-layout/computed-style.html
new file mode 100644
index 0000000..696b551
--- /dev/null
+++ b/html/rendering/widgets/button-layout/computed-style.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>computed style on buttons</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div class="tests">
+ <input type="reset">
+ <input type="button">
+ <input type="submit">
+ <input type="color">
+ <button></button>
+</div>
+<script>
+// "behave as" doesn't change computed value.
+const displayValues = ['inline', 'block', 'list-item', 'inline-block', 'table', 'inline-table', 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row', 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none', 'contents', 'flow', 'flow-root', 'flex', 'grid', 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', 'ruby-text-container', 'inline-flex', 'inline-grid'];
+for (const val of displayValues) {
+  [].slice.call(document.querySelectorAll('.tests > *')).forEach(el => {
+    el.style.display = ''
+    el.style.display = val;
+    const attrs = el.type ? ` type=${el.type}` : '';
+    const tag = `<${el.localName}${attrs}>`;
+    test(() => {
+     assert_not_equals(el.style.display, '', `display: ${val} is not supported`)
+      let expectedVal = val;
+      if (el instanceof HTMLInputElement && val === 'contents') {
+        expectedVal = 'none'; // https://drafts.csswg.org/css-display/#unbox-html
+      }
+      assert_equals(getComputedStyle(el).display, expectedVal);
+    }, `computed display of ${tag} with display: ${val}`);
+  });
+}
+</script>
\ No newline at end of file
diff --git a/html/rendering/widgets/button-layout/display-other.html b/html/rendering/widgets/button-layout/display-other.html
new file mode 100644
index 0000000..6ed3f58
--- /dev/null
+++ b/html/rendering/widgets/button-layout/display-other.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<title>button with other display values</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+body { margin: 0 }
+.float { width: 100px; height: 100px; float: left; background: blue; margin: 10px }
+</style>
+<div class=float></div>
+<button id=button style="display: block;"><div class=float></div></button><span id=after>after</span>
+<script>
+// These should all behave as flow-root.
+const displayValues = ['run-in', 'flow', 'flow-root', 'table', 'list-item',
+                       'table-row-group', 'table-header-group', 'table-footer-group',
+                       'table-row', 'table-cell', 'table-column-group', 'table-column',
+                       'table-caption', 'ruby-base', 'ruby-text', 'ruby-base-container',
+                       'ruby-text-container'];
+const button = document.getElementById('button');
+const after = document.getElementById('after');
+function getValues() {
+  return {
+    buttonLeft: button.offsetLeft,
+    buttonTop: button.offsetTop,
+    buttonWidth: button.clientWidth,
+    buttonHeight: button.clientHeight,
+    afterLeft: after.offsetLeft,
+    afterTop: after.offsetTop,
+  };
+}
+const expected = getValues();
+test(t => {
+  assert_equals(expected.buttonLeft, 120, 'buttonLeft');
+  assert_equals(expected.buttonTop, 0, 'buttonTop');
+  assert_greater_than_equal(expected.buttonWidth, 120, 'buttonWidth');
+  assert_greater_than_equal(expected.buttonHeight, 120, 'buttonHeight');
+  assert_equals(expected.afterLeft, 0, 'afterLeft');
+  assert_greater_than_equal(expected.afterTop, 120, 'afterTop');
+}, 'display: block');
+for (const val of displayValues) {
+  test(t => {
+    t.add_cleanup(() => {
+      button.style.display = 'block';
+    });
+    assert_true(CSS.supports(`display: ${val}`), `display: ${val} is not supported`);
+    button.style.display = val;
+    const values = getValues();
+    for (const prop in values) {
+      assert_equals(values[prop], expected[prop], prop);
+    }
+  }, `display: ${val}`);
+}
+</script>
diff --git a/html/rendering/widgets/button-layout/flex.html b/html/rendering/widgets/button-layout/flex.html
new file mode 100644
index 0000000..ce845eb
--- /dev/null
+++ b/html/rendering/widgets/button-layout/flex.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>button with flex/inline-flex</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#inline-flex { display: inline-flex }
+#flex { display: flex }
+#ref > div { display: flex }
+</style>
+<button id=inline-flex><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=flex><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=ref><div><div>1</div><div>2</div><div>3</div><div>4</div></div></button>
+<script>
+const ref = document.getElementById('ref');
+const expectedWidth = ref.clientWidth;
+const expectedHeight = ref.clientHeight;
+for (const elm of [document.getElementById('inline-flex'),
+                   document.getElementById('flex')]) {
+  test(() => {
+    // check that flex is supported
+    const flexValue = elm.id;
+    assert_equals(getComputedStyle(elm).display, flexValue, `${flexValue} is not supported`);
+    const width = elm.clientWidth;
+    const height = elm.clientHeight;
+    assert_equals(width, expectedWidth, 'clientWidth');
+    assert_equals(height, expectedHeight, 'clientHeight');
+  }, elm.id);
+}
+</script>
diff --git a/html/rendering/widgets/button-layout/grid.html b/html/rendering/widgets/button-layout/grid.html
new file mode 100644
index 0000000..7c2a467
--- /dev/null
+++ b/html/rendering/widgets/button-layout/grid.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>button with grid/inline-grid</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#inline-grid { display: inline-grid }
+#grid { display: grid }
+#ref > div { display: grid }
+#inline-grid, #grid, #ref > div { grid-template: auto auto / auto auto }
+</style>
+<button id=inline-grid><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=grid><div>1</div><div>2</div><div>3</div><div>4</div></button>
+<button id=ref><div><div>1</div><div>2</div><div>3</div><div>4</div></div></button>
+<script>
+const ref = document.getElementById('ref');
+const expectedWidth = ref.clientWidth;
+const expectedHeight = ref.clientHeight;
+for (const elm of [document.getElementById('inline-grid'),
+                   document.getElementById('grid')]) {
+  test(() => {
+    // check that grid is supported
+    const gridValue = elm.id;
+    assert_equals(getComputedStyle(elm).display, gridValue, `${gridValue} is not supported`);
+    const width = elm.clientWidth;
+    const height = elm.clientHeight;
+    assert_equals(width, expectedWidth, 'clientWidth');
+    assert_equals(height, expectedHeight, 'clientHeight');
+  }, elm.id);
+}
+</script>
diff --git a/html/rendering/widgets/button-layout/inline-level-ref.html b/html/rendering/widgets/button-layout/inline-level-ref.html
new file mode 100644
index 0000000..3784cc3
--- /dev/null
+++ b/html/rendering/widgets/button-layout/inline-level-ref.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>Reference for button with inline-level display</title>
+<style>
+button { font: inherit }
+</style>
+<p>There should be three buttons below containing "1" and "2" on separate lines, and "a" and "b" before and after on the same baseline as the "2".</p>
+<p>a<button>1<br>2</button>b</p>
+<p>a<button>1<br>2</button>b</p>
+<p>a<button>1<br>2</button>b</p>
+<p>a<button>1<br>2</button>b</p>
diff --git a/html/rendering/widgets/button-layout/inline-level.html b/html/rendering/widgets/button-layout/inline-level.html
new file mode 100644
index 0000000..e23aba7
--- /dev/null
+++ b/html/rendering/widgets/button-layout/inline-level.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>button with inline-level display</title>
+<link rel=match href=inline-level-ref.html>
+<style>
+button, input { font: inherit }
+.inline { display: inline }
+.inline-block { display: inline-block }
+.inline-table { display: inline-table }
+</style>
+<p>There should be three buttons below containing "1" and "2" on separate lines, and "a" and "b" before and after on the same baseline as the "2".</p>
+<p>a<button class=inline>1<br><span class=check-baseline>2</span></button><span class=ref-baseline>b</span></p>
+<p>a<button class=inline-block>1<br><span class=check-baseline>2</span></button><span class=ref-baseline>b</span></p>
+<p>a<button class=inline-table>1<br><span class=check-baseline>2</span></button><span class=ref-baseline>b</span></p>
+<p>a<input type=button class=inline-table value="1&#10;2">b</p>
+<script>
+const spans = document.querySelectorAll('.check-baseline');
+for (const span of [].slice.call(spans)) {
+  const baseline = span.offsetTop + span.offsetHeight;
+  const refSpan = span.parentNode.nextSibling;
+  const refBaseline = refSpan.offsetTop + refSpan.offsetHeight;
+  if (baseline !== refBaseline) {
+    refSpan.textContent += " (wrong baseline)";
+  }
+}
+</script>
diff --git a/html/rendering/widgets/button-layout/propagate-text-decoration-ref.html b/html/rendering/widgets/button-layout/propagate-text-decoration-ref.html
new file mode 100644
index 0000000..f33a011
--- /dev/null
+++ b/html/rendering/widgets/button-layout/propagate-text-decoration-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<title>Reference for propagating text-decoration into buttons</title>
+<style>
+button { font: inherit }
+</style>
+<p>The text in the following buttons should be underlined.</p>
+<p><button><u>foo</u></button><button><u>foo</u></button></p>
+<p>The text in the following buttons should NOT be underlined.</p>
+<p><button>foo</button><button>foo</button></p>
diff --git a/html/rendering/widgets/button-layout/propagate-text-decoration.html b/html/rendering/widgets/button-layout/propagate-text-decoration.html
new file mode 100644
index 0000000..9bdbbef
--- /dev/null
+++ b/html/rendering/widgets/button-layout/propagate-text-decoration.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>propagating text-decoration into buttons</title>
+<link rel=match href=propagate-text-decoration-ref.html>
+<style>
+button, input { font: inherit }
+.inline > u > * { display: inline }
+.inline-block > u > * { display: inline-block }
+</style>
+<p>The text in the following buttons should be underlined.</p>
+<p class=inline><u><button>foo</button><input type=button value=foo></u></p>
+<p>The text in the following buttons should NOT be underlined.</p>
+<p class=inline-block><u><button>foo</button><input type=button value=foo></u></p>
diff --git a/html/rendering/widgets/button-layout/shrink-wrap.html b/html/rendering/widgets/button-layout/shrink-wrap.html
new file mode 100644
index 0000000..6d61102
--- /dev/null
+++ b/html/rendering/widgets/button-layout/shrink-wrap.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<title>shrink-wrap button</title>
+<link rel=help href=https://html.spec.whatwg.org/multipage/rendering.html#button-layout>
+<link rel=help href=https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<!--
+ minimum preferred width 100px
+ preferred width         250px
+ available width         vary
+-->
+<style>
+div.available-width { border: thin dotted red; margin: 1em 0; padding: 1em 0 }
+button { border: none; margin: 0; padding: 0; background: papayawhip; }
+button > span.minimum-preferred-width { width: 100px }
+button > span { width: 50px; display: inline-block; outline: thin dotted blue }
+</style>
+<div class="available-width" style="width: 50px">
+ <button data-expected="100"><span class="minimum-preferred-width">x</span><span>x</span><span>x</span><span>x</span></button>
+</div>
+
+<div class="available-width" style="width: 200px">
+ <button data-expected="200"><span class="minimum-preferred-width">x</span><span>x</span><span>x</span><span>x</span></button>
+</div>
+
+<div class="available-width" style="width: 300px">
+ <button data-expected="250"><span class="minimum-preferred-width">x</span><span>x</span><span>x</span><span>x</span></button>
+</div>
+
+<script>
+const styles = ['display: block', 'display: inline', 'display: inline-block',
+                'display: list-item', 'display: table', 'display: table-row',
+                'display: table-cell', 'float: left'];
+for (const style of styles) {
+  for (const button of [].slice.call(document.querySelectorAll('button'))) {
+    test(() => {
+      button.setAttribute('style', style);
+      assert_equals(button.clientWidth, parseInt(button.dataset.expected, 10));
+    }, `${style} - available ${button.parentNode.getAttribute('style')}`);
+  }
+}
+</script>