+2017-11-28 Daniel Bates <dabates@apple.com>
+
+ [Cocoa] First pass at implementing alternative presentation button element
+ https://bugs.webkit.org/show_bug.cgi?id=179785
+ Part of <rdar://problem/34917108>
+
+ Reviewed by Brent Fulgham.
+
+ Adds tests to ensure that we can apply and remove the substitution of one or more
+ elements with the alternative presentation button.
+
+ Also added some accessibility tests to ensure that the alternative presentation button
+ can be seen and hit tested by the accessibility machinery. When the alternative presentation
+ button is substituted for an <input> it masquerades as a text button and when it is
+ substituted for an arbitrary HTML element it masquerades as the original element. As a
+ result the accessibility machinery shows an empty role description in the former case
+ because it does find the ARIA label for the button and the accessibility element hierarchy
+ may be incorrect in the latter case. We will fix these issues in a subsequent commit(s).
+
+ * TestExpectations: Skip the test on all platforms. We will selectively enable
+ tests on Cocoa platforms (below).
+ * accessibility/alternative-presentation-button-expected.txt: Added.
+ * accessibility/alternative-presentation-button-input-type-expected.txt: Added.
+ * accessibility/alternative-presentation-button-input-type.html: Added.
+ * accessibility/alternative-presentation-button.html: Added.
+ * fast/forms/alternative-presentation-button/replace-and-remove-expected.html: Added.
+ * fast/forms/alternative-presentation-button/replace-and-remove.html: Added.
+ * fast/forms/alternative-presentation-button/replacement-expected.txt: Added.
+ * fast/forms/alternative-presentation-button/replacement.html: Added.
+ * platform/ios/TestExpectations: Mark tests as PASS so that we run them.
+ * platform/ios/fast/forms/alternative-presentation-button/replacement-expected.txt: Added.
+ * platform/mac/TestExpectations: Mark tests as PASS so that we run them.
+
2017-11-28 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r225209.
swipe [ Skip ]
fast/zooming/ios [ Skip ]
fast/forms/ios [ Skip ]
+fast/forms/alternative-presentation-button [ Skip ]
fast/viewport/ios [ Skip ]
fast/visual-viewport/ios/ [ Skip ]
fast/events/ios [ Skip ]
--- /dev/null
+This tests that the alternative presentation button is an accessible element and can be hit test.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Role: AXRole: AXButton
+Description: AXDescription: alternative presentation button
+
+Hit test alternative presentation button:
+PASS axAlternativePresentationButton.elementAtPoint(x, y).isEqual(axAlternativePresentationButton) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+
+This tests that the alternative presentation button is an accessible element and can be hit test.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+Before substitution:
+Role: AXRole: AXTextField
+Description: AXDescription:
+
+After substitution:
+Role: AXRole: AXButton
+Description: AXDescription:
+
+Hit test alternative presentation button:
+PASS axTextField.elementAtPoint(x, y).isEqual(axTextField) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../resources/js-test.js"></script>
+</head>
+<body>
+<input type="text" id="textfield">
+<p id="description"></p>
+<div id="console"></div>
+<script>
+description("This tests that the alternative presentation button is an accessible element and can be hit test.");
+
+if (window.accessibilityController) {
+ debug("<br>Before substitution:");
+ var axTextField = accessibilityController.accessibleElementById("textfield");
+ dumpRoleAndDescription(axTextField);
+
+ debug("<br>After substitution:");
+ window.internals.substituteWithAlternativePresentationButton([document.getElementById("textfield")], 1);
+ axTextField = accessibilityController.accessibleElementById("textfield");
+ dumpRoleAndDescription(axTextField);
+
+ debug("<br>Hit test alternative presentation button:");
+ var x = axTextField.clickPointX;
+ var y = axTextField.clickPointY;
+ shouldBeTrue("axTextField.elementAtPoint(x, y).isEqual(axTextField)");
+}
+
+function dumpRoleAndDescription(accessibleElement)
+{
+ debug("Role: " + accessibleElement.role);
+ debug("Description: " + accessibleElement.description);
+}
+</script>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../resources/js-test.js"></script>
+</head>
+<body>
+<div id="test">
+ <h2>First name</h2>
+ <input type="text" id="first-name">
+ <h2>Last name</h2>
+ <input type="text" id="last-name">
+</div>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+description("This tests that the alternative presentation button is an accessible element and can be hit test.");
+
+if (window.accessibilityController) {
+ window.internals.substituteWithAlternativePresentationButton(document.getElementById("test").children, 1);
+ var axAlternativePresentationButton = accessibilityController.focusedElement.childAtIndex(0).childAtIndex(0);
+ dumpRoleAndDescription(axAlternativePresentationButton);
+
+ debug("<br>Hit test alternative presentation button:");
+ var x = axAlternativePresentationButton.clickPointX;
+ var y = axAlternativePresentationButton.clickPointY;
+ shouldBeTrue("axAlternativePresentationButton.elementAtPoint(x, y).isEqual(axAlternativePresentationButton)");
+}
+
+function dumpRoleAndDescription(accessibleElement)
+{
+ debug("Role: " + accessibleElement.role);
+ debug("Description: " + accessibleElement.description);
+}
+</script>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<body>
+<div id="test-container">
+ <div class="test">
+ <input type="text">
+ </div>
+ <div class="test">
+ <label for="first-name">First name</label>
+ <input type="text" id="first-name">
+ <label for="last-name">Last name</label>
+ <input type="text" id="last-name">
+ </div>
+ <div class="test">
+ <p>First name</p>
+ <input type="text" id="first-name">
+ <p>Last name</p>
+ <input type="text" id="last-name">
+ </div>
+ <div class="test">
+ <label>Name <input type="text"></label>
+ <p>Some more text.</p>
+ </div>
+ <div>
+ <table>
+ <tr class="test">
+ <td>First name</td>
+ <td><input type="text"></td>
+ <td>Last name</td>
+ <td><input type="text"></td>
+ </tr>
+ </table>
+ </div>
+ <div>
+ <table>
+ <tr class="test">
+ <td>First name <input type="text"></td>
+ <td>Last name <input type="text"></td>
+ </tr>
+ </table>
+ </div>
+ <p>Name</p>
+</div>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html>
+<body>
+<div id="test-container">
+ <div class="test">
+ <input type="text">
+ </div>
+ <div class="test">
+ <label for="first-name">First name</label>
+ <input type="text" id="first-name">
+ <label for="last-name">Last name</label>
+ <input type="text" id="last-name">
+ </div>
+ <div class="test">
+ <p>First name</p>
+ <input type="text" id="first-name">
+ <p>Last name</p>
+ <input type="text" id="last-name">
+ </div>
+ <div class="test">
+ <label>Name <input type="text"></label>
+ <p>Some more text.</p>
+ </div>
+ <div>
+ <table>
+ <tr class="test">
+ <td>First name</td>
+ <td><input type="text"></td>
+ <td>Last name</td>
+ <td><input type="text"></td>
+ </tr>
+ </table>
+ </div>
+ <div>
+ <table>
+ <tr class="test">
+ <td>First name <input type="text"></td>
+ <td>Last name <input type="text"></td>
+ </tr>
+ </table>
+ </div>
+</div>
+<script>
+var id = 0;
+var testContainer = document.getElementById("test-container");
+var tests = testContainer.getElementsByClassName("test");
+for (let test of tests) {
+ if (window.internals)
+ internals.substituteWithAlternativePresentationButton(test.children, ++id);
+}
+
+// Programmatically add element
+var pElement = document.createElement("p");
+pElement.textContent = "Name";
+testContainer.appendChild(pElement);
+if (window.internals)
+ internals.substituteWithAlternativePresentationButton([pElement], ++id);
+
+for (let i = id; i; ) {
+ if (window.internals)
+ internals.removeAlternativePresentationButton(i);
+ --i;
+}
+</script>
+</body>
+</html>
--- /dev/null
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x336
+ RenderBlock {HTML} at (0,0) size 800x336
+ RenderBody {BODY} at (8,8) size 784x312
+ RenderBlock {DIV} at (0,0) size 784x312
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderButton {INPUT} at (2,2) size 211x32 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+ RenderBlock (anonymous) at (3,3) size 205x26
+ RenderBlock {DIV} at (0,0) size 205x26
+ RenderBlock {DIV} at (0,0) size 205x0
+ RenderBlock {DIV} at (0,0) size 205x0
+ RenderBlock {DIV} at (0,0) size 205x26
+ RenderBlock (anonymous) at (0,0) size 205x13
+ RenderInline {SPAN} at (0,0) size 185x13
+ RenderText {#text} at (0,0) size 185x13
+ text run at (0,0) width 185: "alternative presentation button title"
+ RenderBlock {DIV} at (0,13) size 205x13
+ RenderText {#text} at (0,0) size 205x13
+ text run at (0,0) width 205: "alternative presentation button subtitle"
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,36) size 784x36
+ RenderBlock (anonymous) at (0,0) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderBlock (anonymous) at (0,0) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock (anonymous) at (0,0) size 784x18
+ RenderInline {SPAN} at (0,0) size 224x18
+ RenderText {#text} at (0,0) size 224x18
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,18) size 784x18
+ RenderText {#text} at (0,0) size 246x18
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock (anonymous) at (0,36) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,88) size 784x36
+ RenderBlock {P} at (0,0) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock (anonymous) at (0,0) size 784x18
+ RenderInline {SPAN} at (0,0) size 224x18
+ RenderText {#text} at (0,0) size 224x18
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,18) size 784x18
+ RenderText {#text} at (0,0) size 246x18
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock {DIV} at (0,140) size 784x36
+ RenderBlock (anonymous) at (0,0) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderBlock (anonymous) at (0,0) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock (anonymous) at (0,0) size 784x18
+ RenderInline {SPAN} at (0,0) size 224x18
+ RenderText {#text} at (0,0) size 224x18
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,18) size 784x18
+ RenderText {#text} at (0,0) size 246x18
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock (anonymous) at (0,36) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,176) size 784x42
+ RenderTable {TABLE} at (0,0) size 252x42
+ RenderTableSection {TBODY} at (0,0) size 252x42
+ RenderTableRow {TR} at (0,2) size 252x38
+ RenderTableCell {TD} at (2,2) size 248x38 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 246x36
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x36
+ RenderBlock (anonymous) at (0,0) size 246x18
+ RenderInline {SPAN} at (0,0) size 224x18
+ RenderText {#text} at (0,0) size 224x18
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,18) size 246x18
+ RenderText {#text} at (0,0) size 246x18
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock {DIV} at (0,218) size 784x42
+ RenderTable {TABLE} at (0,0) size 252x42
+ RenderTableSection {TBODY} at (0,0) size 252x42
+ RenderTableRow {TR} at (0,2) size 252x38
+ RenderTableCell {TD} at (2,2) size 248x38 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 246x36
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x36
+ RenderBlock (anonymous) at (0,0) size 246x18
+ RenderInline {SPAN} at (0,0) size 224x18
+ RenderText {#text} at (0,0) size 224x18
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,18) size 246x18
+ RenderText {#text} at (0,0) size 246x18
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock {P} at (0,276) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x36
+ RenderBlock (anonymous) at (0,0) size 784x18
+ RenderInline {SPAN} at (0,0) size 224x18
+ RenderText {#text} at (0,0) size 224x18
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,18) size 784x18
+ RenderText {#text} at (0,0) size 246x18
+ text run at (0,0) width 246: "alternative presentation button subtitle"
--- /dev/null
+<!DOCTYPE html>
+<html>
+<body>
+<div id="test-container">
+ <div class="test">
+ <input type="text">
+ </div>
+ <div class="test">
+ <label for="first-name">First name</label>
+ <input type="text" id="first-name">
+ <label for="last-name">Last name</label>
+ <input type="text" id="last-name">
+ </div>
+ <div class="test">
+ <p>First name</p>
+ <input type="text" id="first-name">
+ <p>Last name</p>
+ <input type="text" id="last-name">
+ </div>
+ <div class="test">
+ <label>Name <input type="text"></label>
+ <p>Some more text.</p>
+ </div>
+ <div>
+ <table>
+ <tr class="test">
+ <td>First name</td>
+ <td><input type="text"></td>
+ <td>Last name</td>
+ <td><input type="text"></td>
+ </tr>
+ </table>
+ </div>
+ <div>
+ <table>
+ <tr class="test">
+ <td>First name <input type="text"></td>
+ <td>Last name <input type="text"></td>
+ </tr>
+ </table>
+ </div>
+</div>
+<script>
+var id = 0;
+var testContainer = document.getElementById("test-container");
+var tests = testContainer.getElementsByClassName("test");
+for (let test of tests) {
+ if (window.internals)
+ internals.substituteWithAlternativePresentationButton(test.children, ++id);
+}
+
+// Programmatically add element
+var pElement = testContainer.appendChild(document.createElement("p"));
+if (window.internals)
+ internals.substituteWithAlternativePresentationButton([pElement], ++id);
+</script>
+</body>
+</html>
media/ios [ Pass ]
quicklook [ Pass ]
+fast/forms/alternative-presentation-button [ Pass ]
fast/text-autosizing/ios [ Pass ]
fast/zooming/ios [ Pass ]
http/tests/preload/viewport [ Pass ]
--- /dev/null
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x363
+ RenderBlock {HTML} at (0,0) size 800x363
+ RenderBody {BODY} at (8,8) size 784x339
+ RenderBlock {DIV} at (0,0) size 784x339
+ RenderBlock {DIV} at (0,0) size 784x39
+ RenderButton {INPUT} at (2,2) size 199x36 [bgcolor=#FFFFFF] [border: (1px solid #4C4C4C)]
+ RenderBlock (anonymous) at (6,3) size 187x29
+ RenderBlock {DIV} at (0,0) size 186x28
+ RenderBlock {DIV} at (0,0) size 186x0
+ RenderBlock {DIV} at (0,0) size 186x0
+ RenderBlock {DIV} at (0,0) size 186x28
+ RenderBlock (anonymous) at (0,0) size 186x14
+ RenderInline {SPAN} at (0,0) size 169x14
+ RenderText {#text} at (0,0) size 169x14
+ text run at (0,0) width 169: "alternative presentation button title"
+ RenderBlock {DIV} at (0,14) size 186x14
+ RenderText {#text} at (0,0) size 186x14
+ text run at (0,0) width 186: "alternative presentation button subtitle"
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,39) size 784x40
+ RenderBlock (anonymous) at (0,0) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderBlock (anonymous) at (0,0) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock (anonymous) at (0,0) size 784x20
+ RenderInline {SPAN} at (0,0) size 224x19
+ RenderText {#text} at (0,0) size 224x19
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,20) size 784x20
+ RenderText {#text} at (0,0) size 246x19
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock (anonymous) at (0,40) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,95) size 784x40
+ RenderBlock {P} at (0,0) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock (anonymous) at (0,0) size 784x20
+ RenderInline {SPAN} at (0,0) size 224x19
+ RenderText {#text} at (0,0) size 224x19
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,20) size 784x20
+ RenderText {#text} at (0,0) size 246x19
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock {DIV} at (0,151) size 784x40
+ RenderBlock (anonymous) at (0,0) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderBlock (anonymous) at (0,0) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock (anonymous) at (0,0) size 784x20
+ RenderInline {SPAN} at (0,0) size 224x19
+ RenderText {#text} at (0,0) size 224x19
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,20) size 784x20
+ RenderText {#text} at (0,0) size 246x19
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock (anonymous) at (0,40) size 784x0
+ RenderInline {LABEL} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderText {#text} at (0,0) size 0x0
+ RenderBlock {DIV} at (0,191) size 784x46
+ RenderTable {TABLE} at (0,0) size 252x46
+ RenderTableSection {TBODY} at (0,0) size 252x46
+ RenderTableRow {TR} at (0,2) size 252x42
+ RenderTableCell {TD} at (2,2) size 248x42 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 246x40
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x40
+ RenderBlock (anonymous) at (0,0) size 246x20
+ RenderInline {SPAN} at (0,0) size 224x19
+ RenderText {#text} at (0,0) size 224x19
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,20) size 246x20
+ RenderText {#text} at (0,0) size 246x19
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock {DIV} at (0,237) size 784x46
+ RenderTable {TABLE} at (0,0) size 252x46
+ RenderTableSection {TBODY} at (0,0) size 252x46
+ RenderTableRow {TR} at (0,2) size 252x42
+ RenderTableCell {TD} at (2,2) size 248x42 [r=0 c=0 rs=1 cs=1]
+ RenderBlock {DIV} at (1,1) size 246x40
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x0
+ RenderBlock {DIV} at (0,0) size 246x40
+ RenderBlock (anonymous) at (0,0) size 246x20
+ RenderInline {SPAN} at (0,0) size 224x19
+ RenderText {#text} at (0,0) size 224x19
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,20) size 246x20
+ RenderText {#text} at (0,0) size 246x19
+ text run at (0,0) width 246: "alternative presentation button subtitle"
+ RenderBlock {P} at (0,299) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x0
+ RenderBlock {DIV} at (0,0) size 784x40
+ RenderBlock (anonymous) at (0,0) size 784x20
+ RenderInline {SPAN} at (0,0) size 224x19
+ RenderText {#text} at (0,0) size 224x19
+ text run at (0,0) width 224: "alternative presentation button title"
+ RenderBlock {DIV} at (0,20) size 784x20
+ RenderText {#text} at (0,0) size 246x19
+ text run at (0,0) width 246: "alternative presentation button subtitle"
fast/scrolling/latching [ Pass ]
media/mac [ Pass ]
+fast/forms/alternative-presentation-button [ Pass ]
fast/forms/search/search-padding-cancel-results-buttons.html [ Pass ]
fast/forms/search/search-results-hidden-crash.html [ Pass ]
+2017-11-28 Daniel Bates <dabates@apple.com>
+
+ [Cocoa] First pass at implementing alternative presentation button element
+ https://bugs.webkit.org/show_bug.cgi?id=179785
+ Part of <rdar://problem/34917108>
+
+ Reviewed by Brent Fulgham.
+
+ Implement support for substituting a button for one or more elements in a page.
+ This is a first pass. We will refine the logic and the API/SPI in subsequent
+ commits.
+
+ Tests: accessibility/alternative-presentation-button-input-type.html
+ accessibility/alternative-presentation-button.html
+ fast/forms/alternative-presentation-button/replace-and-remove.html
+ fast/forms/alternative-presentation-button/replacement.html
+
+ * English.lproj/Localizable.strings: Add placeholder strings for localization.
+ * SourcesCocoa.txt: Add some files.
+ * WebCore.xcodeproj/project.pbxproj: Ditto.
+ * dom/Element.h:
+ * editing/Editor.cpp:
+ (WebCore::Editor::clear): Clear out all substitutions. This is called whenever
+ we are navigating between pages.
+ (WebCore::Editor::substituteWithAlternativePresentationButton): Added.
+ (WebCore::Editor::removeAlternativePresentationButton): Added.
+ (WebCore::Editor::didInsertAlternativePresentationButtonElement): Added.
+ (WebCore::Editor::didRemoveAlternativePresentationButtonElement): Added.
+ * editing/Editor.h:
+ * editing/cocoa/AlternativePresentationButtonSubstitution.cpp: Added.
+ (WebCore::AlternativePresentationButtonSubstitution::AlternativePresentationButtonSubstitution):
+ (WebCore::AlternativePresentationButtonSubstitution::initializeSavedDisplayStyles):
+ (WebCore::AlternativePresentationButtonSubstitution::apply):
+ (WebCore::AlternativePresentationButtonSubstitution::unapply):
+ * editing/cocoa/AlternativePresentationButtonSubstitution.h:
+ * html/HTMLInputElement.cpp:
+ (WebCore::HTMLInputElement::alternativePresentationButtonElement const): Added.
+ (WebCore::HTMLInputElement::setTypeWithoutUpdatingAttribute): Added.
+ (WebCore::HTMLInputElement::createInputType): Extracted the logic to create the InputType from
+ HTMLInputElement::updateType() to here and added logic to create the input type for the
+ alternative presentation button. This input type is not web exposed.
+ (WebCore::HTMLInputElement::updateType): Modified to take the name of the InputType object to
+ create as an argument and pass it through to HTMLInputElement::createInputType() to actually
+ create it. Reordered the logic for destroying the shadow tree of the old InputType, deallocating
+ the old InputType, and assigning the new InputType such that we assign the new InputType,
+ destroy the shadow tree of the old InputType, and deallocate the old InputType. This ordering
+ allows AlternativePresentationButtonSubstitution::substitute() to avoid restoring the input
+ type saved before the substitution when the input type is changed by the page as opposed to
+ by SPI.
+ (WebCore::HTMLInputElement::parseAttribute): Pass the parsed type.
+ (WebCore::HTMLInputElement::willAttachRenderers): Ditto.
+ * html/HTMLInputElement.h: Change visibility of removeShadowRoot() from private to public so
+ that it can be called from AlternativePresentationButtonSubstitution.
+ * html/InputType.h:
+ (WebCore::InputType::alternativePresentationButtonElement const): Added.
+ * html/InputTypeNames.cpp:
+ (WebCore::InputTypeNames::alternativePresentationButton): Added.
+ * html/InputTypeNames.h:
+ * html/shadow/cocoa/AlternativePresentationButtonElement.cpp: Added.
+ (WebCore::AlternativePresentationButtonElement::create):
+ (WebCore::AlternativePresentationButtonElement::AlternativePresentationButtonElement):
+ (WebCore::AlternativePresentationButtonElement::insertedIntoAncestor):
+ (WebCore::AlternativePresentationButtonElement::removedFromAncestor):
+ (WebCore::AlternativePresentationButtonElement::didFinishInsertingNode):
+ (WebCore::AlternativePresentationButtonElement::defaultEventHandler):
+ * html/shadow/cocoa/AlternativePresentationButtonElement.h:
+ * html/shadow/cocoa/AlternativePresentationButtonInputType.cpp: Added.
+ (WebCore::AlternativePresentationButtonInputType::AlternativePresentationButtonInputType):
+ (WebCore::AlternativePresentationButtonInputType::formControlType const):
+ (WebCore::AlternativePresentationButtonInputType::appendFormData const):
+ (WebCore::AlternativePresentationButtonInputType::supportsValidation const):
+ (WebCore::AlternativePresentationButtonInputType::isTextButton const):
+ (WebCore::AlternativePresentationButtonInputType::alternativePresentationButtonElement const):
+ (WebCore::AlternativePresentationButtonInputType::createShadowSubtree):
+ (WebCore::AlternativePresentationButtonInputType::destroyShadowSubtree):
+ * html/shadow/cocoa/AlternativePresentationButtonInputType.h:
+ * page/ChromeClient.h:
+ * platform/LocalizedStrings.cpp:
+ (WebCore::AXAlternativePresentationButtonLabel):
+ (WebCore::alternativePresentationButtonTitle):
+ (WebCore::alternativePresentationButtonSubtitle):
+ * platform/LocalizedStrings.h:
+ * testing/Internals.cpp:
+ (WebCore::Internals::substituteWithAlternativePresentationButton): Added.
+ (WebCore::Internals::removeAlternativePresentationButton): Added.
+ * testing/Internals.h:
+ * testing/Internals.idl:
+
2017-11-28 Commit Queue <commit-queue@webkit.org>
Unreviewed, rolling out r225209.
/* Option in segmented control for choosing list type in text editing */
"•" = "•";
+/* Label for the alternative presentation button. */
+"alternative presentation button" = "alternative presentation button";
+
+/* Alternative presentation button title. */
+"alternative presentation button title" = "alternative presentation button title";
+
+/* Alternative presentation button subtitle. */
+"alternative presentation button subtitle" = "alternative presentation button subtitle";
platform/mediastream/libwebrtc/LibWebRTCProviderCocoa.cpp
+#if ENABLE_ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT
+
+ editing/cocoa/AlternativePresentationButtonSubstitution.cpp
+ html/shadow/cocoa/AlternativePresentationButtonElement.cpp
+ html/shadow/cocoa/AlternativePresentationButtonInputType.cpp
+
+#endif
+
#if ENABLE_APPLE_PAY
Modules/applepay/ApplePayContactField.cpp
CE7B2DB11586ABAD0098B3FA /* TextAlternativeWithRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextAlternativeWithRange.h; sourceTree = "<group>"; };
CE7B2DB21586ABAD0098B3FA /* TextAlternativeWithRange.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextAlternativeWithRange.mm; sourceTree = "<group>"; };
CE7E17821C83A49100AD06AF /* ContentSecurityPolicyHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ContentSecurityPolicyHash.h; path = csp/ContentSecurityPolicyHash.h; sourceTree = "<group>"; };
+ CEB828571FBBA0CE005CBEA1 /* AlternativePresentationButtonElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AlternativePresentationButtonElement.h; sourceTree = "<group>"; };
+ CEB828581FBBA0CE005CBEA1 /* AlternativePresentationButtonElement.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AlternativePresentationButtonElement.cpp; sourceTree = "<group>"; };
+ CEB8285A1FBBB273005CBEA1 /* AlternativePresentationButtonInputType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AlternativePresentationButtonInputType.h; sourceTree = "<group>"; };
+ CEB8285B1FBBB273005CBEA1 /* AlternativePresentationButtonInputType.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AlternativePresentationButtonInputType.cpp; sourceTree = "<group>"; };
+ CEB828621FBCC142005CBEA1 /* AlternativePresentationButtonSubstitution.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AlternativePresentationButtonSubstitution.h; sourceTree = "<group>"; };
+ CEB828641FBCE0D3005CBEA1 /* AlternativePresentationButtonSubstitution.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = AlternativePresentationButtonSubstitution.cpp; sourceTree = "<group>"; };
CECADFC2153778FF00E37068 /* DictationAlternative.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DictationAlternative.cpp; sourceTree = "<group>"; };
CECADFC3153778FF00E37068 /* DictationAlternative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DictationAlternative.h; sourceTree = "<group>"; };
CECADFC4153778FF00E37068 /* DictationCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DictationCommand.cpp; sourceTree = "<group>"; };
4150F9ED12B6E0990008C860 /* shadow */ = {
isa = PBXGroup;
children = (
+ CE0B30531FACE93500E3FA79 /* cocoa */,
51C4AA5118B28357007BFE9B /* mac */,
7C1E97251A9F9834007BF0FB /* AutoFillButtonElement.cpp */,
7C1E97261A9F9834007BF0FB /* AutoFillButtonElement.h */,
7C3E510718DF8F1200C112F7 /* cocoa */ = {
isa = PBXGroup;
children = (
+ CEB828641FBCE0D3005CBEA1 /* AlternativePresentationButtonSubstitution.cpp */,
+ CEB828621FBCC142005CBEA1 /* AlternativePresentationButtonSubstitution.h */,
C5227DEF1C3C6DD700F5ED54 /* DataDetection.h */,
C5227DF01C3C6DD700F5ED54 /* DataDetection.mm */,
9B55EEE81B3E8898005342BC /* EditorCocoa.mm */,
path = mediasource;
sourceTree = "<group>";
};
+ CE0B30531FACE93500E3FA79 /* cocoa */ = {
+ isa = PBXGroup;
+ children = (
+ CEB828581FBBA0CE005CBEA1 /* AlternativePresentationButtonElement.cpp */,
+ CEB828571FBBA0CE005CBEA1 /* AlternativePresentationButtonElement.h */,
+ CEB8285B1FBBB273005CBEA1 /* AlternativePresentationButtonInputType.cpp */,
+ CEB8285A1FBBB273005CBEA1 /* AlternativePresentationButtonInputType.h */,
+ );
+ path = cocoa;
+ sourceTree = "<group>";
+ };
CE17AD141C58522F005F4799 /* csp */ = {
isa = PBXGroup;
children = (
RefPtr<ShadowRoot> userAgentShadowRoot() const;
WEBCORE_EXPORT ShadowRoot& ensureUserAgentShadowRoot();
+ void removeShadowRoot();
void setIsDefinedCustomElement(JSCustomElementInterface&);
void setIsFailedCustomElement(JSCustomElementInterface&);
Ref<Node> cloneNodeInternal(Document&, CloningOperation) override;
virtual Ref<Element> cloneElementWithoutAttributesAndChildren(Document&);
- void removeShadowRoot();
-
const RenderStyle& resolveComputedStyle();
const RenderStyle& resolvePseudoElementStyle(PseudoId);
#include "ServicesOverlayController.h"
#endif
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+#include "AlternativePresentationButtonElement.h"
+#include "AlternativePresentationButtonSubstitution.h"
+#endif
+
namespace WebCore {
static bool dispatchBeforeInputEvent(Element& element, const AtomicString& inputType, const String& data = { }, RefPtr<DataTransfer>&& dataTransfer = nullptr, const Vector<RefPtr<StaticRange>>& targetRanges = { }, bool cancelable = true)
m_customCompositionUnderlines.clear();
m_shouldStyleWithCSS = false;
m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv;
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ m_alternativePresentationButtonElementToSubstitutionMap.clear();
+ m_alternativePresentationButtonIdentifierToElementMap.clear();
+ m_lastAlternativePresentationButtonSubstitution = nullptr;
+#endif
}
bool Editor::insertText(const String& text, Event* triggeringEvent, TextEventInputType inputType)
#endif // ENABLE(ATTACHMENT_ELEMENT)
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
+void Editor::substituteWithAlternativePresentationButton(Vector<Ref<Element>>&& elements, const String& identifier)
+{
+ if (elements.isEmpty())
+ return;
+
+ // The implementation of the first element is exchanged for the alternative presentation button.
+ // All other elements are hidden.
+ Ref<Element> elementForAlternativePresentation = WTFMove(elements[0]);
+ elements.remove(0);
+
+ m_lastAlternativePresentationButtonIdentifier = identifier;
+ if (is<HTMLInputElement>(elementForAlternativePresentation))
+ m_lastAlternativePresentationButtonSubstitution = std::make_unique<AlternativePresentationButtonSubstitution>(downcast<HTMLInputElement>(elementForAlternativePresentation.get()), WTFMove(elements));
+ else {
+ // FIXME: This substitution is only safe to do if and only if elementForAlternativePresentation can support a user-
+ // agent shadow root and does not support an author shadow root. Not all elements meet this criterion (e.g. <details>).
+ // See <https://bugs.webkit.org/show_bug.cgi?id=180086> for more details.
+ m_lastAlternativePresentationButtonSubstitution = std::make_unique<AlternativePresentationButtonSubstitution>(elementForAlternativePresentation.get(), WTFMove(elements));
+ }
+ m_lastAlternativePresentationButtonSubstitution->apply();
+}
+
+void Editor::removeAlternativePresentationButton(const String& identifier)
+{
+ if (!m_alternativePresentationButtonIdentifierToElementMap.contains(identifier))
+ return;
+ auto* button = m_alternativePresentationButtonIdentifierToElementMap.take(identifier);
+ ASSERT(m_alternativePresentationButtonElementToSubstitutionMap.contains(button));
+ auto substitution = m_alternativePresentationButtonElementToSubstitutionMap.take(button);
+ substitution->unapply();
+}
+
+void Editor::didInsertAlternativePresentationButtonElement(AlternativePresentationButtonElement& button)
+{
+ ASSERT(!m_alternativePresentationButtonElementToSubstitutionMap.contains(&button));
+ ASSERT(!m_alternativePresentationButtonIdentifierToElementMap.contains(m_lastAlternativePresentationButtonIdentifier));
+ m_alternativePresentationButtonElementToSubstitutionMap.set(&button, WTFMove(m_lastAlternativePresentationButtonSubstitution));
+ m_alternativePresentationButtonIdentifierToElementMap.set(m_lastAlternativePresentationButtonIdentifier, &button);
+ button.setUniqueIdentifier(m_lastAlternativePresentationButtonIdentifier);
+}
+
+void Editor::didRemoveAlternativePresentationButtonElement(AlternativePresentationButtonElement& button)
+{
+ removeAlternativePresentationButton(button.uniqueIdentifier());
+}
+
+#endif // ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
void Editor::handleAcceptedCandidate(TextCheckingResult acceptedCandidate)
{
const VisibleSelection& selection = m_frame.selection().selection();
namespace WebCore {
+class AlternativePresentationButtonElement;
+class AlternativePresentationButtonSubstitution;
class AlternativeTextController;
class ArchiveResource;
class DataTransfer;
void didRemoveAttachmentElement(HTMLAttachmentElement&);
#endif
+ // FIXME: Find a better place for this functionality.
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ // FIXME: Remove the need to pass an identifier for the alternative presentation button.
+ WEBCORE_EXPORT void substituteWithAlternativePresentationButton(Vector<Ref<Element>>&&, const String&);
+ // FIXME: Have this take an AlternativePresentationButtonElement& instead of an identifier.
+ WEBCORE_EXPORT void removeAlternativePresentationButton(const String&);
+
+ void didInsertAlternativePresentationButtonElement(AlternativePresentationButtonElement&);
+ void didRemoveAlternativePresentationButtonElement(AlternativePresentationButtonElement&);
+#endif
+
private:
Document& document() const;
HashSet<String> m_removedAttachmentIdentifiers;
#endif
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ HashMap<AlternativePresentationButtonElement*, std::unique_ptr<AlternativePresentationButtonSubstitution>> m_alternativePresentationButtonElementToSubstitutionMap;
+ HashMap<String, AlternativePresentationButtonElement*> m_alternativePresentationButtonIdentifierToElementMap;
+ std::unique_ptr<AlternativePresentationButtonSubstitution> m_lastAlternativePresentationButtonSubstitution;
+ String m_lastAlternativePresentationButtonIdentifier;
+#endif
+
VisibleSelection m_oldSelectionForEditorUIUpdate;
Timer m_editorUIUpdateTimer;
bool m_editorUIUpdateTimerShouldCheckSpellingAndGrammar { false };
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AlternativePresentationButtonSubstitution.h"
+
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
+#include "AlternativePresentationButtonElement.h"
+#include "HTMLInputElement.h"
+#include "InputTypeNames.h"
+#include "ShadowRoot.h"
+#include "StyleProperties.h"
+
+namespace WebCore {
+
+AlternativePresentationButtonSubstitution::AlternativePresentationButtonSubstitution(HTMLInputElement& element, Vector<Ref<Element>>&& elementsToHide)
+ : m_shadowHost { makeRef(element) }
+{
+ initializeSavedDisplayStyles(WTFMove(elementsToHide));
+}
+
+AlternativePresentationButtonSubstitution::AlternativePresentationButtonSubstitution(Element& element, Vector<Ref<Element>>&& elementsToHide)
+ : m_shadowHost { makeRef(element) }
+ , m_alternativePresentationButtonElement { AlternativePresentationButtonElement::create(element.document()) }
+{
+ initializeSavedDisplayStyles(WTFMove(elementsToHide));
+}
+
+void AlternativePresentationButtonSubstitution::initializeSavedDisplayStyles(Vector<Ref<Element>>&& elements)
+{
+ m_savedDisplayStyles.reserveInitialCapacity(elements.size());
+ for (auto& element : elements) {
+ if (is<StyledElement>(element))
+ m_savedDisplayStyles.uncheckedAppend({ adoptRef(downcast<StyledElement>(element.leakRef())) });
+ }
+}
+
+void AlternativePresentationButtonSubstitution::apply()
+{
+ auto saveStyles = [&] {
+ for (auto& savedDisplayStyle : m_savedDisplayStyles) {
+ auto* styleProperties = savedDisplayStyle.element->inlineStyle();
+ if (!styleProperties)
+ continue;
+ savedDisplayStyle.value = styleProperties->getPropertyValue(CSSPropertyDisplay);
+ savedDisplayStyle.important = styleProperties->propertyIsImportant(CSSPropertyDisplay);
+ savedDisplayStyle.wasSpecified = true;
+ }
+ };
+ auto attachShadowRoot = [&] {
+ if (is<HTMLInputElement>(m_shadowHost)) {
+ m_savedShadowHostInputType = m_shadowHost->attributeWithoutSynchronization(HTMLNames::typeAttr);
+ auto& inputElement = downcast<HTMLInputElement>(m_shadowHost.get());
+ ASSERT(inputElement.type() != InputTypeNames::alternativePresentationButton());
+ inputElement.setTypeWithoutUpdatingAttribute(InputTypeNames::alternativePresentationButton());
+ return;
+ }
+ ASSERT(m_alternativePresentationButtonElement);
+ ASSERT(!m_shadowHost->shadowRoot());
+ ASSERT(!m_shadowHost->userAgentShadowRoot());
+ m_shadowHost->ensureUserAgentShadowRoot().appendChild(*m_alternativePresentationButtonElement);
+ };
+ auto hideElements = [&] {
+ for (auto& savedDisplayStyle : m_savedDisplayStyles)
+ savedDisplayStyle.element->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone, true); // important
+ };
+
+ ASSERT(!m_isApplied);
+ m_isApplied = true;
+ saveStyles();
+ attachShadowRoot();
+ hideElements();
+}
+
+void AlternativePresentationButtonSubstitution::unapply()
+{
+ auto detachShadowRoot = [&] {
+ if (is<HTMLInputElement>(m_shadowHost)) {
+ auto& inputElement = downcast<HTMLInputElement>(m_shadowHost.get());
+ if (inputElement.type() == InputTypeNames::alternativePresentationButton())
+ inputElement.setTypeWithoutUpdatingAttribute(m_savedShadowHostInputType);
+ return;
+ }
+ ASSERT(m_alternativePresentationButtonElement);
+ ASSERT(m_shadowHost->userAgentShadowRoot());
+ ASSERT(m_shadowHost->userAgentShadowRoot()->countChildNodes() == 1);
+ m_shadowHost->userAgentShadowRoot()->removeChild(*m_alternativePresentationButtonElement);
+ ASSERT(!m_shadowHost->userAgentShadowRoot()->countChildNodes());
+ m_shadowHost->removeShadowRoot();
+ };
+ auto restoreStyles = [&] {
+ for (auto& savedDisplayStyle : m_savedDisplayStyles) {
+ if (savedDisplayStyle.wasSpecified)
+ savedDisplayStyle.element->setInlineStyleProperty(CSSPropertyDisplay, savedDisplayStyle.value, savedDisplayStyle.important);
+ else
+ savedDisplayStyle.element->removeInlineStyleProperty(CSSPropertyDisplay);
+ }
+ };
+
+ ASSERT(m_isApplied);
+ m_isApplied = false;
+ restoreStyles();
+ detachShadowRoot();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
+#include <wtf/RefPtr.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class AlternativePresentationButtonElement;
+class Element;
+class HTMLInputElement;
+class StyledElement;
+
+class AlternativePresentationButtonSubstitution {
+public:
+ AlternativePresentationButtonSubstitution(HTMLInputElement&, Vector<Ref<Element>>&& elementsToHide);
+ AlternativePresentationButtonSubstitution(Element&, Vector<Ref<Element>>&& elementsToHide);
+
+ void apply();
+ void unapply();
+
+private:
+ void initializeSavedDisplayStyles(Vector<Ref<Element>>&&);
+
+ Ref<Element> m_shadowHost;
+ AtomicString m_savedShadowHostInputType;
+ RefPtr<AlternativePresentationButtonElement> m_alternativePresentationButtonElement;
+
+ struct InlineDisplayStyle {
+ Ref<StyledElement> element;
+ String value { };
+ bool important { false };
+ bool wasSpecified { false };
+ };
+ Vector<InlineDisplayStyle> m_savedDisplayStyles;
+
+ bool m_isApplied { false };
+};
+
+} // namespace WebCore
+
+#endif
#include "TouchEvent.h"
#endif
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+#include "AlternativePresentationButtonInputType.h"
+#include "InputTypeNames.h"
+#endif
+
namespace WebCore {
using namespace HTMLNames;
return m_inputType->placeholderElement();
}
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+HTMLElement* HTMLInputElement::alternativePresentationButtonElement() const
+{
+ return m_inputType->alternativePresentationButtonElement();
+}
+#endif
+
bool HTMLInputElement::shouldAutocomplete() const
{
if (m_autocomplete != Uninitialized)
setAttributeWithoutSynchronization(typeAttr, type);
}
-void HTMLInputElement::updateType()
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+void HTMLInputElement::setTypeWithoutUpdatingAttribute(const AtomicString& type)
+{
+ updateType(type);
+}
+#endif
+
+inline std::unique_ptr<InputType> HTMLInputElement::createInputType(const AtomicString& type)
+{
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ if (type == InputTypeNames::alternativePresentationButton())
+ return std::make_unique<AlternativePresentationButtonInputType>(*this);
+#endif
+ return InputType::create(*this, type);
+}
+
+void HTMLInputElement::updateType(const AtomicString& newType)
{
ASSERT(m_inputType);
- auto newType = InputType::create(*this, attributeWithoutSynchronization(typeAttr));
+ auto newInputType = createInputType(newType);
m_hasType = true;
- if (m_inputType->formControlType() == newType->formControlType())
+ if (m_inputType->formControlType() == newInputType->formControlType())
return;
removeFromRadioButtonGroup();
bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes();
bool wasSuccessfulSubmitButtonCandidate = m_inputType->canBeSuccessfulSubmitButton();
- m_inputType->destroyShadowSubtree();
-
- m_inputType = WTFMove(newType);
+ std::unique_ptr<InputType> oldInputType = WTFMove(m_inputType);
+ m_inputType = WTFMove(newInputType);
+ oldInputType->destroyShadowSubtree();
m_inputType->createShadowSubtree();
updateInnerTextElementEditability();
unregisterForSuspensionCallbackIfNeeded();
}
} else if (name == typeAttr)
- updateType();
+ updateType(value);
else if (name == valueAttr) {
// Changes to the value attribute may change whether or not this element has a default value.
// If this field is autocomplete=off that might affect the return value of needsSuspensionCallback.
void HTMLInputElement::willAttachRenderers()
{
if (!m_hasType)
- updateType();
+ updateType(attributeWithoutSynchronization(typeAttr));
}
void HTMLInputElement::didAttachRenderers()
HTMLElement* placeholderElement() const final;
WEBCORE_EXPORT HTMLElement* autoFillButtonElement() const;
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ WEBCORE_EXPORT HTMLElement* alternativePresentationButtonElement() const;
+#endif
+
bool checked() const { return m_isChecked; }
WEBCORE_EXPORT void setChecked(bool, TextFieldEventBehavior = DispatchNoEvent);
WEBCORE_EXPORT void setType(const AtomicString&);
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ void setTypeWithoutUpdatingAttribute(const AtomicString&);
+#endif
+
WEBCORE_EXPORT String value() const final;
WEBCORE_EXPORT ExceptionOr<void> setValue(const String&, TextFieldEventBehavior = DispatchNoEvent);
WEBCORE_EXPORT void setValueForUser(const String&);
void requiredAttributeChanged() final;
void initializeInputType();
- void updateType();
+ std::unique_ptr<InputType> createInputType(const AtomicString&);
+ void updateType(const AtomicString&);
void runPostTypeUpdateTasks();
void subtreeHasChanged() final;
virtual HTMLElement* sliderTrackElement() const { return nullptr; }
virtual HTMLElement* placeholderElement() const;
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ virtual HTMLElement* alternativePresentationButtonElement() const { return nullptr; }
+#endif
+
// Miscellaneous functions.
virtual bool rendererIsNeeded();
return name;
}
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+const AtomicString& alternativePresentationButton()
+{
+ static NeverDestroyed<AtomicString> name("alternative-presentation-button", AtomicString::ConstructFromLiteral);
+ return name;
+}
+#endif
+
} // namespace WebCore::InputTypeNames
} // namespace WebCore
const AtomicString& url();
const AtomicString& week();
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+const AtomicString& alternativePresentationButton();
+#endif
+
}
} // namespace WebCore
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AlternativePresentationButtonElement.h"
+
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "DocumentFragment.h"
+#include "Editor.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "HTMLInputElement.h"
+#include "HTMLSpanElement.h"
+#include "HTMLStyleElement.h"
+#include "LocalizedStrings.h"
+#include "MouseEvent.h"
+#include "Text.h"
+#include "UserAgentStyleSheets.h"
+#include <wtf/NeverDestroyed.h>
+#include <wtf/Ref.h>
+
+namespace WebCore {
+
+Ref<AlternativePresentationButtonElement> AlternativePresentationButtonElement::create(Document& document)
+{
+ return adoptRef(*new AlternativePresentationButtonElement { document });
+}
+
+AlternativePresentationButtonElement::AlternativePresentationButtonElement(Document& document)
+ : HTMLDivElement { HTMLNames::divTag, document }
+{
+ setPseudo(AtomicString { "-webkit-alternative-presentation-button", AtomicString::ConstructFromLiteral });
+ setAttributeWithoutSynchronization(HTMLNames::aria_labelAttr, AXAlternativePresentationButtonLabel());
+ setAttributeWithoutSynchronization(HTMLNames::roleAttr, AtomicString { "button", AtomicString::ConstructFromLiteral });
+}
+
+auto AlternativePresentationButtonElement::insertedIntoAncestor(InsertionType type, ContainerNode& parentOfInsertedTree) -> InsertedIntoAncestorResult
+{
+ HTMLElement::insertedIntoAncestor(type, parentOfInsertedTree);
+ if (type.connectedToDocument) {
+ if (auto* frame = document().frame())
+ frame->editor().didInsertAlternativePresentationButtonElement(*this);
+ return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
+ }
+ return InsertedIntoAncestorResult::Done;
+}
+
+void AlternativePresentationButtonElement::removedFromAncestor(RemovalType type, ContainerNode& ancestor)
+{
+ HTMLDivElement::removedFromAncestor(type, ancestor);
+ if (type.disconnectedFromDocument) {
+ if (auto* frame = document().frame())
+ frame->editor().didRemoveAlternativePresentationButtonElement(*this);
+ }
+}
+
+void AlternativePresentationButtonElement::didFinishInsertingNode()
+{
+ auto fragment = document().createDocumentFragment();
+
+#if __has_include(<WebKitAdditions/alternativePresentationButtonElementShadow.css>)
+ static NeverDestroyed<String> styleSheet { alternativePresentationButtonElementShadowUserAgentStyleSheet, String::ConstructFromLiteral };
+ auto style = HTMLStyleElement::create(document());
+ style->setTextContent(styleSheet);
+ fragment->appendChild(style);
+#endif
+
+ auto iconContainer = HTMLDivElement::create(document());
+ iconContainer->setPseudo(AtomicString { "-webkit-alternative-presentation-button-icon-container" , AtomicString::ConstructFromLiteral });
+ fragment->appendChild(iconContainer);
+
+ auto icon = HTMLDivElement::create(document());
+ icon->setPseudo(AtomicString { "-webkit-alternative-presentation-button-icon", AtomicString::ConstructFromLiteral });
+ icon->setAttributeWithoutSynchronization(HTMLNames::classAttr, AtomicString { "center-img", AtomicString::ConstructFromLiteral });
+ iconContainer->appendChild(icon);
+
+ auto textContainer = HTMLDivElement::create(document());
+ textContainer->setPseudo(AtomicString { "-webkit-alternative-presentation-button-text-container", AtomicString::ConstructFromLiteral });
+ textContainer->setAttributeWithoutSynchronization(HTMLNames::classAttr, AtomicString { "center", AtomicString::ConstructFromLiteral });
+ fragment->appendChild(textContainer);
+
+ auto text = HTMLSpanElement::create(document());
+ text->setPseudo(AtomicString { "-webkit-alternative-presentation-button-title", AtomicString::ConstructFromLiteral });
+ text->appendChild(document().createTextNode(alternativePresentationButtonTitle()));
+ textContainer->appendChild(text);
+
+ auto subtext = HTMLDivElement::create(document());
+ subtext->setPseudo(AtomicString { "-webkit-alternative-presentation-button-subtitle", AtomicString::ConstructFromLiteral });
+ subtext->appendChild(document().createTextNode(alternativePresentationButtonSubtitle()));
+ textContainer->appendChild(subtext);
+
+ appendChild(fragment);
+}
+
+void AlternativePresentationButtonElement::defaultEventHandler(Event& event)
+{
+ if (!is<MouseEvent>(event)) {
+ if (!event.defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ MouseEvent& mouseEvent = downcast<MouseEvent>(event);
+
+ if (mouseEvent.type() == eventNames().clickEvent) {
+ document().page()->chrome().client().handleAlternativePresentationButtonClick(*this);
+ event.setDefaultHandled();
+ }
+
+ if (!event.defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
+#include "HTMLDivElement.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class HTMLInputElement;
+
+class AlternativePresentationButtonElement final : public HTMLDivElement {
+public:
+ static Ref<AlternativePresentationButtonElement> create(Document&);
+
+ void setUniqueIdentifier(const String& uniqueIdentifier) { m_uniqueIdentifier = uniqueIdentifier; }
+ const String& uniqueIdentifier() const { return m_uniqueIdentifier; }
+
+private:
+ explicit AlternativePresentationButtonElement(Document&);
+
+ // Node
+ InsertedIntoAncestorResult insertedIntoAncestor(InsertionType, ContainerNode&) final;
+ void removedFromAncestor(RemovalType, ContainerNode&) final;
+ void didFinishInsertingNode() final;
+ void defaultEventHandler(Event&) final;
+
+ String m_uniqueIdentifier;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AlternativePresentationButtonInputType.h"
+
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
+#include "AlternativePresentationButtonElement.h"
+#include "DOMFormData.h"
+#include "HTMLNames.h"
+#include "InputTypeNames.h"
+#include "ShadowRoot.h"
+
+namespace WebCore {
+
+AlternativePresentationButtonInputType::AlternativePresentationButtonInputType(HTMLInputElement& element)
+ : BaseButtonInputType { element }
+{
+}
+
+AlternativePresentationButtonInputType::~AlternativePresentationButtonInputType() = default;
+
+const AtomicString& AlternativePresentationButtonInputType::formControlType() const
+{
+ return InputTypeNames::alternativePresentationButton();
+}
+
+bool AlternativePresentationButtonInputType::appendFormData(DOMFormData& formData, bool multipart) const
+{
+ InputType::appendFormData(formData, multipart);
+ auto& dirnameAttrValue = element().attributeWithoutSynchronization(HTMLNames::dirnameAttr);
+ if (!dirnameAttrValue.isNull())
+ formData.append(dirnameAttrValue, element().directionForFormData());
+ return true;
+}
+
+bool AlternativePresentationButtonInputType::supportsValidation() const
+{
+ return false;
+}
+
+bool AlternativePresentationButtonInputType::isTextButton() const
+{
+ return true;
+}
+
+HTMLElement* AlternativePresentationButtonInputType::alternativePresentationButtonElement() const
+{
+ return m_alternativePresentationButtonElement.get();
+}
+
+void AlternativePresentationButtonInputType::createShadowSubtree()
+{
+ ASSERT(element().shadowRoot());
+ m_alternativePresentationButtonElement = AlternativePresentationButtonElement::create(element().document());
+ element().shadowRoot()->appendChild(*m_alternativePresentationButtonElement);
+}
+
+void AlternativePresentationButtonInputType::destroyShadowSubtree()
+{
+ BaseButtonInputType::destroyShadowSubtree();
+ m_alternativePresentationButtonElement = nullptr;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+
+#include "BaseButtonInputType.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class AlternativePresentationButtonElement;
+
+class AlternativePresentationButtonInputType final : public BaseButtonInputType {
+public:
+ explicit AlternativePresentationButtonInputType(HTMLInputElement&);
+ ~AlternativePresentationButtonInputType();
+
+private:
+ // InputType
+ const AtomicString& formControlType() const final;
+ HTMLElement* alternativePresentationButtonElement() const final;
+ bool supportsValidation() const final;
+ bool isTextButton() const final;
+ void createShadowSubtree() final;
+ void destroyShadowSubtree() final;
+ bool appendFormData(DOMFormData&, bool) const final;
+
+ RefPtr<AlternativePresentationButtonElement> m_alternativePresentationButtonElement;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
virtual void handleAutoFillButtonClick(HTMLInputElement&) { }
+ virtual void handleAlternativePresentationButtonClick(Node&) { }
+
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
virtual void addPlaybackTargetPickerClient(uint64_t /*contextId*/) { }
virtual void removePlaybackTargetPickerClient(uint64_t /*contextId*/) { }
return WEB_UI_STRING("contact info auto fill", "Label for the auto fill contacts button inside a text field.");
}
+String AXAlternativePresentationButtonLabel()
+{
+ return WEB_UI_STRING("alternative presentation button", "Label for the alternative presentation button.");
+}
+
+String alternativePresentationButtonTitle()
+{
+ return WEB_UI_STRING("alternative presentation button title", "Title text for alternative presentation button");
+}
+
+String alternativePresentationButtonSubtitle()
+{
+ return WEB_UI_STRING("alternative presentation button subtitle", "Subtitle text for alternative presentation button");
+}
+
String missingPluginText()
{
return WEB_UI_STRING("Missing Plug-in", "Label text to be used when a plugin is missing");
WEBCORE_EXPORT String exitFullScreenButtonAccessibilityTitle();
#endif
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ String AXAlternativePresentationButtonLabel();
+ String alternativePresentationButtonTitle();
+ String alternativePresentationButtonSubtitle();
+#endif
+
#if USE(GLIB) && defined(GETTEXT_PACKAGE)
#define WEB_UI_STRING(string, description) WebCore::localizedString(_(string))
#define WEB_UI_STRING_KEY(string, key, description) WebCore::localizedString(C_(key, string))
}
#endif
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ExceptionOr<void> Internals::substituteWithAlternativePresentationButton(Vector<RefPtr<Element>>&& elementsFromBindings, const String& identifier)
+{
+ if (!frame())
+ return Exception { InvalidAccessError };
+ if (elementsFromBindings.isEmpty())
+ return Exception { TypeError, ASCIILiteral { "Must specify at least one element to substitute." } };
+ Vector<Ref<Element>> elements;
+ elements.reserveInitialCapacity(elementsFromBindings.size());
+ for (auto& element : elementsFromBindings) {
+ if (element)
+ elements.uncheckedAppend(element.releaseNonNull());
+ }
+ frame()->editor().substituteWithAlternativePresentationButton(WTFMove(elements), identifier);
+ return { };
+}
+
+ExceptionOr<void> Internals::removeAlternativePresentationButton(const String& identifier)
+{
+ if (!frame())
+ return Exception { InvalidAccessError };
+ frame()->editor().removeAlternativePresentationButton(identifier);
+ return { };
+}
+#endif
+
} // namespace WebCore
class WebGLRenderingContext;
class XMLHttpRequest;
-class Internals final : public RefCounted<Internals>, private ContextDestructionObserver
+class Internals final : public RefCounted<Internals>, private ContextDestructionObserver
#if ENABLE(MEDIA_STREAM)
, private RealtimeMediaSource::Observer
#endif
MockPaymentCoordinator& mockPaymentCoordinator() const;
#endif
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ ExceptionOr<void> substituteWithAlternativePresentationButton(Vector<RefPtr<Element>>&&, const String&);
+ ExceptionOr<void> removeAlternativePresentationButton(const String&);
+#endif
+
String timelineDescription(AnimationTimeline&);
void pauseTimeline(AnimationTimeline&);
void setTimelineCurrentTime(AnimationTimeline&, double);
[EnabledAtRuntime=WebAnimations] void pauseTimeline(AnimationTimeline timeline);
[EnabledAtRuntime=WebAnimations] void setTimelineCurrentTime(AnimationTimeline timeline, double currentTime);
[Conditional=APPLE_PAY] readonly attribute MockPaymentCoordinator mockPaymentCoordinator;
+
+ [Conditional=ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT, MayThrowException] void substituteWithAlternativePresentationButton(sequence<Element> elements, DOMString identifier);
+ [Conditional=ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT, MayThrowException] void removeAlternativePresentationButton(DOMString identifier);
};
+2017-11-28 Daniel Bates <dabates@apple.com>
+
+ [Cocoa] First pass at implementing alternative presentation button element
+ https://bugs.webkit.org/show_bug.cgi?id=179785
+ Part of <rdar://problem/34917108>
+
+ Reviewed by Brent Fulgham.
+
+ Expose SPI to substitute the alternative presentation button for one or more elements
+ and remove the alternative presentation button. Add a private delegate callback when
+ the alternative presentation button is clicked.
+
+ * UIProcess/API/APIUIClient.h:
+ (API::UIClient::didClickAlternativePresentationButton): Added.
+ * UIProcess/API/C/WKPageUIClient.h:
+ * UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
+ * UIProcess/Cocoa/UIDelegate.h:
+ * UIProcess/Cocoa/UIDelegate.mm:
+ (WebKit::UIDelegate::setDelegate): Wired up delegate callback.
+ (WebKit::UIDelegate::UIClient::didClickAlternativePresentationButton): Added.
+ * UIProcess/WebPageProxy.cpp:
+ (WebKit::WebPageProxy::handleAlternativePresentationButtonClick): Added.
+ * UIProcess/WebPageProxy.h:
+ * UIProcess/WebPageProxy.messages.in:
+ * WebProcess/InjectedBundle/API/APIInjectedBundlePageUIClient.h:
+ (API::InjectedBundle::PageUIClient::didClickAlternativePresentationButton): Added.
+ * WebProcess/InjectedBundle/API/Cocoa/WKWebProcessPlugInFrame.h:
+ * WebProcess/InjectedBundle/API/Cocoa/WKWebProcessPlugInFrame.mm:
+ (-[WKWebProcessPlugInFrame substituteElements:withAlternativePresentationButtonWithIdentifier:]): Added.
+ (-[WKWebProcessPlugInFrame removeAlternativePresentationButton:]): Added.
+ * WebProcess/InjectedBundle/API/Cocoa/WKWebProcessPlugInFramePrivate.h:
+ * WebProcess/InjectedBundle/API/c/WKBundleFrame.cpp:
+ (WKBundleSubstituteWithAlternativePresentationButton): Added.
+ (WKBundleRemoveAlternativePresentationButton): Added.
+ * WebProcess/InjectedBundle/API/c/WKBundleFramePrivate.h:
+ * WebProcess/InjectedBundle/API/c/WKBundlePageUIClient.h:
+ * WebProcess/InjectedBundle/InjectedBundlePageUIClient.cpp:
+ (WebKit::InjectedBundlePageUIClient::didClickAlternativePresentationButton): Added.
+ * WebProcess/InjectedBundle/InjectedBundlePageUIClient.h:
+ * WebProcess/WebCoreSupport/WebChromeClient.cpp:
+ (WebKit::WebChromeClient::handleAlternativePresentationButtonClick): Added.
+ * WebProcess/WebCoreSupport/WebChromeClient.h:
+
2017-11-27 Chris Dumez <cdumez@apple.com>
ASSERTION FAILED: addResult.isNewEntry WebCore::SWServerRegistration::addClientUsingRegistration(WebCore::ServiceWorkerClientIdentifier const&) + 141
virtual void didClickAutoFillButton(WebKit::WebPageProxy&, Object*) { }
+ virtual void didClickAlternativePresentationButton(WebKit::WebPageProxy&, Object*) { }
+
virtual void imageOrMediaDocumentSizeChanged(const WebCore::IntSize&) { }
virtual void didExceedBackgroundResourceLimitWhileInForeground(WebKit::WebPageProxy&, WKResourceLimit) { }
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WKPageUIClient_h
-#define WKPageUIClient_h
+#pragma once
#include <WebKit/WKBase.h>
#include <WebKit/WKEvent.h>
typedef void (*WKPageDecidePolicyForUserMediaPermissionRequestCallback)(WKPageRef page, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionRequestRef permissionRequest, const void* clientInfo);
typedef void (*WKCheckUserMediaPermissionCallback)(WKPageRef page, WKFrameRef frame, WKSecurityOriginRef userMediaDocumentOrigin, WKSecurityOriginRef topLevelDocumentOrigin, WKUserMediaPermissionCheckRef devicesRequest, const void *clientInfo);
typedef void (*WKPageDidClickAutoFillButtonCallback)(WKPageRef page, WKTypeRef userData, const void *clientInfo);
+typedef void (*WKPageDidClickAlternativePresentationButtonCallback)(WKPageRef page, WKTypeRef userData, const void *clientInfo);
typedef void (*WKPageMediaSessionMetadataDidChangeCallback)(WKPageRef page, WKMediaSessionMetadataRef metadata, const void* clientInfo);
typedef void (*WKHandleAutoplayEventCallback)(WKPageRef page, WKAutoplayEvent event, WKAutoplayEventFlags flags, const void* clientInfo);
typedef void (*WKFullscreenMayReturnToInlineCallback)(WKPageRef page, const void* clientInfo);
typedef void (*WKDidLosePointerLockCallback)(WKPageRef page, const void* clientInfo);
typedef void (*WKHasVideoInPictureInPictureDidChangeCallback)(WKPageRef page, bool hasVideoInPictureInPicture, const void* clientInfo);
typedef void (*WKDidExceedBackgroundResourceLimitWhileInForegroundCallback)(WKPageRef page, WKResourceLimit limit, const void* clientInfo);
-
+
// Deprecated
typedef WKPageRef (*WKPageCreateNewPageCallback_deprecatedForUseWithV0)(WKPageRef page, WKDictionaryRef features, WKEventModifiers modifiers, WKEventMouseButton mouseButton, const void *clientInfo);
typedef void (*WKPageMouseDidMoveOverElementCallback_deprecatedForUseWithV0)(WKPageRef page, WKEventModifiers modifiers, WKTypeRef userData, const void *clientInfo);
WKHandleAutoplayEventCallback handleAutoplayEvent;
// Version 10.
- WKHasVideoInPictureInPictureDidChangeCallback hasVideoInPictureInPictureDidChange;
+ WKHasVideoInPictureInPictureDidChangeCallback hasVideoInPictureInPictureDidChange;
WKDidExceedBackgroundResourceLimitWhileInForegroundCallback didExceedBackgroundResourceLimitWhileInForeground;
} WKPageUIClientV10;
-
+
+typedef struct WKPageUIClientV11 {
+ WKPageUIClientBase base;
+
+ // Version 0.
+ WKPageCreateNewPageCallback_deprecatedForUseWithV0 createNewPage_deprecatedForUseWithV0;
+ WKPageUIClientCallback showPage;
+ WKPageUIClientCallback close;
+ WKPageTakeFocusCallback takeFocus;
+ WKPageFocusCallback focus;
+ WKPageUnfocusCallback unfocus;
+ WKPageRunJavaScriptAlertCallback_deprecatedForUseWithV0 runJavaScriptAlert_deprecatedForUseWithV0;
+ WKPageRunJavaScriptConfirmCallback_deprecatedForUseWithV0 runJavaScriptConfirm_deprecatedForUseWithV0;
+ WKPageRunJavaScriptPromptCallback_deprecatedForUseWithV0 runJavaScriptPrompt_deprecatedForUseWithV0;
+ WKPageSetStatusTextCallback setStatusText;
+ WKPageMouseDidMoveOverElementCallback_deprecatedForUseWithV0 mouseDidMoveOverElement_deprecatedForUseWithV0;
+ WKPageMissingPluginButtonClickedCallback_deprecatedForUseWithV0 missingPluginButtonClicked_deprecatedForUseWithV0;
+ WKPageDidNotHandleKeyEventCallback didNotHandleKeyEvent;
+ WKPageDidNotHandleWheelEventCallback didNotHandleWheelEvent;
+ WKPageGetToolbarsAreVisibleCallback toolbarsAreVisible;
+ WKPageSetToolbarsAreVisibleCallback setToolbarsAreVisible;
+ WKPageGetMenuBarIsVisibleCallback menuBarIsVisible;
+ WKPageSetMenuBarIsVisibleCallback setMenuBarIsVisible;
+ WKPageGetStatusBarIsVisibleCallback statusBarIsVisible;
+ WKPageSetStatusBarIsVisibleCallback setStatusBarIsVisible;
+ WKPageGetIsResizableCallback isResizable;
+ WKPageSetIsResizableCallback setIsResizable;
+ WKPageGetWindowFrameCallback getWindowFrame;
+ WKPageSetWindowFrameCallback setWindowFrame;
+ WKPageRunBeforeUnloadConfirmPanelCallback_deprecatedForUseWithV6 runBeforeUnloadConfirmPanel_deprecatedForUseWithV6;
+ WKPageUIClientCallback didDraw;
+ WKPageUIClientCallback pageDidScroll;
+ WKPageExceededDatabaseQuotaCallback exceededDatabaseQuota;
+ WKPageRunOpenPanelCallback runOpenPanel;
+ WKPageDecidePolicyForGeolocationPermissionRequestCallback decidePolicyForGeolocationPermissionRequest;
+ WKPageHeaderHeightCallback headerHeight;
+ WKPageFooterHeightCallback footerHeight;
+ WKPageDrawHeaderCallback drawHeader;
+ WKPageDrawFooterCallback drawFooter;
+ WKPagePrintFrameCallback printFrame;
+ WKPageUIClientCallback runModal;
+ void* unused1; // Used to be didCompleteRubberBandForMainFrame
+ WKPageSaveDataToFileInDownloadsFolderCallback saveDataToFileInDownloadsFolder;
+ void* shouldInterruptJavaScript_unavailable;
+
+ // Version 1.
+ WKPageCreateNewPageCallback_deprecatedForUseWithV1 createNewPage_deprecatedForUseWithV1;
+ WKPageMouseDidMoveOverElementCallback mouseDidMoveOverElement;
+ WKPageDecidePolicyForNotificationPermissionRequestCallback decidePolicyForNotificationPermissionRequest;
+ WKPageUnavailablePluginButtonClickedCallback_deprecatedForUseWithV1 unavailablePluginButtonClicked_deprecatedForUseWithV1;
+
+ // Version 2.
+ WKPageShowColorPickerCallback showColorPicker;
+ WKPageHideColorPickerCallback hideColorPicker;
+ WKPageUnavailablePluginButtonClickedCallback unavailablePluginButtonClicked;
+
+ // Version 3.
+ WKPagePinnedStateDidChangeCallback pinnedStateDidChange;
+
+ // Version 4.
+ void* unused2; // Used to be didBeginTrackingPotentialLongMousePress.
+ void* unused3; // Used to be didRecognizeLongMousePress.
+ void* unused4; // Used to be didCancelTrackingPotentialLongMousePress.
+ WKPageIsPlayingAudioDidChangeCallback isPlayingAudioDidChange;
+
+ // Version 5.
+ WKPageDecidePolicyForUserMediaPermissionRequestCallback decidePolicyForUserMediaPermissionRequest;
+ WKPageDidClickAutoFillButtonCallback didClickAutoFillButton;
+ WKPageRunJavaScriptAlertCallback_deprecatedForUseWithV5 runJavaScriptAlert_deprecatedForUseWithV5;
+ WKPageRunJavaScriptConfirmCallback_deprecatedForUseWithV5 runJavaScriptConfirm_deprecatedForUseWithV5;
+ WKPageRunJavaScriptPromptCallback_deprecatedForUseWithV5 runJavaScriptPrompt_deprecatedForUseWithV5;
+ WKPageMediaSessionMetadataDidChangeCallback mediaSessionMetadataDidChange;
+
+ // Version 6.
+ WKPageCreateNewPageCallback createNewPage;
+ WKPageRunJavaScriptAlertCallback runJavaScriptAlert;
+ WKPageRunJavaScriptConfirmCallback runJavaScriptConfirm;
+ WKPageRunJavaScriptPromptCallback runJavaScriptPrompt;
+ WKCheckUserMediaPermissionCallback checkUserMediaPermissionForOrigin;
+
+ // Version 7.
+ WKPageRunBeforeUnloadConfirmPanelCallback runBeforeUnloadConfirmPanel;
+ WKFullscreenMayReturnToInlineCallback fullscreenMayReturnToInline;
+
+ // Version 8.
+ WKRequestPointerLockCallback requestPointerLock;
+ WKDidLosePointerLockCallback didLosePointerLock;
+
+ // Version 9.
+ WKHandleAutoplayEventCallback handleAutoplayEvent;
+
+ // Version 10.
+ WKHasVideoInPictureInPictureDidChangeCallback hasVideoInPictureInPictureDidChange;
+ WKDidExceedBackgroundResourceLimitWhileInForegroundCallback didExceedBackgroundResourceLimitWhileInForeground;
+
+ // Version 11.
+ WKPageDidClickAlternativePresentationButtonCallback didClickAlternativePresentationButton;
+} WKPageUIClientV11;
+
#ifdef __cplusplus
}
#endif
-
-#endif // WKPageUIClient_h
- (void)_webView:(WKWebView *)webView didNotHandleWheelEvent:(NSEvent *)event WK_API_AVAILABLE(macosx(WK_MAC_TBA));
- (void)_webView:(WKWebView *)webView handleAutoplayEvent:(_WKAutoplayEvent)event withFlags:(_WKAutoplayEventFlags)flags WK_API_AVAILABLE(macosx(WK_MAC_TBA));
- (void)_webView:(WKWebView *)webView didClickAutoFillButtonWithUserInfo:(id <NSSecureCoding>)userInfo WK_API_AVAILABLE(macosx(WK_MAC_TBA));
+- (void)_webView:(WKWebView *)webView didClickAlternativePresentationButtonWithUserInfo:(id <NSSecureCoding>)userInfo WK_API_AVAILABLE(macosx(WK_MAC_TBA));
- (void)_webView:(WKWebView *)webView getToolbarsAreVisibleWithCompletionHandler:(void(^)(BOOL))completionHandler WK_API_AVAILABLE(macosx(WK_MAC_TBA));
- (void)_webView:(WKWebView *)webView saveDataToFile:(NSData *)data suggestedFilename:(NSString *)suggestedFilename mimeType:(NSString *)mimeType originatingURL:(NSURL *)url WK_API_AVAILABLE(macosx(WK_MAC_TBA));
- (CGFloat)_webViewHeaderHeight:(WKWebView *)webView WK_API_AVAILABLE(macosx(WK_MAC_TBA));
void unavailablePluginButtonClicked(WebPageProxy&, WKPluginUnavailabilityReason, API::Dictionary&) final;
void mouseDidMoveOverElement(WebPageProxy&, const WebHitTestResultData&, WebEvent::Modifiers, API::Object*);
void didClickAutoFillButton(WebPageProxy&, API::Object*) final;
+ void didClickAlternativePresentationButton(WebPageProxy&, API::Object*) final;
void toolbarsAreVisible(WebPageProxy&, Function<void(bool)>&&) final;
bool runOpenPanel(WebPageProxy*, WebFrameProxy*, const WebCore::SecurityOriginData&, API::OpenPanelParameters*, WebOpenPanelResultListenerProxy*) final;
void didExceedBackgroundResourceLimitWhileInForeground(WebPageProxy&, WKResourceLimit) final;
bool webViewHandleAutoplayEventWithFlags : 1;
bool webViewUnavailablePlugInButtonClicked : 1;
bool webViewDidClickAutoFillButtonWithUserInfo : 1;
+ bool webViewDidClickAlternativePresentationButtonWithUserInfo : 1;
bool webViewDrawHeaderInRectForPageWithTitleURL : 1;
bool webViewDrawFooterInRectForPageWithTitleURL : 1;
bool webViewGetWindowFrameWithCompletionHandler : 1;
m_delegateMethods.webViewUnavailablePlugInButtonClicked = [delegate respondsToSelector:@selector(_webView:unavailablePlugInButtonClickedWithReason:plugInInfo:)];
m_delegateMethods.webViewHandleAutoplayEventWithFlags = [delegate respondsToSelector:@selector(_webView:handleAutoplayEvent:withFlags:)];
m_delegateMethods.webViewDidClickAutoFillButtonWithUserInfo = [delegate respondsToSelector:@selector(_webView:didClickAutoFillButtonWithUserInfo:)];
+ m_delegateMethods.webViewDidClickAlternativePresentationButtonWithUserInfo = [delegate respondsToSelector:@selector(_webView:didClickAlternativePresentationButtonWithUserInfo:)];
m_delegateMethods.webViewDrawHeaderInRectForPageWithTitleURL = [delegate respondsToSelector:@selector(_webView:drawHeaderInRect:forPageWithTitle:URL:)];
m_delegateMethods.webViewDrawFooterInRectForPageWithTitleURL = [delegate respondsToSelector:@selector(_webView:drawFooterInRect:forPageWithTitle:URL:)];
m_delegateMethods.webViewHeaderHeight = [delegate respondsToSelector:@selector(_webViewHeaderHeight:)];
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView didClickAutoFillButtonWithUserInfo:userInfo ? static_cast<id <NSSecureCoding>>(userInfo->wrapper()) : nil];
}
-
+
+void UIDelegate::UIClient::didClickAlternativePresentationButton(WebPageProxy&, API::Object* userInfo)
+{
+ if (!m_uiDelegate.m_delegateMethods.webViewDidClickAlternativePresentationButtonWithUserInfo)
+ return;
+
+ auto delegate = m_uiDelegate.m_delegate.get();
+ if (!delegate)
+ return;
+
+ [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView didClickAlternativePresentationButtonWithUserInfo:userInfo ? static_cast<id <NSSecureCoding>>(userInfo->wrapper()) : nil];
+}
+
void UIDelegate::UIClient::handleAutoplayEvent(WebPageProxy&, WebCore::AutoplayEvent event, OptionSet<WebCore::AutoplayEventFlags> flags)
{
if (!m_uiDelegate.m_delegateMethods.webViewHandleAutoplayEventWithFlags)
m_uiClient->didClickAutoFillButton(*this, m_process->transformHandlesToObjects(userData.object()).get());
}
+void WebPageProxy::handleAlternativePresentationButtonClick(const UserData& userData)
+{
+ m_uiClient->didClickAlternativePresentationButton(*this, m_process->transformHandlesToObjects(userData.object()).get());
+}
+
#if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
void WebPageProxy::addPlaybackTargetPickerClient(uint64_t contextId)
{
void handleAutoFillButtonClick(const UserData&);
+ void handleAlternativePresentationButtonClick(const UserData&);
+
void finishInitializingWebPageAfterProcessLaunch();
void handleMessage(IPC::Connection&, const String& messageName, const UserData& messageBody);
HandleSynchronousMessage(String messageName, WebKit::UserData messageBody) -> (WebKit::UserData returnData) WantsConnection
HandleAutoFillButtonClick(WebKit::UserData userData);
+
+ HandleAlternativePresentationButtonClick(WebKit::UserData userData)
+
ContentRuleListNotification(WebCore::URL url, Vector<String> identifiers, Vector<String> notifications)
#if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef APIInjectedBundlePageUIClient_h
-#define APIInjectedBundlePageUIClient_h
+#pragma once
#include "WebEvent.h"
#include <runtime/ConsoleTypes.h>
virtual WTF::String plugInExtraScript() const { return emptyString(); }
virtual void didClickAutoFillButton(WebKit::WebPage&, WebKit::InjectedBundleNodeHandle&, RefPtr<API::Object>&) { }
+
+ virtual void didClickAlternativePresentationButton(WebKit::WebPage&, WebKit::InjectedBundleNodeHandle&, RefPtr<API::Object>&) { }
};
} // namespace InjectedBundle
} // namespace API
-
-#endif // APIInjectedBundlePageUIClient_h
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#import "_WKFrameHandleInternal.h"
#import <JavaScriptCore/JSValue.h>
#import <WebCore/CertificateInfo.h>
+#import <WebCore/Editor.h>
+#import <WebCore/Element.h>
#import <WebCore/Frame.h>
#import <WebCore/IntPoint.h>
#import <WebCore/LinkIconCollector.h>
#import <WebCore/LinkIconType.h>
+#import <wtf/Vector.h>
using namespace WebKit;
return [JSValue valueWithJSValueRef:valueRef inContext:[self jsContextForWorld:world]];
}
+- (void)substituteElements:(NSArray<WKWebProcessPlugInNodeHandle *> *)elements withAlternativePresentationButtonWithIdentifier:(NSString *)identifier
+{
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ size_t size = static_cast<size_t>(elements.count);
+ Vector<Ref<WebCore::Element>> coreElements;
+ coreElements.reserveInitialCapacity(size);
+ for (size_t i = 0; i < size; ++i) {
+ auto* plugInNodeHandle = [elements objectAtIndex:static_cast<NSUInteger>(i)];
+ if (!plugInNodeHandle)
+ continue;
+ auto& coreNode = *plugInNodeHandle._nodeHandle.coreNode();
+ if (is<WebCore::Element>(coreNode))
+ coreElements.uncheckedAppend(downcast<WebCore::Element>(coreNode));
+ }
+ _frame->coreFrame()->editor().substituteWithAlternativePresentationButton(WTFMove(coreElements), identifier);
+#endif
+}
+
+- (void)removeAlternativePresentationButton:(NSString *)identifier
+{
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ _frame->coreFrame()->editor().removeAlternativePresentationButton(identifier);
+#endif
+}
+
- (WKWebProcessPlugInBrowserContextController *)_browserContextController
{
return wrapper(*_frame->page());
@property (nonatomic, readonly) WKWebProcessPlugInFrame *_parentFrame;
+- (void)substituteElements:(NSArray<WKWebProcessPlugInNodeHandle *> *)elements withAlternativePresentationButtonWithIdentifier:(NSString *)identifier;
+- (void)removeAlternativePresentationButton:(NSString *)identifier;
+
@end
#endif // WK_API_ENABLED
#include "WebFrame.h"
#include "WebPage.h"
#include <WebCore/Document.h>
+#include <WebCore/Editor.h>
#include <WebCore/FocusController.h>
#include <WebCore/Frame.h>
#include <WebCore/FrameLoader.h>
coreFrame->page()->focusController().setFocusedFrame(coreFrame);
}
+
+void WKBundleSubstituteWithAlternativePresentationButton(WKBundleFrameRef frame, WKArrayRef elements, WKStringRef identifier)
+{
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ auto* coreFrame = toImpl(frame)->coreFrame();
+ if (!coreFrame)
+ return;
+ API::Array* apiAssociatedElements = toImpl(elements);
+ Vector<Ref<Element>> coreElements;
+ coreElements.reserveInitialCapacity(apiAssociatedElements->size());
+ // FIXME: Add a non-constant iterator for API::Array.
+ for (const auto* nodeHandle : apiAssociatedElements->elementsOfType<InjectedBundleNodeHandle>()) {
+ if (!nodeHandle)
+ continue;
+ Node& coreNode = *const_cast<InjectedBundleNodeHandle*>(nodeHandle)->coreNode();
+ if (is<Element>(coreNode))
+ coreElements.uncheckedAppend(downcast<Element>(coreNode));
+ }
+ coreFrame->editor().substituteWithAlternativePresentationButton(WTFMove(coreElements), toWTFString(identifier));
+#else
+ UNUSED_PARAM(frame);
+ UNUSED_PARAM(elements);
+ UNUSED_PARAM(identifier);
+#endif
+}
+
+void WKBundleRemoveAlternativePresentationButton(WKBundleFrameRef frame, WKStringRef identifier)
+{
+#if ENABLE(ALTERNATIVE_PRESENTATION_BUTTON_ELEMENT)
+ if (auto* coreFrame = toImpl(frame)->coreFrame())
+ coreFrame->editor().removeAlternativePresentationButton(toWTFString(identifier));
+#else
+ UNUSED_PARAM(frame);
+ UNUSED_PARAM(identifier);
+#endif
+}
WK_EXPORT void WKBundleFrameFocus(WKBundleFrameRef frame);
+WK_EXPORT void WKBundleSubstituteWithAlternativePresentationButton(WKBundleFrameRef frame, WKArrayRef elements, WKStringRef identifier);
+WK_EXPORT void WKBundleRemoveAlternativePresentationButton(WKBundleFrameRef frame, WKStringRef identifier);
+
#ifdef __cplusplus
}
#endif
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WKBundlePageUIClient_h
-#define WKBundlePageUIClient_h
+#pragma once
#include <WebKit/WKBase.h>
#include <WebKit/WKEvent.h>
typedef WKStringRef (*WKBundlePagePlugInCreateExtraStyleSheetCallback)(const void *clientInfo);
typedef WKStringRef (*WKBundlePagePlugInCreateExtraScriptCallback)(const void *clientInfo);
typedef void (*WKBundlePageDidClickAutoFillButtonCallback)(WKBundlePageRef page, WKBundleNodeHandleRef inputElement, WKTypeRef* userData, const void *clientInfo);
+typedef void (*WKBundlePageDidClickAlternativePresentationButtonCallback)(WKBundlePageRef page, WKBundleNodeHandleRef inputElement, WKTypeRef* userData, const void *clientInfo);
typedef struct WKBundlePageUIClientBase {
int version;
WKBundlePageDidClickAutoFillButtonCallback didClickAutoFillButton;
} WKBundlePageUIClientV3;
-#endif // WKBundlePageUIClient_h
+typedef struct WKBundlePageUIClientV4 {
+ WKBundlePageUIClientBase base;
+
+ // Version 0.
+ WKBundlePageWillAddMessageToConsoleCallback willAddMessageToConsole;
+ WKBundlePageWillSetStatusbarTextCallback willSetStatusbarText;
+ WKBundlePageWillRunJavaScriptAlertCallback willRunJavaScriptAlert;
+ WKBundlePageWillRunJavaScriptConfirmCallback willRunJavaScriptConfirm;
+ WKBundlePageWillRunJavaScriptPromptCallback willRunJavaScriptPrompt;
+ WKBundlePageMouseDidMoveOverElementCallback mouseDidMoveOverElement;
+ WKBundlePageDidScrollCallback pageDidScroll;
+ void* unused1;
+ WKBundlePageGenerateFileForUploadCallback shouldGenerateFileForUpload;
+ WKBundlePageGenerateFileForUploadCallback generateFileForUpload;
+ void* unused2;
+ WKBundlePageStatusBarIsVisibleCallback statusBarIsVisible;
+ WKBundlePageMenuBarIsVisibleCallback menuBarIsVisible;
+ WKBundlePageToolbarsAreVisibleCallback toolbarsAreVisible;
+
+ // Version 1.
+ WKBundlePageReachedAppCacheOriginQuotaCallback didReachApplicationCacheOriginQuota;
+
+ // Version 2.
+ WKBundlePageExceededDatabaseQuotaCallback didExceedDatabaseQuota;
+ WKBundlePagePlugInCreateStartLabelTitleCallback createPlugInStartLabelTitle;
+ WKBundlePagePlugInCreateStartLabelSubtitleCallback createPlugInStartLabelSubtitle;
+ WKBundlePagePlugInCreateExtraStyleSheetCallback createPlugInExtraStyleSheet;
+ WKBundlePagePlugInCreateExtraScriptCallback createPlugInExtraScript;
+
+ // Version 3.
+ void* unused3;
+ void* unused4;
+ void* unused5;
+
+ WKBundlePageDidClickAutoFillButtonCallback didClickAutoFillButton;
+
+ // Version 4.
+ WKBundlePageDidClickAlternativePresentationButtonCallback didClickAlternativePresentationButton;
+} WKBundlePageUIClientV4;
userData = adoptRef(toImpl(userDataToPass));
}
+void InjectedBundlePageUIClient::didClickAlternativePresentationButton(WebPage& page, InjectedBundleNodeHandle& nodeHandle, RefPtr<API::Object>& userData)
+{
+ if (!m_client.didClickAlternativePresentationButton)
+ return;
+
+ WKTypeRef userDataToPass = nullptr;
+ m_client.didClickAlternativePresentationButton(toAPI(&page), toAPI(&nodeHandle), &userDataToPass, m_client.base.clientInfo);
+ userData = adoptRef(toImpl(userDataToPass));
+}
+
} // namespace WebKit
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InjectedBundlePageUIClient_h
-#define InjectedBundlePageUIClient_h
+#pragma once
#include "APIClient.h"
#include "APIInjectedBundlePageUIClient.h"
class Object;
template<> struct ClientTraits<WKBundlePageUIClientBase> {
- typedef std::tuple<WKBundlePageUIClientV0, WKBundlePageUIClientV1, WKBundlePageUIClientV2, WKBundlePageUIClientV3> Versions;
+ typedef std::tuple<WKBundlePageUIClientV0, WKBundlePageUIClientV1, WKBundlePageUIClientV2, WKBundlePageUIClientV3, WKBundlePageUIClientV4> Versions;
};
}
String plugInExtraScript() const override;
void didClickAutoFillButton(WebPage&, InjectedBundleNodeHandle&, RefPtr<API::Object>& userData) override;
+
+ void didClickAlternativePresentationButton(WebPage&, InjectedBundleNodeHandle&, RefPtr<API::Object>& userData) override;
};
} // namespace WebKit
-
-#endif // InjectedBundlePageUIClient_h
m_page.send(Messages::WebPageProxy::HandleAutoFillButtonClick(UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
}
+void WebChromeClient::handleAlternativePresentationButtonClick(Node& node)
+{
+ RefPtr<API::Object> userData;
+
+ // Notify the bundle client.
+ auto nodeHandle = InjectedBundleNodeHandle::getOrCreate(node);
+ m_page.injectedBundleUIClient().didClickAlternativePresentationButton(m_page, nodeHandle.get(), userData);
+
+ // Notify the UIProcess.
+ m_page.send(Messages::WebPageProxy::HandleAlternativePresentationButtonClick(UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
+}
+
#if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
void WebChromeClient::addPlaybackTargetPickerClient(uint64_t contextId)
void handleAutoFillButtonClick(WebCore::HTMLInputElement&) final;
+ void handleAlternativePresentationButtonClick(WebCore::Node&) final;
+
#if ENABLE(WIRELESS_PLAYBACK_TARGET) && !PLATFORM(IOS)
void addPlaybackTargetPickerClient(uint64_t /*contextId*/) final;
void removePlaybackTargetPickerClient(uint64_t /*contextId*/) final;
+2017-11-28 Daniel Bates <dabates@apple.com>
+
+ [Cocoa] First pass at implementing alternative presentation button element
+ https://bugs.webkit.org/show_bug.cgi?id=179785
+ Part of <rdar://problem/34917108>
+
+ Reviewed by Brent Fulgham.
+
+ Add a test that substitutes the alternative presentation button for an element in
+ the page and clicks it.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj: Add test.
+ * TestWebKitAPI/Tests/WebKitCocoa/ClickAlternativePresentationButton.mm: Added.
+ (didClickAlternativePresentationButton):
+ (-[ClickAlternativePresentationButton webProcessPlugIn:didCreateBrowserContextController:]):
+ * TestWebKitAPI/Tests/WebKitCocoa/UIDelegate.mm:
+ (TEST):
+ (-[AlternativePresentationButtonDelegate _webView:didClickAlternativePresentationButtonWithUserInfo:]):
+ (-[AlternativePresentationButtonDelegate webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:]):
+
2017-11-28 Carlos Garcia Campos <cgarcia@igalia.com>
REGRESSION(r225166): [GTK] Skipped unit tests are considered failures after glib upgrade
CE06DF9B1E1851F200E570C9 /* SecurityOrigin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */; };
CE14F1A4181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE14F1A2181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html */; };
CE1866491F72E8F100A0CAB6 /* MarkerSubrange.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE1866471F72E8F100A0CAB6 /* MarkerSubrange.cpp */; };
+ CE2C13881FBDF98D0032DD6C /* ClickAlternativePresentationButton.mm in Sources */ = {isa = PBXBuildFile; fileRef = CE2C13861FBD125C0032DD6C /* ClickAlternativePresentationButton.mm */; };
CE3524F81B1431F60028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F21B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp */; };
CE3524F91B1441C40028A7C5 /* TextFieldDidBeginAndEndEditing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */; };
CE3524FA1B1443890028A7C5 /* input-focus-blur.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CE3524F51B142BBB0028A7C5 /* input-focus-blur.html */; };
CE06DF9A1E1851F200E570C9 /* SecurityOrigin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SecurityOrigin.cpp; sourceTree = "<group>"; };
CE14F1A2181873B0001C2705 /* WillPerformClientRedirectToURLCrash.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = WillPerformClientRedirectToURLCrash.html; sourceTree = "<group>"; };
CE1866471F72E8F100A0CAB6 /* MarkerSubrange.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MarkerSubrange.cpp; sourceTree = "<group>"; };
+ CE2C13861FBD125C0032DD6C /* ClickAlternativePresentationButton.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ClickAlternativePresentationButton.mm; sourceTree = "<group>"; };
CE32C7C718184C4900CD8C28 /* WillPerformClientRedirectToURLCrash.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WillPerformClientRedirectToURLCrash.mm; sourceTree = "<group>"; };
CE3524F11B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextFieldDidBeginAndEndEditing.cpp; sourceTree = "<group>"; };
CE3524F21B142B8D0028A7C5 /* TextFieldDidBeginAndEndEditing_Bundle.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TextFieldDidBeginAndEndEditing_Bundle.cpp; sourceTree = "<group>"; };
37A709AC1E3EA7E800CA5969 /* BundleRangeHandleProtocol.h */,
1C2B817E1C891E4200A5529F /* CancelFontSubresource.mm */,
1C2B81811C891EFA00A5529F /* CancelFontSubresourcePlugIn.mm */,
+ CE2C13861FBD125C0032DD6C /* ClickAlternativePresentationButton.mm */,
5CB18BA71F5645B200EE23C4 /* ClickAutoFillButton.mm */,
1AAD19F51C7CE20300831E47 /* Coding.mm */,
7C3DB8E21D12129B00AE8CC3 /* CommandBackForward.mm */,
A13EBBB01B87436F00097110 /* BundleParametersPlugIn.mm in Sources */,
37A709AF1E3EA97E00CA5969 /* BundleRangeHandlePlugIn.mm in Sources */,
1C2B81831C891F0900A5529F /* CancelFontSubresourcePlugIn.mm in Sources */,
+ CE2C13881FBDF98D0032DD6C /* ClickAlternativePresentationButton.mm in Sources */,
5CB18BA81F5645E300EE23C4 /* ClickAutoFillButton.mm in Sources */,
A14FC58B1B89927100D107EB /* ContentFilteringPlugIn.mm in Sources */,
A13EBBAB1B87434600097110 /* PlatformUtilitiesCocoa.mm in Sources */,
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if WK_API_ENABLED
+
+#import <WebKit/WKBundleNodeHandlePrivate.h>
+#import <WebKit/WKBundlePage.h>
+#import <WebKit/WKBundlePageUIClient.h>
+#import <WebKit/WKDOMDocument.h>
+#import <WebKit/WKDOMElement.h>
+#import <WebKit/WKDOMNodePrivate.h>
+#import <WebKit/WKDOMText.h>
+#import <WebKit/WKString.h>
+#import <WebKit/WKWebProcessPlugIn.h>
+#import <WebKit/WKWebProcessPlugInBrowserContextControllerPrivate.h>
+#import <WebKit/WKWebProcessPlugInFrame.h>
+#import <WebKit/WKWebProcessPlugInFramePrivate.h>
+#import <WebKit/WKWebProcessPlugInNodeHandle.h>
+#import <WebKit/WKWebProcessPlugInScriptWorld.h>
+#import <wtf/RetainPtr.h>
+
+void didClickAlternativePresentationButton(WKBundlePageRef, WKBundleNodeHandleRef, WKTypeRef* userData, const void *)
+{
+ *userData = WKStringCreateWithUTF8CString("user data string");
+}
+
+@interface ClickAlternativePresentationButton : NSObject <WKWebProcessPlugIn>
+@end
+
+@implementation ClickAlternativePresentationButton
+
+- (void)webProcessPlugIn:(WKWebProcessPlugInController *)plugInController didCreateBrowserContextController:(WKWebProcessPlugInBrowserContextController *)browserContextController
+{
+ WKBundlePageUIClientV4 client;
+ memset(&client, 0, sizeof(client));
+ client.base.version = 4;
+ client.didClickAlternativePresentationButton = didClickAlternativePresentationButton;
+ WKBundlePageSetUIClient([browserContextController _bundlePageRef], &client.base);
+
+ WKDOMDocument *document = [browserContextController mainFrameDocument];
+ WKDOMElement *inputElement = [document createElement:@"input"];
+ [[document body] appendChild:inputElement];
+
+ auto *jsContext = [[browserContextController mainFrame] jsContextForWorld:[WKWebProcessPlugInScriptWorld normalWorld]];
+ auto *jsValue = [jsContext evaluateScript:@"document.querySelector('input')"];
+
+ RetainPtr<NSArray<WKWebProcessPlugInNodeHandle *>> elements = @[[WKWebProcessPlugInNodeHandle nodeHandleWithJSValue:jsValue inContext:jsContext]];
+ [[browserContextController mainFrame] substituteElements:elements.get() withAlternativePresentationButtonWithIdentifier:@"1"];
+
+ [jsContext evaluateScript:@"alert('ready for click!')"];
+}
+
+@end
+
+#endif // WK_API_ENABLED
[webView mouseDownAtPoint:buttonLocation simulatePressure:NO];
[webView mouseUpAtPoint:buttonLocation];
TestWebKitAPI::Util::run(&done);
+ readyForClick = false;
+}
+
+@interface AlternativePresentationButtonDelegate : NSObject <WKUIDelegatePrivate>
+@end
+
+@implementation AlternativePresentationButtonDelegate
+
+- (void)_webView:(WKWebView *)webView didClickAlternativePresentationButtonWithUserInfo:(id <NSSecureCoding>)userInfo
+{
+ ASSERT_TRUE([(id<NSObject>)userInfo isKindOfClass:[NSString class]]);
+ ASSERT_STREQ([(NSString*)userInfo UTF8String], "user data string");
+ done = true;
+}
+
+- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
+{
+ completionHandler();
+ ASSERT_STREQ(message.UTF8String, "ready for click!");
+ readyForClick = true;
+}
+
+@end
+
+TEST(WebKit, ClickAlternativePresentationButton)
+{
+ WKWebViewConfiguration *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"ClickAlternativePresentationButton"];
+
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600) configuration:configuration]);
+ auto delegate = adoptNS([[AlternativePresentationButtonDelegate alloc] init]);
+ [webView setUIDelegate:delegate.get()];
+ TestWebKitAPI::Util::run(&readyForClick);
+ NSPoint buttonLocation = NSMakePoint(130, 575);
+ [webView mouseDownAtPoint:buttonLocation simulatePressure:NO];
+ [webView mouseUpAtPoint:buttonLocation];
+ TestWebKitAPI::Util::run(&done);
+ readyForClick = false;
}
@interface AutoFillAvailableDelegate : NSObject <WKUIDelegatePrivate>