Bug 587910 - SVG SMIL accessKey timing, r=dholbert,smaug; sr=roc; a=roc

This commit is contained in:
Brian Birtles 2010-08-28 15:41:05 +09:00
parent b6108f1817
commit ae0dbb0db8
9 changed files with 527 additions and 19 deletions

View File

@ -60,9 +60,8 @@ const PRUint32 MSEC_PER_MIN = 1000 * 60;
const PRUint32 MSEC_PER_HOUR = 1000 * 60 * 60;
const PRInt32 DECIMAL_BASE = 10;
// XXX SVG/SMIL Animation use 'accessKey' whilst SMIL3 uses 'accesskey'
// We should allow both
#define ACCESSKEY_PREFIX NS_LITERAL_STRING("accessKey(")
#define ACCESSKEY_PREFIX_LC NS_LITERAL_STRING("accesskey(") // SMIL2+
#define ACCESSKEY_PREFIX_CC NS_LITERAL_STRING("accessKey(") // SVG/SMIL ANIM
#define REPEAT_PREFIX NS_LITERAL_STRING("repeat(")
#define WALLCLOCK_PREFIX NS_LITERAL_STRING("wallclock(")
@ -264,13 +263,17 @@ ParseOptionalOffset(const nsAString& aSpec, nsSMILTimeValueSpecParams& aResult)
nsresult
ParseAccessKey(const nsAString& aSpec, nsSMILTimeValueSpecParams& aResult)
{
NS_ABORT_IF_FALSE(StringBeginsWith(aSpec, ACCESSKEY_PREFIX),
NS_ABORT_IF_FALSE(StringBeginsWith(aSpec, ACCESSKEY_PREFIX_CC) ||
StringBeginsWith(aSpec, ACCESSKEY_PREFIX_LC),
"Calling ParseAccessKey on non-accesskey-type spec");
nsSMILTimeValueSpecParams result;
result.mType = nsSMILTimeValueSpecParams::ACCESSKEY;
const PRUnichar* start = aSpec.BeginReading() + ACCESSKEY_PREFIX.Length();
NS_ABORT_IF_FALSE(
ACCESSKEY_PREFIX_LC.Length() == ACCESSKEY_PREFIX_CC.Length(),
"Case variations for accesskey prefix differ in length");
const PRUnichar* start = aSpec.BeginReading() + ACCESSKEY_PREFIX_LC.Length();
const PRUnichar* end = aSpec.EndReading();
// Expecting at least <accesskey> + ')'
@ -703,7 +706,8 @@ nsSMILParserUtils::ParseTimeValueSpecParams(const nsAString& aSpec,
}
// accesskey type
else if (StringBeginsWith(spec, ACCESSKEY_PREFIX)) {
else if (StringBeginsWith(spec, ACCESSKEY_PREFIX_LC) ||
StringBeginsWith(spec, ACCESSKEY_PREFIX_CC)) {
rv = ParseAccessKey(spec, aResult);
}

View File

@ -121,6 +121,8 @@ nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
// Fill in the event symbol to simplify handling later
if (mParams.mType == nsSMILTimeValueSpecParams::REPEAT) {
mParams.mEventSymbol = nsGkAtoms::repeatEvent;
} else if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
mParams.mEventSymbol = nsGkAtoms::keypress;
}
ResolveReferences(aContextNode);
@ -131,9 +133,7 @@ nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
void
nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
{
if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE &&
mParams.mType != nsSMILTimeValueSpecParams::EVENT &&
mParams.mType != nsSMILTimeValueSpecParams::REPEAT)
if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE && !IsEventBased())
return;
NS_ABORT_IF_FALSE(aContextNode,
@ -155,6 +155,10 @@ nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
} else if (mParams.mType == nsSMILTimeValueSpecParams::EVENT) {
Element* target = mOwner->GetTargetElement();
mReferencedElement.ResetWithElement(target);
} else if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
nsIDocument* doc = aContextNode->GetCurrentDoc();
NS_ABORT_IF_FALSE(doc, "We are in the document but current doc is null");
mReferencedElement.ResetWithElement(doc->GetRootElement());
} else {
NS_ABORT_IF_FALSE(PR_FALSE, "Syncbase or repeat spec without ID");
}
@ -277,11 +281,12 @@ nsSMILTimeValueSpec::UpdateReferencedElement(Element* aFrom, Element* aTo)
case nsSMILTimeValueSpecParams::EVENT:
case nsSMILTimeValueSpecParams::REPEAT:
case nsSMILTimeValueSpecParams::ACCESSKEY:
RegisterEventListener(aTo);
break;
default:
// not a referencing-type or not yet supported
// not a referencing-type
break;
}
}
@ -319,8 +324,7 @@ nsSMILTimeValueSpec::GetTimedElement(Element* aElement)
void
nsSMILTimeValueSpec::RegisterEventListener(Element* aTarget)
{
NS_ABORT_IF_FALSE(mParams.mType == nsSMILTimeValueSpecParams::EVENT ||
mParams.mType == nsSMILTimeValueSpecParams::REPEAT,
NS_ABORT_IF_FALSE(IsEventBased(),
"Attempting to register event-listener for unexpected nsSMILTimeValueSpec"
" type");
NS_ABORT_IF_FALSE(mParams.mEventSymbol,
@ -373,7 +377,23 @@ nsSMILTimeValueSpec::GetEventListenerManager(Element* aTarget,
NS_ABORT_IF_FALSE(aSystemGroup && !*aSystemGroup,
"Bad out param for system group");
nsIEventListenerManager* elm = aTarget->GetListenerManager(PR_TRUE);
nsCOMPtr<nsPIDOMEventTarget> piTarget;
if (mParams.mType == nsSMILTimeValueSpecParams::ACCESSKEY) {
nsIDocument* doc = aTarget->GetCurrentDoc();
if (!doc)
return nsnull;
nsPIDOMWindow* win = doc->GetWindow();
if (!win)
return nsnull;
piTarget = do_QueryInterface(win);
} else {
piTarget = aTarget;
}
if (!piTarget)
return nsnull;
nsIEventListenerManager* elm = piTarget->GetListenerManager(PR_TRUE);
if (!elm)
return nsnull;
@ -413,9 +433,23 @@ nsSMILTimeValueSpec::HandleEvent(nsIDOMEvent* aEvent)
PRBool
nsSMILTimeValueSpec::CheckEventDetail(nsIDOMEvent *aEvent)
{
if (mParams.mType != nsSMILTimeValueSpecParams::REPEAT)
return PR_TRUE;
switch (mParams.mType)
{
case nsSMILTimeValueSpecParams::REPEAT:
return CheckRepeatEventDetail(aEvent);
case nsSMILTimeValueSpecParams::ACCESSKEY:
return CheckAccessKeyEventDetail(aEvent);
default:
// nothing to check
return PR_TRUE;
}
}
PRBool
nsSMILTimeValueSpec::CheckRepeatEventDetail(nsIDOMEvent *aEvent)
{
nsCOMPtr<nsIDOMTimeEvent> timeEvent = do_QueryInterface(aEvent);
if (!timeEvent) {
NS_WARNING("Received a repeat event that was not a DOMTimeEvent");
@ -424,7 +458,64 @@ nsSMILTimeValueSpec::CheckEventDetail(nsIDOMEvent *aEvent)
PRInt32 detail;
timeEvent->GetDetail(&detail);
return detail == mParams.mRepeatIterationOrAccessKey;
return detail > 0 && (PRUint32)detail == mParams.mRepeatIterationOrAccessKey;
}
PRBool
nsSMILTimeValueSpec::CheckAccessKeyEventDetail(nsIDOMEvent *aEvent)
{
nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aEvent);
if (!keyEvent) {
NS_WARNING("Received an accesskey event that was not a DOMKeyEvent");
return PR_FALSE;
}
// Ignore the key event if any modifier keys are pressed UNLESS we're matching
// on the charCode in which case we ignore the state of the shift and alt keys
// since they might be needed to generate the character in question.
PRBool isCtrl;
PRBool isMeta;
keyEvent->GetCtrlKey(&isCtrl);
keyEvent->GetMetaKey(&isMeta);
if (isCtrl || isMeta)
return PR_FALSE;
PRUint32 code;
keyEvent->GetCharCode(&code);
if (code)
return code == mParams.mRepeatIterationOrAccessKey;
// Only match on the keyCode if it corresponds to some ASCII character that
// does not produce a charCode.
// In this case we can safely bail out if either alt or shift is pressed since
// they won't already be incorporated into the keyCode unlike the charCode.
PRBool isAlt;
PRBool isShift;
keyEvent->GetAltKey(&isAlt);
keyEvent->GetShiftKey(&isShift);
if (isAlt || isShift)
return PR_FALSE;
keyEvent->GetKeyCode(&code);
switch (code)
{
case nsIDOMKeyEvent::DOM_VK_BACK_SPACE:
return mParams.mRepeatIterationOrAccessKey == 0x08;
case nsIDOMKeyEvent::DOM_VK_RETURN:
case nsIDOMKeyEvent::DOM_VK_ENTER:
return mParams.mRepeatIterationOrAccessKey == 0x0A ||
mParams.mRepeatIterationOrAccessKey == 0x0D;
case nsIDOMKeyEvent::DOM_VK_ESCAPE:
return mParams.mRepeatIterationOrAccessKey == 0x1B;
case nsIDOMKeyEvent::DOM_VK_DELETE:
return mParams.mRepeatIterationOrAccessKey == 0x7F;
default:
return PR_FALSE;
}
}
nsSMILTimeValue

View File

@ -99,6 +99,8 @@ protected:
nsIDOMEventGroup** aSystemGroup);
void HandleEvent(nsIDOMEvent* aEvent);
PRBool CheckEventDetail(nsIDOMEvent* aEvent);
PRBool CheckRepeatEventDetail(nsIDOMEvent* aEvent);
PRBool CheckAccessKeyEventDetail(nsIDOMEvent* aEvent);
nsSMILTimeValue ConvertBetweenTimeContainers(const nsSMILTimeValue& aSrcTime,
const nsSMILTimeContainer* aSrcContainer);

View File

@ -54,6 +54,7 @@ _TEST_FILES = \
smilAnimateMotionValueLists.js \
smilTestUtils.js \
smilXHR_helper.svg \
test_smilAccessKey.xhtml \
test_smilAnimateMotion.xhtml \
test_smilAnimateMotionInvalidValues.xhtml \
test_smilAnimateMotionOverrideRules.xhtml \

View File

@ -0,0 +1,360 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test for SMIL accessKey support</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=587910">Mozilla Bug
587910</a>
<p id="display"></p>
<div id="content" style="display: none">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px">
<circle cx="20" cy="20" r="15" fill="blue" id="circle"/>
</svg>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[
/** Test for SMIL accessKey support **/
const gSvgns = "http://www.w3.org/2000/svg";
var gSvg = document.getElementById("svg");
SimpleTest.waitForExplicitFinish();
function main()
{
gSvg.pauseAnimations();
// Basic syntax
testOk('accessKey(a)', 'a');
testOk(' accessKey(a) ', 'a');
testNotOk('accessKey (a)', 'a');
testNotOk('accessKey( a)', 'a');
testNotOk('accessKey(a )', 'a');
testNotOk('accessKey(a)', 'b');
testNotOk('accessKey()', ' ');
// Test the test framework itself
testOk('accessKey(a)', 97);
// Allow for either accessKey (SVG / SMIL Animation) or accesskey (SMIL2+)
testOk('accesskey(a)', 'a');
// Offset
testOk('accessKey(a)+0s', 'a');
testOk('accessKey(a) + 0min', 'a');
testOk('accessKey(a) -0h', 'a');
testOk('accessKey(a)+100ms', 'a', 0, 0.1);
testOk('accessKey(a)-0.1s', 'a', 0, -0.1);
// Id references are not allowed
testNotOk('svg.accessKey(a)', 'a');
testNotOk('window.accessKey(a)', 'a');
// Case sensitivity
testOk('accessKey(A)', 'A');
testNotOk('accessKey(a)', 'A');
testNotOk('accessKey(A)', 'a');
// Test unusual characters
testOk('accessKey(-)', '-');
testOk('accessKey(\\)', '\\');
testOk('accessKey( )', ' ');
testOk('accessKey(\x0D)', 0, 13); // Return
testOk('accessKey(\x0D)', 0, 14); // Enter
testOk('accessKey(\n)', 0, 13); // New line
testOk('accessKey(\r)', 0, 13); // Carriage return
testOk('accessKey(\x08)', 0, 8); // Backspace
testOk('accessKey(\x1B)', 0, 0x1B); // Escape
testOk('accessKey(\x7F)', 0, 46); // Del
// Check some disallowed keys
// -- For now we don't allow tab since the interaction with focus causes
// confusing results
testNotOk('accessKey(\x09)', 0, 9); // Tab
// Test setting the keyCode field
testNotOk('accessKey(a)', 0, 97);
testOk('accessKey(a)', 97, 66); // Give priority to charCode field
testNotOk('accessKey(a)', 98, 97); // Give priority to charCode field
// Test unicode
testOk("accessKey(\u20AC)", 8364); // euro-symbol
// Test an astral character just to make sure we don't crash
testOk("accessKey(\uD835\uDC00)", 119808); // mathematical bold capital A
// 0x1D400
// Test bad surrogate pairs don't confuse us either
testNotOk("accessKey(\uD800\uD800)", 97);
testNotOk("accessKey(\uD80020)", 97);
testNotOk("accessKey(\uD800)", 97);
// Test modifiers
// -- When matching on charCode ignore shift and alt
testNotOk('accessKey(a)', 'a', 0, 0, { ctrl: true });
testNotOk('accessKey(a)', 'a', 0, 0, { meta: true });
testOk('accessKey(a)', 'a', 0, 0, { alt: true });
testOk('accessKey(a)', 'a', 0, 0, { shift: true });
testNotOk('accessKey(a)', 'a', 0, 0, { shift: true, ctrl: true });
testNotOk('accessKey(a)', 'a', 0, 0, { alt: true, meta: true });
// -- When matching on keyCode ignore all
testNotOk('accessKey(\x0D)', 0, 13, 0, { ctrl: true });
testNotOk('accessKey(\x0D)', 0, 13, 0, { meta: true });
testNotOk('accessKey(\x0D)', 0, 13, 0, { alt: true });
testNotOk('accessKey(\x0D)', 0, 13, 0, { shift: true });
testNotOk('accessKey(\x0D)', 0, 13, 0, { shift: true, ctrl: true });
testOpenEnd();
testPreventDefault();
testDispatchToWindow();
testAdoptNode();
testFauxEvent();
SimpleTest.finish();
}
function testOk(spec, charCode, keyCode, offset, modifiers)
{
if (typeof offset == 'undefined') offset = 0;
var msg = "No interval created for '" + spec +
"' with input [charCode: " + charCode + "; keyCode: " + keyCode + "]" +
getModifiersDescr(modifiers);
ok(test(spec, charCode, keyCode, offset, modifiers), msg);
}
function testNotOk(spec, charCode, keyCode, offset, modifiers)
{
if (typeof offset == 'undefined') offset = 0;
var msg = "Interval unexpectedly created for '" + spec +
"' with input [charCode: " + charCode + "; keyCode: " + keyCode + "]" +
getModifiersDescr(modifiers);
ok(!test(spec, charCode, keyCode, offset, modifiers), msg);
}
function getModifiersDescr(modifiers)
{
if (typeof modifiers != 'object')
return '';
var str = ' modifiers set:';
for (var key in modifiers) {
if (modifiers[key]) str += ' ' + key;
}
return str;
}
function test(spec, charCode, keyCode, offset, modifiers)
{
gSvg.setCurrentTime(1);
ok(gSvg.animationsPaused(), "Expected animations to be paused");
var anim = createAnim(spec);
var evt = createEvent(charCode, keyCode, modifiers);
document.getElementById('circle').dispatchEvent(evt);
var gotStartTimeOk = true;
try {
var start = anim.getStartTime();
if (offset) {
var expected = gSvg.getCurrentTime() + offset;
ok(Math.abs(expected - start) <= 0.00001,
"Unexpected start time for animation with begin: " + spec +
" got " + start + ", expected " + expected);
} else {
is(start, gSvg.getCurrentTime() + offset,
"Unexpected start time for animation with begin: " + spec);
}
} catch(e) {
is(e.code, DOMException.INVALID_STATE_ERR,
"Unexpected exception code: " + e);
gotStartTimeOk = false;
}
removeElement(anim);
return gotStartTimeOk;
}
function createAnim(beginSpec)
{
var anim = document.createElementNS(gSvgns, 'animate');
anim.setAttribute('attributeName', 'cx');
anim.setAttribute('values', '0; 100');
anim.setAttribute('dur', '10s');
anim.setAttribute('begin', beginSpec);
return document.getElementById('circle').appendChild(anim);
}
function createEvent(charCode, keyCode, modifiers)
{
if (typeof charCode == 'string') {
is(charCode.length, 1,
"If charCode is a string it should be 1 character long");
charCode = charCode.charCodeAt(0);
} else if (typeof charCode == 'undefined') {
charCode = 0;
}
args = { ctrl: false, alt: false, shift: false, meta: false };
if (typeof modifiers == 'object') {
for (var key in modifiers)
args[key] = modifiers[key];
}
if (typeof keyCode == 'undefined') keyCode = 0;
var evt = document.createEvent("KeyboardEvent");
evt.initKeyEvent("keypress", true, true, window,
args['ctrl'],
args['alt'],
args['shift'],
args['meta'],
keyCode,
charCode);
return evt;
}
function testOpenEnd()
{
// Test that an end specification with an accesskey value is treated as open
// ended
gSvg.setCurrentTime(0);
ok(gSvg.animationsPaused(), "Expected animations to be paused");
var anim = createAnim('0s; 2s');
anim.setAttribute('end', '1s; accessKey(a)');
gSvg.setCurrentTime(2);
try {
is(anim.getStartTime(), 2,
"Unexpected start time for second interval of open-ended animation");
} catch(e) {
is(e.code, DOMException.INVALID_STATE_ERR,
"Unexpected exception code:" + e.code);
ok(false, "Failed to recognise accessKey as qualifying for creating an " +
"open-ended interval");
}
removeElement(anim);
}
function testPreventDefault()
{
// SVG/SMIL don't specify what should happen if preventDefault is called on
// the keypress event. For now, for consistency with event timing we ignore
// it.
gSvg.setCurrentTime(1);
ok(gSvg.animationsPaused(), "Expected animations to be paused");
var anim = createAnim('accessKey(a)');
var evt = createEvent('a');
var circle = document.getElementById('circle');
var func = function(evt) { evt.preventDefault(); }
circle.addEventListener('keypress', func, false);
circle.dispatchEvent(evt);
try {
var start = anim.getStartTime();
} catch(e) {
ok(false, "preventDefault() cancelled accessKey handling");
}
circle.removeEventListener('keypress', func, false);
removeElement(anim);
}
function testDispatchToWindow()
{
gSvg.setCurrentTime(1);
ok(gSvg.animationsPaused(), "Expected animations to be paused");
var anim = createAnim('accessKey(a)');
var evt = createEvent('a');
window.dispatchEvent(evt);
try {
var start = anim.getStartTime();
} catch(e) {
ok(false, "Key event dispatched to the window failed to trigger " +
"accesskey handling");
}
removeElement(anim);
}
function testAdoptNode()
{
gSvg.setCurrentTime(1);
ok(gSvg.animationsPaused(), "Expected animations to be paused");
// Create a new document with an animation element
var newdoc = document.implementation.createDocument(gSvgns, 'svg', null);
var anim = newdoc.createElementNS(gSvgns, 'animate');
anim.setAttribute('attributeName', 'cx');
anim.setAttribute('values', '0; 100');
anim.setAttribute('dur', '10s');
anim.setAttribute('begin', 'accesskey(a)');
newdoc.documentElement.appendChild(anim);
// Adopt
ok(anim.ownerDocument !== document,
"Expected newly created animation to belong to a different doc");
document.adoptNode(anim);
document.getElementById('circle').appendChild(anim);
ok(anim.ownerDocument === document,
"Expected newly created animation to belong to the same doc");
var evt = createEvent('a');
// Now fire an event at the original window and check nothing happens
newdoc.dispatchEvent(evt);
try {
var start = anim.getStartTime();
ok(false, "Adopted node still receiving accesskey events from old doc");
} catch(e) {
// Ok
}
// And then fire at our window
document.dispatchEvent(evt);
try {
var start = anim.getStartTime();
} catch(e) {
ok(false, "Adopted node failed to catch accesskey event");
}
removeElement(anim);
}
function testFauxEvent()
{
// Test a non-KeyEvent labelled as a key event
gSvg.setCurrentTime(0);
ok(gSvg.animationsPaused(), "Expected animations to be paused");
var anim = createAnim('accessKey(a)');
var evt = document.createEvent("SVGEvents");
evt.initEvent("keypress", true, true);
document.getElementById('circle').dispatchEvent(evt);
// We're really just testing that the above didn't crash us, but while we're
// at it, just do a sanity check that we didn't also create an interval
try {
var start = anim.getStartTime();
ok(false, "Faux event generated interval");
} catch(e) {
// All is well
}
removeElement(anim);
}
window.addEventListener("load", main, false);
]]>
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="
document.documentElement.pauseAnimations();
document.documentElement.setCurrentTime(0);
keypress(0x20);
delayedSnapshot(2)">
<script xlink:href="event-util.js" type="text/javascript"/>
<circle id="circle" r="10"/>
<rect width="100" height="100" fill="red">
<set attributeName="fill" attributeType="CSS"
to="green" begin="accesskey(&#x20;)" dur="4s"/>
<set attributeName="width" attributeType="XML"
to="200" begin="accesskey(&#x21;)" dur="4s"/>
</rect>
</svg>

After

Width:  |  Height:  |  Size: 647 B

View File

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="
document.documentElement.pauseAnimations();
document.documentElement.setCurrentTime(0);
keypress(0x0D);
delayedSnapshot(2)">
<script xlink:href="event-util.js" type="text/javascript"/>
<circle id="circle" r="10"/>
<rect width="100" height="100" fill="red">
<set attributeName="fill" attributeType="CSS"
to="green" begin="accesskey(&#x0D;)" dur="4s"/>
</rect>
</svg>

After

Width:  |  Height:  |  Size: 544 B

View File

@ -1,21 +1,37 @@
// Allows a moment for events to be processed then performs a seek and runs
// a snapshot.
function delayedSnapshot(seekTimeInSeconds) {
function delayedSnapshot(seekTimeInSeconds)
{
// Allow time for events to be processed
window.setTimeout(finish, 10, seekTimeInSeconds);
}
function finish(seekTimeInSeconds) {
function finish(seekTimeInSeconds)
{
document.documentElement.pauseAnimations();
if (seekTimeInSeconds)
document.documentElement.setCurrentTime(seekTimeInSeconds);
document.documentElement.removeAttribute("class");
}
function click(targetId) {
function click(targetId)
{
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("click", true, true, window,
0, 0, 0, 0, 0, false, false, false, false, 0, null);
var target = document.getElementById(targetId);
target.dispatchEvent(evt);
}
function keypress(charCode)
{
var evt = document.createEvent("KeyboardEvent");
evt.initKeyEvent("keypress", true, true, window,
false, // ctrlKeyArg
false, // altKeyArg
false, // shiftKeyArg
false, // metaKeyArg
0, // keyCode
charCode);
document.documentElement.dispatchEvent(evt);
}

View File

@ -27,3 +27,5 @@
== event-target-surgery-2.svg green-box-ref.svg
== event-target-surgery-3.svg green-box-ref.svg
== event-target-non-svg-1.xhtml green-box-ref.xhtml
== accesskey-entity-1.svg green-box-ref.svg
== accesskey-entity-2.svg green-box-ref.svg