зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1311279, scroll the select popup when click+drag is used, r=mconley
This commit is contained in:
Родитель
c8d9aa19d1
Коммит
03860c9589
|
@ -79,12 +79,18 @@ const PAGECONTENT_TRANSLATED =
|
|||
"</iframe>" +
|
||||
"</div></body></html>";
|
||||
|
||||
function openSelectPopup(selectPopup, withMouse, selector = "select", win = window) {
|
||||
function openSelectPopup(selectPopup, mode = "key", selector = "select", win = window) {
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
|
||||
|
||||
if (withMouse) {
|
||||
return Promise.all([popupShownPromise,
|
||||
BrowserTestUtils.synthesizeMouseAtCenter(selector, { }, win.gBrowser.selectedBrowser)]);
|
||||
if (mode == "click" || mode == "mousedown") {
|
||||
let mousePromise;
|
||||
if (mode == "click") {
|
||||
mousePromise = BrowserTestUtils.synthesizeMouseAtCenter(selector, { }, win.gBrowser.selectedBrowser);
|
||||
} else {
|
||||
mousePromise = BrowserTestUtils.synthesizeMouse(selector, 5, 5, { type: "mousedown" }, win.gBrowser.selectedBrowser);
|
||||
}
|
||||
|
||||
return Promise.all([popupShownPromise, mousePromise]);
|
||||
}
|
||||
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", { altKey: true, code: "ArrowDown" }, win);
|
||||
|
@ -185,7 +191,7 @@ function* doSelectTests(contentType, dtd) {
|
|||
is((yield getChangeEvents()), 1, "After closed - number of change events");
|
||||
|
||||
// Opening and closing the popup without changing the value should not fire a change event.
|
||||
yield openSelectPopup(selectPopup, true);
|
||||
yield openSelectPopup(selectPopup, "click");
|
||||
yield hideSelectPopup(selectPopup, "escape");
|
||||
is((yield getInputEvents()), 1, "Open and close with no change - number of input events");
|
||||
is((yield getChangeEvents()), 1, "Open and close with no change - number of change events");
|
||||
|
@ -194,7 +200,7 @@ function* doSelectTests(contentType, dtd) {
|
|||
is((yield getInputEvents()), 1, "Tab away from select with no change - number of input events");
|
||||
is((yield getChangeEvents()), 1, "Tab away from select with no change - number of change events");
|
||||
|
||||
yield openSelectPopup(selectPopup, true);
|
||||
yield openSelectPopup(selectPopup, "click");
|
||||
EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
|
||||
yield hideSelectPopup(selectPopup, "escape");
|
||||
is((yield getInputEvents()), isWindows ? 2 : 1, "Open and close with change - number of input events");
|
||||
|
@ -236,7 +242,7 @@ add_task(function*() {
|
|||
let selectPopup = menulist.menupopup;
|
||||
|
||||
// First, try it when a different <select> element than the one that is open is removed
|
||||
yield openSelectPopup(selectPopup, true, "#one");
|
||||
yield openSelectPopup(selectPopup, "click", "#one");
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
|
||||
content.document.body.removeChild(content.document.getElementById("two"));
|
||||
|
@ -250,7 +256,7 @@ add_task(function*() {
|
|||
yield hideSelectPopup(selectPopup);
|
||||
|
||||
// Next, try it when the same <select> element than the one that is open is removed
|
||||
yield openSelectPopup(selectPopup, true, "#three");
|
||||
yield openSelectPopup(selectPopup, "click", "#three");
|
||||
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
|
||||
|
@ -261,7 +267,7 @@ add_task(function*() {
|
|||
ok(true, "Popup hidden when select is removed");
|
||||
|
||||
// Finally, try it when the tab is closed while the select popup is open.
|
||||
yield openSelectPopup(selectPopup, true, "#one");
|
||||
yield openSelectPopup(selectPopup, "click", "#one");
|
||||
|
||||
popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
|
@ -279,7 +285,7 @@ add_task(function*() {
|
|||
let selectPopup = menulist.menupopup;
|
||||
|
||||
// First, get the position of the select popup when no translations have been applied.
|
||||
yield openSelectPopup(selectPopup, false);
|
||||
yield openSelectPopup(selectPopup);
|
||||
|
||||
let rect = selectPopup.getBoundingClientRect();
|
||||
let expectedX = rect.left;
|
||||
|
@ -320,7 +326,7 @@ add_task(function*() {
|
|||
});
|
||||
});
|
||||
|
||||
yield openSelectPopup(selectPopup, false);
|
||||
yield openSelectPopup(selectPopup);
|
||||
|
||||
expectedX += step[2];
|
||||
expectedY += step[3];
|
||||
|
@ -391,7 +397,7 @@ add_task(function* test_event_order() {
|
|||
|
||||
for (let mode of ["enter", "click"]) {
|
||||
let expected = mode == "enter" ? expectedEnter : expectedClick;
|
||||
yield openSelectPopup(selectPopup, true, mode == "enter" ? "#one" : "#two");
|
||||
yield openSelectPopup(selectPopup, "click", mode == "enter" ? "#one" : "#two");
|
||||
|
||||
let eventsPromise = ContentTask.spawn(browser, [mode, expected], function*([contentMode, contentExpected]) {
|
||||
return new Promise((resolve) => {
|
||||
|
@ -443,6 +449,38 @@ function* performLargePopupTests(win) {
|
|||
let selectPopup = win.document.getElementById("ContentSelectDropdown").menupopup;
|
||||
let browserRect = browser.getBoundingClientRect();
|
||||
|
||||
// Check if a drag-select works and scrolls the list.
|
||||
yield openSelectPopup(selectPopup, "mousedown", "select", win);
|
||||
|
||||
let scrollPos = selectPopup.scrollBox.scrollTop;
|
||||
let popupRect = selectPopup.getBoundingClientRect();
|
||||
|
||||
// First, check that scrolling does not occur when the mouse is moved over the
|
||||
// anchor button but not the popup yet.
|
||||
EventUtils.synthesizeMouseAtPoint(popupRect.left + 5, popupRect.top - 10, { type: "mousemove" }, win);
|
||||
is(selectPopup.scrollBox.scrollTop, scrollPos, "scroll position after mousemove over button");
|
||||
|
||||
EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.top + 10, { type: "mousemove" }, win);
|
||||
|
||||
// Dragging above the popup scrolls it up.
|
||||
EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.top - 20, { type: "mousemove" }, win);
|
||||
ok(selectPopup.scrollBox.scrollTop < scrollPos - 5, "scroll position at drag up");
|
||||
|
||||
// Dragging below the popup scrolls it down.
|
||||
scrollPos = selectPopup.scrollBox.scrollTop;
|
||||
EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove" }, win);
|
||||
ok(selectPopup.scrollBox.scrollTop > scrollPos + 5, "scroll position at drag down");
|
||||
|
||||
// Releasing the mouse button and moving the mouse does not change the scroll position.
|
||||
scrollPos = selectPopup.scrollBox.scrollTop;
|
||||
EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.bottom + 25, { type: "mouseup" }, win);
|
||||
is(selectPopup.scrollBox.scrollTop, scrollPos, "scroll position at mouseup");
|
||||
|
||||
EventUtils.synthesizeMouseAtPoint(popupRect.left + 20, popupRect.bottom + 20, { type: "mousemove" }, win);
|
||||
is(selectPopup.scrollBox.scrollTop, scrollPos, "scroll position at mouseup again");
|
||||
|
||||
yield hideSelectPopup(selectPopup, "escape", win);
|
||||
|
||||
let positions = [
|
||||
"margin-top: 300px;",
|
||||
"position: fixed; bottom: 100px;",
|
||||
|
@ -450,8 +488,8 @@ function* performLargePopupTests(win) {
|
|||
];
|
||||
|
||||
let position;
|
||||
while (true) {
|
||||
yield openSelectPopup(selectPopup, false, "select", win);
|
||||
while (positions.length) {
|
||||
yield openSelectPopup(selectPopup, "key", "select", win);
|
||||
|
||||
let rect = selectPopup.getBoundingClientRect();
|
||||
ok(rect.top >= browserRect.top, "Popup top position in within browser area");
|
||||
|
@ -481,14 +519,11 @@ function* performLargePopupTests(win) {
|
|||
yield hideSelectPopup(selectPopup, "enter", win);
|
||||
|
||||
position = positions.shift();
|
||||
if (!position) {
|
||||
break;
|
||||
}
|
||||
|
||||
let contentPainted = BrowserTestUtils.contentPainted(browser);
|
||||
yield ContentTask.spawn(browser, position, function*(contentPosition) {
|
||||
let select = content.document.getElementById("one");
|
||||
select.setAttribute("style", contentPosition);
|
||||
select.setAttribute("style", contentPosition || "");
|
||||
select.getBoundingClientRect();
|
||||
});
|
||||
yield contentPainted;
|
||||
|
@ -622,7 +657,7 @@ add_task(function* test_mousemove_correcttarget() {
|
|||
|
||||
// The popup should be closed when fullscreen mode is entered or exited.
|
||||
for (let steps = 0; steps < 2; steps++) {
|
||||
yield openSelectPopup(selectPopup, true);
|
||||
yield openSelectPopup(selectPopup, "click");
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
|
||||
let sizeModeChanged = BrowserTestUtils.waitForEvent(window, "sizemodechange");
|
||||
BrowserFullScreen();
|
||||
|
|
|
@ -246,6 +246,12 @@
|
|||
</xul:arrowscrollbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<field name="scrollBox" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "class", "popup-internal-box");
|
||||
</field>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="popupshowing" phase="target">
|
||||
<![CDATA[
|
||||
|
@ -633,9 +639,9 @@
|
|||
|
||||
<binding id="popup-scrollbars" extends="chrome://global/content/bindings/popup.xml#popup">
|
||||
<content>
|
||||
<xul:hbox class="popup-internal-box" flex="1" orient="vertical" style="overflow: auto;">
|
||||
<xul:scrollbox class="popup-internal-box" flex="1" orient="vertical" style="overflow: auto;">
|
||||
<children/>
|
||||
</xul:hbox>
|
||||
</xul:scrollbox>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
|
|
|
@ -21,6 +21,15 @@
|
|||
<children/>
|
||||
</xul:box>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<method name="scrollByIndex">
|
||||
<parameter name="index"/>
|
||||
<body>
|
||||
this.boxObject.scrollByIndex(index);
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="arrowscrollbox" extends="chrome://global/content/bindings/scrollbox.xml#scrollbox-base">
|
||||
|
|
|
@ -15,6 +15,9 @@ const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
|||
// Maximum number of rows to display in the select dropdown.
|
||||
const MAX_ROWS = 20;
|
||||
|
||||
// Interval between autoscrolls
|
||||
const AUTOSCROLL_INTERVAL = 25;
|
||||
|
||||
// Minimum elements required to show select search
|
||||
const SEARCH_MINIMUM_ELEMENTS = 40;
|
||||
|
||||
|
@ -24,6 +27,9 @@ var currentZoom = 1;
|
|||
var closedWithEnter = false;
|
||||
|
||||
this.SelectParentHelper = {
|
||||
draggedOverPopup: false,
|
||||
scrollTimer: 0,
|
||||
|
||||
populate(menulist, items, selectedIndex, zoom) {
|
||||
// Clear the current contents of the popup
|
||||
menulist.menupopup.textContent = "";
|
||||
|
@ -65,6 +71,11 @@ this.SelectParentHelper = {
|
|||
constraintRect.width, constraintRect.height);
|
||||
menupopup.setConstraintRect(constraintRect);
|
||||
menupopup.openPopupAtScreenRect(AppConstants.platform == "macosx" ? "selection" : "after_start", rect.left, rect.top, rect.width, rect.height, false, false);
|
||||
|
||||
// Set up for dragging
|
||||
menupopup.setCaptureAlways();
|
||||
this.draggedOverPopup = false;
|
||||
menupopup.addEventListener("mousemove", this);
|
||||
},
|
||||
|
||||
hide(menulist, browser) {
|
||||
|
@ -73,9 +84,19 @@ this.SelectParentHelper = {
|
|||
}
|
||||
},
|
||||
|
||||
clearScrollTimer() {
|
||||
if (this.scrollTimer) {
|
||||
let win = currentBrowser.ownerDocument.defaultView;
|
||||
win.clearInterval(this.scrollTimer);
|
||||
this.scrollTimer = 0;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "mouseup":
|
||||
this.clearScrollTimer();
|
||||
currentMenulist.menupopup.removeEventListener("mousemove", this);
|
||||
currentBrowser.messageManager.sendAsyncMessage("Forms:MouseUp", {});
|
||||
break;
|
||||
|
||||
|
@ -87,6 +108,38 @@ this.SelectParentHelper = {
|
|||
currentBrowser.messageManager.sendAsyncMessage("Forms:MouseOut", {});
|
||||
break;
|
||||
|
||||
case "mousemove":
|
||||
let menupopup = currentMenulist.menupopup;
|
||||
let popupRect = menupopup.getOuterScreenRect();
|
||||
|
||||
this.clearScrollTimer();
|
||||
|
||||
// If dragging outside the top or bottom edge of the popup, but within
|
||||
// the popup area horizontally, scroll the list in that direction. The
|
||||
// draggedOverPopup flag is used to ensure that scrolling does not start
|
||||
// until the mouse has moved over the popup first, preventing scrolling
|
||||
// while over the dropdown button.
|
||||
if (event.screenX >= popupRect.left && event.screenX <= popupRect.right) {
|
||||
if (!this.draggedOverPopup) {
|
||||
if (event.screenY > popupRect.top && event.screenY < popupRect.bottom) {
|
||||
this.draggedOverPopup = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.draggedOverPopup &&
|
||||
(event.screenY <= popupRect.top || event.screenY >= popupRect.bottom)) {
|
||||
let scrollAmount = event.screenY <= popupRect.top ? -1 : 1;
|
||||
menupopup.scrollBox.scrollByIndex(scrollAmount);
|
||||
|
||||
let win = currentBrowser.ownerDocument.defaultView;
|
||||
this.scrollTimer = win.setInterval(function() {
|
||||
menupopup.scrollBox.scrollByIndex(scrollAmount);
|
||||
}, AUTOSCROLL_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case "keydown":
|
||||
if (event.keyCode == event.DOM_VK_RETURN) {
|
||||
closedWithEnter = true;
|
||||
|
@ -112,6 +165,8 @@ this.SelectParentHelper = {
|
|||
currentBrowser.messageManager.sendAsyncMessage("Forms:DismissedDropDown", {});
|
||||
let popup = event.target;
|
||||
this._unregisterListeners(currentBrowser, popup);
|
||||
this.clearScrollTimer();
|
||||
popup.releaseCapture();
|
||||
popup.parentNode.hidden = true;
|
||||
currentBrowser = null;
|
||||
currentMenulist = null;
|
||||
|
|
Загрузка…
Ссылка в новой задаче