<!DOCTYPE html> <html> <head> <title>Testing ns*Event::Assign*EventData()</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> <style> #a { background-color: transparent; transition: background-color 0.1s linear; } #a:focus { background-color: red; } .slidin { border: green 1px solid; width: 10px; height: 10px; animation-name: slidein; animation-duration: 1s; } @keyframes slidein { from { margin-left: 100%; } to { margin-left: 0; } } #pointer-target { border: 1px dashed red; background: yellow; margin: 0px 10px; padding: 0px 10px; } #scrollable-div { background: green; overflow: auto; width: 30px; height: 30px; } #scrolled-div { background: magenta; width: 10px; height: 10px; } #form { background: silver; padding: 0px 10px; } #animated-div { background: cyan; padding: 0px 10px; } </style> </head> <body> <div id="display"> <input id="input-text"> <button id="button">button</button> <a id="a" href="about:blank">hyper link</a> <span id="pointer-target">span</span> <div id="scrollable-div"><div id="scrolled-div"></div></div> <form id="form">form</form> <div id="animated-div"> </div> </div> <div id="content" style="display: none"> </div> <pre id="test"> </pre> <script class="testbody" type="application/javascript"> SimpleTest.waitForExplicitFinish(); SimpleTest.expectAssertions(0, 34); const kIsMac = (navigator.platform.indexOf("Mac") == 0); const kIsWin = (navigator.platform.indexOf("Win") == 0); var gEvent = null; var gCopiedEvent = []; var gCallback = null; var gCallPreventDefault = false; function onEvent(aEvent) { if (gCallPreventDefault) { aEvent.preventDefault(); } gEvent = aEvent; for (var attr in aEvent) { if (!attr.match(/^[A-Z0-9_]+$/) && // ignore const attributes attr != "multipleActionsPrevented" && // multipleActionsPrevented isn't defined in any DOM event specs. typeof(aEvent[attr]) != "function") { gCopiedEvent.push({ name: attr, value: aEvent[attr]}); } } setTimeout(gCallback, 0); } const kTests = [ { description: "InternalScrollPortEvent (overflow, vertical)", targetID: "scrollable-div", eventType: "overflow", dispatchEvent: function () { document.getElementById("scrolled-div").style.height = "500px"; }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalScrollPortEvent (overflow, horizontal)", targetID: "scrollable-div", eventType: "overflow", dispatchEvent: function () { document.getElementById("scrolled-div").style.width = "500px"; }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalScrollAreaEvent (MozScrolledAreaChanged, spreading)", target: function () { return document; }, eventType: "MozScrolledAreaChanged", dispatchEvent: function () { document.getElementById("scrollable-div").style.width = "50000px"; document.getElementById("scrollable-div").style.height = "50000px"; }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalScrollAreaEvent (MozScrolledAreaChanged, shrinking)", target: function () { return document; }, eventType: "MozScrolledAreaChanged", dispatchEvent: function () { document.getElementById("scrollable-div").style.width = "30px"; document.getElementById("scrollable-div").style.height = "30px"; }, canRun: function () { return true; }, todoMismatch: [], }, { description: "WidgetKeyboardEvent (keydown of 'a' key without modifiers)", targetID: "input-text", eventType: "keydown", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_A : MAC_VK_ANSI_A, {}, "a", "a"); }, canRun: function () { return (kIsMac || kIsWin); }, todoMismatch: [], }, { description: "WidgetKeyboardEvent (keyup of 'a' key without modifiers)", targetID: "input-text", eventType: "keydown", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_A : MAC_VK_ANSI_A, {}, "a", "a"); }, canRun: function () { return (kIsMac || kIsWin); }, todoMismatch: [], }, { description: "WidgetKeyboardEvent (keypress of 'b' key with Shift)", targetID: "input-text", eventType: "keypress", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_B : MAC_VK_ANSI_B, { shiftKey: true }, "B", "B"); }, canRun: function () { return (kIsMac || kIsWin); }, todoMismatch: [], }, { description: "WidgetKeyboardEvent (keypress of 'c' key with Accel)", targetID: "input-text", eventType: "keypress", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_C : MAC_VK_ANSI_C, { accelKey: true }, kIsWin ? "\u0003" : "c", "c"); }, canRun: function () { return (kIsMac || kIsWin); }, todoMismatch: [], }, { description: "WidgetKeyboardEvent (keyup during composition)", targetID: "input-text", eventType: "keyup", dispatchEvent: function () { setAndObserveCompositionPref(true, () => { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeCompositionChange({ "composition": { "string": "\u306D", "clauses": [ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE } ] }, "caret": { "start": 1, "length": 0 }, "key": { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A }, }); synthesizeComposition({ type: "compositioncommitasis" }); setAndObserveCompositionPref(null, runNextTest); }); return true; }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetKeyboardEvent (keydown during composition)", targetID: "input-text", eventType: "keydown", dispatchEvent: function () { setAndObserveCompositionPref(true, () => { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeCompositionChange({ "composition": { "string": "\u306D", "clauses": [ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE } ] }, "caret": { "start": 1, "length": 0 } }); synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter", code: "Enter" } }); setAndObserveCompositionPref(null, runNextTest); }); return true; }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetMouseEvent (mousedown of left button without modifier)", targetID: "button", eventType: "mousedown", dispatchEvent: function () { synthesizeMouseAtCenter(document.getElementById(this.targetID), { button: 0 }); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "WidgetMouseEvent (click of middle button with Shift)", // XXX I'm not sure why middle click event isn't fired on button element. targetID: "input-text", eventType: "click", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeMouseAtCenter(document.getElementById(this.targetID), { button: 1, shiftKey: true, pressure: 0.5 }); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "WidgetMouseEvent (mouseup of right button with Alt)", targetID: "button", eventType: "mouseup", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeMouseAtCenter(document.getElementById(this.targetID), { button: 2, altKey: true }); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "WidgetDragEvent", targetID: "input-text", eventType: "dragstart", dispatchEvent: function () { return; }, canRun: function () { todo(false, "WidgetDragEvent isn't tested"); return false; }, todoMismatch: [], }, { description: "WidgetTextEvent (text)", targetID: "input-text", eventType: "text", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeComposition({ type: "compositioncommit", data: "\u306D" }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetCompositionEvent (compositionupdate)", targetID: "input-text", eventType: "compositionupdate", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeComposition({ type: "compositioncommit", data: "\u30E9\u30FC\u30E1\u30F3" }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "InternalEditorInputEvent (input at key input)", targetID: "input-text", eventType: "input", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_B : MAC_VK_ANSI_B, { shiftKey: true }, "B", "B"); }, canRun: function () { return (kIsMac || kIsWin); }, todoMismatch: [], }, { description: "InternalEditorInputEvent (input at composing)", targetID: "input-text", eventType: "input", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; document.getElementById(this.targetID).focus(); synthesizeCompositionChange({ "composition": { "string": "\u30E9\u30FC\u30E1\u30F3", "clauses": [ { "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE } ] }, "caret": { "start": 4, "length": 0 } }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "InternalEditorInputEvent (input at committing)", targetID: "input-text", eventType: "input", dispatchEvent: function () { synthesizeComposition({ type: "compositioncommitasis" }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetMouseScrollEvent (DOMMouseScroll, vertical)", targetID: "input-text", eventType: "DOMMouseScroll", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeWheel(document.getElementById(this.targetID), 3, 4, { deltaY: 30, lineOrPageDeltaY: 2 }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetMouseScrollEvent (DOMMouseScroll, horizontal)", targetID: "input-text", eventType: "DOMMouseScroll", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeWheel(document.getElementById(this.targetID), 4, 5, { deltaX: 30, lineOrPageDeltaX: 2, shiftKey: true }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetMouseScrollEvent (MozMousePixelScroll, vertical)", targetID: "input-text", eventType: "MozMousePixelScroll", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeWheel(document.getElementById(this.targetID), 3, 4, { deltaY: 20, lineOrPageDeltaY: 1, altKey: true }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetMouseScrollEvent (MozMousePixelScroll, horizontal)", targetID: "input-text", eventType: "MozMousePixelScroll", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeWheel(document.getElementById(this.targetID), 4, 5, { deltaX: 20, lineOrPageDeltaX: 1, ctrlKey: true }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetWheelEvent (wheel, vertical)", targetID: "input-text", eventType: "wheel", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeWheel(document.getElementById(this.targetID), 3, 4, { deltaY: 20, lineOrPageDeltaY: 1, altKey: true }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetWheelEvent (wheel, horizontal)", targetID: "input-text", eventType: "wheel", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeWheel(document.getElementById(this.targetID), 4, 5, { deltaX: 20, lineOrPageDeltaX: 1, ctrlKey: true }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetWheelEvent (wheel, both)", targetID: "input-text", eventType: "wheel", dispatchEvent: function () { document.getElementById(this.targetID).value = ""; synthesizeWheel(document.getElementById(this.targetID), 4, 5, { deltaX: 20, deltaY: 10, lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetTouchEvent (touchstart)", target: function () { return document; }, eventType: "touchstart", dispatchEvent: function () { synthesizeTouchAtPoint(1, 2, { id: 10, rx: 4, ry: 3, angle: 0, force: 1, shiftKey: true}); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetTouchEvent (touchend)", target: function () { return document; }, eventType: "touchend", dispatchEvent: function () { synthesizeTouchAtPoint(4, 6, { id: 5, rx: 1, ry: 2, angle: 0.5, force: 0.8, ctrlKey: true}); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "InternalFormEvent (reset)", targetID: "form", eventType: "reset", dispatchEvent: function () { document.getElementById("form").reset(); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "WidgetCommandEvent", targetID: "input-text", eventType: "", dispatchEvent: function () { return; }, canRun: function () { todo(false, "WidgetCommandEvent isn't tested"); return false; }, todoMismatch: [], }, { description: "InternalClipboardEvent (copy)", targetID: "input-text", eventType: "copy", dispatchEvent: function () { document.getElementById("input-text").value = "go to clipboard!"; document.getElementById("input-text").focus(); document.getElementById("input-text").select(); synthesizeKey("c", { accelKey: true }); }, canRun: function () { return true; }, todoMismatch: [ ], }, { description: "InternalUIEvent (DOMActivate)", targetID: "button", eventType: "DOMActivate", dispatchEvent: function () { synthesizeMouseAtCenter(document.getElementById(this.targetID), { button: 0, shiftKey: true }); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalFocusEvent (focus)", targetID: "input-text", eventType: "focus", dispatchEvent: function () { document.getElementById(this.targetID).focus(); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalFocusEvent (blur)", targetID: "input-text", eventType: "blur", dispatchEvent: function () { document.getElementById(this.targetID).blur(); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "WidgetSimpleGestureEvent", targetID: "", eventType: "", dispatchEvent: function () { return; }, canRun: function () { // Simple gesture event may be handled before it comes content. // So, we cannot test it in this test. todo(false, "WidgetSimpleGestureEvent isn't tested"); return false; }, todoMismatch: [], }, { description: "InternalTransitionEvent (transitionend)", targetID: "a", eventType: "transitionend", dispatchEvent: function () { document.getElementById(this.targetID).focus(); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalAnimationEvent (animationend)", targetID: "animated-div", eventType: "animationend", dispatchEvent: function () { document.getElementById(this.targetID).className = "slidin"; }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalMutationEvent (DOMAttrModified)", targetID: "animated-div", eventType: "DOMAttrModified", dispatchEvent: function () { document.getElementById(this.targetID).setAttribute("x-data", "foo"); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalMutationEvent (DOMNodeInserted)", targetID: "animated-div", eventType: "DOMNodeInserted", dispatchEvent: function () { var div = document.createElement("div"); div.id = "inserted-div"; document.getElementById("animated-div").appendChild(div); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "InternalMutationEvent (DOMNodeRemoved)", targetID: "animated-div", eventType: "DOMNodeRemoved", dispatchEvent: function () { document.getElementById("animated-div").removeChild(document.getElementById("inserted-div")); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "PointerEvent (pointerdown)", targetID: "pointer-target", eventType: "pointerdown", dispatchEvent: function () { var elem = document.getElementById(this.targetID); var rect = elem.getBoundingClientRect(); synthesizePointer(elem, rect.width/2, rect.height/2, { type: this.eventType, button: 1, clickCount: 1, inputSource: 2, pressure: 0.25, isPrimary: true }); }, canRun: function () { return true; }, todoMismatch: [], }, { description: "PointerEvent (pointerup)", targetID: "pointer-target", eventType: "pointerup", dispatchEvent: function () { var elem = document.getElementById(this.targetID); var rect = elem.getBoundingClientRect(); synthesizePointer(elem, rect.width/2, rect.height/2, { type: this.eventType, button: -1, ctrlKey: true, shiftKey: true, altKey: true, isSynthesized: false }); }, canRun: function () { return true; }, todoMismatch: [], }, ]; /** * Sets or clears dom.keyboardevent.dispatch_during_composition and calls the * given callback when the change is observed. * * @param aValue * Pass null to clear the pref. Otherwise pass a bool. * @param aCallback * Called when the pref change is observed. */ function setAndObserveCompositionPref(aValue, aCallback) { let pref = "dom.keyboardevent.dispatch_during_composition"; let branch = SpecialPowers.Cc["@mozilla.org/preferences-service;1"]. getService(SpecialPowers.Ci.nsIPrefService). getBranch(pref); let obs = SpecialPowers.wrapCallback(function () { branch.removeObserver("", obs); // Make sure the code under test sees the change first, so executeSoon(). SimpleTest.executeSoon(aCallback); }); branch.addObserver("", obs, false); if (aValue === null) { SpecialPowers.clearUserPref(pref); } else { SpecialPowers.setBoolPref(pref, aValue); } } function doTest(aTest) { if (!aTest.canRun()) { SimpleTest.executeSoon(runNextTest); return; } gEvent = null; gCopiedEvent = []; var target = aTest.target ? aTest.target() : document.getElementById(aTest.targetID); target.addEventListener(aTest.eventType, onEvent, true); gCallback = function () { var description = aTest.description + " (gCallPreventDefault=" + gCallPreventDefault + ")"; target.removeEventListener(aTest.eventType, onEvent, true); ok(gEvent !== null, description + ": failed to get duplicated event"); ok(gCopiedEvent.length > 0, description + ": count of attribute of the event must be larger than 0"); for (var i = 0; i < gCopiedEvent.length; ++i) { var name = gCopiedEvent[i].name; if (name == "rangeOffset") { todo(false, description + ": " + name + " attribute value is never reset (" + gEvent[name] + ")"); } else if (name == "eventPhase") { is(gEvent[name], 0, description + ": mismatch with fixed value (" + name + ")"); } else if (name == "rangeParent" || name == "currentTarget") { is(gEvent[name], null, description + ": mismatch with fixed value (" + name + ")"); } else if (aTest.todoMismatch.indexOf(name) >= 0) { todo_is(gEvent[name], gCopiedEvent[i].value, description + ": mismatch (" + name + ")"); } else if (name == "offsetX" || name == "offsetY") { // do nothing; these are defined to return different values during event dispatch // vs not during event dispatch } else { is(gEvent[name], gCopiedEvent[i].value, description + ": mismatch (" + name + ")"); } } if (!testWillCallRunNextTest) { runNextTest(); } }; var testWillCallRunNextTest = aTest.dispatchEvent(); } var gIndex = -1; function runNextTest() { if (++gIndex == kTests.length) { if (gCallPreventDefault) { finish(); return; } // Test with a call of preventDefault() of the events. gCallPreventDefault = true; gIndex = -1; // Restoring the initial state of all elements. document.getElementById("scrollable-div").style.height = "30px"; document.getElementById("scrollable-div").style.width = "30px"; document.getElementById("scrolled-div").style.height = "10px"; document.getElementById("scrolled-div").style.width = "10px"; document.getElementById("input-text").value = ""; document.getElementById("animated-div").className = ""; document.getElementById("animated-div").removeAttribute("x-data"); if (document.activeElement) { document.activeElement.blur(); } window.requestAnimationFrame(function () { setTimeout(runNextTest, 0); }); return; } doTest(kTests[gIndex]); } function init() { SpecialPowers.pushPrefEnv({"set":[["middlemouse.contentLoadURL", false], ["middlemouse.paste", false], ["general.autoScroll", false], ["mousewheel.default.action", 0], ["mousewheel.default.action.override_x", -1], ["mousewheel.with_shift.action", 0], ["mousewheel.with_shift.action.override_x", -1], ["mousewheel.with_control.action", 0], ["mousewheel.with_control.action.override_x", -1], ["mousewheel.with_alt.action", 0], ["mousewheel.with_alt.action.override_x", -1], ["mousewheel.with_meta.action", 0], ["mousewheel.with_meta.action.override_x", -1]]}, runNextTest); } function finish() { SimpleTest.finish(); } SimpleTest.waitForFocus(init); </script> </body>