gecko-dev/widget/tests/test_assign_event_data.html
Jim Chen e559b260ee Bug 1137567 - Make nsWindow for Android use TextEventDispatcher; r=esawin r=rbarker r=masayuki r=snorp
Bug 1137567 - 1. Allow dispatching key events during composition; r=esawin

We potentially dispatch key events during composition to provide
compatibility for pages that only listen to key events.

Bug 1137567 - 2. Allow keyboard events in DispatchInputEvent when not on APZ thread; r=rbarker

We use nsIWidget::DispatchInputEvent to dispatch our keyboard events on
the Gecko thread, which on Android is not the APZ controller thread. We
should allow these events to pass instead of crashing.

Bug 1137567 - 3. Add GeckoEditableSupport class to support TextEventDispatcher; r=masayuki

Add a separate GeckoEditableSupport class, which implements
TextEventDispatcherListener and uses TextEventDispatcher for IME
operations. The new class is entirely separate from nsWindow to allow it
to be independently used in content processes as well.

Most of the code is copied from nsWindow::GeckoViewSupport, and adapted
to use TextEventDispatcher.

Bug 1137567 - 4. Make nsWindow::WindowPtr available for outside classes; r=snorp

Make nsWindow::WindowPtr available not just for classes inside nsWindow
but for outside classes as well. Also, add support for RefPtr native
objects to nsWindow::NativePtr.

Bug 1137567 - 5. Use GeckoEditableSupport in nsWindow; r=esawin

Use the new GeckoEditableSupport class in nsWindow to replace the
previous code in nsWindow::GeckoViewSupport. GeckoEditable native
methods now go to GeckoEditableSupport instead of GeckoViewSupport.

Several native methods in GeckoEditable are changed from
dispatchTo="proxy" to dispatchTo="gecko", because we no longer need the
special nsWindow::WindowEvent wrapper for our native calls.

Bug 1137567 - 6. Use pushPrefEnv in test_assign_event_data.html; r=masayuki

setAndObserveCompositionPref in test_assign_event_data.html does not
invoke the callback if the pref is already set. This patch changes it to
use SpecialPowers.pushPrefEnv so the callback is always invoked.
2017-03-01 15:29:30 -05:00

740 lines
24 KiB
HTML

<!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">&nbsp;</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";
if (aValue === null) {
SpecialPowers.pushPrefEnv({"clear": [[pref]]}, aCallback);
} else {
SpecialPowers.pushPrefEnv({"set": [[pref, aValue]]}, aCallback);
}
}
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>