commit 052c86d22f40fac0089c7990fdc2c73beab63b33 parent 3fff33da5b78a9d8dd36763f9f7a7f27386d3316 Author: Edgar Chen <echen@mozilla.com> Date: Tue, 18 Nov 2025 21:25:31 +0000 Bug 568313 - Make buttons draggable when a drag is started from the button itself; r=masayuki We already support dragging a button when the drag starts from its text content. Differential Revision: https://phabricator.services.mozilla.com/D272337 Diffstat:
21 files changed, 766 insertions(+), 129 deletions(-)
diff --git a/dom/base/nsContentAreaDragDrop.cpp b/dom/base/nsContentAreaDragDrop.cpp @@ -466,15 +466,20 @@ nsresult DragDataProducer::Produce(DataTransfer* aDataTransfer, bool* aCanDrag, bool haveSelectedContent = false; // only drag form elements by using the alt key, - // otherwise buttons and select widgets are hard to use + // otherwise select widgets are hard to use // Note that while <object> elements implement nsIFormControl, we should // really allow dragging them if they happen to be images. + // XXX Other browsers allow dragging ohter type of form element as well. if (!mIsAltKeyPressed) { - const auto* form = nsIFormControl::FromNodeOrNull(mTarget); - if (form && form->ControlType() != FormControlType::Object) { - *aCanDrag = false; - return NS_OK; + if (const auto* form = nsIFormControl::FromNodeOrNull(mTarget)) { + if (form->IsConceptButton()) { + return NS_OK; + } + if (form->ControlType() != FormControlType::Object) { + *aCanDrag = false; + return NS_OK; + } } } diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/button_inside_draggable_element.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/button_inside_draggable_element.html.ini @@ -0,0 +1,4 @@ +[button_inside_draggable_element.html] + [Draggable button should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_button.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_button.html.ini @@ -0,0 +1,4 @@ +[draggable_button.html] + [Button inside draggable element should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_button.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_button.html.ini @@ -0,0 +1,4 @@ +[draggable_input_button.html] + [Draggable input button should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_image.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_image.html.ini @@ -0,0 +1,4 @@ +[draggable_input_image.html] + [Draggable input image button should be draggable from image] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_reset.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_reset.html.ini @@ -0,0 +1,4 @@ +[draggable_input_reset.html] + [Draggable input reset button should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_submit.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/draggable_input_submit.html.ini @@ -0,0 +1,4 @@ +[draggable_input_submit.html] + [Draggable input submit button should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_button_inside_draggable_element.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_button_inside_draggable_element.html.ini @@ -0,0 +1,4 @@ +[input_button_inside_draggable_element.html] + [Input button inside draggable element should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_image_inside_draggable_element.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_image_inside_draggable_element.html.ini @@ -0,0 +1,4 @@ +[input_image_inside_draggable_element.html] + [Input image button inside draggable element should be draggable from image] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_reset_inside_draggable_element.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_reset_inside_draggable_element.html.ini @@ -0,0 +1,4 @@ +[input_reset_inside_draggable_element.html] + [Input reset button inside draggable element should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_submit_inside_draggable_element.html.ini b/testing/web-platform/meta/html/editing/dnd/interactiveelements/input_submit_inside_draggable_element.html.ini @@ -0,0 +1,4 @@ +[input_submit_inside_draggable_element.html] + [Input submit button inside draggable element should be draggable from border] + expected: + if os == "android": FAIL diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/button_inside_draggable_element.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/button_inside_draggable_element.html @@ -13,43 +13,68 @@ </head> <body> - <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p> - <div draggable="true" id="element"> + <p>Press your mouse button down on the orange block and drag downwards. It should drag the draggable element.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the draggable element.</p> + <div draggable="true" id="outer"> <button id="inner">Dummy text</button> </div> <script> - const element = document.getElementById("element"); + const outer = document.getElementById("outer"); const inner = document.getElementById("inner"); - promise_test(t => { - return new Promise((resolve, reject) => { - let didReceiveDragStart = false; - - const dragStartListener = ev => { - ev.preventDefault(); - didReceiveDragStart = true; - }; - element.addEventListener("dragstart", dragStartListener, { once: true }); - t.add_cleanup(() => element.removeEventListener("dragstart", dragStartListener)); - - element.addEventListener("mouseup", () => { - resolve(didReceiveDragStart); - }, { once: true }); - - const centerH = inner.getBoundingClientRect().height / 2; - new test_driver.Actions() - // Move pointer to the center of the top-border. - .pointerMove(0, 10 - centerH, { origin: inner }) - .pointerDown() - // Move pointer to the center of the button. - .pointerMove(0, 0, { origin: inner }) - .pointerUp() - .send(); - }).then(didReceiveDragStart => { - assert_false(didReceiveDragStart, "dragstart should not fire"); - }); - }, "Button inside draggable element should not be draggable from border"); + function testDragButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "outer", "should drag outer element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable button should be draggable from border"); + + testDragButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable button should be draggable from button text"); </script> </body> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_button.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_button.html @@ -13,43 +13,68 @@ </head> <body> - <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p> - <div id="element"> + <p>Press your mouse button down on the orange block and drag downwards. It should drag the button.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the button.</p> + <div id="outer"> <button draggable="true" id="inner">Dummy text</button> </div> <script> - const element = document.getElementById("element"); + const outer = document.getElementById("outer"); const inner = document.getElementById("inner"); - promise_test(function(t) { - return new Promise((resolve, reject) => { - let didReceiveDragStart = false; - - const dragStartListener = ev => { - ev.preventDefault(); - didReceiveDragStart = true; - }; - element.addEventListener("dragstart", dragStartListener, { once: true }); - t.add_cleanup(() => element.removeEventListener("dragstart", dragStartListener)); - - element.addEventListener("mouseup", () => { - resolve(didReceiveDragStart); - }, { once: true }); - - const centerH = inner.getBoundingClientRect().height / 2; - new test_driver.Actions() - // Move pointer to the center of the top-border. - .pointerMove(0, 10 - centerH, { origin: inner }) - .pointerDown() - // Move pointer to the center of the button. - .pointerMove(0, 0, { origin: inner }) - .pointerUp() - .send(); - }).then(didReceiveDragStart => { - assert_false(didReceiveDragStart, "dragstart should not fire"); - }); - }, "Draggable button should not be draggable from border"); + function testDragButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "inner", "should drag inner element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Button inside draggable element should be draggable from border"); + + testDragButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Button inside draggable element should be draggable from button text"); </script> </body> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_button.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_button.html @@ -13,43 +13,68 @@ </head> <body> - <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p> - <div id="element"> + <p>Press your mouse button down on the orange block and drag downwards. It should drag the input button.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the input button.</p> + <div id="outer"> <input type="button" value="Dummy text" draggable="true" id="inner"> </div> <script> - const element = document.getElementById("element"); + const outer = document.getElementById("outer"); const inner = document.getElementById("inner"); - promise_test(t => { - return new Promise((resolve, reject) => { - let didReceiveDragStart = false; - - const dragStartListener = ev => { - ev.preventDefault(); - didReceiveDragStart = true; - }; - element.addEventListener("dragstart", dragStartListener, { once: true }); - t.add_cleanup(() => element.removeEventListener("dragstart", dragStartListener)); - - element.addEventListener("mouseup", () => { - resolve(didReceiveDragStart); - }, { once: true }); - - const centerH = inner.getBoundingClientRect().height / 2; - new test_driver.Actions() - // Move pointer to the center of the top-border. - .pointerMove(0, 10 - centerH, { origin: inner }) - .pointerDown() - // Move pointer to the center of the button. - .pointerMove(0, 0, { origin: inner }) - .pointerUp() - .send(); - }).then(didReceiveDragStart => { - assert_false(didReceiveDragStart, "dragstart should not fire"); - }); - }, "Draggable input button should not be draggable from border"); + function testDragInputButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "inner", "should drag inner element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragInputButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the input button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input button should be draggable from border"); + + testDragInputButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the input button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input button should be draggable from button text"); </script> </body> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_image.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_image.html @@ -0,0 +1,84 @@ +<!doctype html> +<html> + <head> + <title>Draggable input image</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <style type="text/css"> +#inner { border: 1px solid orange; border-top-width: 20px; } + </style> + </head> + <body> + + <p>Press your mouse button down on the orange block and drag downwards. It should drag the input image.</p> + <p>Press your mouse button down on the image and drag downwards. It should drag the input image.</p> + <div id="outer"> + <input type="image" src="../resources/circle.png" value="Dummy text" draggable="true" id="inner"> + </div> + + <script> + const outer = document.getElementById("outer"); + const inner = document.getElementById("inner"); + + function testDragInputImageButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "inner", "should drag inner element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + promise_test(async t => { + await new EventWatcher(t, window, "load").wait_for("load"); + }, "Wait for image load"); + + testDragInputImageButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input image button should be draggable from border"); + + testDragInputImageButton(async () => { + new test_driver.Actions() + // Move pointer to the center of the image. + .pointerMove(0, 10, { origin: inner }) + .pointerDown() + // Move pointer downwards. + .pointerMove(0, 30, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input image button should be draggable from image"); + </script> + + </body> +</html> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_reset.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_reset.html @@ -0,0 +1,81 @@ +<!doctype html> +<html> + <head> + <title>Draggable input reset</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <style type="text/css"> +#inner { border: 1px solid orange; border-top-width: 20px; } + </style> + </head> + <body> + + <p>Press your mouse button down on the orange block and drag downwards. It should drag the input reset.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the input reset.</p> + <div id="outer"> + <input type="reset" value="Dummy text" draggable="true" id="inner"> + </div> + + <script> + const outer = document.getElementById("outer"); + const inner = document.getElementById("inner"); + + function testDragInputResetButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "inner", "should drag inner element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragInputResetButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input reset button should be draggable from border"); + + testDragInputResetButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the input button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input reset button should be draggable from button text"); + </script> + + </body> +</html> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_submit.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/draggable_input_submit.html @@ -0,0 +1,81 @@ +<!doctype html> +<html> + <head> + <title>Draggable input submit</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <style type="text/css"> +#inner { border: 1px solid orange; border-top-width: 20px; } + </style> + </head> + <body> + + <p>Press your mouse button down on the orange block and drag downwards. It should drag the input submit.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the input submit.</p> + <div id="outer"> + <input type="submit" value="Dummy text" draggable="true" id="inner"> + </div> + + <script> + const outer = document.getElementById("outer"); + const inner = document.getElementById("inner"); + + function testDragInputSubmitButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "inner", "should drag inner element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragInputSubmitButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input submit button should be draggable from border"); + + testDragInputSubmitButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the input button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Draggable input submit button should be draggable from button text"); + </script> + + </body> +</html> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_button_inside_draggable_element.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_button_inside_draggable_element.html @@ -13,43 +13,68 @@ </head> <body> - <p>Press your mouse button down on the orange block and drag downwards. It should <strong>not</strong> drag the block or text.</p> - <div draggable="true" id="element"> + <p>Press your mouse button down on the orange block and drag downwards. It should drag the draggable element.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the draggable element.</p> + <div draggable="true" id="outer"> <input type="button" value="Dummy text" id="inner"> </div> <script> - const element = document.getElementById("element"); + const outer = document.getElementById("outer"); const inner = document.getElementById("inner"); - promise_test(t => { - return new Promise((resolve, reject) => { - let didReceiveDragStart = false; - - const dragStartListener = ev => { - ev.preventDefault(); - didReceiveDragStart = true; - }; - element.addEventListener("dragstart", dragStartListener, { once: true }); - t.add_cleanup(() => element.removeEventListener("dragstart", dragStartListener)); - - element.addEventListener("mouseup", () => { - resolve(didReceiveDragStart); - }, { once: true }); - - const centerH = inner.getBoundingClientRect().height / 2; - new test_driver.Actions() - // Move pointer to the center of the top-border. - .pointerMove(0, 10 - centerH, { origin: inner }) - .pointerDown() - // Move pointer to the center of the button. - .pointerMove(0, 0, { origin: inner }) - .pointerUp() - .send(); - }).then(didReceiveDragStart => { - assert_false(didReceiveDragStart, "dragstart should not fire"); - }); - }, "Input button inside draggable element should not be draggable from border"); + function testDragInputButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "outer", "should drag outer element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragInputButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Input button inside draggable element should be draggable from border"); + + testDragInputButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Input button inside draggable element should be draggable from button text"); </script> </body> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_image_inside_draggable_element.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_image_inside_draggable_element.html @@ -0,0 +1,84 @@ +<!doctype html> +<html> + <head> + <title>Input image inside draggable element</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <style type="text/css"> +#inner { border: 1px solid orange; border-top-width: 20px; } + </style> + </head> + <body> + + <p>Press your mouse button down on the orange block and drag downwards. It should drag the draggable element.</p> + <p>Press your mouse button down on the image and drag downwards. It should drag the draggable element.</p> + <div draggable="true" id="outer"> + <input type="image" src="../resources/circle.png" value="Dummy text" id="inner"> + </div> + + <script> + const outer = document.getElementById("outer"); + const inner = document.getElementById("inner"); + + function testDragInputImageButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "outer", "should drag outer element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + promise_test(async t => { + await new EventWatcher(t, window, "load").wait_for("load"); + }, "Wait for image load"); + + testDragInputImageButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Input image button inside draggable element should be draggable from border"); + + testDragInputImageButton(async () => { + new test_driver.Actions() + // Move pointer to the center of the image. + .pointerMove(0, 10, { origin: inner }) + .pointerDown() + // Move pointer downwards. + .pointerMove(0, 30, { origin: inner }) + .pointerUp() + .send(); + }, "Input image button inside draggable element should be draggable from image"); + </script> + + </body> +</html> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_reset_inside_draggable_element.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_reset_inside_draggable_element.html @@ -0,0 +1,81 @@ +<!doctype html> +<html> + <head> + <title>Input reset inside draggable element</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <style type="text/css"> +#inner { border: 1px solid orange; border-top-width: 20px; } + </style> + </head> + <body> + + <p>Press your mouse button down on the orange block and drag downwards. It should drag the draggable element.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the draggable element.</p> + <div draggable="true" id="outer"> + <input type="reset" value="Dummy text" id="inner"> + </div> + + <script> + const outer = document.getElementById("outer"); + const inner = document.getElementById("inner"); + + function testDragInputResetButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "outer", "should drag outer element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragInputResetButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Input reset button inside draggable element should be draggable from border"); + + testDragInputResetButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Input reset button inside draggable element should be draggable from button text"); + </script> + + </body> +</html> diff --git a/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_submit_inside_draggable_element.html b/testing/web-platform/tests/html/editing/dnd/interactiveelements/input_submit_inside_draggable_element.html @@ -0,0 +1,81 @@ +<!doctype html> +<html> + <head> + <title>Input submit inside draggable element</title> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <style type="text/css"> +#inner { border: 1px solid orange; border-top-width: 20px; } + </style> + </head> + <body> + + <p>Press your mouse button down on the orange block and drag downwards. It should drag the draggable element.</p> + <p>Press your mouse button down on the text and drag rightward. It should drag the draggable element.</p> + <div draggable="true" id="outer"> + <input type="submit" value="Dummy text" id="inner"> + </div> + + <script> + const outer = document.getElementById("outer"); + const inner = document.getElementById("inner"); + + function testDragInputSubmitButton(dragActionFun, msg) { + promise_test(t => { + return new Promise((resolve, reject) => { + let result = {}; + + const dragStartListener = ev => { + ev.preventDefault(); + result.didReceiveDragStart = true; + result.targetId = ev.target.id; + result.dataTransferItemsCount = ev.dataTransfer.items.length; + result.dataTransferTypesCount = ev.dataTransfer.types.length; + }; + outer.addEventListener("dragstart", dragStartListener, { once: true }); + t.add_cleanup(() => outer.removeEventListener("dragstart", dragStartListener)); + + outer.addEventListener("mouseup", () => { + resolve(result); + }, { once: true }); + + dragActionFun(); + }).then(result => { + assert_true(result.didReceiveDragStart, "dragstart should not fire"); + assert_equals(result.targetId, "outer", "should drag outer element"); + assert_equals(result.dataTransferItemsCount, 0, "dataTransfer should have no items"); + assert_equals(result.dataTransferTypesCount, 0, "dataTransfer should have no types"); + }); + }, msg); + } + + testDragInputSubmitButton(async () => { + const centerH = inner.getBoundingClientRect().height / 2; + new test_driver.Actions() + // Move pointer to the center of the top-border. + .pointerMove(0, 10 - centerH, { origin: inner }) + .pointerDown() + // Move pointer to the center of the button. + .pointerMove(0, 0, { origin: inner }) + .pointerUp() + .send(); + }, "Input submit button inside draggable element should be draggable from border"); + + testDragInputSubmitButton(async () => { + const centerW = inner.getBoundingClientRect().width / 2; + new test_driver.Actions() + // Move pointer to the start of the button text. + .pointerMove(5 - centerW, 10, { origin: inner }) + .pointerDown() + // Move pointer rightward. + .pointerMove(0, 10, { origin: inner }) + .pointerUp() + .send(); + }, "Input submit button inside draggable element should be draggable from button text"); + </script> + + </body> +</html>