From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- toolkit/content/tests/widgets/audio.ogg | Bin 0 -> 14293 bytes toolkit/content/tests/widgets/audio.wav | Bin 0 -> 1422 bytes toolkit/content/tests/widgets/chrome.ini | 45 + .../widgets/file_videocontrols_jsdisabled.html | 2 + toolkit/content/tests/widgets/head.js | 67 + toolkit/content/tests/widgets/image-zh.png | Bin 0 -> 5442 bytes toolkit/content/tests/widgets/image.png | Bin 0 -> 7061 bytes toolkit/content/tests/widgets/mochitest.ini | 68 + toolkit/content/tests/widgets/popup_shared.js | 586 ++++++ toolkit/content/tests/widgets/seek_with_sound.ogg | Bin 0 -> 299507 bytes toolkit/content/tests/widgets/test-webvtt-1.vtt | 10 + toolkit/content/tests/widgets/test-webvtt-2.vtt | 10 + .../widgets/test_audiocontrols_dimensions.html | 66 + .../widgets/test_audiocontrols_fullscreen.html | 61 + toolkit/content/tests/widgets/test_bug1654500.html | 33 + toolkit/content/tests/widgets/test_bug898940.html | 31 + .../tests/widgets/test_contextmenu_menugroup.xhtml | 102 + .../tests/widgets/test_contextmenu_nested.xhtml | 132 ++ .../tests/widgets/test_editor_currentURI.xhtml | 37 + .../tests/widgets/test_image_recognition.html | 67 + .../test_image_recognition_unsupported.html | 36 + .../tests/widgets/test_image_recognition_zh.html | 53 + .../tests/widgets/test_label_checkbox.xhtml | 42 + toolkit/content/tests/widgets/test_menubar.xhtml | 30 + .../tests/widgets/test_mousecapture_area.html | 106 + .../tests/widgets/test_moz_button_group.html | 236 +++ toolkit/content/tests/widgets/test_moz_label.html | 142 ++ .../tests/widgets/test_moz_support_link.html | 153 ++ toolkit/content/tests/widgets/test_moz_toggle.html | 85 + .../content/tests/widgets/test_nac_mutations.html | 65 + .../tests/widgets/test_panel_item_accesskey.html | 105 + .../widgets/test_panel_list_accessibility.html | 79 + .../widgets/test_panel_list_in_xul_panel.html | 87 + .../test_panel_list_min_width_from_anchor.html | 70 + .../test_panel_list_shadow_node_anchor.html | 96 + .../content/tests/widgets/test_popupanchor.xhtml | 387 ++++ .../content/tests/widgets/test_popupreflows.xhtml | 96 + .../tests/widgets/test_tree_column_reorder.xhtml | 75 + .../widgets/test_ua_widget_elementFromPoint.html | 22 + .../tests/widgets/test_ua_widget_sandbox.html | 101 + .../tests/widgets/test_ua_widget_unbind.html | 56 + .../content/tests/widgets/test_videocontrols.html | 564 +++++ .../tests/widgets/test_videocontrols_audio.html | 40 + .../test_videocontrols_audio_direction.html | 31 + .../test_videocontrols_clickToPlay_ariaLabel.html | 56 + .../test_videocontrols_closed_caption_menu.html | 144 ++ .../tests/widgets/test_videocontrols_error.html | 60 + .../tests/widgets/test_videocontrols_focus.html | 111 + .../test_videocontrols_iframe_fullscreen.html | 60 + .../widgets/test_videocontrols_jsdisabled.html | 69 + .../widgets/test_videocontrols_keyhandler.html | 150 ++ .../widgets/test_videocontrols_onclickplay.html | 74 + .../widgets/test_videocontrols_orientation.html | 63 + .../test_videocontrols_scrubber_position.html | 51 + ..._videocontrols_scrubber_position_nopreload.html | 123 ++ .../tests/widgets/test_videocontrols_size.html | 179 ++ .../widgets/test_videocontrols_standalone.html | 131 ++ .../test_videocontrols_video_direction.html | 31 + .../widgets/test_videocontrols_video_noaudio.html | 42 + .../tests/widgets/test_videocontrols_vtt.html | 112 + toolkit/content/tests/widgets/tree_shared.js | 2181 ++++++++++++++++++++ toolkit/content/tests/widgets/video.ogg | Bin 0 -> 285310 bytes .../widgets/videocontrols_direction-1-ref.html | 10 + .../tests/widgets/videocontrols_direction-1a.html | 10 + .../tests/widgets/videocontrols_direction-1b.html | 10 + .../tests/widgets/videocontrols_direction-1c.html | 10 + .../tests/widgets/videocontrols_direction-1d.html | 10 + .../tests/widgets/videocontrols_direction-1e.html | 10 + .../widgets/videocontrols_direction-2-ref.html | 10 + .../tests/widgets/videocontrols_direction-2a.html | 10 + .../tests/widgets/videocontrols_direction-2b.html | 10 + .../tests/widgets/videocontrols_direction-2c.html | 10 + .../tests/widgets/videocontrols_direction-2d.html | 10 + .../tests/widgets/videocontrols_direction-2e.html | 10 + .../tests/widgets/videocontrols_direction_test.js | 119 ++ toolkit/content/tests/widgets/videomask.css | 23 + .../tests/widgets/window_label_checkbox.xhtml | 46 + toolkit/content/tests/widgets/window_menubar.xhtml | 1015 +++++++++ 78 files changed, 9034 insertions(+) create mode 100644 toolkit/content/tests/widgets/audio.ogg create mode 100644 toolkit/content/tests/widgets/audio.wav create mode 100644 toolkit/content/tests/widgets/chrome.ini create mode 100644 toolkit/content/tests/widgets/file_videocontrols_jsdisabled.html create mode 100644 toolkit/content/tests/widgets/head.js create mode 100644 toolkit/content/tests/widgets/image-zh.png create mode 100644 toolkit/content/tests/widgets/image.png create mode 100644 toolkit/content/tests/widgets/mochitest.ini create mode 100644 toolkit/content/tests/widgets/popup_shared.js create mode 100644 toolkit/content/tests/widgets/seek_with_sound.ogg create mode 100644 toolkit/content/tests/widgets/test-webvtt-1.vtt create mode 100644 toolkit/content/tests/widgets/test-webvtt-2.vtt create mode 100644 toolkit/content/tests/widgets/test_audiocontrols_dimensions.html create mode 100644 toolkit/content/tests/widgets/test_audiocontrols_fullscreen.html create mode 100644 toolkit/content/tests/widgets/test_bug1654500.html create mode 100644 toolkit/content/tests/widgets/test_bug898940.html create mode 100644 toolkit/content/tests/widgets/test_contextmenu_menugroup.xhtml create mode 100644 toolkit/content/tests/widgets/test_contextmenu_nested.xhtml create mode 100644 toolkit/content/tests/widgets/test_editor_currentURI.xhtml create mode 100644 toolkit/content/tests/widgets/test_image_recognition.html create mode 100644 toolkit/content/tests/widgets/test_image_recognition_unsupported.html create mode 100644 toolkit/content/tests/widgets/test_image_recognition_zh.html create mode 100644 toolkit/content/tests/widgets/test_label_checkbox.xhtml create mode 100644 toolkit/content/tests/widgets/test_menubar.xhtml create mode 100644 toolkit/content/tests/widgets/test_mousecapture_area.html create mode 100644 toolkit/content/tests/widgets/test_moz_button_group.html create mode 100644 toolkit/content/tests/widgets/test_moz_label.html create mode 100644 toolkit/content/tests/widgets/test_moz_support_link.html create mode 100644 toolkit/content/tests/widgets/test_moz_toggle.html create mode 100644 toolkit/content/tests/widgets/test_nac_mutations.html create mode 100644 toolkit/content/tests/widgets/test_panel_item_accesskey.html create mode 100644 toolkit/content/tests/widgets/test_panel_list_accessibility.html create mode 100644 toolkit/content/tests/widgets/test_panel_list_in_xul_panel.html create mode 100644 toolkit/content/tests/widgets/test_panel_list_min_width_from_anchor.html create mode 100644 toolkit/content/tests/widgets/test_panel_list_shadow_node_anchor.html create mode 100644 toolkit/content/tests/widgets/test_popupanchor.xhtml create mode 100644 toolkit/content/tests/widgets/test_popupreflows.xhtml create mode 100644 toolkit/content/tests/widgets/test_tree_column_reorder.xhtml create mode 100644 toolkit/content/tests/widgets/test_ua_widget_elementFromPoint.html create mode 100644 toolkit/content/tests/widgets/test_ua_widget_sandbox.html create mode 100644 toolkit/content/tests/widgets/test_ua_widget_unbind.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_audio.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_audio_direction.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_clickToPlay_ariaLabel.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_closed_caption_menu.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_error.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_focus.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_iframe_fullscreen.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_jsdisabled.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_keyhandler.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_onclickplay.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_orientation.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_scrubber_position.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_scrubber_position_nopreload.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_size.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_standalone.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_video_direction.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html create mode 100644 toolkit/content/tests/widgets/test_videocontrols_vtt.html create mode 100644 toolkit/content/tests/widgets/tree_shared.js create mode 100644 toolkit/content/tests/widgets/video.ogg create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-1-ref.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-1a.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-1b.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-1c.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-1d.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-1e.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-2-ref.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-2a.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-2b.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-2c.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-2d.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction-2e.html create mode 100644 toolkit/content/tests/widgets/videocontrols_direction_test.js create mode 100644 toolkit/content/tests/widgets/videomask.css create mode 100644 toolkit/content/tests/widgets/window_label_checkbox.xhtml create mode 100644 toolkit/content/tests/widgets/window_menubar.xhtml (limited to 'toolkit/content/tests/widgets') diff --git a/toolkit/content/tests/widgets/audio.ogg b/toolkit/content/tests/widgets/audio.ogg new file mode 100644 index 0000000000..a553c23e73 Binary files /dev/null and b/toolkit/content/tests/widgets/audio.ogg differ diff --git a/toolkit/content/tests/widgets/audio.wav b/toolkit/content/tests/widgets/audio.wav new file mode 100644 index 0000000000..c6fd5cb869 Binary files /dev/null and b/toolkit/content/tests/widgets/audio.wav differ diff --git a/toolkit/content/tests/widgets/chrome.ini b/toolkit/content/tests/widgets/chrome.ini new file mode 100644 index 0000000000..73cc6c1521 --- /dev/null +++ b/toolkit/content/tests/widgets/chrome.ini @@ -0,0 +1,45 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + image.png + image-zh.png + tree_shared.js + popup_shared.js + window_label_checkbox.xhtml + window_menubar.xhtml + seek_with_sound.ogg +prefs = + app.support.baseURL='https://support.mozilla.org/' + +[test_contextmenu_menugroup.xhtml] +skip-if = os == 'linux' # Bug 1115088 +[test_contextmenu_nested.xhtml] +skip-if = os == 'linux' # Bug 1116215 +[test_editor_currentURI.xhtml] +[test_image_recognition.html] +run-if = os == "mac" # Mac only feature. +[test_image_recognition_unsupported.html] +skip-if = os == 'mac' +[test_image_recognition_zh.html] +run-if = os == "mac" && os_version != "10.15" # Mac only feature, requires > 10.15 to support multilingual results. +[test_label_checkbox.xhtml] +[test_menubar.xhtml] +skip-if = os == 'mac' +[test_moz_button_group.html] +[test_moz_label.html] +[test_moz_support_link.html] +[test_moz_toggle.html] +[test_panel_item_accesskey.html] +[test_panel_list_accessibility.html] +[test_panel_list_in_xul_panel.html] +[test_panel_list_min_width_from_anchor.html] +[test_popupanchor.xhtml] +skip-if = os == 'linux' || (verify && (os == 'win')) # Bug 1335894 perma-fail on linux 16.04 +[test_popupreflows.xhtml] +[test_tree_column_reorder.xhtml] +[test_videocontrols_focus.html] +support-files = + head.js + video.ogg +skip-if = toolkit == 'android' +[test_videocontrols_onclickplay.html] diff --git a/toolkit/content/tests/widgets/file_videocontrols_jsdisabled.html b/toolkit/content/tests/widgets/file_videocontrols_jsdisabled.html new file mode 100644 index 0000000000..56917b69ac --- /dev/null +++ b/toolkit/content/tests/widgets/file_videocontrols_jsdisabled.html @@ -0,0 +1,2 @@ + + diff --git a/toolkit/content/tests/widgets/head.js b/toolkit/content/tests/widgets/head.js new file mode 100644 index 0000000000..d7473fa92d --- /dev/null +++ b/toolkit/content/tests/widgets/head.js @@ -0,0 +1,67 @@ +"use strict"; + +const InspectorUtils = SpecialPowers.InspectorUtils; + +var tests = []; + +function waitForCondition(condition, nextTest, errorMsg) { + var tries = 0; + var interval = setInterval(function () { + if (tries >= 30) { + ok(false, errorMsg); + moveOn(); + } + var conditionPassed; + try { + conditionPassed = condition(); + } catch (e) { + ok(false, e + "\n" + e.stack); + conditionPassed = false; + } + if (conditionPassed) { + moveOn(); + } + tries++; + }, 100); + var moveOn = function () { + clearInterval(interval); + nextTest(); + }; +} + +function getElementWithinVideo(video, aValue) { + const shadowRoot = SpecialPowers.wrap(video).openOrClosedShadowRoot; + return shadowRoot.getElementById(aValue); +} + +/** + * Runs querySelectorAll on an element's shadow root. + * @param {Element} element + * @param {string} selector + */ +function shadowRootQuerySelectorAll(element, selector) { + const shadowRoot = SpecialPowers.wrap(element).openOrClosedShadowRoot; + return shadowRoot?.querySelectorAll(selector); +} + +function executeTests() { + return tests + .map(fn => () => new Promise(fn)) + .reduce((promise, task) => promise.then(task), Promise.resolve()); +} + +function once(target, name, cb) { + let p = new Promise(function (resolve, reject) { + target.addEventListener( + name, + function () { + resolve(); + }, + { once: true } + ); + }); + if (cb) { + p.then(cb); + } + return p; +} diff --git a/toolkit/content/tests/widgets/image-zh.png b/toolkit/content/tests/widgets/image-zh.png new file mode 100644 index 0000000000..944a12d39e Binary files /dev/null and b/toolkit/content/tests/widgets/image-zh.png differ diff --git a/toolkit/content/tests/widgets/image.png b/toolkit/content/tests/widgets/image.png new file mode 100644 index 0000000000..3faa11b221 Binary files /dev/null and b/toolkit/content/tests/widgets/image.png differ diff --git a/toolkit/content/tests/widgets/mochitest.ini b/toolkit/content/tests/widgets/mochitest.ini new file mode 100644 index 0000000000..4b33e6aa8a --- /dev/null +++ b/toolkit/content/tests/widgets/mochitest.ini @@ -0,0 +1,68 @@ +[DEFAULT] +support-files = + audio.wav + audio.ogg + file_videocontrols_jsdisabled.html + seek_with_sound.ogg + video.ogg + head.js + tree_shared.js + test-webvtt-1.vtt + test-webvtt-2.vtt + videocontrols_direction-1-ref.html + videocontrols_direction-1a.html + videocontrols_direction-1b.html + videocontrols_direction-1c.html + videocontrols_direction-1d.html + videocontrols_direction-1e.html + videocontrols_direction-2-ref.html + videocontrols_direction-2a.html + videocontrols_direction-2b.html + videocontrols_direction-2c.html + videocontrols_direction-2d.html + videocontrols_direction-2e.html + videocontrols_direction_test.js + videomask.css + +[test_audiocontrols_dimensions.html] +[test_audiocontrols_fullscreen.html] +[test_mousecapture_area.html] +[test_nac_mutations.html] +[test_panel_list_shadow_node_anchor.html] +support-files = + ../../widgets/panel-item.css + ../../widgets/panel-list.js + ../../widgets/panel-list.css +[test_ua_widget_elementFromPoint.html] +[test_ua_widget_sandbox.html] +[test_ua_widget_unbind.html] +[test_videocontrols.html] +tags = fullscreen +skip-if = (toolkit == 'android') || (os == 'linux') #TIMED_OUT #Bug 1484210 #Bug 1511256 +[test_videocontrols_keyhandler.html] +skip-if = (toolkit == 'android') || (os == 'linux') #Bug 1366957 +[test_videocontrols_vtt.html] +[test_videocontrols_iframe_fullscreen.html] +[test_videocontrols_size.html] +[test_videocontrols_audio.html] +[test_videocontrols_audio_direction.html] +skip-if = xorigin # Rendering of reftest videocontrols_direction-2a.html should not be different to the reference, fails/passes inconsistently +[test_videocontrols_jsdisabled.html] +skip-if = toolkit == 'android' # bug 1272646 +[test_videocontrols_scrubber_position.html] +[test_videocontrols_scrubber_position_nopreload.html] +[test_videocontrols_standalone.html] +skip-if = toolkit == 'android' # bug 1075573 +[test_videocontrols_video_direction.html] +skip-if = + os == 'win' + xorigin # Rendering of reftest videocontrols_direction-2a.html should not be different to the reference, fails/passes inconsistently +[test_videocontrols_video_noaudio.html] +[test_bug898940.html] +skip-if = (os == "win" && processor == "aarch64") # aarch64 due to 1536347 +[test_videocontrols_error.html] +[test_videocontrols_orientation.html] +skip-if = true # Bug 1483656 +[test_bug1654500.html] +[test_videocontrols_clickToPlay_ariaLabel.html] +[test_videocontrols_closed_caption_menu.html] diff --git a/toolkit/content/tests/widgets/popup_shared.js b/toolkit/content/tests/widgets/popup_shared.js new file mode 100644 index 0000000000..3799c5622d --- /dev/null +++ b/toolkit/content/tests/widgets/popup_shared.js @@ -0,0 +1,586 @@ +/* + * This script is used for menu and popup tests. Call startPopupTests to start + * the tests, passing an array of tests as an argument. Each test is an object + * with the following properties: + * testname - name of the test + * test - function to call to perform the test + * events - a list of events that are expected to be fired in sequence + * as a result of calling the 'test' function. This list should be + * an array of strings of the form "eventtype targetid" where + * 'eventtype' is the event type and 'targetid' is the id of + * target of the event. This function will be passed two + * arguments, the testname and the step argument. + * Alternatively, events may be a function which returns the array + * of events. This can be used when the events vary per platform. + * result - function to call after all the events have fired to check + * for additional results. May be null. This function will be + * passed two arguments, the testname and the step argument. + * steps - optional array of values. The test will be repeated for + * each step, passing each successive value within the array to + * the test and result functions + * autohide - if set, should be set to the id of a popup to hide after + * the test is complete. This is a convenience for some tests. + * condition - an optional function which, if it returns false, causes the + * test to be skipped. + * end - used for debugging. Set to true to stop the tests after running + * this one. + */ + +const menuactiveAttribute = "_moz-menuactive"; + +var gPopupTests = null; +var gTestIndex = -1; +var gTestStepIndex = 0; +var gTestEventIndex = 0; +var gActualEvents = []; +var gAutoHide = false; +var gExpectedEventDetails = null; +var gExpectedTriggerNode = null; +var gWindowUtils; +var gPopupWidth = -1, + gPopupHeight = -1; + +function startPopupTests(tests) { + document.addEventListener("popupshowing", eventOccurred); + document.addEventListener("popupshown", eventOccurred); + document.addEventListener("popuphiding", eventOccurred); + document.addEventListener("popuphidden", eventOccurred); + document.addEventListener("command", eventOccurred); + document.addEventListener("DOMMenuItemActive", eventOccurred); + document.addEventListener("DOMMenuItemInactive", eventOccurred); + document.addEventListener("DOMMenuInactive", eventOccurred); + document.addEventListener("DOMMenuBarActive", eventOccurred); + document.addEventListener("DOMMenuBarInactive", eventOccurred); + + // This is useful to explicitly finish a test that shouldn't trigger events. + document.addEventListener("TestDone", eventOccurred); + + gPopupTests = tests; + gWindowUtils = SpecialPowers.getDOMWindowUtils(window); + + goNext(); +} + +if (!window.opener && window.arguments) { + window.opener = window.arguments[0]; +} + +function finish() { + if (window.opener) { + window.close(); + window.opener.SimpleTest.finish(); + return; + } + SimpleTest.finish(); +} + +function ok(condition, message) { + if (window.opener) { + window.opener.SimpleTest.ok(condition, message); + } else { + SimpleTest.ok(condition, message); + } +} + +function info(message) { + if (window.opener) { + window.opener.SimpleTest.info(message); + } else { + SimpleTest.info(message); + } +} + +function is(left, right, message) { + if (window.opener) { + window.opener.SimpleTest.is(left, right, message); + } else { + SimpleTest.is(left, right, message); + } +} + +function disableNonTestMouse(aDisable) { + gWindowUtils.disableNonTestMouseEvents(aDisable); +} + +function eventOccurred(event) { + if (gPopupTests.length <= gTestIndex) { + ok(false, "Extra " + event.type + " event fired"); + return; + } + + var test = gPopupTests[gTestIndex]; + if ("autohide" in test && gAutoHide) { + if (event.type == "DOMMenuInactive") { + gAutoHide = false; + setTimeout(goNextStep, 0); + } + return; + } + + var events = test.events; + if (typeof events == "function") { + events = events(); + } + if (events) { + if (events.length <= gTestEventIndex) { + ok( + false, + "Extra " + + event.type + + " event fired for " + + event.target.id + + " " + + gPopupTests[gTestIndex].testname + ); + return; + } + + gActualEvents.push(`${event.type} ${event.target.id}`); + + var eventitem = events[gTestEventIndex].split(" "); + var matches; + if (eventitem[1] == "#tooltip") { + is( + event.originalTarget.localName, + "tooltip", + test.testname + " event.originalTarget.localName is 'tooltip'" + ); + is( + event.originalTarget.getAttribute("default"), + "true", + test.testname + " event.originalTarget default attribute is 'true'" + ); + matches = + event.originalTarget.localName == "tooltip" && + event.originalTarget.getAttribute("default") == "true"; + } else { + is( + event.type, + eventitem[0], + test.testname + " event type " + event.type + " fired" + ); + is( + event.target.id, + eventitem[1], + test.testname + " event target ID " + event.target.id + ); + matches = eventitem[0] == event.type && eventitem[1] == event.target.id; + } + + var modifiersMask = eventitem[2]; + if (modifiersMask) { + var m = ""; + m += event.altKey ? "1" : "0"; + m += event.ctrlKey ? "1" : "0"; + m += event.shiftKey ? "1" : "0"; + m += event.metaKey ? "1" : "0"; + is(m, modifiersMask, test.testname + " modifiers mask matches"); + } + + var expectedState; + switch (event.type) { + case "popupshowing": + expectedState = "showing"; + break; + case "popupshown": + expectedState = "open"; + break; + case "popuphiding": + expectedState = "hiding"; + break; + case "popuphidden": + expectedState = "closed"; + break; + } + + if (gExpectedTriggerNode && event.type == "popupshowing") { + if (gExpectedTriggerNode == "notset") { + // check against null instead + gExpectedTriggerNode = null; + } + + is( + event.originalTarget.triggerNode, + gExpectedTriggerNode, + test.testname + " popupshowing triggerNode" + ); + } + + if (expectedState) { + is( + event.originalTarget.state, + expectedState, + test.testname + " " + event.type + " state" + ); + } + + if (matches) { + gTestEventIndex++; + if (events.length <= gTestEventIndex) { + setTimeout(checkResult, 0); + } + } else { + info(`Actual events so far: ${JSON.stringify(gActualEvents)}`); + } + } +} + +async function checkResult() { + var step = null; + var test = gPopupTests[gTestIndex]; + if ("steps" in test) { + step = test.steps[gTestStepIndex]; + } + + if ("result" in test) { + await test.result(test.testname, step); + } + + if ("autohide" in test) { + gAutoHide = true; + document.getElementById(test.autohide).hidePopup(); + return; + } + + goNextStep(); +} + +function goNextStep() { + info(`events: ${JSON.stringify(gActualEvents)}`); + gTestEventIndex = 0; + gActualEvents = []; + + var step = null; + var test = gPopupTests[gTestIndex]; + if ("steps" in test) { + gTestStepIndex++; + step = test.steps[gTestStepIndex]; + if (gTestStepIndex < test.steps.length) { + test.test(test.testname, step); + return; + } + } + + goNext(); +} + +function goNext() { + // We want to continue after the next animation frame so that + // we're in a stable state and don't get spurious mouse events at unexpected targets. + window.requestAnimationFrame(function () { + setTimeout(goNextStepSync, 0); + }); +} + +function goNextStepSync() { + if ( + gTestIndex >= 0 && + "end" in gPopupTests[gTestIndex] && + gPopupTests[gTestIndex].end + ) { + finish(); + return; + } + + gTestIndex++; + gTestStepIndex = 0; + if (gTestIndex < gPopupTests.length) { + var test = gPopupTests[gTestIndex]; + // Set the location hash so it's easy to see which test is running + document.location.hash = test.testname; + info("Starting " + test.testname); + + // skip the test if the condition returns false + if ("condition" in test && !test.condition()) { + goNext(); + return; + } + + // start with the first step if there are any + var step = null; + if ("steps" in test) { + step = test.steps[gTestStepIndex]; + } + + test.test(test.testname, step); + + // no events to check for so just check the result + if (!("events" in test)) { + checkResult(); + } else if (typeof test.events == "function" && !test.events().length) { + checkResult(); + } + } else { + finish(); + } +} + +function openMenu(menu) { + if ("open" in menu) { + menu.open = true; + } else if (menu.hasMenu()) { + menu.openMenu(true); + } else { + synthesizeMouse(menu, 4, 4, {}); + } +} + +function closeMenu(menu, popup) { + if ("open" in menu) { + menu.open = false; + } else if (menu.hasMenu()) { + menu.openMenu(false); + } else { + popup.hidePopup(); + } +} + +function checkActive(popup, id, testname) { + var activeok = true; + var children = popup.childNodes; + for (var c = 0; c < children.length; c++) { + var child = children[c]; + if ( + (id == child.id && child.getAttribute(menuactiveAttribute) != "true") || + (id != child.id && child.hasAttribute(menuactiveAttribute) != "") + ) { + activeok = false; + break; + } + } + ok(activeok, testname + " item " + (id ? id : "none") + " active"); +} + +function checkOpen(menuid, testname) { + var menu = document.getElementById(menuid); + if ("open" in menu) { + ok(menu.open, testname + " " + menuid + " menu is open"); + } else if (menu.hasMenu()) { + ok( + menu.getAttribute("open") == "true", + testname + " " + menuid + " menu is open" + ); + } +} + +function checkClosed(menuid, testname) { + var menu = document.getElementById(menuid); + if ("open" in menu) { + ok(!menu.open, testname + " " + menuid + " menu is open"); + } else if (menu.hasMenu()) { + ok(!menu.hasAttribute("open"), testname + " " + menuid + " menu is closed"); + } +} + +function convertPosition(anchor, align) { + if (anchor == "topleft" && align == "topleft") { + return "overlap"; + } + if (anchor == "topleft" && align == "topright") { + return "start_before"; + } + if (anchor == "topleft" && align == "bottomleft") { + return "before_start"; + } + if (anchor == "topright" && align == "topleft") { + return "end_before"; + } + if (anchor == "topright" && align == "bottomright") { + return "before_end"; + } + if (anchor == "bottomleft" && align == "bottomright") { + return "start_after"; + } + if (anchor == "bottomleft" && align == "topleft") { + return "after_start"; + } + if (anchor == "bottomright" && align == "bottomleft") { + return "end_after"; + } + if (anchor == "bottomright" && align == "topright") { + return "after_end"; + } + return ""; +} + +/* + * When checking position of the bottom or right edge of the popup's rect, + * use this instead of strict equality check of rounded values, + * because we snap the top/left edges to pixel boundaries, + * which can shift the bottom/right up to 0.5px from its "ideal" location, + * and could cause it to round differently. (See bug 622507.) + */ +function isWithinHalfPixel(a, b, message) { + ok(Math.abs(a - b) <= 0.5, `${message}: ${a}, ${b}`); +} + +function compareEdge(anchor, popup, edge, offsetX, offsetY, testname) { + testname += " " + edge; + + checkOpen(anchor.id, testname); + + var anchorrect = anchor.getBoundingClientRect(); + var popuprect = popup.getBoundingClientRect(); + + if (gPopupWidth == -1) { + ok( + Math.round(popuprect.right) - Math.round(popuprect.left) && + Math.round(popuprect.bottom) - Math.round(popuprect.top), + testname + " size" + ); + } else { + is(Math.round(popuprect.width), gPopupWidth, testname + " width"); + is(Math.round(popuprect.height), gPopupHeight, testname + " height"); + } + + var spaceIdx = edge.indexOf(" "); + if (spaceIdx > 0) { + let cornerX, cornerY; + let [position, align] = edge.split(" "); + switch (position) { + case "topleft": + cornerX = anchorrect.left; + cornerY = anchorrect.top; + break; + case "topcenter": + cornerX = anchorrect.left + anchorrect.width / 2; + cornerY = anchorrect.top; + break; + case "topright": + cornerX = anchorrect.right; + cornerY = anchorrect.top; + break; + case "leftcenter": + cornerX = anchorrect.left; + cornerY = anchorrect.top + anchorrect.height / 2; + break; + case "rightcenter": + cornerX = anchorrect.right; + cornerY = anchorrect.top + anchorrect.height / 2; + break; + case "bottomleft": + cornerX = anchorrect.left; + cornerY = anchorrect.bottom; + break; + case "bottomcenter": + cornerX = anchorrect.left + anchorrect.width / 2; + cornerY = anchorrect.bottom; + break; + case "bottomright": + cornerX = anchorrect.right; + cornerY = anchorrect.bottom; + break; + } + + switch (align) { + case "topleft": + cornerX += offsetX; + cornerY += offsetY; + break; + case "topright": + cornerX += -popuprect.width + offsetX; + cornerY += offsetY; + break; + case "bottomleft": + cornerX += offsetX; + cornerY += -popuprect.height + offsetY; + break; + case "bottomright": + cornerX += -popuprect.width + offsetX; + cornerY += -popuprect.height + offsetY; + break; + } + + is( + Math.round(popuprect.left), + Math.round(cornerX), + testname + " x position" + ); + is( + Math.round(popuprect.top), + Math.round(cornerY), + testname + " y position" + ); + return; + } + + if (edge == "after_pointer") { + is( + Math.round(popuprect.left), + Math.round(anchorrect.left) + offsetX, + testname + " x position" + ); + is( + Math.round(popuprect.top), + Math.round(anchorrect.top) + offsetY + 21, + testname + " y position" + ); + return; + } + + if (edge == "overlap") { + is( + Math.round(anchorrect.left) + offsetY, + Math.round(popuprect.left), + testname + " position1" + ); + is( + Math.round(anchorrect.top) + offsetY, + Math.round(popuprect.top), + testname + " position2" + ); + return; + } + + if (edge.indexOf("before") == 0) { + isWithinHalfPixel( + anchorrect.top + offsetY, + popuprect.bottom, + testname + " position1" + ); + } else if (edge.indexOf("after") == 0) { + is( + Math.round(anchorrect.bottom) + offsetY, + Math.round(popuprect.top), + testname + " position1" + ); + } else if (edge.indexOf("start") == 0) { + isWithinHalfPixel( + anchorrect.left + offsetX, + popuprect.right, + testname + " position1" + ); + } else if (edge.indexOf("end") == 0) { + is( + Math.round(anchorrect.right) + offsetX, + Math.round(popuprect.left), + testname + " position1" + ); + } + + if (0 < edge.indexOf("before")) { + is( + Math.round(anchorrect.top) + offsetY, + Math.round(popuprect.top), + testname + " position2" + ); + } else if (0 < edge.indexOf("after")) { + isWithinHalfPixel( + anchorrect.bottom + offsetY, + popuprect.bottom, + testname + " position2" + ); + } else if (0 < edge.indexOf("start")) { + is( + Math.round(anchorrect.left) + offsetX, + Math.round(popuprect.left), + testname + " position2" + ); + } else if (0 < edge.indexOf("end")) { + isWithinHalfPixel( + anchorrect.right + offsetX, + popuprect.right, + testname + " position2" + ); + } +} diff --git a/toolkit/content/tests/widgets/seek_with_sound.ogg b/toolkit/content/tests/widgets/seek_with_sound.ogg new file mode 100644 index 0000000000..c86d9946bd Binary files /dev/null and b/toolkit/content/tests/widgets/seek_with_sound.ogg differ diff --git a/toolkit/content/tests/widgets/test-webvtt-1.vtt b/toolkit/content/tests/widgets/test-webvtt-1.vtt new file mode 100644 index 0000000000..20c9fc94cc --- /dev/null +++ b/toolkit/content/tests/widgets/test-webvtt-1.vtt @@ -0,0 +1,10 @@ +WEBVTT + +1 +00:00:00.000 --> 00:00:02.000 +This is a one line text track + +2 +00:00:05.000 --> 00:00:07.000 +- This is a new text track but bolded +- This line should be italicized diff --git a/toolkit/content/tests/widgets/test-webvtt-2.vtt b/toolkit/content/tests/widgets/test-webvtt-2.vtt new file mode 100644 index 0000000000..48dd63a9ee --- /dev/null +++ b/toolkit/content/tests/widgets/test-webvtt-2.vtt @@ -0,0 +1,10 @@ +WEBVTT + +1 +00:00:00.000 --> 00:00:02.000 +Voici un text track en une ligne + +2 +00:00:05.000 --> 00:00:07.000 +- Voici un nouveau text track en gras +- Cette ligne devrait être en italiques diff --git a/toolkit/content/tests/widgets/test_audiocontrols_dimensions.html b/toolkit/content/tests/widgets/test_audiocontrols_dimensions.html new file mode 100644 index 0000000000..2d29fe9be4 --- /dev/null +++ b/toolkit/content/tests/widgets/test_audiocontrols_dimensions.html @@ -0,0 +1,66 @@ + + + + Audio controls test + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_audiocontrols_fullscreen.html b/toolkit/content/tests/widgets/test_audiocontrols_fullscreen.html new file mode 100644 index 0000000000..6963c3da1e --- /dev/null +++ b/toolkit/content/tests/widgets/test_audiocontrols_fullscreen.html @@ -0,0 +1,61 @@ + + + + Audio controls test + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_bug1654500.html b/toolkit/content/tests/widgets/test_bug1654500.html new file mode 100644 index 0000000000..9bb05ba9c8 --- /dev/null +++ b/toolkit/content/tests/widgets/test_bug1654500.html @@ -0,0 +1,33 @@ + + +Clear disabled/readonly datetime inputs + + +

Disabled inputs should be able to be cleared just as they can be set with real values

+ + + + + diff --git a/toolkit/content/tests/widgets/test_bug898940.html b/toolkit/content/tests/widgets/test_bug898940.html new file mode 100644 index 0000000000..f2349c9a4c --- /dev/null +++ b/toolkit/content/tests/widgets/test_bug898940.html @@ -0,0 +1,31 @@ + + + + Test that an audio element that's already playing when controls are attached displays the controls + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_contextmenu_menugroup.xhtml b/toolkit/content/tests/widgets/test_contextmenu_menugroup.xhtml new file mode 100644 index 0000000000..88511001b7 --- /dev/null +++ b/toolkit/content/tests/widgets/test_contextmenu_menugroup.xhtml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+    
+ + + + diff --git a/toolkit/content/tests/widgets/test_moz_label.html b/toolkit/content/tests/widgets/test_moz_label.html new file mode 100644 index 0000000000..80a9600930 --- /dev/null +++ b/toolkit/content/tests/widgets/test_moz_label.html @@ -0,0 +1,142 @@ + + + + + MozLabel tests + + + + + + + +

+
+ + + + + + + + + + + + + + +
+
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_moz_support_link.html b/toolkit/content/tests/widgets/test_moz_support_link.html new file mode 100644 index 0000000000..00c7b235ec --- /dev/null +++ b/toolkit/content/tests/widgets/test_moz_support_link.html @@ -0,0 +1,153 @@ + + + + + MozSupportLink tests + + + + + + + +

+
+ testElement + + testElement2 + + + + + + + +
+

+
+
+
diff --git a/toolkit/content/tests/widgets/test_moz_toggle.html b/toolkit/content/tests/widgets/test_moz_toggle.html
new file mode 100644
index 0000000000..62f248c599
--- /dev/null
+++ b/toolkit/content/tests/widgets/test_moz_toggle.html
@@ -0,0 +1,85 @@
+
+
+
+  
+  MozToggle tests
+  
+  
+  
+  
+  
+  
+
+
+

+
+ +
+
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_nac_mutations.html b/toolkit/content/tests/widgets/test_nac_mutations.html new file mode 100644 index 0000000000..3e4896bec2 --- /dev/null +++ b/toolkit/content/tests/widgets/test_nac_mutations.html @@ -0,0 +1,65 @@ + +UA Widget mutation observer test + + + + +
+ diff --git a/toolkit/content/tests/widgets/test_panel_item_accesskey.html b/toolkit/content/tests/widgets/test_panel_item_accesskey.html new file mode 100644 index 0000000000..a35e94d456 --- /dev/null +++ b/toolkit/content/tests/widgets/test_panel_item_accesskey.html @@ -0,0 +1,105 @@ + + + + + Test Panel Item Accesskey Support + + + + + + +

+ +
+ + First item + Second item + Third item + +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_panel_list_accessibility.html b/toolkit/content/tests/widgets/test_panel_list_accessibility.html new file mode 100644 index 0000000000..c53b48e0a9 --- /dev/null +++ b/toolkit/content/tests/widgets/test_panel_list_accessibility.html @@ -0,0 +1,79 @@ + + + + + Test Panel List Accessibility + + + + + + +

+ +
+ + + one + two + three + +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_panel_list_in_xul_panel.html b/toolkit/content/tests/widgets/test_panel_list_in_xul_panel.html new file mode 100644 index 0000000000..ea5a9fc25c --- /dev/null +++ b/toolkit/content/tests/widgets/test_panel_list_in_xul_panel.html @@ -0,0 +1,87 @@ + + + + + Test Panel List In XUL Panel + + + + + + + +

+ +
+ + + one + two + three + four + five + six + +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_panel_list_min_width_from_anchor.html b/toolkit/content/tests/widgets/test_panel_list_min_width_from_anchor.html new file mode 100644 index 0000000000..0708174a88 --- /dev/null +++ b/toolkit/content/tests/widgets/test_panel_list_min_width_from_anchor.html @@ -0,0 +1,70 @@ + + + + + Test Panel List Min-width From Anchor + + + + + + + +

+ +
+ + + one + two + three + four + five + six + +
+ +
+
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_panel_list_shadow_node_anchor.html b/toolkit/content/tests/widgets/test_panel_list_shadow_node_anchor.html new file mode 100644 index 0000000000..9f265d4cf9 --- /dev/null +++ b/toolkit/content/tests/widgets/test_panel_list_shadow_node_anchor.html @@ -0,0 +1,96 @@ + + + + + Bug 1802215 - Allow <panel-list> to be anchored to shadow DOM nodes + + + + + + + + +

+
+ + + An item + Another item + + +
+

+
+
diff --git a/toolkit/content/tests/widgets/test_popupanchor.xhtml b/toolkit/content/tests/widgets/test_popupanchor.xhtml
new file mode 100644
index 0000000000..5c78cf62fc
--- /dev/null
+++ b/toolkit/content/tests/widgets/test_popupanchor.xhtml
@@ -0,0 +1,387 @@
+
+
+
+
+
+
+  
+  
+
+  
+
+
+
+
+
+
+

The anchor --> v <--

+
+ + + + +
diff --git a/toolkit/content/tests/widgets/test_popupreflows.xhtml b/toolkit/content/tests/widgets/test_popupreflows.xhtml new file mode 100644 index 0000000000..c3f8068779 --- /dev/null +++ b/toolkit/content/tests/widgets/test_popupreflows.xhtml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + +

The anchor --> v <--

+ +
diff --git a/toolkit/content/tests/widgets/test_tree_column_reorder.xhtml b/toolkit/content/tests/widgets/test_tree_column_reorder.xhtml new file mode 100644 index 0000000000..d1dc9c1c20 --- /dev/null +++ b/toolkit/content/tests/widgets/test_tree_column_reorder.xhtml @@ -0,0 +1,75 @@ + + + + + + + + diff --git a/toolkit/content/tests/widgets/test_ua_widget_elementFromPoint.html b/toolkit/content/tests/widgets/test_ua_widget_elementFromPoint.html new file mode 100644 index 0000000000..d52ec48f22 --- /dev/null +++ b/toolkit/content/tests/widgets/test_ua_widget_elementFromPoint.html @@ -0,0 +1,22 @@ + +UA Widget getElementFromPoint + + + +
+ diff --git a/toolkit/content/tests/widgets/test_ua_widget_sandbox.html b/toolkit/content/tests/widgets/test_ua_widget_sandbox.html new file mode 100644 index 0000000000..cc53e1c6d9 --- /dev/null +++ b/toolkit/content/tests/widgets/test_ua_widget_sandbox.html @@ -0,0 +1,101 @@ + + + + UA Widget sandbox test + + + + + + +

+ +
+
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_ua_widget_unbind.html b/toolkit/content/tests/widgets/test_ua_widget_unbind.html new file mode 100644 index 0000000000..89ae6c0f00 --- /dev/null +++ b/toolkit/content/tests/widgets/test_ua_widget_unbind.html @@ -0,0 +1,56 @@ + + + + UA Widget unbind test + + + + + + +

+ +
+
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols.html b/toolkit/content/tests/widgets/test_videocontrols.html new file mode 100644 index 0000000000..32cd23df6a --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols.html @@ -0,0 +1,564 @@ + + + + Video controls test + + + + + + +

+ +
+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_audio.html b/toolkit/content/tests/widgets/test_videocontrols_audio.html new file mode 100644 index 0000000000..ad528f4c27 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_audio.html @@ -0,0 +1,40 @@ + + + + Video controls with Audio file test + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_audio_direction.html b/toolkit/content/tests/widgets/test_videocontrols_audio_direction.html new file mode 100644 index 0000000000..b656de0103 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_audio_direction.html @@ -0,0 +1,31 @@ + + + + Video controls directionality test + + + + + + +

+ +
+
+ +
+
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_clickToPlay_ariaLabel.html b/toolkit/content/tests/widgets/test_videocontrols_clickToPlay_ariaLabel.html new file mode 100644 index 0000000000..a787358394 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_clickToPlay_ariaLabel.html @@ -0,0 +1,56 @@ + + + + Video controls test - clickToPlayAriaLabel + + + + + + +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_closed_caption_menu.html b/toolkit/content/tests/widgets/test_videocontrols_closed_caption_menu.html new file mode 100644 index 0000000000..39d6ff494f --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_closed_caption_menu.html @@ -0,0 +1,144 @@ + + + + Video controls test - KeyHandler + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_error.html b/toolkit/content/tests/widgets/test_videocontrols_error.html new file mode 100644 index 0000000000..af90a4672a --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_error.html @@ -0,0 +1,60 @@ + + + + Video controls test - Error + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_focus.html b/toolkit/content/tests/widgets/test_videocontrols_focus.html new file mode 100644 index 0000000000..71282a4d08 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_focus.html @@ -0,0 +1,111 @@ + + + + + Video controls test - Focus + + + + + + +

+ +
+
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_iframe_fullscreen.html b/toolkit/content/tests/widgets/test_videocontrols_iframe_fullscreen.html new file mode 100644 index 0000000000..0a74b25609 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_iframe_fullscreen.html @@ -0,0 +1,60 @@ + + + + Video controls test - iframe + + + + + + +

+ +
+ + + +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_jsdisabled.html b/toolkit/content/tests/widgets/test_videocontrols_jsdisabled.html new file mode 100644 index 0000000000..f3fdecc47f --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_jsdisabled.html @@ -0,0 +1,69 @@ + + + + Video controls test + + + + + + +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_keyhandler.html b/toolkit/content/tests/widgets/test_videocontrols_keyhandler.html new file mode 100644 index 0000000000..5b771fc745 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_keyhandler.html @@ -0,0 +1,150 @@ + + + + Video controls test - KeyHandler + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_onclickplay.html b/toolkit/content/tests/widgets/test_videocontrols_onclickplay.html new file mode 100644 index 0000000000..9023512ab7 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_onclickplay.html @@ -0,0 +1,74 @@ + + + + Video controls test + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_orientation.html b/toolkit/content/tests/widgets/test_videocontrols_orientation.html new file mode 100644 index 0000000000..26b2144e14 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_orientation.html @@ -0,0 +1,63 @@ + + + + Video controls test + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_scrubber_position.html b/toolkit/content/tests/widgets/test_videocontrols_scrubber_position.html new file mode 100644 index 0000000000..b1d2ab9e74 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_scrubber_position.html @@ -0,0 +1,51 @@ + + + + + Video controls test - Initial scrubber position + + + + + + +

+ +
+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_scrubber_position_nopreload.html b/toolkit/content/tests/widgets/test_videocontrols_scrubber_position_nopreload.html new file mode 100644 index 0000000000..9fbb6fbcb5 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_scrubber_position_nopreload.html @@ -0,0 +1,123 @@ + + + + + Video controls test - Initial scrubber position when preload is turned off + + + + + + +

+ +
+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_size.html b/toolkit/content/tests/widgets/test_videocontrols_size.html new file mode 100644 index 0000000000..559cc66e86 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_size.html @@ -0,0 +1,179 @@ + + + + Video controls test - Size + + + + + + +

+ +
+ + + + + + + + + + + + + +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_standalone.html b/toolkit/content/tests/widgets/test_videocontrols_standalone.html new file mode 100644 index 0000000000..14208923dd --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_standalone.html @@ -0,0 +1,131 @@ + + + + Video controls test + + + + + + + +

+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_video_direction.html b/toolkit/content/tests/widgets/test_videocontrols_video_direction.html new file mode 100644 index 0000000000..45cf7f6363 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_video_direction.html @@ -0,0 +1,31 @@ + + + + Video controls directionality test + + + + + + +

+ +
+
+ +
+
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html b/toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html new file mode 100644 index 0000000000..bfc8018466 --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_video_noaudio.html @@ -0,0 +1,42 @@ + + + + Video controls test + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/test_videocontrols_vtt.html b/toolkit/content/tests/widgets/test_videocontrols_vtt.html new file mode 100644 index 0000000000..2f8d70f35a --- /dev/null +++ b/toolkit/content/tests/widgets/test_videocontrols_vtt.html @@ -0,0 +1,112 @@ + + + + Video controls test - VTT + + + + + + +

+ +
+ +
+ +
+
+
+ + diff --git a/toolkit/content/tests/widgets/tree_shared.js b/toolkit/content/tests/widgets/tree_shared.js new file mode 100644 index 0000000000..62b93fdb69 --- /dev/null +++ b/toolkit/content/tests/widgets/tree_shared.js @@ -0,0 +1,2181 @@ +// This file expects the following globals to be defined at various times. +/* globals getCustomTreeViewCellInfo */ + +// This files relies on these specific Chrome/XBL globals +/* globals TreeColumns, TreeColumn */ + +var columns_simpletree = [ + { name: "name", label: "Name", key: true, properties: "one two" }, + { name: "address", label: "Address" }, +]; + +var columns_hiertree = [ + { + name: "name", + label: "Name", + primary: true, + key: true, + properties: "one two", + }, + { name: "address", label: "Address" }, + { name: "planet", label: "Planet" }, + { name: "gender", label: "Gender", cycler: true }, +]; + +// XXXndeakin still to add some tests for: +// cycler columns, checkbox cells + +// this test function expects a tree to have 8 rows in it when it isn't +// expanded. The tree should only display four rows at a time. If editable, +// the cell at row 1 and column 0 must be editable, and the cell at row 2 and +// column 1 must not be editable. +async function testtag_tree( + treeid, + treerowinfoid, + seltype, + columnstype, + testid +) { + // Stop keystrokes that aren't handled by the tree from leaking out and + // scrolling the main Mochitests window! + function preventDefault(event) { + event.preventDefault(); + } + document.addEventListener("keypress", preventDefault); + + var multiple = seltype == "multiple"; + + var tree = document.getElementById(treeid); + var treerowinfo = document.getElementById(treerowinfoid); + var rowInfo; + if (testid == "tree view") { + rowInfo = getCustomTreeViewCellInfo(); + } else { + rowInfo = convertDOMtoTreeRowInfo(treerowinfo, 0, { value: -1 }); + } + var columnInfo = + columnstype == "simple" ? columns_simpletree : columns_hiertree; + + is(tree.selType, seltype == "multiple" ? "" : seltype, testid + " seltype"); + + // note: the functions below should be in this order due to changes made in later tests + + await testtag_tree_treecolpicker(tree, columnInfo, testid); + testtag_tree_columns(tree, columnInfo, testid); + testtag_tree_TreeSelection(tree, testid, multiple); + testtag_tree_TreeSelection_UI(tree, testid, multiple); + testtag_tree_TreeView(tree, testid, rowInfo); + + is(tree.editable, false, "tree should not be editable"); + // currently, the editable flag means that tree editing cannot be invoked + // by the user. However, editing can still be started with a script. + is(tree.editingRow, -1, testid + " initial editingRow"); + is(tree.editingColumn, null, testid + " initial editingColumn"); + + testtag_tree_UI_editing(tree, testid, rowInfo); + + is( + tree.editable, + false, + "tree should not be editable after testtag_tree_UI_editing" + ); + // currently, the editable flag means that tree editing cannot be invoked + // by the user. However, editing can still be started with a script. + is(tree.editingRow, -1, testid + " initial editingRow (continued)"); + is(tree.editingColumn, null, testid + " initial editingColumn (continued)"); + + var ecolumn = tree.columns[0]; + ok( + !tree.startEditing(1, ecolumn), + "non-editable trees shouldn't start editing" + ); + is( + tree.editingRow, + -1, + testid + " failed startEditing shouldn't set editingRow" + ); + is( + tree.editingColumn, + null, + testid + " failed startEditing shouldn't set editingColumn" + ); + + tree.editable = true; + + ok(tree.startEditing(1, ecolumn), "startEditing should have returned true"); + is(tree.editingRow, 1, testid + " startEditing editingRow"); + is(tree.editingColumn, ecolumn, testid + " startEditing editingColumn"); + is( + tree.getAttribute("editing"), + "true", + testid + " startEditing editing attribute" + ); + + tree.stopEditing(true); + is(tree.editingRow, -1, testid + " stopEditing editingRow"); + is(tree.editingColumn, null, testid + " stopEditing editingColumn"); + is( + tree.hasAttribute("editing"), + false, + testid + " stopEditing editing attribute" + ); + + tree.startEditing(-1, ecolumn); + is( + tree.editingRow == -1 && tree.editingColumn == null, + true, + testid + " startEditing -1 editingRow" + ); + tree.startEditing(15, ecolumn); + is( + tree.editingRow == -1 && tree.editingColumn == null, + true, + testid + " startEditing 15 editingRow" + ); + tree.startEditing(1, null); + is( + tree.editingRow == -1 && tree.editingColumn == null, + true, + testid + " startEditing null column editingRow" + ); + tree.startEditing(2, tree.columns[1]); + is( + tree.editingRow == -1 && tree.editingColumn == null, + true, + testid + " startEditing non editable cell editingRow" + ); + + tree.startEditing(1, ecolumn); + var inputField = tree.inputField; + is(inputField.localName, "input", testid + "inputField"); + inputField.value = "Changed Value"; + tree.stopEditing(true); + is( + tree.view.getCellText(1, ecolumn), + "Changed Value", + testid + "edit cell accept" + ); + + // this cell can be edited, but stopEditing(false) means don't accept the change. + tree.startEditing(1, ecolumn); + inputField.value = "Second Value"; + tree.stopEditing(false); + is( + tree.view.getCellText(1, ecolumn), + "Changed Value", + testid + "edit cell no accept" + ); + + tree.editable = false; + + // do the sorting tests last as it will cause the rows to rearrange + // skip them for the custom tree view + if (testid != "tree view") { + testtag_tree_TreeView_rows_sort(tree, testid, rowInfo); + } + + testtag_tree_wheel(tree); + + document.removeEventListener("keypress", preventDefault); + + SimpleTest.finish(); +} + +async function testtag_tree_treecolpicker(tree, expectedColumns, testid) { + testid += " "; + + async function showAndHideTreecolpicker() { + let treecolpicker = tree.querySelector("treecolpicker"); + let treecolpickerMenupopup = treecolpicker.querySelector("menupopup"); + await new Promise(resolve => { + treecolpickerMenupopup.addEventListener("popupshown", resolve, { + once: true, + }); + treecolpicker.querySelector("button").click(); + }); + let menuitems = treecolpicker.querySelectorAll("menuitem"); + // Ignore the last "Restore Column Order" menu in the count: + is( + menuitems.length - 1, + expectedColumns.length, + testid + "Same number of columns" + ); + for (var c = 0; c < expectedColumns.length; c++) { + is( + menuitems[c].textContent, + expectedColumns[c].label, + testid + "treecolpicker menu matches" + ); + ok( + !menuitems[c].querySelector("label").hidden, + testid + "label not hidden" + ); + } + await new Promise(resolve => { + treecolpickerMenupopup.addEventListener("popuphidden", resolve, { + once: true, + }); + treecolpickerMenupopup.hidePopup(); + }); + } + + // Regression test for Bug 1549931 (menuitem content being hidden upon second open) + await showAndHideTreecolpicker(); + await showAndHideTreecolpicker(); +} + +function testtag_tree_columns(tree, expectedColumns, testid) { + testid += " "; + + var columns = tree.columns; + + is( + TreeColumns.isInstance(columns), + true, + testid + "columns is a TreeColumns" + ); + is(columns.count, expectedColumns.length, testid + "TreeColumns count"); + is(columns.length, expectedColumns.length, testid + "TreeColumns length"); + + var treecols = tree.getElementsByTagName("treecols")[0]; + var treecol = treecols.getElementsByTagName("treecol"); + + var x = 0; + var primary = null, + sorted = null, + key = null; + for (var c = 0; c < expectedColumns.length; c++) { + var adjtestid = testid + " column " + c + " "; + var column = columns[c]; + var expectedColumn = expectedColumns[c]; + is(columns.getColumnAt(c), column, adjtestid + "getColumnAt"); + is( + columns.getNamedColumn(expectedColumn.name), + column, + adjtestid + "getNamedColumn" + ); + is(columns.getColumnFor(treecol[c]), column, adjtestid + "getColumnFor"); + if (expectedColumn.primary) { + primary = column; + } + if (expectedColumn.sorted) { + sorted = column; + } + if (expectedColumn.key) { + key = column; + } + + // XXXndeakin on Windows and Linux, some columns are one pixel to the + // left of where they should be. Could just be a rounding issue. + var adj = 1; + is( + column.x + adj >= x, + true, + adjtestid + + "position is after last column " + + column.x + + "," + + column.width + + "," + + x + ); + is(column.width > 0, true, adjtestid + "width is greater than 0"); + x = column.x + column.width; + + // now check the TreeColumn properties + is(TreeColumn.isInstance(column), true, adjtestid + "is a TreeColumn"); + is(column.element, treecol[c], adjtestid + "element is treecol"); + is(column.columns, columns, adjtestid + "columns is TreeColumns"); + is(column.id, expectedColumn.name, adjtestid + "name"); + is(column.index, c, adjtestid + "index"); + is(column.primary, primary == column, adjtestid + "column is primary"); + + is( + column.cycler, + "cycler" in expectedColumn && expectedColumn.cycler, + adjtestid + "column is cycler" + ); + is( + column.editable, + "editable" in expectedColumn && expectedColumn.editable, + adjtestid + "column is editable" + ); + + is( + column.type, + "type" in expectedColumn ? expectedColumn.type : 1, + adjtestid + "type" + ); + + is( + column.getPrevious(), + c > 0 ? columns[c - 1] : null, + adjtestid + "getPrevious" + ); + is( + column.getNext(), + c < columns.length - 1 ? columns[c + 1] : null, + adjtestid + "getNext" + ); + + // check the view's getColumnProperties method + var properties = tree.view.getColumnProperties(column); + var expectedProperties = expectedColumn.properties; + is( + properties, + expectedProperties ? expectedProperties : "", + adjtestid + "getColumnProperties" + ); + } + + is(columns.getFirstColumn(), columns[0], testid + "getFirstColumn"); + is( + columns.getLastColumn(), + columns[columns.length - 1], + testid + "getLastColumn" + ); + is(columns.getPrimaryColumn(), primary, testid + "getPrimaryColumn"); + is(columns.getSortedColumn(), sorted, testid + "getSortedColumn"); + is(columns.getKeyColumn(), key, testid + "getKeyColumn"); + + is(columns.getColumnAt(-1), null, testid + "getColumnAt under"); + is(columns.getColumnAt(columns.length), null, testid + "getColumnAt over"); + is(columns.getNamedColumn(""), null, testid + "getNamedColumn null"); + is( + columns.getNamedColumn("unknown"), + null, + testid + "getNamedColumn unknown" + ); + is(columns.getColumnFor(null), null, testid + "getColumnFor null"); + is(columns.getColumnFor(tree), null, testid + "getColumnFor other"); +} + +function testtag_tree_TreeSelection(tree, testid, multiple) { + testid += " selection "; + + var selection = tree.view.selection; + is( + selection instanceof Ci.nsITreeSelection, + true, + testid + "selection is a TreeSelection" + ); + is(selection.single, !multiple, testid + "single"); + + testtag_tree_TreeSelection_State(tree, testid + "initial", -1, []); + is(selection.shiftSelectPivot, -1, testid + "initial shiftSelectPivot"); + + selection.currentIndex = 2; + testtag_tree_TreeSelection_State(tree, testid + "set currentIndex", 2, []); + tree.currentIndex = 3; + testtag_tree_TreeSelection_State( + tree, + testid + "set tree.currentIndex", + 3, + [] + ); + + // test the select() method, which should deselect all rows and select + // a single row + selection.select(1); + testtag_tree_TreeSelection_State(tree, testid + "select 1", 1, [1]); + selection.select(3); + testtag_tree_TreeSelection_State(tree, testid + "select 2", 3, [3]); + selection.select(3); + testtag_tree_TreeSelection_State(tree, testid + "select same", 3, [3]); + + selection.currentIndex = 1; + testtag_tree_TreeSelection_State( + tree, + testid + "set currentIndex with single selection", + 1, + [3] + ); + + tree.currentIndex = 2; + testtag_tree_TreeSelection_State( + tree, + testid + "set tree.currentIndex with single selection", + 2, + [3] + ); + + // check the toggleSelect method. In single selection mode, it only toggles on when + // there isn't currently a selection. + selection.toggleSelect(2); + testtag_tree_TreeSelection_State( + tree, + testid + "toggleSelect 1", + 2, + multiple ? [2, 3] : [3] + ); + selection.toggleSelect(2); + selection.toggleSelect(3); + testtag_tree_TreeSelection_State(tree, testid + "toggleSelect 2", 3, []); + + // the current index doesn't change after a selectAll, so it should still be set to 1 + // selectAll has no effect on single selection trees + selection.currentIndex = 1; + selection.selectAll(); + testtag_tree_TreeSelection_State( + tree, + testid + "selectAll 1", + 1, + multiple ? [0, 1, 2, 3, 4, 5, 6, 7] : [] + ); + selection.toggleSelect(2); + testtag_tree_TreeSelection_State( + tree, + testid + "toggleSelect after selectAll", + 2, + multiple ? [0, 1, 3, 4, 5, 6, 7] : [2] + ); + selection.clearSelection(); + testtag_tree_TreeSelection_State(tree, testid + "clearSelection", 2, []); + selection.toggleSelect(3); + selection.toggleSelect(1); + if (multiple) { + selection.selectAll(); + testtag_tree_TreeSelection_State( + tree, + testid + "selectAll 2", + 1, + [0, 1, 2, 3, 4, 5, 6, 7] + ); + } + selection.currentIndex = 2; + selection.clearSelection(); + testtag_tree_TreeSelection_State( + tree, + testid + "clearSelection after selectAll", + 2, + [] + ); + + is(selection.shiftSelectPivot, -1, testid + "shiftSelectPivot set to -1"); + + // rangedSelect and clearRange set the currentIndex to the endIndex. The + // shiftSelectPivot property will be set to startIndex. + selection.rangedSelect(1, 3, false); + testtag_tree_TreeSelection_State( + tree, + testid + "rangedSelect no augment", + multiple ? 3 : 2, + multiple ? [1, 2, 3] : [] + ); + is( + selection.shiftSelectPivot, + multiple ? 1 : -1, + testid + "shiftSelectPivot after rangedSelect no augment" + ); + if (multiple) { + selection.select(1); + selection.rangedSelect(0, 2, true); + testtag_tree_TreeSelection_State( + tree, + testid + "rangedSelect augment", + 2, + [0, 1, 2] + ); + is( + selection.shiftSelectPivot, + 0, + testid + "shiftSelectPivot after rangedSelect augment" + ); + + selection.clearRange(1, 3); + testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment", 3, [ + 0, + ]); + + // check that rangedSelect can take a start value higher than end + selection.rangedSelect(3, 1, false); + testtag_tree_TreeSelection_State( + tree, + testid + "rangedSelect reverse", + 1, + [1, 2, 3] + ); + is( + selection.shiftSelectPivot, + 3, + testid + "shiftSelectPivot after rangedSelect reverse" + ); + + // check that setting the current index doesn't change the selection + selection.currentIndex = 0; + testtag_tree_TreeSelection_State( + tree, + testid + "currentIndex with range selection", + 0, + [1, 2, 3] + ); + } + + // both values of rangedSelect may be the same + selection.rangedSelect(2, 2, false); + testtag_tree_TreeSelection_State(tree, testid + "rangedSelect one row", 2, [ + 2, + ]); + is( + selection.shiftSelectPivot, + 2, + testid + "shiftSelectPivot after selecting one row" + ); + + if (multiple) { + selection.rangedSelect(2, 3, true); + + // a start index of -1 means from the last point + selection.rangedSelect(-1, 0, true); + testtag_tree_TreeSelection_State( + tree, + testid + "rangedSelect -1 existing selection", + 0, + [0, 1, 2, 3] + ); + is( + selection.shiftSelectPivot, + 2, + testid + "shiftSelectPivot after -1 existing selection" + ); + + selection.currentIndex = 2; + selection.rangedSelect(-1, 0, false); + testtag_tree_TreeSelection_State( + tree, + testid + "rangedSelect -1 from currentIndex", + 0, + [0, 1, 2] + ); + is( + selection.shiftSelectPivot, + 2, + testid + "shiftSelectPivot -1 from currentIndex" + ); + } + + // XXXndeakin need to test out of range values but these don't work properly + /* + selection.select(-1); + testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment -1", -1, []); + + selection.select(8); + testtag_tree_TreeSelection_State(tree, testid + "rangedSelect augment 8", 3, [0]); +*/ +} + +function testtag_tree_TreeSelection_UI(tree, testid, multiple) { + testid += " selection UI "; + + var selection = tree.view.selection; + selection.clearSelection(); + selection.currentIndex = 0; + tree.focus(); + + var keydownFired = 0; + var keypressFired = 0; + function keydownListener(event) { + keydownFired++; + } + function keypressListener(event) { + keypressFired++; + } + + // check that cursor up and down keys navigate up and down + // select event fires after a delay so don't expect it. The reason it fires after a delay + // is so that cursor navigation allows quicking skimming over a set of items without + // actually firing events in-between, improving performance. The select event will only + // be fired on the row where the cursor stops. + window.addEventListener("keydown", keydownListener); + window.addEventListener("keypress", keypressListener); + + synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down"); + testtag_tree_TreeSelection_State(tree, testid + "key down", 1, [1], 0); + + synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up"); + testtag_tree_TreeSelection_State(tree, testid + "key up", 0, [0], 0); + + synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up at start"); + testtag_tree_TreeSelection_State(tree, testid + "key up at start", 0, [0], 0); + + // pressing down while the last row is selected should not fire a select event, + // as the selection won't have changed. Also the view is not scrolled in this case. + selection.select(7); + synthesizeKeyExpectEvent("VK_DOWN", {}, tree, "!select", "key down at end"); + testtag_tree_TreeSelection_State(tree, testid + "key down at end", 7, [7], 0); + + // pressing keys while at the edge of the visible rows should scroll the list + tree.scrollToRow(4); + selection.select(4); + synthesizeKeyExpectEvent("VK_UP", {}, tree, "!select", "key up with scroll"); + is(tree.getFirstVisibleRow(), 3, testid + "key up with scroll"); + + tree.scrollToRow(0); + selection.select(3); + synthesizeKeyExpectEvent( + "VK_DOWN", + {}, + tree, + "!select", + "key down with scroll" + ); + is(tree.getFirstVisibleRow(), 1, testid + "key down with scroll"); + + // accel key and cursor movement adjust currentIndex but should not change + // the selection. In single selection mode, the selection will not change, + // but instead will just scroll up or down a line + tree.scrollToRow(0); + selection.select(1); + synthesizeKeyExpectEvent( + "VK_DOWN", + { accelKey: true }, + tree, + "!select", + "key down with accel" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key down with accel", + multiple ? 2 : 1, + [1] + ); + if (!multiple) { + is(tree.getFirstVisibleRow(), 1, testid + "key down with accel and scroll"); + } + + tree.scrollToRow(4); + selection.select(4); + synthesizeKeyExpectEvent( + "VK_UP", + { accelKey: true }, + tree, + "!select", + "key up with accel" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key up with accel", + multiple ? 3 : 4, + [4] + ); + if (!multiple) { + is(tree.getFirstVisibleRow(), 3, testid + "key up with accel and scroll"); + } + + // do this three times, one for each state of pageUpOrDownMovesSelection, + // and then once with the accel key pressed + for (let t = 0; t < 3; t++) { + let testidmod = ""; + if (t == 2) { + testidmod = " with accel"; + } else if (t == 1) { + testidmod = " rev"; + } + var keymod = t == 2 ? { accelKey: true } : {}; + + var moveselection = tree.pageUpOrDownMovesSelection; + if (t == 2) { + moveselection = !moveselection; + } + + tree.scrollToRow(4); + selection.currentIndex = 6; + selection.select(6); + var expected = moveselection ? 4 : 6; + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + keymod, + tree, + "!select", + "key page up" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key page up" + testidmod, + expected, + [expected], + moveselection ? 4 : 0 + ); + + expected = moveselection ? 0 : 6; + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + keymod, + tree, + "!select", + "key page up again" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key page up again" + testidmod, + expected, + [expected], + 0 + ); + + expected = moveselection ? 0 : 6; + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + keymod, + tree, + "!select", + "key page up at start" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key page up at start" + testidmod, + expected, + [expected], + 0 + ); + + tree.scrollToRow(0); + selection.currentIndex = 1; + selection.select(1); + expected = moveselection ? 3 : 1; + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + keymod, + tree, + "!select", + "key page down" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key page down" + testidmod, + expected, + [expected], + moveselection ? 0 : 4 + ); + + expected = moveselection ? 7 : 1; + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + keymod, + tree, + "!select", + "key page down again" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key page down again" + testidmod, + expected, + [expected], + 4 + ); + + expected = moveselection ? 7 : 1; + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + keymod, + tree, + "!select", + "key page down at start" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key page down at start" + testidmod, + expected, + [expected], + 4 + ); + + if (t < 2) { + tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection; + } + } + + tree.scrollToRow(4); + selection.select(6); + synthesizeKeyExpectEvent("VK_HOME", {}, tree, "!select", "key home"); + testtag_tree_TreeSelection_State(tree, testid + "key home", 0, [0], 0); + + tree.scrollToRow(0); + selection.select(1); + synthesizeKeyExpectEvent("VK_END", {}, tree, "!select", "key end"); + testtag_tree_TreeSelection_State(tree, testid + "key end", 7, [7], 4); + + // in single selection mode, the selection doesn't change in this case + tree.scrollToRow(4); + selection.select(6); + synthesizeKeyExpectEvent( + "VK_HOME", + { accelKey: true }, + tree, + "!select", + "key home with accel" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key home with accel", + multiple ? 0 : 6, + [6], + 0 + ); + + tree.scrollToRow(0); + selection.select(1); + synthesizeKeyExpectEvent( + "VK_END", + { accelKey: true }, + tree, + "!select", + "key end with accel" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key end with accel", + multiple ? 7 : 1, + [1], + 4 + ); + + // next, test cursor navigation with selection. Here the select event will be fired + selection.select(1); + var eventExpected = multiple ? "select" : "!select"; + synthesizeKeyExpectEvent( + "VK_DOWN", + { shiftKey: true }, + tree, + eventExpected, + "key shift down to select" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift down to select", + multiple ? 2 : 1, + multiple ? [1, 2] : [1] + ); + is( + selection.shiftSelectPivot, + multiple ? 1 : -1, + testid + "key shift down to select shiftSelectPivot" + ); + synthesizeKeyExpectEvent( + "VK_UP", + { shiftKey: true }, + tree, + eventExpected, + "key shift up to unselect" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift up to unselect", + 1, + [1] + ); + is( + selection.shiftSelectPivot, + multiple ? 1 : -1, + testid + "key shift up to unselect shiftSelectPivot" + ); + if (multiple) { + synthesizeKeyExpectEvent( + "VK_UP", + { shiftKey: true }, + tree, + "select", + "key shift up to select" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift up to select", + 0, + [0, 1] + ); + is( + selection.shiftSelectPivot, + 1, + testid + "key shift up to select shiftSelectPivot" + ); + synthesizeKeyExpectEvent( + "VK_DOWN", + { shiftKey: true }, + tree, + "select", + "key shift down to unselect" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift down to unselect", + 1, + [1] + ); + is( + selection.shiftSelectPivot, + 1, + testid + "key shift down to unselect shiftSelectPivot" + ); + } + + // do this twice, one for each state of pageUpOrDownMovesSelection, however + // when selecting with the shift key, pageUpOrDownMovesSelection is ignored + // and the selection always changes + var lastidx = tree.view.rowCount - 1; + for (let t = 0; t < 2; t++) { + let testidmod = t == 0 ? "" : " rev"; + + // If the top or bottom visible row is the current row, pressing shift and + // page down / page up selects one page up or one page down. Otherwise, the + // selection is made to the top or bottom of the visible area. + tree.scrollToRow(lastidx - 3); + selection.currentIndex = 6; + selection.select(6); + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + { shiftKey: true }, + tree, + eventExpected, + "key shift page up" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page up" + testidmod, + multiple ? 4 : 6, + multiple ? [4, 5, 6] : [6] + ); + if (multiple) { + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + { shiftKey: true }, + tree, + "select", + "key shift page up again" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page up again" + testidmod, + 0, + [0, 1, 2, 3, 4, 5, 6] + ); + // no change in the selection, so no select event should be fired + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + { shiftKey: true }, + tree, + "!select", + "key shift page up at start" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page up at start" + testidmod, + 0, + [0, 1, 2, 3, 4, 5, 6] + ); + // deselect by paging down again + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + { shiftKey: true }, + tree, + "select", + "key shift page down deselect" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page down deselect" + testidmod, + 3, + [3, 4, 5, 6] + ); + } + + tree.scrollToRow(1); + selection.currentIndex = 2; + selection.select(2); + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + { shiftKey: true }, + tree, + eventExpected, + "key shift page down" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page down" + testidmod, + multiple ? 4 : 2, + multiple ? [2, 3, 4] : [2] + ); + if (multiple) { + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + { shiftKey: true }, + tree, + "select", + "key shift page down again" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page down again" + testidmod, + 7, + [2, 3, 4, 5, 6, 7] + ); + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + { shiftKey: true }, + tree, + "!select", + "key shift page down at start" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page down at start" + testidmod, + 7, + [2, 3, 4, 5, 6, 7] + ); + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + { shiftKey: true }, + tree, + "select", + "key shift page up deselect" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page up deselect" + testidmod, + 4, + [2, 3, 4] + ); + } + + // test when page down / page up is pressed when the view is scrolled such + // that the selection is not visible + if (multiple) { + tree.scrollToRow(3); + selection.currentIndex = 1; + selection.select(1); + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + { shiftKey: true }, + tree, + eventExpected, + "key shift page down with view scrolled down" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page down with view scrolled down" + testidmod, + 6, + [1, 2, 3, 4, 5, 6], + 3 + ); + + tree.scrollToRow(2); + selection.currentIndex = 6; + selection.select(6); + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + { shiftKey: true }, + tree, + eventExpected, + "key shift page up with view scrolled up" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page up with view scrolled up" + testidmod, + 2, + [2, 3, 4, 5, 6], + 2 + ); + + tree.scrollToRow(2); + selection.currentIndex = 0; + selection.select(0); + // don't expect the select event, as the selection won't have changed + synthesizeKeyExpectEvent( + "VK_PAGE_UP", + { shiftKey: true }, + tree, + "!select", + "key shift page up at start with view scrolled down" + ); + testtag_tree_TreeSelection_State( + tree, + testid + + "key shift page up at start with view scrolled down" + + testidmod, + 0, + [0], + 0 + ); + + tree.scrollToRow(0); + selection.currentIndex = 7; + selection.select(7); + // don't expect the select event, as the selection won't have changed + synthesizeKeyExpectEvent( + "VK_PAGE_DOWN", + { shiftKey: true }, + tree, + "!select", + "key shift page down at end with view scrolled up" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift page down at end with view scrolled up" + testidmod, + 7, + [7], + 4 + ); + } + + tree.pageUpOrDownMovesSelection = !tree.pageUpOrDownMovesSelection; + } + + tree.scrollToRow(4); + selection.select(5); + synthesizeKeyExpectEvent( + "VK_HOME", + { shiftKey: true }, + tree, + eventExpected, + "key shift home" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift home", + multiple ? 0 : 5, + multiple ? [0, 1, 2, 3, 4, 5] : [5], + multiple ? 0 : 4 + ); + + tree.scrollToRow(0); + selection.select(3); + synthesizeKeyExpectEvent( + "VK_END", + { shiftKey: true }, + tree, + eventExpected, + "key shift end" + ); + testtag_tree_TreeSelection_State( + tree, + testid + "key shift end", + multiple ? 7 : 3, + multiple ? [3, 4, 5, 6, 7] : [3], + multiple ? 4 : 0 + ); + + // pressing space selects a row, pressing accel + space unselects a row + selection.select(2); + selection.currentIndex = 4; + synthesizeKeyExpectEvent(" ", {}, tree, "select", "key space on"); + // in single selection mode, space shouldn't do anything + testtag_tree_TreeSelection_State( + tree, + testid + "key space on", + 4, + multiple ? [2, 4] : [2] + ); + + if (multiple) { + synthesizeKeyExpectEvent( + " ", + { accelKey: true }, + tree, + "select", + "key space off" + ); + testtag_tree_TreeSelection_State(tree, testid + "key space off", 4, [2]); + } + + // check that clicking on a row selects it + tree.scrollToRow(0); + selection.select(2); + selection.currentIndex = 2; + if (0) { + // XXXndeakin disable these tests for now + mouseOnCell(tree, 1, tree.columns[1], "mouse on row"); + testtag_tree_TreeSelection_State( + tree, + testid + "mouse on row", + 1, + [1], + 0, + null + ); + } + + // restore the scroll position to the start of the page + sendKey("HOME"); + + window.removeEventListener("keydown", keydownListener); + window.removeEventListener("keypress", keypressListener); + is(keydownFired, multiple ? 63 : 40, "keydown event wasn't fired properly"); + is(keypressFired, multiple ? 2 : 1, "keypress event wasn't fired properly"); +} + +function testtag_tree_UI_editing(tree, testid, rowInfo) { + testid += " editing UI "; + + // check editing UI + var ecolumn = tree.columns[0]; + var rowIndex = 2; + + // temporary make the tree editable to test mouse double click + var wasEditable = tree.editable; + if (!wasEditable) { + tree.editable = true; + } + + // if this is a container save its current open status + var row = rowInfo.rows[rowIndex]; + var wasOpen = null; + if (tree.view.isContainer(row)) { + wasOpen = tree.view.isContainerOpen(row); + } + + mouseDblClickOnCell(tree, rowIndex, ecolumn, testid + "edit on double click"); + is(tree.editingColumn, ecolumn, testid + "editing column"); + is(tree.editingRow, rowIndex, testid + "editing row"); + + // ensure that we don't expand an expandable container on edit + if (wasOpen != null) { + is( + tree.view.isContainerOpen(row), + wasOpen, + testid + "opened container node on edit" + ); + } + + // ensure to restore editable attribute + if (!wasEditable) { + tree.editable = false; + } + + var ci = tree.currentIndex; + + // cursor navigation should not change the selection while editing + var testKey = function (key) { + synthesizeKeyExpectEvent( + key, + {}, + tree, + "!select", + "key " + key + " with editing" + ); + is( + tree.editingRow == rowIndex && + tree.editingColumn == ecolumn && + tree.currentIndex == ci, + true, + testid + "key " + key + " while editing" + ); + }; + + testKey("VK_DOWN"); + testKey("VK_UP"); + testKey("VK_PAGE_DOWN"); + testKey("VK_PAGE_UP"); + testKey("VK_HOME"); + testKey("VK_END"); + + // XXXndeakin figure out how to send characters to the textbox + // inputField.inputField.focus() + // synthesizeKeyExpectEvent(inputField.inputField, "b", null, ""); + // tree.stopEditing(true); + // is(tree.view.getCellText(0, ecolumn), "b", testid + "edit cell"); + + // Restore initial state. + tree.stopEditing(false); +} + +function testtag_tree_TreeView(tree, testid, rowInfo) { + testid += " view "; + + var columns = tree.columns; + var view = tree.view; + + is(view instanceof Ci.nsITreeView, true, testid + "view is a TreeView"); + is(view.rowCount, rowInfo.rows.length, testid + "rowCount"); + + testtag_tree_TreeView_rows(tree, testid, rowInfo, 0); + + // note that this will only work for content trees currently + view.setCellText(0, columns[1], "Changed Value"); + is(view.getCellText(0, columns[1]), "Changed Value", "setCellText"); + + view.setCellValue(1, columns[0], "Another Changed Value"); + is(view.getCellValue(1, columns[0]), "Another Changed Value", "setCellText"); +} + +function testtag_tree_TreeView_rows(tree, testid, rowInfo, startRow) { + var r; + var columns = tree.columns; + var view = tree.view; + var length = rowInfo.rows.length; + + // methods to test along with the functions which determine the expected value + var checkRowMethods = { + isContainer(row) { + return row.container; + }, + isContainerOpen(row) { + return false; + }, + isContainerEmpty(row) { + return row.children != null && !row.children.rows.length; + }, + isSeparator(row) { + return row.separator; + }, + getRowProperties(row) { + return row.properties; + }, + getLevel(row) { + return row.level; + }, + getParentIndex(row) { + return row.parent; + }, + hasNextSibling(row) { + return r < startRow + length - 1; + }, + }; + + var checkCellMethods = { + getCellText(row, cell) { + return cell.label; + }, + getCellValue(row, cell) { + return cell.value; + }, + getCellProperties(row, cell) { + return cell.properties; + }, + isEditable(row, cell) { + return cell.editable; + }, + getImageSrc(row, cell) { + return cell.image; + }, + }; + + var failedMethods = {}; + var checkMethod, actual, expected; + var toggleOpenStateOK = true; + + for (r = startRow; r < length; r++) { + var row = rowInfo.rows[r]; + for (var c = 0; c < row.cells.length; c++) { + var cell = row.cells[c]; + + for (checkMethod in checkCellMethods) { + expected = checkCellMethods[checkMethod](row, cell); + actual = view[checkMethod](r, columns[c]); + if (actual !== expected) { + failedMethods[checkMethod] = true; + is( + actual, + expected, + testid + + "row " + + r + + " column " + + c + + " " + + checkMethod + + " is incorrect" + ); + } + } + } + + // compare row properties + for (checkMethod in checkRowMethods) { + expected = checkRowMethods[checkMethod](row, r); + if (checkMethod == "hasNextSibling") { + actual = view[checkMethod](r, r); + } else { + actual = view[checkMethod](r); + } + if (actual !== expected) { + failedMethods[checkMethod] = true; + is( + actual, + expected, + testid + "row " + r + " " + checkMethod + " is incorrect" + ); + } + } + /* + // open and recurse into containers + if (row.container) { + view.toggleOpenState(r); + if (!view.isContainerOpen(r)) { + toggleOpenStateOK = false; + is(view.isContainerOpen(r), true, testid + "row " + r + " toggleOpenState open"); + } + testtag_tree_TreeView_rows(tree, testid + "container " + r + " ", row.children, r + 1); + view.toggleOpenState(r); + if (view.isContainerOpen(r)) { + toggleOpenStateOK = false; + is(view.isContainerOpen(r), false, testid + "row " + r + " toggleOpenState close"); + } + } +*/ + } + + for (var failedMethod in failedMethods) { + if (failedMethod in checkRowMethods) { + delete checkRowMethods[failedMethod]; + } + if (failedMethod in checkCellMethods) { + delete checkCellMethods[failedMethod]; + } + } + + for (checkMethod in checkRowMethods) { + is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod); + } + for (checkMethod in checkCellMethods) { + is(checkMethod + " ok", checkMethod + " ok", testid + checkMethod); + } + if (toggleOpenStateOK) { + is("toggleOpenState ok", "toggleOpenState ok", testid + "toggleOpenState"); + } +} + +function testtag_tree_TreeView_rows_sort(tree, testid, rowInfo) { + // check if cycleHeader sorts the columns + var columnIndex = 0; + var view = tree.view; + var column = tree.columns[columnIndex]; + var columnElement = column.element; + var sortkey = columnElement.getAttribute("sort"); + if (sortkey) { + view.cycleHeader(column); + is(tree.getAttribute("sort"), sortkey, "cycleHeader sort"); + is( + tree.getAttribute("sortDirection"), + "ascending", + "cycleHeader sortDirection ascending" + ); + is( + columnElement.getAttribute("sortDirection"), + "ascending", + "cycleHeader column sortDirection" + ); + is( + columnElement.getAttribute("sortActive"), + "true", + "cycleHeader column sortActive" + ); + view.cycleHeader(column); + is( + tree.getAttribute("sortDirection"), + "descending", + "cycleHeader sortDirection descending" + ); + is( + columnElement.getAttribute("sortDirection"), + "descending", + "cycleHeader column sortDirection descending" + ); + view.cycleHeader(column); + is( + tree.getAttribute("sortDirection"), + "", + "cycleHeader sortDirection natural" + ); + is( + columnElement.getAttribute("sortDirection"), + "", + "cycleHeader column sortDirection natural" + ); + // XXXndeakin content view isSorted needs to be tested + } + + // Check that clicking on column header sorts the column. + var columns = getSortedColumnArray(tree); + is( + columnElement.getAttribute("sortDirection"), + "", + "cycleHeader column sortDirection" + ); + + // Click once on column header and check sorting has cycled once. + mouseClickOnColumnHeader(columns, columnIndex, 0, 1); + is( + columnElement.getAttribute("sortDirection"), + "ascending", + "single click cycleHeader column sortDirection ascending" + ); + + // Now simulate a double click. + mouseClickOnColumnHeader(columns, columnIndex, 0, 2); + if (navigator.platform.indexOf("Win") == 0) { + // Windows cycles only once on double click. + is( + columnElement.getAttribute("sortDirection"), + "descending", + "double click cycleHeader column sortDirection descending" + ); + // 1 single clicks should restore natural sorting. + mouseClickOnColumnHeader(columns, columnIndex, 0, 1); + } + + // Check we have gone back to natural sorting. + is( + columnElement.getAttribute("sortDirection"), + "", + "cycleHeader column sortDirection" + ); + + columnElement.setAttribute("sorthints", "twostate"); + view.cycleHeader(column); + is( + tree.getAttribute("sortDirection"), + "ascending", + "cycleHeader sortDirection ascending twostate" + ); + view.cycleHeader(column); + is( + tree.getAttribute("sortDirection"), + "descending", + "cycleHeader sortDirection ascending twostate" + ); + view.cycleHeader(column); + is( + tree.getAttribute("sortDirection"), + "ascending", + "cycleHeader sortDirection ascending twostate again" + ); + columnElement.removeAttribute("sorthints"); + view.cycleHeader(column); + view.cycleHeader(column); + + is( + columnElement.getAttribute("sortDirection"), + "", + "cycleHeader column sortDirection reset" + ); +} + +// checks if the current and selected rows are correct +// current is the index of the current row +// selected is an array of the indicies of the selected rows +// viewidx is the row that should be visible at the top of the tree +function testtag_tree_TreeSelection_State( + tree, + testid, + current, + selected, + viewidx +) { + var selection = tree.view.selection; + + is(selection.count, selected.length, testid + " count"); + is(tree.currentIndex, current, testid + " currentIndex"); + is(selection.currentIndex, current, testid + " TreeSelection currentIndex"); + if (viewidx !== null && viewidx !== undefined) { + is(tree.getFirstVisibleRow(), viewidx, testid + " first visible row"); + } + + var actualSelected = []; + var count = tree.view.rowCount; + for (var s = 0; s < count; s++) { + if (selection.isSelected(s)) { + actualSelected.push(s); + } + } + + is( + compareArrays(selected, actualSelected), + true, + testid + " selection [" + selected + "]" + ); + + actualSelected = []; + var rangecount = selection.getRangeCount(); + for (var r = 0; r < rangecount; r++) { + var start = {}, + end = {}; + selection.getRangeAt(r, start, end); + for (var rs = start.value; rs <= end.value; rs++) { + actualSelected.push(rs); + } + } + + is( + compareArrays(selected, actualSelected), + true, + testid + " range selection [" + selected + "]" + ); +} + +function testtag_tree_column_reorder() { + // Make sure the tree is scrolled into the view, otherwise the test will + // fail + var testframe = window.parent.document.getElementById("testframe"); + if (testframe) { + testframe.scrollIntoView(); + } + + var tree = document.getElementById("tree-column-reorder"); + var numColumns = tree.columns.count; + + var reference = []; + for (let i = 0; i < numColumns; i++) { + reference.push("col_" + i); + } + + // Drag the first column to each position + for (let i = 0; i < numColumns - 1; i++) { + synthesizeColumnDrag(tree, i, i + 1, true); + arrayMove(reference, i, i + 1, true); + checkColumns(tree, reference, "drag first column right"); + } + + // And back + for (let i = numColumns - 1; i >= 1; i--) { + synthesizeColumnDrag(tree, i, i - 1, false); + arrayMove(reference, i, i - 1, false); + checkColumns(tree, reference, "drag last column left"); + } + + // Drag each column one column left + for (let i = 1; i < numColumns; i++) { + synthesizeColumnDrag(tree, i, i - 1, false); + arrayMove(reference, i, i - 1, false); + checkColumns(tree, reference, "drag each column left"); + } + + // And back + for (let i = numColumns - 2; i >= 0; i--) { + synthesizeColumnDrag(tree, i, i + 1, true); + arrayMove(reference, i, i + 1, true); + checkColumns(tree, reference, "drag each column right"); + } + + // Drag each column 5 to the right + for (let i = 0; i < numColumns - 5; i++) { + synthesizeColumnDrag(tree, i, i + 5, true); + arrayMove(reference, i, i + 5, true); + checkColumns(tree, reference, "drag each column 5 to the right"); + } + + // And to the left + for (let i = numColumns - 6; i >= 5; i--) { + synthesizeColumnDrag(tree, i, i - 5, false); + arrayMove(reference, i, i - 5, false); + checkColumns(tree, reference, "drag each column 5 to the left"); + } + + // Test that moving a column after itself does not move anything + synthesizeColumnDrag(tree, 0, 0, true); + checkColumns(tree, reference, "drag to itself"); + is(document.treecolDragging, null, "drag to itself completed"); + + // XXX roc should this be here??? + SimpleTest.finish(); +} + +function testtag_tree_wheel(aTree) { + const deltaModes = [ + WheelEvent.DOM_DELTA_PIXEL, // 0 + WheelEvent.DOM_DELTA_LINE, // 1 + WheelEvent.DOM_DELTA_PAGE, // 2 + ]; + function helper(aStart, aDelta, aIntDelta, aDeltaMode) { + aTree.scrollToRow(aStart); + var expected; + if (!aIntDelta) { + expected = aStart; + } else if (aDeltaMode != WheelEvent.DOM_DELTA_PAGE) { + expected = aStart + aIntDelta; + } else if (aIntDelta > 0) { + expected = aStart + aTree.getPageLength(); + } else { + expected = aStart - aTree.getPageLength(); + } + + if (expected < 0) { + expected = 0; + } + if (expected > aTree.view.rowCount - aTree.getPageLength()) { + expected = aTree.view.rowCount - aTree.getPageLength(); + } + synthesizeWheel(aTree.body, 1, 1, { + deltaMode: aDeltaMode, + deltaY: aDelta, + lineOrPageDeltaY: aIntDelta, + }); + is( + aTree.getFirstVisibleRow(), + expected, + "testtag_tree_wheel: vertical, starting " + + aStart + + " delta " + + aDelta + + " lineOrPageDelta " + + aIntDelta + + " aDeltaMode " + + aDeltaMode + ); + + aTree.scrollToRow(aStart); + // Check that horizontal scrolling has no effect + synthesizeWheel(aTree.body, 1, 1, { + deltaMode: aDeltaMode, + deltaX: aDelta, + lineOrPageDeltaX: aIntDelta, + }); + is( + aTree.getFirstVisibleRow(), + aStart, + "testtag_tree_wheel: horizontal, starting " + + aStart + + " delta " + + aDelta + + " lineOrPageDelta " + + aIntDelta + + " aDeltaMode " + + aDeltaMode + ); + } + + var defaultPrevented = 0; + + function wheelListener(event) { + defaultPrevented++; + } + window.addEventListener("wheel", wheelListener); + + deltaModes.forEach(function (aDeltaMode) { + var delta = aDeltaMode == WheelEvent.DOM_DELTA_PIXEL ? 5.0 : 0.3; + helper(2, -delta, 0, aDeltaMode); + helper(2, -delta, -1, aDeltaMode); + helper(2, delta, 0, aDeltaMode); + helper(2, delta, 1, aDeltaMode); + helper(2, -2 * delta, 0, aDeltaMode); + helper(2, -2 * delta, -1, aDeltaMode); + helper(2, 2 * delta, 0, aDeltaMode); + helper(2, 2 * delta, 1, aDeltaMode); + }); + + window.removeEventListener("wheel", wheelListener); + is(defaultPrevented, 48, "wheel event default prevented"); +} + +async function testtag_tree_scroll() { + const tree = document.querySelector("tree"); + + info("Scroll down with the content scrollbar at the top"); + await doScrollTest({ + tree, + initialTreeScrollRow: 0, + initialContainerScrollTop: 0, + scrollDelta: 10, + isTreeScrollExpected: true, + }); + + info("Scroll down with the content scrollbar at the middle"); + await doScrollTest({ + tree, + initialTreeScrollRow: 3, + initialContainerScrollTop: 0, + scrollDelta: 10, + isTreeScrollExpected: true, + }); + + info("Scroll down with the content scrollbar at the bottom"); + await doScrollTest({ + tree, + initialTreeScrollRow: 9, + initialContainerScrollTop: 0, + scrollDelta: 10, + isTreeScrollExpected: false, + }); + + info("Scroll up with the content scrollbar at the bottom"); + await doScrollTest({ + tree, + initialTreeScrollRow: 9, + initialContainerScrollTop: 50, + scrollDelta: -10, + isTreeScrollExpected: true, + }); + + info("Scroll up with the content scrollbar at the middle"); + await doScrollTest({ + tree, + initialTreeScrollRow: 5, + initialContainerScrollTop: 50, + scrollDelta: -10, + isTreeScrollExpected: true, + }); + + info("Scroll up with the content scrollbar at the top"); + await doScrollTest({ + tree, + initialTreeScrollRow: 0, + initialContainerScrollTop: 50, + scrollDelta: -10, + isTreeScrollExpected: false, + }); + + info("Check whether the tree is not scrolled when the parent is scrolling"); + await doScrollWhileScrollingParent(tree); + + info( + "Check whether the tree component consumes wheel events even if the scroll is located at edge as long as the events are handled as the same series" + ); + await doScrollInSameSeries({ + tree, + initialTreeScrollRow: 0, + initialContainerScrollTop: 0, + scrollDelta: 10, + }); + await doScrollInSameSeries({ + tree, + initialTreeScrollRow: 9, + initialContainerScrollTop: 50, + scrollDelta: -10, + }); + + SimpleTest.finish(); +} + +async function doScrollInSameSeries({ + tree, + initialTreeScrollRow, + initialContainerScrollTop, + scrollDelta, +}) { + // Set enough value to mousewheel.scroll_series_timeout pref to ensure the wheel + // event fired as the same series. + Services.prefs.setIntPref("mousewheel.scroll_series_timeout", 1000); + + const scrollbar = tree.shadowRoot.querySelector( + "scrollbar[orient='vertical']" + ); + const parent = tree.parentElement; + + tree.scrollToRow(initialTreeScrollRow); + parent.scrollTop = initialContainerScrollTop; + + // Scroll until the scrollbar was moved to the specified amount. + await SimpleTest.promiseWaitForCondition(async () => { + await nativeScroll(tree, 10, 10, scrollDelta); + const curpos = scrollbar.getAttribute("curpos"); + return ( + (scrollDelta < 0 && curpos == 0) || + (scrollDelta > 0 && curpos == scrollbar.getAttribute("maxpos")) + ); + }); + + // More scroll as the same series. + for (let i = 0; i < 10; i++) { + await nativeScroll(tree, 10, 10, scrollDelta); + } + + is( + parent.scrollTop, + initialContainerScrollTop, + "The wheel events are condumed in tree component" + ); + const utils = SpecialPowers.getDOMWindowUtils(window); + ok(!utils.getWheelScrollTarget(), "The parent should not handle the event"); + + Services.prefs.clearUserPref("mousewheel.scroll_series_timeout"); +} + +async function doScrollWhileScrollingParent(tree) { + // Set enough value to mousewheel.scroll_series_timeout pref to ensure the wheel + // event fired as the same series. + Services.prefs.setIntPref("mousewheel.scroll_series_timeout", 1000); + + const scrollbar = tree.shadowRoot.querySelector( + "scrollbar[orient='vertical']" + ); + const parent = tree.parentElement; + + // Set initial scroll amount. + tree.scrollToRow(0); + parent.scrollTop = 0; + + const scrollAmount = scrollbar.getAttribute("curpos"); + + // Scroll parent from top to bottom. + await SimpleTest.promiseWaitForCondition(async () => { + await nativeScroll(parent, 10, 10, 10); + return parent.scrollTop === parent.scrollTopMax; + }); + + is( + scrollAmount, + scrollbar.getAttribute("curpos"), + "The tree should not be scrolled" + ); + + const utils = SpecialPowers.getDOMWindowUtils(window); + await SimpleTest.promiseWaitForCondition(() => !utils.getWheelScrollTarget()); + Services.prefs.clearUserPref("mousewheel.scroll_series_timeout"); +} + +async function doScrollTest({ + tree, + initialTreeScrollRow, + initialContainerScrollTop, + scrollDelta, + isTreeScrollExpected, +}) { + const scrollbar = tree.shadowRoot.querySelector( + "scrollbar[orient='vertical']" + ); + const container = tree.parentElement; + + // Set initial scroll amount. + tree.scrollToRow(initialTreeScrollRow); + container.scrollTop = initialContainerScrollTop; + + const treeScrollAmount = scrollbar.getAttribute("curpos"); + const containerScrollAmount = container.scrollTop; + + // Wait until changing either scroll. + await SimpleTest.promiseWaitForCondition(async () => { + await nativeScroll(tree, 10, 10, scrollDelta); + return ( + treeScrollAmount !== scrollbar.getAttribute("curpos") || + containerScrollAmount !== container.scrollTop + ); + }); + + is( + treeScrollAmount !== scrollbar.getAttribute("curpos"), + isTreeScrollExpected, + "Scroll of tree is expected" + ); + is( + containerScrollAmount !== container.scrollTop, + !isTreeScrollExpected, + "Scroll of container is expected" + ); + + // Wait until finishing wheel scroll transaction. + const utils = SpecialPowers.getDOMWindowUtils(window); + await SimpleTest.promiseWaitForCondition(() => !utils.getWheelScrollTarget()); +} + +async function nativeScroll(component, offsetX, offsetY, scrollDelta) { + const utils = SpecialPowers.getDOMWindowUtils(window); + const x = component.screenX + offsetX; + const y = component.screenY + offsetX; + + // Mouse move event. + await new Promise(resolve => { + window.addEventListener("mousemove", resolve, { once: true }); + utils.sendNativeMouseEvent( + x, + y, + utils.NATIVE_MOUSE_MESSAGE_MOVE, + 0, + {}, + component + ); + }); + + // Wheel event. + await new Promise(resolve => { + window.addEventListener("wheel", resolve, { once: true }); + utils.sendNativeMouseScrollEvent( + x, + y, + // nativeVerticalWheelEventMsg is defined in apz_test_native_event_utils.js + // eslint-disable-next-line no-undef + nativeVerticalWheelEventMsg(), + 0, + // nativeScrollUnits is defined in apz_test_native_event_utils.js + // eslint-disable-next-line no-undef + -nativeScrollUnits(component, scrollDelta), + 0, + 0, + 0, + component + ); + }); + + // promiseApzFlushedRepaints is defined in apz_test_utils.js + // eslint-disable-next-line no-undef + await promiseApzFlushedRepaints(); +} + +function synthesizeColumnDrag( + aTree, + aMouseDownColumnNumber, + aMouseUpColumnNumber, + aAfter +) { + var columns = getSortedColumnArray(aTree); + + var down = columns[aMouseDownColumnNumber].element; + var up = columns[aMouseUpColumnNumber].element; + + // Target the initial mousedown in the middle of the column header so we + // avoid the extra hit test space given to the splitter + var columnWidth = down.getBoundingClientRect().width; + var splitterHitWidth = columnWidth / 2; + synthesizeMouse(down, splitterHitWidth, 3, { type: "mousedown" }); + + var offsetX = 0; + if (aAfter) { + offsetX = columnWidth; + } + + if (aMouseUpColumnNumber > aMouseDownColumnNumber) { + for (let i = aMouseDownColumnNumber; i <= aMouseUpColumnNumber; i++) { + let move = columns[i].element; + synthesizeMouse(move, offsetX, 3, { type: "mousemove" }); + } + } else { + for (let i = aMouseDownColumnNumber; i >= aMouseUpColumnNumber; i--) { + let move = columns[i].element; + synthesizeMouse(move, offsetX, 3, { type: "mousemove" }); + } + } + + synthesizeMouse(up, offsetX, 3, { type: "mouseup" }); +} + +function arrayMove(aArray, aFrom, aTo, aAfter) { + var o = aArray.splice(aFrom, 1)[0]; + if (aTo > aFrom) { + aTo--; + } + + if (aAfter) { + aTo++; + } + + aArray.splice(aTo, 0, o); +} + +function getSortedColumnArray(aTree) { + var columns = aTree.columns; + var array = []; + for (let i = 0; i < columns.length; i++) { + array.push(columns.getColumnAt(i)); + } + + array.sort(function (a, b) { + var o1 = parseInt(a.element.style.order); + var o2 = parseInt(b.element.style.order); + return o1 - o2; + }); + return array; +} + +function checkColumns(aTree, aReference, aMessage) { + var columns = getSortedColumnArray(aTree); + var ids = []; + columns.forEach(function (e) { + ids.push(e.element.id); + }); + is(compareArrays(ids, aReference), true, aMessage); +} + +function mouseOnCell(tree, row, column, testname) { + var rect = tree.getCoordsForCellItem(row, column, "text"); + + synthesizeMouseExpectEvent( + tree.body, + rect.x, + rect.y, + {}, + tree, + "select", + testname + ); +} + +function mouseClickOnColumnHeader( + aColumns, + aColumnIndex, + aButton, + aClickCount +) { + var columnHeader = aColumns[aColumnIndex].element; + var columnHeaderRect = columnHeader.getBoundingClientRect(); + var columnWidth = columnHeaderRect.right - columnHeaderRect.left; + // For multiple click we send separate click events, with increasing + // clickCount. This simulates the common behavior of multiple clicks. + for (let i = 1; i <= aClickCount; i++) { + // Target the middle of the column header. + synthesizeMouse(columnHeader, columnWidth / 2, 3, { + button: aButton, + clickCount: i, + }); + } +} + +function mouseDblClickOnCell(tree, row, column, testname) { + // select the row we will edit + var selection = tree.view.selection; + selection.select(row); + tree.ensureRowIsVisible(row); + + // get cell coordinates + var rect = tree.getCoordsForCellItem(row, column, "text"); + + synthesizeMouse(tree.body, rect.x, rect.y, { clickCount: 2 }); +} + +function compareArrays(arr1, arr2) { + if (arr1.length != arr2.length) { + return false; + } + + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] != arr2[i]) { + return false; + } + } + + return true; +} + +function convertDOMtoTreeRowInfo(treechildren, level, rowidx) { + var obj = { rows: [] }; + + var parentidx = rowidx.value; + + treechildren = treechildren.childNodes; + for (var r = 0; r < treechildren.length; r++) { + rowidx.value++; + + var treeitem = treechildren[r]; + if (treeitem.hasChildNodes()) { + var treerow = treeitem.firstChild; + var cellInfo = []; + for (var c = 0; c < treerow.childNodes.length; c++) { + var cell = treerow.childNodes[c]; + cellInfo.push({ + label: "" + cell.getAttribute("label"), + value: cell.getAttribute("value"), + properties: cell.getAttribute("properties"), + editable: cell.getAttribute("editable") != "false", + selectable: cell.getAttribute("selectable") != "false", + image: cell.getAttribute("src"), + mode: cell.hasAttribute("mode") + ? parseInt(cell.getAttribute("mode")) + : 3, + }); + } + + var descendants = treeitem.lastChild; + var children = + treerow == descendants + ? null + : convertDOMtoTreeRowInfo(descendants, level + 1, rowidx); + obj.rows.push({ + cells: cellInfo, + properties: treerow.getAttribute("properties"), + container: treeitem.getAttribute("container") == "true", + separator: treeitem.localName == "treeseparator", + children, + level, + parent: parentidx, + }); + } + } + + return obj; +} diff --git a/toolkit/content/tests/widgets/video.ogg b/toolkit/content/tests/widgets/video.ogg new file mode 100644 index 0000000000..ac7ece3519 Binary files /dev/null and b/toolkit/content/tests/widgets/video.ogg differ diff --git a/toolkit/content/tests/widgets/videocontrols_direction-1-ref.html b/toolkit/content/tests/widgets/videocontrols_direction-1-ref.html new file mode 100644 index 0000000000..1f7e76a7d0 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-1-ref.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-1a.html b/toolkit/content/tests/widgets/videocontrols_direction-1a.html new file mode 100644 index 0000000000..a4d3546294 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-1a.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-1b.html b/toolkit/content/tests/widgets/videocontrols_direction-1b.html new file mode 100644 index 0000000000..a14b11d5ff --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-1b.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-1c.html b/toolkit/content/tests/widgets/videocontrols_direction-1c.html new file mode 100644 index 0000000000..0885ebd893 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-1c.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-1d.html b/toolkit/content/tests/widgets/videocontrols_direction-1d.html new file mode 100644 index 0000000000..a39accec72 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-1d.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-1e.html b/toolkit/content/tests/widgets/videocontrols_direction-1e.html new file mode 100644 index 0000000000..25e7c2c1f9 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-1e.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-2-ref.html b/toolkit/content/tests/widgets/videocontrols_direction-2-ref.html new file mode 100644 index 0000000000..630177883c --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-2-ref.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-2a.html b/toolkit/content/tests/widgets/videocontrols_direction-2a.html new file mode 100644 index 0000000000..2e40cdc1a7 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-2a.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-2b.html b/toolkit/content/tests/widgets/videocontrols_direction-2b.html new file mode 100644 index 0000000000..2e4dadb6ff --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-2b.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-2c.html b/toolkit/content/tests/widgets/videocontrols_direction-2c.html new file mode 100644 index 0000000000..a43b03e8f9 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-2c.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-2d.html b/toolkit/content/tests/widgets/videocontrols_direction-2d.html new file mode 100644 index 0000000000..52d56f1ccd --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-2d.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction-2e.html b/toolkit/content/tests/widgets/videocontrols_direction-2e.html new file mode 100644 index 0000000000..58bc30e2b3 --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction-2e.html @@ -0,0 +1,10 @@ + + + + + + + +
+ + diff --git a/toolkit/content/tests/widgets/videocontrols_direction_test.js b/toolkit/content/tests/widgets/videocontrols_direction_test.js new file mode 100644 index 0000000000..e937f06b3f --- /dev/null +++ b/toolkit/content/tests/widgets/videocontrols_direction_test.js @@ -0,0 +1,119 @@ +// This file expects `tests` to have been declared in the global scope. +/* global tests */ + +var RemoteCanvas = function (url, id) { + this.url = url; + this.id = id; + this.snapshot = null; +}; + +RemoteCanvas.CANVAS_WIDTH = 200; +RemoteCanvas.CANVAS_HEIGHT = 200; + +RemoteCanvas.prototype.compare = function (otherCanvas, expected) { + return compareSnapshots(this.snapshot, otherCanvas.snapshot, expected)[0]; +}; + +RemoteCanvas.prototype.load = function (callback) { + var iframe = document.createElement("iframe"); + iframe.id = this.id + "-iframe"; + iframe.width = RemoteCanvas.CANVAS_WIDTH + "px"; + iframe.height = RemoteCanvas.CANVAS_HEIGHT + "px"; + iframe.src = this.url; + var me = this; + iframe.addEventListener("load", function () { + info("iframe loaded"); + var m = iframe.contentDocument.getElementById("av"); + m.addEventListener( + "suspend", + function (aEvent) { + setTimeout(function () { + let mediaElement = + iframe.contentDocument.querySelector("audio, video"); + const { widget } = SpecialPowers.wrap(iframe.contentWindow) + .windowGlobalChild.getActor("UAWidgets") + .widgets.get(mediaElement); + widget.impl.Utils.l10n.translateRoots().then(() => { + me.remotePageLoaded(callback); + }); + }, 0); + }, + { once: true } + ); + m.src = m.getAttribute("source"); + }); + window.document.body.appendChild(iframe); +}; + +RemoteCanvas.prototype.remotePageLoaded = function (callback) { + var ldrFrame = document.getElementById(this.id + "-iframe"); + this.snapshot = snapshotWindow(ldrFrame.contentWindow); + this.snapshot.id = this.id + "-canvas"; + window.document.body.appendChild(this.snapshot); + callback(this); +}; + +RemoteCanvas.prototype.cleanup = function () { + var iframe = document.getElementById(this.id + "-iframe"); + iframe.remove(); + var canvas = document.getElementById(this.id + "-canvas"); + canvas.remove(); +}; + +function runTest(index) { + var canvases = []; + function testCallback(canvas) { + canvases.push(canvas); + + if (canvases.length == 2) { + // when both canvases are loaded + var expectedEqual = currentTest.op == "=="; + var result = canvases[0].compare(canvases[1], expectedEqual); + ok( + result, + "Rendering of reftest " + + currentTest.test + + " should " + + (expectedEqual ? "not " : "") + + "be different to the reference" + ); + + if (result) { + canvases[0].cleanup(); + canvases[1].cleanup(); + } else { + info("Snapshot of canvas 1: " + canvases[0].snapshot.toDataURL()); + info("Snapshot of canvas 2: " + canvases[1].snapshot.toDataURL()); + } + + if (index < tests.length - 1) { + runTest(index + 1); + } else { + SimpleTest.finish(); + } + } + } + + var currentTest = tests[index]; + var testCanvas = new RemoteCanvas(currentTest.test, "test-" + index); + testCanvas.load(testCallback); + + var refCanvas = new RemoteCanvas(currentTest.ref, "ref-" + index); + refCanvas.load(testCallback); +} + +SimpleTest.waitForExplicitFinish(); +SimpleTest.requestCompleteLog(); + +window.addEventListener( + "load", + function () { + SpecialPowers.pushPrefEnv( + { set: [["media.cache_size", 40000]] }, + function () { + runTest(0); + } + ); + }, + true +); diff --git a/toolkit/content/tests/widgets/videomask.css b/toolkit/content/tests/widgets/videomask.css new file mode 100644 index 0000000000..066d441388 --- /dev/null +++ b/toolkit/content/tests/widgets/videomask.css @@ -0,0 +1,23 @@ +html, body { + margin: 0; + padding: 0; +} + +audio, video { + width: 140px; + height: 100px; + background-color: black; +} + +/** + * Create a mask for the video direction tests which covers up the throbber. + */ +#mask { + position: absolute; + z-index: 3; + width: 140px; + height: 72px; + background-color: green; + top: 0; + right: 0; +} diff --git a/toolkit/content/tests/widgets/window_label_checkbox.xhtml b/toolkit/content/tests/widgets/window_label_checkbox.xhtml new file mode 100644 index 0000000000..e1a8258b92 --- /dev/null +++ b/toolkit/content/tests/widgets/window_label_checkbox.xhtml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + diff --git a/toolkit/content/tests/widgets/window_menubar.xhtml b/toolkit/content/tests/widgets/window_menubar.xhtml new file mode 100644 index 0000000000..c4ced844ad --- /dev/null +++ b/toolkit/content/tests/widgets/window_menubar.xhtml @@ -0,0 +1,1015 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Outside menubar + + + + + + + -- cgit v1.2.3