зеркало из https://github.com/mozilla/gecko-dev.git
Bug 485157: SMIL event timing, part 6 repeat timing, r=dholbert, sr=roc, a=roc
This commit is contained in:
Родитель
9896ec8948
Коммит
a146a4eac7
|
@ -1320,6 +1320,7 @@ GK_ATOM(onrepeat, "onrepeat")
|
|||
GK_ATOM(onrepeatEvent, "onrepeatEvent")
|
||||
GK_ATOM(repeatCount, "repeatCount")
|
||||
GK_ATOM(repeatDur, "repeatDur")
|
||||
GK_ATOM(repeatEvent, "repeatEvent")
|
||||
GK_ATOM(restart, "restart")
|
||||
GK_ATOM(to, "to")
|
||||
GK_ATOM(XML, "XML")
|
||||
|
|
|
@ -60,7 +60,9 @@ const PRUint32 MSEC_PER_MIN = 1000 * 60;
|
|||
const PRUint32 MSEC_PER_HOUR = 1000 * 60 * 60;
|
||||
const PRInt32 DECIMAL_BASE = 10;
|
||||
|
||||
#define ACCESSKEY_PREFIX NS_LITERAL_STRING("accesskey(")
|
||||
// XXX SVG/SMIL Animation use 'accessKey' whilst SMIL3 uses 'accesskey'
|
||||
// We should allow both
|
||||
#define ACCESSKEY_PREFIX NS_LITERAL_STRING("accessKey(")
|
||||
#define REPEAT_PREFIX NS_LITERAL_STRING("repeat(")
|
||||
#define WALLCLOCK_PREFIX NS_LITERAL_STRING("wallclock(")
|
||||
|
||||
|
@ -363,6 +365,9 @@ ParseElementBaseTimeValueSpec(const nsAString& aSpec,
|
|||
// element-name.repeat(3)
|
||||
// event\.name
|
||||
//
|
||||
// Technically `repeat(3)' is permitted but the behaviour in this case is not
|
||||
// defined (for SMIL Animation) so we don't support it here.
|
||||
//
|
||||
|
||||
const PRUnichar* tokenStart = aSpec.BeginReading();
|
||||
const PRUnichar* tokenEnd = GetTokenEnd(aSpec, PR_TRUE);
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "nsIEventListenerManager.h"
|
||||
#include "nsIDOMEventGroup.h"
|
||||
#include "nsGUIEvent.h"
|
||||
#include "nsIDOMTimeEvent.h"
|
||||
#include "nsString.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
@ -117,6 +118,11 @@ nsSMILTimeValueSpec::SetSpec(const nsAString& aStringSpec,
|
|||
mOwner->AddInstanceTime(new nsSMILInstanceTime(mParams.mOffset), mIsBegin);
|
||||
}
|
||||
|
||||
// Fill in the event symbol to simplify handling later
|
||||
if (mParams.mType == nsSMILTimeValueSpecParams::REPEAT) {
|
||||
mParams.mEventSymbol = nsGkAtoms::repeatEvent;
|
||||
}
|
||||
|
||||
ResolveReferences(aContextNode);
|
||||
|
||||
return rv;
|
||||
|
@ -126,7 +132,8 @@ void
|
|||
nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
|
||||
{
|
||||
if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE &&
|
||||
!IsEventBased())
|
||||
mParams.mType != nsSMILTimeValueSpecParams::EVENT &&
|
||||
mParams.mType != nsSMILTimeValueSpecParams::REPEAT)
|
||||
return;
|
||||
|
||||
NS_ABORT_IF_FALSE(aContextNode,
|
||||
|
@ -145,11 +152,11 @@ nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
|
|||
if (mParams.mDependentElemID) {
|
||||
mReferencedElement.ResetWithID(aContextNode,
|
||||
nsDependentAtomString(mParams.mDependentElemID));
|
||||
} else if (IsEventBased()) {
|
||||
} else if (mParams.mType == nsSMILTimeValueSpecParams::EVENT) {
|
||||
Element* target = mOwner->GetTargetElement();
|
||||
mReferencedElement.ResetWithElement(target);
|
||||
} else {
|
||||
NS_ABORT_IF_FALSE(PR_FALSE, "Syncbase element without ID");
|
||||
NS_ABORT_IF_FALSE(PR_FALSE, "Syncbase or repeat spec without ID");
|
||||
}
|
||||
UpdateReferencedElement(oldReferencedElement, mReferencedElement.get());
|
||||
}
|
||||
|
@ -257,13 +264,25 @@ nsSMILTimeValueSpec::UpdateReferencedElement(Element* aFrom, Element* aTo)
|
|||
|
||||
UnregisterFromReferencedElement(aFrom);
|
||||
|
||||
if (mParams.mType == nsSMILTimeValueSpecParams::SYNCBASE) {
|
||||
nsSMILTimedElement* to = GetTimedElement(aTo);
|
||||
if (to) {
|
||||
to->AddDependent(*this);
|
||||
switch (mParams.mType)
|
||||
{
|
||||
case nsSMILTimeValueSpecParams::SYNCBASE:
|
||||
{
|
||||
nsSMILTimedElement* to = GetTimedElement(aTo);
|
||||
if (to) {
|
||||
to->AddDependent(*this);
|
||||
}
|
||||
}
|
||||
} else if (mParams.mType == nsSMILTimeValueSpecParams::EVENT) {
|
||||
break;
|
||||
|
||||
case nsSMILTimeValueSpecParams::EVENT:
|
||||
case nsSMILTimeValueSpecParams::REPEAT:
|
||||
RegisterEventListener(aTo);
|
||||
break;
|
||||
|
||||
default:
|
||||
// not a referencing-type or not yet supported
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,7 +298,7 @@ nsSMILTimeValueSpec::UnregisterFromReferencedElement(Element* aElement)
|
|||
timedElement->RemoveDependent(*this);
|
||||
}
|
||||
mOwner->RemoveInstanceTimesForCreator(this, mIsBegin);
|
||||
} else if (mParams.mType == nsSMILTimeValueSpecParams::EVENT) {
|
||||
} else if (IsEventBased()) {
|
||||
UnregisterEventListener(aElement);
|
||||
}
|
||||
}
|
||||
|
@ -300,8 +319,10 @@ nsSMILTimeValueSpec::GetTimedElement(Element* aElement)
|
|||
void
|
||||
nsSMILTimeValueSpec::RegisterEventListener(Element* aTarget)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mParams.mType == nsSMILTimeValueSpecParams::EVENT,
|
||||
"Attempting to register event-listener for non-event nsSMILTimeValueSpec");
|
||||
NS_ABORT_IF_FALSE(mParams.mType == nsSMILTimeValueSpecParams::EVENT ||
|
||||
mParams.mType == nsSMILTimeValueSpecParams::REPEAT,
|
||||
"Attempting to register event-listener for unexpected nsSMILTimeValueSpec"
|
||||
" type");
|
||||
NS_ABORT_IF_FALSE(mParams.mEventSymbol,
|
||||
"Attempting to register event-listener but there is no event name");
|
||||
|
||||
|
@ -367,8 +388,9 @@ void
|
|||
nsSMILTimeValueSpec::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mEventListener, "Got event without an event listener");
|
||||
NS_ABORT_IF_FALSE(mParams.mType == nsSMILTimeValueSpecParams::EVENT,
|
||||
"Got event for non-event nsSMILTimeValueSpec");
|
||||
NS_ABORT_IF_FALSE(IsEventBased(),
|
||||
"Got event for non-event nsSMILTimeValueSpec");
|
||||
NS_ABORT_IF_FALSE(aEvent, "No event supplied");
|
||||
|
||||
// XXX In the long run we should get the time from the event itself which will
|
||||
// store the time in global document time which we'll need to convert to our
|
||||
|
@ -377,6 +399,9 @@ nsSMILTimeValueSpec::HandleEvent(nsIDOMEvent* aEvent)
|
|||
if (!container)
|
||||
return;
|
||||
|
||||
if (!CheckEventDetail(aEvent))
|
||||
return;
|
||||
|
||||
nsSMILTime currentTime = container->GetCurrentTime();
|
||||
nsSMILTimeValue newTime(currentTime + mParams.mOffset.GetMillis());
|
||||
|
||||
|
@ -385,6 +410,23 @@ nsSMILTimeValueSpec::HandleEvent(nsIDOMEvent* aEvent)
|
|||
mOwner->AddInstanceTime(newInstance, mIsBegin);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsSMILTimeValueSpec::CheckEventDetail(nsIDOMEvent *aEvent)
|
||||
{
|
||||
if (mParams.mType != nsSMILTimeValueSpecParams::REPEAT)
|
||||
return PR_TRUE;
|
||||
|
||||
nsCOMPtr<nsIDOMTimeEvent> timeEvent = do_QueryInterface(aEvent);
|
||||
if (!timeEvent) {
|
||||
NS_WARNING("Received a repeat event that was not a DOMTimeEvent");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRInt32 detail;
|
||||
timeEvent->GetDetail(&detail);
|
||||
return detail == mParams.mRepeatIterationOrAccessKey;
|
||||
}
|
||||
|
||||
nsSMILTimeValue
|
||||
nsSMILTimeValueSpec::ConvertBetweenTimeContainers(
|
||||
const nsSMILTimeValue& aSrcTime,
|
||||
|
|
|
@ -98,6 +98,7 @@ protected:
|
|||
nsIEventListenerManager* GetEventListenerManager(Element* aElement,
|
||||
nsIDOMEventGroup** aSystemGroup);
|
||||
void HandleEvent(nsIDOMEvent* aEvent);
|
||||
PRBool CheckEventDetail(nsIDOMEvent* aEvent);
|
||||
nsSMILTimeValue ConvertBetweenTimeContainers(const nsSMILTimeValue& aSrcTime,
|
||||
const nsSMILTimeContainer* aSrcContainer);
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ _TEST_FILES = \
|
|||
test_smilGetSimpleDuration.xhtml \
|
||||
test_smilKeySplines.xhtml \
|
||||
test_smilKeyTimesPacedMode.xhtml \
|
||||
test_smilRepeatTiming.xhtml \
|
||||
test_smilSetCurrentTime.xhtml \
|
||||
test_smilSync.xhtml \
|
||||
test_smilSyncbaseTarget.xhtml \
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=485157
|
||||
-->
|
||||
<head>
|
||||
<title>Test repeat timing</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=485157">Mozilla Bug
|
||||
485157</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px">
|
||||
<rect width="100" height="100" fill="green">
|
||||
<set attributeName="width" to="100" dur="20s" repeatCount="5" begin="0s"
|
||||
id="a" onrepeat="startWaiting(evt)"/>
|
||||
<set attributeName="fill" attributeType="CSS" to="green"
|
||||
begin="a.repeat(1)" onbegin="expectedBegin()" dur="20s"/>
|
||||
<set attributeName="x" to="100"
|
||||
begin="a.repeat(2)" onbegin="unexpectedBegin(this)" dur="20s"/>
|
||||
<set attributeName="y" to="100"
|
||||
begin="a.repeat(0)" onbegin="unexpectedBegin(this)" dur="20s"/>
|
||||
<set attributeName="width" to="100"
|
||||
begin="a.repeat(-1)" onbegin="unexpectedBegin(this)" dur="20s"/>
|
||||
<set attributeName="height" to="100"
|
||||
begin="a.repeat(a)" onbegin="unexpectedBegin(this)" dur="20s"/>
|
||||
</rect>
|
||||
</svg>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
<![CDATA[
|
||||
/** Test SMIL repeat timing **/
|
||||
|
||||
/* Global Variables */
|
||||
const gTimeoutDur = 5000; // Time until we give up waiting for events in ms
|
||||
var gSvg = document.getElementById('svg');
|
||||
var gRect = document.getElementById('circle');
|
||||
var gTimeoutID;
|
||||
var gGotBegin = false;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function testBegin()
|
||||
{
|
||||
gSvg.setCurrentTime(19.999);
|
||||
}
|
||||
|
||||
function startWaiting(evt)
|
||||
{
|
||||
is(evt.detail, 1, "Unexpected repeat event received: test broken");
|
||||
if (gGotBegin)
|
||||
return;
|
||||
|
||||
gTimeoutID = setTimeout(timeoutFail, gTimeoutDur);
|
||||
}
|
||||
|
||||
function timeoutFail()
|
||||
{
|
||||
ok(false, "Timed out waiting for begin event");
|
||||
finish();
|
||||
}
|
||||
|
||||
function expectedBegin()
|
||||
{
|
||||
is(gGotBegin, false,
|
||||
"Got begin event more than once for non-repeating animation");
|
||||
gGotBegin = true;
|
||||
clearTimeout(gTimeoutID);
|
||||
// Wait a moment before finishing in case there are erroneous events waiting
|
||||
// to be processed.
|
||||
setTimeout(finish, 10);
|
||||
}
|
||||
|
||||
function unexpectedBegin(elem)
|
||||
{
|
||||
ok(false, "Got unexpected begin from animation with spec: " +
|
||||
elem.getAttribute('begin'));
|
||||
}
|
||||
|
||||
function finish()
|
||||
{
|
||||
gSvg.pauseAnimations();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("load", testBegin, false);
|
||||
]]>
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче